Netdev List
 help / color / mirror / Atom feed
* [PATCH 4/5] fsl/qe: Add QE TDM lib
From: Zhao Qiang @ 2016-03-30  8:50 UTC (permalink / raw)
  To: davem
  Cc: akpm, gregkh, oss, xiaobo.xie, linux-kernel, netdev, linuxppc-dev,
	Zhao Qiang
In-Reply-To: <1459327830-19829-1-git-send-email-qiang.zhao@nxp.com>

QE has module to support TDM, some other protocols
supported by QE are based on TDM.
add a qe-tdm lib, this lib provides functions to the protocols
using TDM to configurate QE-TDM.

Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com>
---
 drivers/soc/fsl/qe/Kconfig    |   4 +
 drivers/soc/fsl/qe/Makefile   |   1 +
 drivers/soc/fsl/qe/qe_tdm.c   | 271 ++++++++++++++++++++++++++++++++++++++++++
 include/soc/fsl/qe/immap_qe.h |   5 +-
 include/soc/fsl/qe/qe_tdm.h   |  94 +++++++++++++++
 5 files changed, 371 insertions(+), 4 deletions(-)
 create mode 100644 drivers/soc/fsl/qe/qe_tdm.c
 create mode 100644 include/soc/fsl/qe/qe_tdm.h

diff --git a/drivers/soc/fsl/qe/Kconfig b/drivers/soc/fsl/qe/Kconfig
index 20978f2..463cf29 100644
--- a/drivers/soc/fsl/qe/Kconfig
+++ b/drivers/soc/fsl/qe/Kconfig
@@ -31,6 +31,10 @@ config UCC
 	bool
 	default y if UCC_FAST || UCC_SLOW
 
+config QE_TDM
+	bool
+	select UCC_FAST
+
 config QE_USB
 	bool
 	default y if USB_FSL_QE
diff --git a/drivers/soc/fsl/qe/Makefile b/drivers/soc/fsl/qe/Makefile
index ffac541..2031d38 100644
--- a/drivers/soc/fsl/qe/Makefile
+++ b/drivers/soc/fsl/qe/Makefile
@@ -6,5 +6,6 @@ obj-$(CONFIG_CPM)	+= qe_common.o
 obj-$(CONFIG_UCC)	+= ucc.o
 obj-$(CONFIG_UCC_SLOW)	+= ucc_slow.o
 obj-$(CONFIG_UCC_FAST)	+= ucc_fast.o
+obj-$(CONFIG_QE_TDM)	+= qe_tdm.o
 obj-$(CONFIG_QE_USB)	+= usb.o
 obj-$(CONFIG_QE_GPIO)	+= gpio.o
diff --git a/drivers/soc/fsl/qe/qe_tdm.c b/drivers/soc/fsl/qe/qe_tdm.c
new file mode 100644
index 0000000..9a2374d
--- /dev/null
+++ b/drivers/soc/fsl/qe/qe_tdm.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2015 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Authors:	Zhao Qiang <qiang.zhao@nxp.com>
+ *
+ * Description:
+ * QE TDM API Set - TDM specific routines implementations.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <soc/fsl/qe/qe_tdm.h>
+
+static enum tdm_framer_t set_tdm_framer(const char *tdm_framer_type)
+{
+	if (strcmp(tdm_framer_type, "e1") == 0)
+		return TDM_FRAMER_E1;
+	else
+		return TDM_FRAMER_T1;
+}
+
+static void set_si_param(struct ucc_tdm *utdm, struct ucc_tdm_info *ut_info)
+{
+	struct si_mode_info *si_info = &ut_info->si_info;
+
+	if (utdm->tdm_mode == TDM_INTERNAL_LOOPBACK) {
+		si_info->simr_crt = 1;
+		si_info->simr_rfsd = 0;
+	}
+}
+
+int ucc_of_parse_tdm(struct device_node *np, struct ucc_tdm *utdm,
+		     struct ucc_tdm_info *ut_info)
+{
+	const char *sprop;
+	int ret = 0;
+	u32 val;
+	struct resource *res;
+	struct device_node *np2;
+	static int siram_init_flag;
+	struct platform_device *pdev;
+
+	sprop = of_get_property(np, "fsl,rx-sync-clock", NULL);
+	if (sprop) {
+		ut_info->uf_info.rx_sync = qe_clock_source(sprop);
+		if ((ut_info->uf_info.rx_sync < QE_CLK_NONE) ||
+		    (ut_info->uf_info.rx_sync > QE_RSYNC_PIN)) {
+			pr_err("QE-TDM: Invalid rx-sync-clock property\n");
+			return -EINVAL;
+		}
+	} else {
+		pr_err("QE-TDM: Invalid rx-sync-clock property\n");
+		return -EINVAL;
+	}
+
+	sprop = of_get_property(np, "fsl,tx-sync-clock", NULL);
+	if (sprop) {
+		ut_info->uf_info.tx_sync = qe_clock_source(sprop);
+		if ((ut_info->uf_info.tx_sync < QE_CLK_NONE) ||
+		    (ut_info->uf_info.tx_sync > QE_TSYNC_PIN)) {
+			pr_err("QE-TDM: Invalid tx-sync-clock property\n");
+		return -EINVAL;
+		}
+	} else {
+		pr_err("QE-TDM: Invalid tx-sync-clock property\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32_index(np, "fsl,tx-timeslot-mask", 0, &val);
+	if (ret) {
+		pr_err("QE-TDM: Invalid tx-timeslot-mask property\n");
+		return -EINVAL;
+	}
+	utdm->tx_ts_mask = val;
+
+	ret = of_property_read_u32_index(np, "fsl,rx-timeslot-mask", 0, &val);
+	if (ret) {
+		ret = -EINVAL;
+		pr_err("QE-TDM: Invalid rx-timeslot-mask property\n");
+		return ret;
+	}
+	utdm->rx_ts_mask = val;
+
+	ret = of_property_read_u32_index(np, "fsl,tdm-id", 0, &val);
+	if (ret) {
+		ret = -EINVAL;
+		pr_err("QE-TDM: No fsl,tdm-id property for this UCC\n");
+		return ret;
+	}
+	utdm->tdm_port = val;
+	ut_info->uf_info.tdm_num = utdm->tdm_port;
+
+	if (of_get_property(np, "fsl,tdm-internal-loopback", NULL))
+		utdm->tdm_mode = TDM_INTERNAL_LOOPBACK;
+	else
+		utdm->tdm_mode = TDM_NORMAL;
+
+	sprop = of_get_property(np, "fsl,tdm-framer-type", NULL);
+	if (!sprop) {
+		ret = -EINVAL;
+		pr_err("QE-TDM: No tdm-framer-type property for UCC\n");
+		return ret;
+	}
+	utdm->tdm_framer_type = set_tdm_framer(sprop);
+
+	ret = of_property_read_u32_index(np, "fsl,siram-entry-id", 0, &val);
+	if (ret) {
+		ret = -EINVAL;
+		pr_err("QE-TDM: No siram entry id for UCC\n");
+		return ret;
+	}
+	utdm->siram_entry_id = val;
+
+	set_si_param(utdm, ut_info);
+
+	np2 = of_find_compatible_node(NULL, NULL, "fsl,t1040-qe-si");
+	if (!np2)
+		return -EINVAL;
+
+	pdev = of_find_device_by_node(np2);
+	if (!pdev) {
+		pr_err("%s: failed to lookup pdev\n", np2->name);
+		of_node_put(np2);
+		return -EINVAL;
+	}
+
+	of_node_put(np2);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	utdm->si_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(utdm->si_regs)) {
+		ret = PTR_ERR(utdm->si_regs);
+		goto err_miss_siram_property;
+	}
+
+	np2 = of_find_compatible_node(NULL, NULL, "fsl,t1040-qe-siram");
+	if (!np2) {
+		ret = -EINVAL;
+		goto err_miss_siram_property;
+	}
+
+	pdev = of_find_device_by_node(np2);
+	if (!pdev) {
+		ret = -EINVAL;
+		pr_err("%s: failed to lookup pdev\n", np2->name);
+		of_node_put(np2);
+		goto err_miss_siram_property;
+	}
+
+	of_node_put(np2);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	utdm->siram = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(utdm->siram)) {
+		ret = PTR_ERR(utdm->siram);
+		goto err_miss_siram_property;
+	}
+
+	if (siram_init_flag == 0) {
+		memset_io(utdm->siram, 0,  res->end - res->start + 1);
+		siram_init_flag = 1;
+	}
+
+	return ret;
+
+err_miss_siram_property:
+	devm_iounmap(&pdev->dev, utdm->si_regs);
+	return ret;
+}
+
+void ucc_tdm_init(struct ucc_tdm *utdm, struct ucc_tdm_info *ut_info)
+{
+	struct si1 __iomem *si_regs;
+	u16 __iomem *siram;
+	u16 siram_entry_valid;
+	u16 siram_entry_closed;
+	u16 ucc_num;
+	u8 csel;
+	u16 sixmr;
+	u16 tdm_port;
+	u32 siram_entry_id;
+	u32 mask;
+	int i;
+
+	si_regs = utdm->si_regs;
+	siram = utdm->siram;
+	ucc_num = ut_info->uf_info.ucc_num;
+	tdm_port = utdm->tdm_port;
+	siram_entry_id = utdm->siram_entry_id;
+
+	if (utdm->tdm_framer_type == TDM_FRAMER_T1)
+		utdm->num_of_ts = 24;
+	if (utdm->tdm_framer_type == TDM_FRAMER_E1)
+		utdm->num_of_ts = 32;
+
+	/* set siram table */
+	csel = (ucc_num < 4) ? ucc_num + 9 : ucc_num - 3;
+
+	siram_entry_valid = SIR_CSEL(csel) | SIR_BYTE | SIR_CNT(0);
+	siram_entry_closed = SIR_IDLE | SIR_BYTE | SIR_CNT(0);
+
+	for (i = 0; i < utdm->num_of_ts; i++) {
+		mask = 0x01 << i;
+
+		if (utdm->tx_ts_mask & mask)
+			iowrite16be(siram_entry_valid,
+				    &siram[siram_entry_id * 32 + i]);
+		else
+			iowrite16be(siram_entry_closed,
+				    &siram[siram_entry_id * 32 + i]);
+
+		if (utdm->rx_ts_mask & mask)
+			iowrite16be(siram_entry_valid,
+				    &siram[siram_entry_id * 32 + 0x200 +  i]);
+		else
+			iowrite16be(siram_entry_closed,
+				    &siram[siram_entry_id * 32 + 0x200 +  i]);
+	}
+
+	setbits16(&siram[(siram_entry_id * 32) + (utdm->num_of_ts - 1)],
+		  SIR_LAST);
+	setbits16(&siram[(siram_entry_id * 32) + 0x200 + (utdm->num_of_ts - 1)],
+		  SIR_LAST);
+
+	/* Set SIxMR register */
+	sixmr = SIMR_SAD(siram_entry_id);
+
+	sixmr &= ~SIMR_SDM_MASK;
+
+	if (utdm->tdm_mode == TDM_INTERNAL_LOOPBACK)
+		sixmr |= SIMR_SDM_INTERNAL_LOOPBACK;
+	else
+		sixmr |= SIMR_SDM_NORMAL;
+
+	sixmr |= SIMR_RFSD(ut_info->si_info.simr_rfsd) |
+			SIMR_TFSD(ut_info->si_info.simr_tfsd);
+
+	if (ut_info->si_info.simr_crt)
+		sixmr |= SIMR_CRT;
+	if (ut_info->si_info.simr_sl)
+		sixmr |= SIMR_SL;
+	if (ut_info->si_info.simr_ce)
+		sixmr |= SIMR_CE;
+	if (ut_info->si_info.simr_fe)
+		sixmr |= SIMR_FE;
+	if (ut_info->si_info.simr_gm)
+		sixmr |= SIMR_GM;
+
+	switch (tdm_port) {
+	case 0:
+		iowrite16be(sixmr, &si_regs->sixmr1[0]);
+		break;
+	case 1:
+		iowrite16be(sixmr, &si_regs->sixmr1[1]);
+		break;
+	case 2:
+		iowrite16be(sixmr, &si_regs->sixmr1[2]);
+		break;
+	case 3:
+		iowrite16be(sixmr, &si_regs->sixmr1[3]);
+		break;
+	default:
+		pr_err("QE-TDM: can not find tdm sixmr reg\n");
+		break;
+	}
+}
diff --git a/include/soc/fsl/qe/immap_qe.h b/include/soc/fsl/qe/immap_qe.h
index bedbff8..c76ef30 100644
--- a/include/soc/fsl/qe/immap_qe.h
+++ b/include/soc/fsl/qe/immap_qe.h
@@ -159,10 +159,7 @@ struct spi {
 
 /* SI */
 struct si1 {
-	__be16	siamr1;		/* SI1 TDMA mode register */
-	__be16	sibmr1;		/* SI1 TDMB mode register */
-	__be16	sicmr1;		/* SI1 TDMC mode register */
-	__be16	sidmr1;		/* SI1 TDMD mode register */
+	__be16	sixmr1[4];	/* SI1 TDMx (x = A B C D) mode register */
 	u8	siglmr1_h;	/* SI1 global mode register high */
 	u8	res0[0x1];
 	u8	sicmdr1_h;	/* SI1 command register high */
diff --git a/include/soc/fsl/qe/qe_tdm.h b/include/soc/fsl/qe/qe_tdm.h
new file mode 100644
index 0000000..e7ae93a
--- /dev/null
+++ b/include/soc/fsl/qe/qe_tdm.h
@@ -0,0 +1,94 @@
+/*
+ * Internal header file for QE TDM mode routines.
+ *
+ * Copyright (C) 2015 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Authors:	Zhao Qiang <qiang.zhao@nxp.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version
+ */
+
+#ifndef CONFIG_QE_TDM_H
+#define CONFIG_QE_TDM_H
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+
+#include <soc/fsl/qe/immap_qe.h>
+#include <soc/fsl/qe/qe.h>
+
+#include <soc/fsl/qe/ucc.h>
+#include <soc/fsl/qe/ucc_fast.h>
+
+/* SI RAM entries */
+#define SIR_LAST	0x0001
+#define SIR_BYTE	0x0002
+#define SIR_CNT(x)	((x) << 2)
+#define SIR_CSEL(x)	((x) << 5)
+#define SIR_SGS		0x0200
+#define SIR_SWTR	0x4000
+#define SIR_MCC		0x8000
+#define SIR_IDLE	0
+
+/* SIxMR fields */
+#define SIMR_SAD(x) ((x) << 12)
+#define SIMR_SDM_NORMAL	0x0000
+#define SIMR_SDM_INTERNAL_LOOPBACK	0x0800
+#define SIMR_SDM_MASK	0x0c00
+#define SIMR_CRT	0x0040
+#define SIMR_SL		0x0020
+#define SIMR_CE		0x0010
+#define SIMR_FE		0x0008
+#define SIMR_GM		0x0004
+#define SIMR_TFSD(n)	(n)
+#define SIMR_RFSD(n)	((n) << 8)
+
+enum tdm_ts_t {
+	TDM_TX_TS,
+	TDM_RX_TS
+};
+
+enum tdm_framer_t {
+	TDM_FRAMER_T1,
+	TDM_FRAMER_E1
+};
+
+enum tdm_mode_t {
+	TDM_INTERNAL_LOOPBACK,
+	TDM_NORMAL
+};
+
+struct si_mode_info {
+	u8 simr_rfsd;
+	u8 simr_tfsd;
+	u8 simr_crt;
+	u8 simr_sl;
+	u8 simr_ce;
+	u8 simr_fe;
+	u8 simr_gm;
+};
+
+struct ucc_tdm_info {
+	struct ucc_fast_info uf_info;
+	struct si_mode_info si_info;
+};
+
+struct ucc_tdm {
+	u16 tdm_port;		/* port for this tdm:TDMA,TDMB */
+	u32 siram_entry_id;
+	u16 __iomem *siram;
+	struct si1 __iomem *si_regs;
+	enum tdm_framer_t tdm_framer_type;
+	enum tdm_mode_t tdm_mode;
+	u8 num_of_ts;		/* the number of timeslots in this tdm frame */
+	u32 tx_ts_mask;		/* tx time slot mask */
+	u32 rx_ts_mask;		/* rx time slot mask */
+};
+
+int ucc_of_parse_tdm(struct device_node *np, struct ucc_tdm *utdm,
+		     struct ucc_tdm_info *ut_info);
+void ucc_tdm_init(struct ucc_tdm *utdm, struct ucc_tdm_info *ut_info);
+#endif
-- 
2.1.0.27.g96db324

^ permalink raw reply related

* [PATCH 5/5] drivers/net: support hdlc function for QE-UCC
From: Zhao Qiang @ 2016-03-30  8:50 UTC (permalink / raw)
  To: davem
  Cc: akpm, gregkh, oss, xiaobo.xie, linux-kernel, netdev, linuxppc-dev,
	Zhao Qiang
In-Reply-To: <1459327830-19829-1-git-send-email-qiang.zhao@nxp.com>

The driver add hdlc support for Freescale QUICC Engine.
It support NMSI and TSA mode.

Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com>
---
 MAINTAINERS                    |    6 +
 drivers/net/wan/Kconfig        |   12 +
 drivers/net/wan/Makefile       |    1 +
 drivers/net/wan/fsl_ucc_hdlc.c | 1339 ++++++++++++++++++++++++++++++++++++++++
 drivers/net/wan/fsl_ucc_hdlc.h |  140 +++++
 include/soc/fsl/qe/ucc_fast.h  |    4 +
 6 files changed, 1502 insertions(+)
 create mode 100644 drivers/net/wan/fsl_ucc_hdlc.c
 create mode 100644 drivers/net/wan/fsl_ucc_hdlc.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 74bbff3..428d6ed 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4572,6 +4572,12 @@ F:	drivers/net/ethernet/freescale/gianfar*
 X:	drivers/net/ethernet/freescale/gianfar_ptp.c
 F:	Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
 
+FREESCALE QUICC ENGINE UCC HDLC DRIVER
+M:	Zhao Qiang <qiang.zhao@nxp.com>
+L:	linuxppc-dev@lists.ozlabs.org
+S:	Maintained
+F:	drivers/net/wan/fsl_ucc_hdlc*
+
 FREESCALE QUICC ENGINE UCC UART DRIVER
 M:	Timur Tabi <timur@tabi.org>
 L:	linuxppc-dev@lists.ozlabs.org
diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig
index a2fdd15..cc424b2 100644
--- a/drivers/net/wan/Kconfig
+++ b/drivers/net/wan/Kconfig
@@ -280,6 +280,18 @@ config DSCC4
 	  To compile this driver as a module, choose M here: the
 	  module will be called dscc4.
 
+config FSL_UCC_HDLC
+	tristate "Freescale QUICC Engine HDLC support"
+	depends on HDLC
+	select QE_TDM
+	select QUICC_ENGINE
+	help
+	  Driver for Freescale QUICC Engine HDLC controller. The driver
+	  support HDLC run on NMSI and TDM mode.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called fsl_ucc_hdlc.
+
 config DSCC4_PCISYNC
 	bool "Etinc PCISYNC features"
 	depends on DSCC4
diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile
index c135ef4..25fec40 100644
--- a/drivers/net/wan/Makefile
+++ b/drivers/net/wan/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_WANXL)		+= wanxl.o
 obj-$(CONFIG_PCI200SYN)		+= pci200syn.o
 obj-$(CONFIG_PC300TOO)		+= pc300too.o
 obj-$(CONFIG_IXP4XX_HSS)	+= ixp4xx_hss.o
+obj-$(CONFIG_FSL_UCC_HDLC)	+= fsl_ucc_hdlc.o
 
 clean-files := wanxlfw.inc
 $(obj)/wanxl.o:	$(obj)/wanxlfw.inc
diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c
new file mode 100644
index 0000000..9958ec1
--- /dev/null
+++ b/drivers/net/wan/fsl_ucc_hdlc.c
@@ -0,0 +1,1339 @@
+/* Freescale QUICC Engine HDLC Device Driver
+ *
+ * Copyright 2014 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/hdlc.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/stddef.h>
+#include <soc/fsl/qe/qe_tdm.h>
+#include <uapi/linux/if_arp.h>
+
+#include "fsl_ucc_hdlc.h"
+
+#define DRV_DESC "Freescale QE UCC HDLC Driver"
+#define DRV_NAME "ucc_hdlc"
+
+#define TDM_PPPOHT_SLIC_MAXIN
+/* #define DEBUG */
+/* #define QE_HDLC_TEST */
+#define BROKEN_FRAME_INFO
+
+static struct ucc_tdm_info utdm_primary_info = {
+	.uf_info = {
+		.tsa = 0,
+		.cdp = 0,
+		.cds = 1,
+		.ctsp = 1,
+		.ctss = 1,
+		.revd = 0,
+		.urfs = 256,
+		.utfs = 256,
+		.urfet = 128,
+		.urfset = 192,
+		.utfet = 128,
+		.utftt = 0x40,
+		.ufpt = 256,
+		.mode = UCC_FAST_PROTOCOL_MODE_HDLC,
+		.ttx_trx = UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_NORMAL,
+		.tenc = UCC_FAST_TX_ENCODING_NRZ,
+		.renc = UCC_FAST_RX_ENCODING_NRZ,
+		.tcrc = UCC_FAST_16_BIT_CRC,
+		.synl = UCC_FAST_SYNC_LEN_NOT_USED,
+	},
+
+	.si_info = {
+#ifdef CONFIG_FSL_PQ_MDS_T1
+		.simr_rfsd = 1,		/* TDM card need 1 bit delay */
+		.simr_tfsd = 0,
+#else
+#ifdef TDM_PPPOHT_SLIC_MAXIN
+		.simr_rfsd = 1,
+		.simr_tfsd = 2,
+#else
+		.simr_rfsd = 0,
+		.simr_tfsd = 0,
+#endif
+#endif
+		.simr_crt = 0,
+		.simr_sl = 0,
+		.simr_ce = 1,
+		.simr_fe = 1,
+		.simr_gm = 0,
+	},
+};
+
+static struct ucc_tdm_info utdm_info[MAX_HDLC_NUM];
+
+#ifdef DEBUG
+static void mem_disp(u8 *addr, int size)
+{
+	void *i;
+	int size16_aling = (size >> 4) << 4;
+	int size4_aling = (size >> 2) << 2;
+	int not_align = 0;
+
+	if (size % 16)
+		not_align = 1;
+
+	for (i = addr;  i < addr + size16_aling; i += 16) {
+		u32 *i32 = i;
+
+		pr_info("0x%08p: %08x %08x %08x %08x\r\n",
+			i32, be32_to_cpu(i32[0]), be32_to_cpu(i32[1]),
+			be32_to_cpu(i32[2]), be32_to_cpu(i32[3]));
+	}
+
+	if (not_align == 1)
+		pr_info("0x%08p: ", i);
+	for (; i < addr + size4_aling; i += 4)
+		pr_info("%08x ", be32_to_cpu(*((u32 *)(i))));
+	for (; i < addr + size; i++)
+		pr_info("%02x", *((u8 *)(i)));
+	if (not_align == 1)
+		pr_info("\r\n");
+}
+
+static void dump_ucc(struct ucc_hdlc_private *priv)
+{
+	struct ucc_hdlc_param *ucc_pram;
+
+	ucc_pram = priv->ucc_pram;
+
+	dev_info(priv->dev, "DumpiniCC %d Registers\n",
+		 priv->ut_info->uf_info.ucc_num);
+	ucc_fast_dump_regs(priv->uccf);
+	dev_info(priv->dev, "Dumping UCC %d Parameter RAM\n",
+		 priv->ut_info->uf_info.ucc_num);
+	dev_info(priv->dev, "rbase = 0x%x\n", ioread32be(&ucc_pram->rbase));
+	dev_info(priv->dev, "rbptr = 0x%x\n", ioread32be(&ucc_pram->rbptr));
+	dev_info(priv->dev, "mrblr = 0x%x\n", ioread16be(&ucc_pram->mrblr));
+	dev_info(priv->dev, "rbdlen = 0x%x\n", ioread16be(&ucc_pram->rbdlen));
+	dev_info(priv->dev, "rbdstat = 0x%x\n", ioread16be(&ucc_pram->rbdstat));
+	dev_info(priv->dev, "rstate = 0x%x\n", ioread32be(&ucc_pram->rstate));
+	dev_info(priv->dev, "rdptr = 0x%x\n", ioread32be(&ucc_pram->rdptr));
+	dev_info(priv->dev, "riptr = 0x%x\n", ioread16be(&ucc_pram->riptr));
+	dev_info(priv->dev, "tbase = 0x%x\n", ioread32be(&ucc_pram->tbase));
+	dev_info(priv->dev, "tbptr = 0x%x\n", ioread32be(&ucc_pram->tbptr));
+	dev_info(priv->dev, "tbdlen = 0x%x\n", ioread16be(&ucc_pram->tbdlen));
+	dev_info(priv->dev, "tbdstat = 0x%x\n", ioread16be(&ucc_pram->tbdstat));
+	dev_info(priv->dev, "tstate = 0x%x\n", ioread32be(&ucc_pram->tstate));
+	dev_info(priv->dev, "tdptr = 0x%x\n", ioread32be(&ucc_pram->tdptr));
+	dev_info(priv->dev, "tiptr = 0x%x\n", ioread16be(&ucc_pram->tiptr));
+	dev_info(priv->dev, "rcrc = 0x%x\n", ioread32be(&ucc_pram->rcrc));
+	dev_info(priv->dev, "tcrc = 0x%x\n", ioread32be(&ucc_pram->tcrc));
+	dev_info(priv->dev, "c_mask = 0x%x\n", ioread32be(&ucc_pram->c_mask));
+	dev_info(priv->dev, "c_pers = 0x%x\n", ioread32be(&ucc_pram->c_pres));
+	dev_info(priv->dev, "disfc = 0x%x\n", ioread16be(&ucc_pram->disfc));
+	dev_info(priv->dev, "crcec = 0x%x\n", ioread16be(&ucc_pram->crcec));
+}
+
+static void dump_bds(struct ucc_hdlc_private *priv)
+{
+	int length;
+
+	if (priv->tx_bd_base) {
+		length = sizeof(struct qe_bd) * TX_BD_RING_LEN;
+		dev_info(priv->dev, " Dump tx BDs\n");
+		mem_disp((u8 *)priv->tx_bd_base, length);
+	}
+
+	if (priv->rx_bd_base) {
+		length = sizeof(struct qe_bd) * RX_BD_RING_LEN;
+		dev_info(priv->dev, " Dump rx BDs\n");
+		mem_disp((u8 *)priv->rx_bd_base, length);
+	}
+}
+
+static void dump_priv(struct ucc_hdlc_private *priv)
+{
+	dev_info(priv->dev, "ut_info = 0x%x\n", (u32)priv->ut_info);
+	dev_info(priv->dev, "uccf = 0x%x\n", (u32)priv->uccf);
+	dev_info(priv->dev, "uf_regs = 0x%x\n", (u32)priv->uf_regs);
+	dev_info(priv->dev, "si_regs = 0x%x\n", (u32)priv->utdm->si_regs);
+	dev_info(priv->dev, "ucc_pram = 0x%x\n", (u32)priv->ucc_pram);
+	dev_info(priv->dev, "tdm_port = 0x%x\n", (u32)priv->utdm->tdm_port);
+	dev_info(priv->dev, "siram_entry_id = 0x%x\n",
+		 priv->utdm->siram_entry_id);
+	dev_info(priv->dev, "siram = 0x%x\n", (u32)priv->utdm->siram);
+	dev_info(priv->dev, "tdm_mode = 0x%x\n", (u32)priv->utdm->tdm_mode);
+	dev_info(priv->dev, "tdm_framer_type; = 0x%x\n",
+		 (u32)priv->utdm->tdm_framer_type);
+	dev_info(priv->dev, "rx_buffer; = 0x%x\n", (u32)priv->rx_buffer);
+	dev_info(priv->dev, "tx_buffer; = 0x%x\n", (u32)priv->tx_buffer);
+	dev_info(priv->dev, "dma_rx_addr; = 0x%x\n", (u32)priv->dma_rx_addr);
+	dev_info(priv->dev, "tx_bd; = 0x%x\n", (u32)priv->tx_bd_base);
+	dev_info(priv->dev, "rx_bd; = 0x%x\n", (u32)priv->rx_bd_base);
+	dev_info(priv->dev, "curtx_bd = 0x%x\n", (u32)priv->curtx_bd);
+	dev_info(priv->dev, "currx_bd = 0x%x\n", (u32)priv->currx_bd);
+	dev_info(priv->dev, "ucc_pram_offset = 0x%x\n", priv->ucc_pram_offset);
+}
+
+#endif /* DEBUG */
+
+static int uhdlc_init(struct ucc_hdlc_private *priv)
+{
+	struct ucc_tdm_info *ut_info;
+	struct ucc_fast_info *uf_info;
+	u32 cecr_subblock;
+	u32 bd_status;
+	int ret, i;
+	void *bd_buffer;
+	dma_addr_t bd_dma_addr;
+	u32 riptr;
+	u32 tiptr;
+	u32 gumr;
+
+	ut_info = priv->ut_info;
+	uf_info = &ut_info->uf_info;
+
+	if (priv->tsa) {
+		uf_info->tsa = 1;
+		uf_info->ctsp = 1;
+	}
+	uf_info->uccm_mask = (u32)((UCC_HDLC_UCCE_RXB | UCC_HDLC_UCCE_RXF |
+				UCC_HDLC_UCCE_TXB) << 16);
+
+	if (ucc_fast_init(uf_info, &priv->uccf)) {
+		dev_err(priv->dev, "Failed to init uccf.");
+		return -ENOMEM;
+	}
+
+	priv->uf_regs = priv->uccf->uf_regs;
+	ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX);
+
+	/* Loopback mode */
+	if (priv->loopback) {
+		pr_info("TDM Mode: Loopback Mode\n");
+		gumr = ioread32be(&priv->uf_regs->gumr);
+		gumr |= (0x40000000 | UCC_FAST_GUMR_CDS | UCC_FAST_GUMR_TCI);
+		gumr &= ~(UCC_FAST_GUMR_CTSP | UCC_FAST_GUMR_RSYN);
+		iowrite32be(gumr, &priv->uf_regs->gumr);
+	}
+
+	/* Initialize SI */
+	if (priv->tsa)
+		ucc_tdm_init(priv->utdm, priv->ut_info);
+
+	/* Write to QE CECR, UCCx channel to Stop Transmission */
+	cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num);
+	ret = qe_issue_cmd(QE_STOP_TX, cecr_subblock,
+			   (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0);
+
+	/* Set UPSMR normal mode (need fixed)*/
+	iowrite32be(0, &priv->uf_regs->upsmr);
+
+	priv->rx_ring_size = RX_BD_RING_LEN;
+	priv->tx_ring_size = TX_BD_RING_LEN;
+	/* Alloc Rx BD */
+	priv->rx_bd_base = dma_alloc_coherent(priv->dev,
+			RX_BD_RING_LEN * sizeof(struct qe_bd *),
+			&priv->dma_rx_bd, GFP_KERNEL);
+
+	if (IS_ERR_VALUE((unsigned long)priv->rx_bd_base)) {
+		dev_err(priv->dev, "Cannot allocate MURAM memory for RxBDs\n");
+		ret = -ENOMEM;
+		goto rxbd_alloc_error;
+	}
+
+	/* Alloc Tx BD */
+	priv->tx_bd_base = dma_alloc_coherent(priv->dev,
+			TX_BD_RING_LEN * sizeof(struct qe_bd *),
+			&priv->dma_tx_bd, GFP_KERNEL);
+
+	if (IS_ERR_VALUE((unsigned long)priv->tx_bd_base)) {
+		dev_err(priv->dev, "Cannot allocate MURAM memory for TxBDs\n");
+		ret = -ENOMEM;
+		goto txbd_alloc_error;
+	}
+
+	/* Alloc parameter ram for ucc hdlc */
+	priv->ucc_pram_offset = qe_muram_alloc(sizeof(priv->ucc_pram),
+				ALIGNMENT_OF_UCC_HDLC_PRAM);
+
+	if (IS_ERR_VALUE(priv->ucc_pram_offset)) {
+		dev_err(priv->dev, "Can not allocate MURAM for hdlc prameter.\n");
+		ret = -ENOMEM;
+		goto pram_alloc_error;
+	}
+
+	priv->rx_skbuff = kmalloc_array(priv->rx_ring_size,
+			sizeof(*priv->rx_skbuff), GFP_KERNEL);
+	if (!priv->rx_skbuff)
+		goto rx_skb_alloc_error;
+	for (i = 0; i < priv->rx_ring_size; i++)
+		priv->rx_skbuff[i] = NULL;
+
+	priv->tx_skbuff = kmalloc_array(priv->tx_ring_size,
+			sizeof(*priv->tx_skbuff), GFP_KERNEL);
+	if (!priv->tx_skbuff)
+		goto tx_skb_alloc_error;
+	for (i = 0; i < priv->tx_ring_size; i++)
+		priv->tx_skbuff[i] = NULL;
+
+	priv->skb_curtx = 0;
+	priv->skb_dirtytx = 0;
+	priv->curtx_bd = priv->tx_bd_base;
+	priv->dirty_tx = priv->tx_bd_base;
+	priv->currx_bd = priv->rx_bd_base;
+	priv->currx_bdnum = 0;
+
+	/* init parameter base */
+	cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num);
+	ret = qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock,
+			   QE_CR_PROTOCOL_UNSPECIFIED, priv->ucc_pram_offset);
+
+	priv->ucc_pram = (struct ucc_hdlc_param __iomem *)
+					qe_muram_addr(priv->ucc_pram_offset);
+
+	/* Zero out parameter ram */
+	memset_io(priv->ucc_pram, 0, sizeof(struct ucc_hdlc_param));
+
+	/* Alloc riptr, tiptr */
+	riptr = qe_muram_alloc(32, 32);
+	if (IS_ERR_VALUE(riptr)) {
+		dev_err(priv->dev, "Cannot allocate MURAM mem for Receive internal temp data pointer\n");
+		ret = -ENOMEM;
+		goto riptr_alloc_error;
+	}
+
+	tiptr = qe_muram_alloc(32, 32);
+	if (IS_ERR_VALUE(tiptr)) {
+		dev_err(priv->dev, "Cannot allocate MURAM mem for Transmit internal temp data pointer\n");
+		ret = -ENOMEM;
+		goto tiptr_alloc_error;
+	}
+
+	/* Set RIPTR, TIPTR */
+	iowrite16be((u16)riptr, &priv->ucc_pram->riptr);
+	iowrite16be((u16)tiptr, &priv->ucc_pram->tiptr);
+
+	/* Set MRBLR */
+	iowrite16be((u16)MAX_RX_BUF_LENGTH, &priv->ucc_pram->mrblr);
+
+		/* Set RBASE, TBASE */
+	iowrite32be((u32)priv->dma_rx_bd, &priv->ucc_pram->rbase);
+	iowrite32be((u32)priv->dma_tx_bd, &priv->ucc_pram->tbase);
+
+	/* Set RSTATE, TSTATE */
+	iowrite32be(0x30000000, &priv->ucc_pram->rstate);
+	iowrite32be(0x30000000, &priv->ucc_pram->tstate);
+
+	/* Set C_MASK, C_PRES for 16bit CRC */
+	iowrite32be(0x0000F0B8, &priv->ucc_pram->c_mask);
+	iowrite32be(0x0000FFFF, &priv->ucc_pram->c_pres);
+
+	iowrite16be(MAX_RX_BUF_LENGTH + 8, &priv->ucc_pram->mflr);
+	iowrite16be(1, &priv->ucc_pram->rfthr);
+	iowrite16be(1, &priv->ucc_pram->rfcnt);
+	iowrite16be(DEFAULT_ADDR_MASK, &priv->ucc_pram->hmask);
+	iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr1);
+	iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr2);
+	iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr3);
+	iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr4);
+
+	/* Get BD buffer */
+	bd_buffer = dma_alloc_coherent(priv->dev,
+				       (RX_BD_RING_LEN + TX_BD_RING_LEN) *
+				       MAX_RX_BUF_LENGTH,
+				       &bd_dma_addr, GFP_KERNEL);
+
+	if (!bd_buffer) {
+		dev_err(priv->dev, "Could not allocate buffer descriptors\n");
+		return -ENOMEM;
+	}
+
+	memset(bd_buffer, 0, (RX_BD_RING_LEN + TX_BD_RING_LEN)
+			* MAX_RX_BUF_LENGTH);
+
+	priv->rx_buffer = bd_buffer;
+	priv->tx_buffer = bd_buffer + RX_BD_RING_LEN * MAX_RX_BUF_LENGTH;
+
+	priv->dma_rx_addr = bd_dma_addr;
+	priv->dma_tx_addr = bd_dma_addr + RX_BD_RING_LEN * MAX_RX_BUF_LENGTH;
+
+	for (i = 0; i < RX_BD_RING_LEN; i++) {
+		if (i < (RX_BD_RING_LEN - 1))
+			bd_status = R_E | R_I;
+		else
+			bd_status = R_E | R_I | R_W;
+
+		iowrite32be(bd_status, (u32 *)(priv->rx_bd_base + i));
+		iowrite32be(priv->dma_rx_addr + i * MAX_RX_BUF_LENGTH,
+			    &priv->rx_bd_base[i].buf);
+	}
+
+	for (i = 0; i < TX_BD_RING_LEN; i++) {
+		if (i < (TX_BD_RING_LEN - 1))
+			bd_status =  T_I | T_TC;
+		else
+			bd_status =  T_I | T_TC | T_W;
+
+		iowrite32be(bd_status, (u32 *)(priv->tx_bd_base + i));
+		iowrite32be(priv->dma_tx_addr + i * MAX_RX_BUF_LENGTH,
+			    &priv->tx_bd_base[i].buf);
+	}
+
+	return 0;
+
+tiptr_alloc_error:
+	qe_muram_free(riptr);
+riptr_alloc_error:
+	kfree(priv->tx_skbuff);
+tx_skb_alloc_error:
+	kfree(priv->rx_skbuff);
+rx_skb_alloc_error:
+	qe_muram_free(priv->ucc_pram_offset);
+pram_alloc_error:
+	dma_free_coherent(priv->dev,
+			  TX_BD_RING_LEN * sizeof(struct qe_bd),
+			  priv->tx_bd_base, priv->dma_tx_bd);
+txbd_alloc_error:
+	dma_free_coherent(priv->dev,
+			  RX_BD_RING_LEN * sizeof(struct qe_bd),
+			  priv->rx_bd_base, priv->dma_rx_bd);
+rxbd_alloc_error:
+	ucc_fast_free(priv->uccf);
+
+	return ret;
+}
+
+static netdev_tx_t ucc_hdlc_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	hdlc_device *hdlc = dev_to_hdlc(dev);
+	struct ucc_hdlc_private *priv = (struct ucc_hdlc_private *)hdlc->priv;
+	struct qe_bd __iomem *bd;
+	u32 bd_status;
+	unsigned long flags;
+#ifdef QE_HDLC_TEST
+	u8 *send_buf;
+	int i;
+#endif
+	u16 *proto_head, tmp_head;
+
+	switch (dev->type) {
+	case ARPHRD_RAWHDLC:
+		if (skb_headroom(skb) < HDLC_HEAD_LEN) {
+			dev->stats.tx_dropped++;
+			dev_kfree_skb(skb);
+			netdev_err(dev, "No enough space for hdlc head\n");
+			return -ENOMEM;
+		}
+
+		skb_push(skb, HDLC_HEAD_LEN);
+
+		proto_head = (u16 *)skb->data;
+		tmp_head = *proto_head;
+		tmp_head = (tmp_head & HDLC_HEAD_MASK) |
+			    htons(DEFAULT_HDLC_HEAD);
+		*proto_head = tmp_head;
+
+		dev->stats.tx_bytes += skb->len;
+		break;
+
+	case ARPHRD_PPP:
+		proto_head = (u16 *)skb->data;
+		if (*proto_head != ntohs(DEFAULT_PPP_HEAD)) {
+			dev->stats.tx_dropped++;
+			dev_kfree_skb(skb);
+			netdev_err(dev, "Wrong ppp header\n");
+			return -ENOMEM;
+		}
+
+		dev->stats.tx_bytes += skb->len;
+		break;
+
+	default:
+		dev->stats.tx_dropped++;
+		dev_kfree_skb(skb);
+		netdev_err(dev, "Protocol not supported!\n");
+		return -ENOMEM;
+
+	} /*switch right bracket*/
+
+#ifdef QE_HDLC_TEST
+	pr_info("Tx data skb->len:%d ", skb->len);
+	send_buf = (u8 *)skb->data;
+	pr_info("\nTransmitted data:\n");
+	for (i = 0; (i < 16); i++) {
+		if (i == skb->len)
+			pr_info("++++");
+		else
+		pr_info("%02x\n", send_buf[i]);
+	}
+#endif
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/* Start from the next BD that should be filled */
+	bd = priv->curtx_bd;
+	bd_status = ioread32be((u32 __iomem *)bd);
+	/* Save the skb pointer so we can free it later */
+	priv->tx_skbuff[priv->skb_curtx] = skb;
+
+	/* Update the current skb pointer (wrapping if this was the last) */
+	priv->skb_curtx =
+	    (priv->skb_curtx + 1) & TX_RING_MOD_MASK(TX_BD_RING_LEN);
+
+	/* copy skb data to tx buffer for sdma processing */
+	memcpy(priv->tx_buffer + (be32_to_cpu(bd->buf) - priv->dma_tx_addr),
+	       skb->data, skb->len);
+
+	/* set bd status and length */
+	bd_status = (bd_status & T_W) | T_R | T_I | T_L | T_TC | skb->len;
+
+	iowrite32be(bd_status, (u32 __iomem *)bd);
+
+	/* Move to next BD in the ring */
+	if (!(bd_status & T_W))
+		bd += 1;
+	else
+		bd = priv->tx_bd_base;
+
+	if (bd == priv->dirty_tx) {
+		if (!netif_queue_stopped(dev))
+			netif_stop_queue(dev);
+	}
+
+	priv->curtx_bd = bd;
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return NETDEV_TX_OK;
+}
+
+static int hdlc_tx_done(struct ucc_hdlc_private *priv)
+{
+	/* Start from the next BD that should be filled */
+	struct net_device *dev = priv->ndev;
+	struct qe_bd *bd;		/* BD pointer */
+	u32 bd_status;
+
+	bd = priv->dirty_tx;
+	bd_status = ioread32be((u32 __iomem *)bd);
+
+	/* Normal processing. */
+	while ((bd_status & T_R) == 0) {
+		struct sk_buff *skb;
+
+		/* BD contains already transmitted buffer.   */
+		/* Handle the transmitted buffer and release */
+		/* the BD to be used with the current frame  */
+
+		skb = priv->tx_skbuff[priv->skb_dirtytx];
+		if (!skb)
+			break;
+#ifdef QE_HDLC_TEST
+		pr_info("TxBD: %x\n", bd_status);
+#endif
+		dev->stats.tx_packets++;
+		memset(priv->tx_buffer +
+		       (be32_to_cpu(bd->buf) - priv->dma_tx_addr),
+		       0, skb->len);
+		dev_kfree_skb_irq(skb);
+
+		priv->tx_skbuff[priv->skb_dirtytx] = NULL;
+		priv->skb_dirtytx =
+		    (priv->skb_dirtytx +
+		     1) & TX_RING_MOD_MASK(TX_BD_RING_LEN);
+
+		/* We freed a buffer, so now we can restart transmission */
+		if (netif_queue_stopped(dev))
+			netif_wake_queue(dev);
+
+		/* Advance the confirmation BD pointer */
+		if (!(bd_status & T_W))
+			bd += 1;
+		else
+			bd = priv->tx_bd_base;
+		bd_status = ioread32be((u32 __iomem *)bd);
+	}
+	priv->dirty_tx = bd;
+
+	return 0;
+}
+
+static int hdlc_rx_done(struct ucc_hdlc_private *priv, int rx_work_limit)
+{
+	struct net_device *dev = priv->ndev;
+	struct sk_buff *skb;
+	hdlc_device *hdlc = dev_to_hdlc(dev);
+	struct qe_bd *bd;
+	u32 bd_status;
+	u16 length, howmany = 0;
+	u8 *bdbuffer;
+#ifdef QE_HDLC_TEST
+	int i;
+	static int entry;
+#endif
+
+	bd = priv->currx_bd;
+	bd_status = ioread32be((u32 __iomem *)bd);
+
+	/* while there are received buffers and BD is full (~R_E) */
+	while (!((bd_status & (R_E)) || (--rx_work_limit < 0))) {
+		if (bd_status & R_CR) {
+#ifdef BROKEN_FRAME_INFO
+			pr_info("Broken Frame with RxBD: %x\n", bd_status);
+#endif
+			dev->stats.rx_dropped++;
+			goto recycle;
+		}
+		bdbuffer = priv->rx_buffer +
+			(priv->currx_bdnum * MAX_RX_BUF_LENGTH);
+		length = (u16)(bd_status & BD_LENGTH_MASK);
+
+#ifdef QE_HDLC_TEST
+		pr_info("Received data length:%d", length);
+		pr_info("while entry times:%d", entry++);
+
+		pr_info("\nReceived data:\n");
+		for (i = 0; (i < 16); i++) {
+			if (i == length)
+				pr_info("++++");
+			else
+			pr_info("%02x\n", bdbuffer[i]);
+		}
+#endif
+
+		switch (dev->type) {
+		case ARPHRD_RAWHDLC:
+			bdbuffer += HDLC_HEAD_LEN;
+			length -= (HDLC_HEAD_LEN + HDLC_CRC_SIZE);
+
+			skb = dev_alloc_skb(length);
+			if (!skb) {
+				dev->stats.rx_dropped++;
+				return -ENOMEM;
+			}
+
+			skb_put(skb, length);
+			skb->len = length;
+			skb->dev = dev;
+			memcpy(skb->data, bdbuffer, length);
+			break;
+
+		case ARPHRD_PPP:
+			length -= HDLC_CRC_SIZE;
+
+			skb = dev_alloc_skb(length);
+			if (!skb) {
+				dev->stats.rx_dropped++;
+				return -ENOMEM;
+			}
+
+			skb_put(skb, length);
+			skb->len = length;
+			skb->dev = dev;
+			memcpy(skb->data, bdbuffer, length);
+			break;
+		}
+
+		dev->stats.rx_packets++;
+		dev->stats.rx_bytes += skb->len;
+		howmany++;
+		if (hdlc->proto)
+			skb->protocol = hdlc_type_trans(skb, dev);
+#ifdef QE_HDLC_TEST
+		pr_info("skb->protocol:%x\n", skb->protocol);
+#endif
+		netif_receive_skb(skb);
+
+recycle:
+		iowrite32be((bd_status & ~BD_LENGTH_MASK) | R_E | R_I,
+			    (u32 *)bd);
+
+		/* update to point at the next bd */
+		if (bd_status & R_W) {
+			priv->currx_bdnum = 0;
+			bd = priv->rx_bd_base;
+		} else {
+			if (priv->currx_bdnum < (RX_BD_RING_LEN - 1))
+				priv->currx_bdnum += 1;
+			else
+				priv->currx_bdnum = RX_BD_RING_LEN - 1;
+
+			bd += 1;
+		}
+
+		bd_status = ioread32be((u32 __iomem *)bd);
+	}
+
+	priv->currx_bd = bd;
+	return howmany;
+}
+
+static int ucc_hdlc_poll(struct napi_struct *napi, int budget)
+{
+	struct ucc_hdlc_private *priv = container_of(napi,
+						     struct ucc_hdlc_private,
+						     napi);
+	int howmany;
+
+	/* Tx event processing */
+	spin_lock(&priv->lock);
+		hdlc_tx_done(priv);
+	spin_unlock(&priv->lock);
+
+	howmany = 0;
+	howmany += hdlc_rx_done(priv, budget - howmany);
+
+	if (howmany < budget) {
+		napi_complete(napi);
+		qe_setbits32(priv->uccf->p_uccm,
+			     (UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS) << 16);
+	}
+
+	return howmany;
+}
+
+static irqreturn_t ucc_hdlc_irq_handler(int irq, void *dev_id)
+{
+	struct ucc_hdlc_private *priv = (struct ucc_hdlc_private *)dev_id;
+	struct net_device *dev = priv->ndev;
+	struct ucc_fast_private *uccf;
+	struct ucc_tdm_info *ut_info;
+	u32 ucce;
+	u32 uccm;
+
+	ut_info = priv->ut_info;
+	uccf = priv->uccf;
+
+	ucce = ioread32be(uccf->p_ucce);
+	uccm = ioread32be(uccf->p_uccm);
+	ucce &= uccm;
+	iowrite32be(ucce, uccf->p_ucce);
+#ifdef QE_HDLC_TEST
+	pr_info("irq ucce:%x\n", ucce);
+#endif
+
+	if ((ucce >> 16) & (UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS)) {
+		if (napi_schedule_prep(&priv->napi)) {
+			uccm &= ~((UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS)
+				  << 16);
+			iowrite32be(uccm, uccf->p_uccm);
+			__napi_schedule(&priv->napi);
+		}
+	}
+
+	/* Errors and other events */
+	if (ucce >> 16 & UCC_HDLC_UCCE_BSY)
+		dev->stats.rx_errors++;
+	if (ucce >> 16 & UCC_HDLC_UCCE_TXE)
+		dev->stats.tx_errors++;
+
+	return IRQ_HANDLED;
+}
+
+static int uhdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	const size_t size = sizeof(te1_settings);
+	te1_settings line;
+	struct ucc_hdlc_private *priv = netdev_priv(dev);
+
+	if (cmd != SIOCWANDEV)
+		return hdlc_ioctl(dev, ifr, cmd);
+
+	switch (ifr->ifr_settings.type) {
+	case IF_GET_IFACE:
+		ifr->ifr_settings.type = IF_IFACE_E1;
+		if (ifr->ifr_settings.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		line.clock_type = priv->clocking;
+		line.clock_rate = 0;
+		line.loopback = 0;
+
+		if (copy_to_user(ifr->ifr_settings.ifs_ifsu.sync, &line, size))
+			return -EFAULT;
+		return 0;
+
+	default:
+		return hdlc_ioctl(dev, ifr, cmd);
+	}
+}
+
+static int uhdlc_open(struct net_device *dev)
+{
+	u32 cecr_subblock;
+	hdlc_device *hdlc = dev_to_hdlc(dev);
+	struct ucc_hdlc_private *priv = hdlc->priv;
+	struct ucc_tdm *utdm = priv->utdm;
+
+	if (priv->hdlc_busy != 1) {
+		if (request_irq(priv->ut_info->uf_info.irq,
+				ucc_hdlc_irq_handler, 0,
+				"hdlc", (void *)priv)) {
+			dev_err(priv->dev, "request_irq for ucc hdlc failed\n");
+			return -ENODEV;
+		}
+		cecr_subblock = ucc_fast_get_qe_cr_subblock(
+					priv->ut_info->uf_info.ucc_num);
+
+		qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock,
+			     (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0);
+
+		ucc_fast_enable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX);
+
+		/* Enable the TDM port */
+		if (priv->tsa)
+			utdm->si_regs->siglmr1_h |= (0x1 << utdm->tdm_port);
+
+		priv->hdlc_busy = 1;
+		netif_device_attach(priv->ndev);
+		napi_enable(&priv->napi);
+		netif_start_queue(dev);
+		hdlc_open(dev);
+	} else {
+		dev_err(priv->dev, "HDLC IS RUNNING!\n");
+	}
+
+#ifdef DEBUG
+	dump_priv(priv);
+	dump_ucc(priv);
+	dump_bds(priv);
+#endif
+	return 0;
+}
+
+static void uhdlc_memclean(struct ucc_hdlc_private *priv)
+{
+	qe_muram_free(priv->ucc_pram->riptr);
+	qe_muram_free(priv->ucc_pram->tiptr);
+
+	if (priv->rx_bd_base) {
+		dma_free_coherent(priv->dev,
+				  RX_BD_RING_LEN * sizeof(struct qe_bd),
+				  priv->rx_bd_base, priv->dma_rx_bd);
+
+		priv->rx_bd_base = NULL;
+		priv->dma_rx_bd = 0;
+	}
+
+	if (priv->tx_bd_base) {
+		dma_free_coherent(priv->dev,
+				  TX_BD_RING_LEN * sizeof(struct qe_bd),
+				  priv->tx_bd_base, priv->dma_tx_bd);
+
+		priv->tx_bd_base = NULL;
+		priv->dma_tx_bd = 0;
+	}
+
+	if (priv->ucc_pram) {
+		qe_muram_free(priv->ucc_pram_offset);
+		priv->ucc_pram = NULL;
+		priv->ucc_pram_offset = 0;
+	 }
+
+	kfree(priv->rx_skbuff);
+	priv->rx_skbuff = NULL;
+
+	kfree(priv->tx_skbuff);
+	priv->tx_skbuff = NULL;
+
+	if (priv->uf_regs) {
+		iounmap(priv->uf_regs);
+		priv->uf_regs = NULL;
+	}
+
+	if (priv->uccf) {
+		ucc_fast_free(priv->uccf);
+		priv->uccf = NULL;
+	}
+
+	if (priv->rx_buffer) {
+		dma_free_coherent(priv->dev,
+				  RX_BD_RING_LEN * MAX_RX_BUF_LENGTH,
+				  priv->rx_buffer, priv->dma_rx_addr);
+		priv->rx_buffer = NULL;
+		priv->dma_rx_addr = 0;
+	}
+
+	if (priv->tx_buffer) {
+		dma_free_coherent(priv->dev,
+				  TX_BD_RING_LEN * MAX_RX_BUF_LENGTH,
+				  priv->tx_buffer, priv->dma_tx_addr);
+		priv->tx_buffer = NULL;
+		priv->dma_tx_addr = 0;
+	}
+}
+
+static int uhdlc_close(struct net_device *dev)
+{
+	struct ucc_hdlc_private *priv = dev_to_hdlc(dev)->priv;
+	struct ucc_tdm *utdm = priv->utdm;
+	u32 cecr_subblock;
+
+	napi_disable(&priv->napi);
+	cecr_subblock = ucc_fast_get_qe_cr_subblock(
+				priv->ut_info->uf_info.ucc_num);
+
+	qe_issue_cmd(QE_GRACEFUL_STOP_TX, cecr_subblock,
+		     (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0);
+	qe_issue_cmd(QE_CLOSE_RX_BD, cecr_subblock,
+		     (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0);
+
+	if (priv->tsa)
+		utdm->si_regs->siglmr1_h &= ~(0x1 << utdm->tdm_port);
+
+	ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX);
+
+	free_irq(priv->ut_info->uf_info.irq, priv);
+	netif_stop_queue(dev);
+	priv->hdlc_busy = 0;
+
+	return 0;
+}
+
+static int ucc_hdlc_attach(struct net_device *dev, unsigned short encoding,
+			   unsigned short parity)
+{
+	struct ucc_hdlc_private *priv = dev_to_hdlc(dev)->priv;
+
+	if (encoding != ENCODING_NRZ &&
+	    encoding != ENCODING_NRZI)
+		return -EINVAL;
+
+	if (parity != PARITY_NONE &&
+	    parity != PARITY_CRC32_PR1_CCITT &&
+	    parity != PARITY_CRC16_PR1_CCITT)
+		return -EINVAL;
+
+	priv->encoding = encoding;
+	priv->parity = parity;
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static void store_clk_config(struct ucc_hdlc_private *priv)
+{
+	struct qe_mux *qe_mux_reg = &qe_immr->qmx;
+
+	/* store si clk */
+	priv->cmxsi1cr_h = ioread32be(&qe_mux_reg->cmxsi1cr_h);
+	priv->cmxsi1cr_l = ioread32be(&qe_mux_reg->cmxsi1cr_l);
+
+	/* store si sync */
+	priv->cmxsi1syr = ioread32be(&qe_mux_reg->cmxsi1syr);
+
+	/* store ucc clk */
+	memcpy_fromio(priv->cmxucr, qe_mux_reg->cmxucr, 4 * sizeof(u32));
+}
+
+static void resume_clk_config(struct ucc_hdlc_private *priv)
+{
+	struct qe_mux *qe_mux_reg = &qe_immr->qmx;
+
+	memcpy_toio(qe_mux_reg->cmxucr, priv->cmxucr, 4 * sizeof(u32));
+
+	iowrite32be(priv->cmxsi1cr_h, &qe_mux_reg->cmxsi1cr_h);
+	iowrite32be(priv->cmxsi1cr_l, &qe_mux_reg->cmxsi1cr_l);
+
+	iowrite32be(priv->cmxsi1syr, &qe_mux_reg->cmxsi1syr);
+}
+
+static int uhdlc_suspend(struct device *dev)
+{
+	struct ucc_hdlc_private *priv = dev_get_drvdata(dev);
+	struct ucc_tdm_info *ut_info;
+	struct ucc_fast __iomem *uf_regs;
+
+	if (!priv)
+		return -EINVAL;
+
+	if (!netif_running(priv->ndev))
+		return 0;
+
+	netif_device_detach(priv->ndev);
+	napi_disable(&priv->napi);
+
+	ut_info = priv->ut_info;
+	uf_regs = priv->uf_regs;
+
+	/* backup gumr guemr*/
+	priv->gumr = ioread32be(&uf_regs->gumr);
+	priv->guemr = ioread8(&uf_regs->guemr);
+
+	priv->ucc_pram_bak = kmalloc(sizeof(*priv->ucc_pram_bak),
+					GFP_KERNEL);
+	if (!priv->ucc_pram_bak)
+		return -ENOMEM;
+
+	/* backup HDLC parameter */
+	memcpy_fromio(priv->ucc_pram_bak, priv->ucc_pram,
+		      sizeof(struct ucc_hdlc_param));
+
+	/* store the clk configuration */
+	store_clk_config(priv);
+
+	/* save power */
+	ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX);
+
+	dev_dbg(dev, "ucc hdlc suspend\n");
+	return 0;
+}
+
+static int uhdlc_resume(struct device *dev)
+{
+	struct ucc_hdlc_private *priv = dev_get_drvdata(dev);
+	struct ucc_tdm *utdm = priv->utdm;
+	struct ucc_tdm_info *ut_info;
+	struct ucc_fast __iomem *uf_regs;
+	struct ucc_fast_private *uccf;
+	struct ucc_fast_info *uf_info;
+	int ret, i;
+	u32 cecr_subblock, bd_status;
+
+	if (!priv)
+		return -EINVAL;
+
+	if (!netif_running(priv->ndev))
+		return 0;
+
+	ut_info = priv->ut_info;
+	uf_info = &ut_info->uf_info;
+	uf_regs = priv->uf_regs;
+	uccf = priv->uccf;
+
+	/* restore gumr guemr */
+	iowrite8(priv->guemr, &uf_regs->guemr);
+	iowrite32be(priv->gumr, &uf_regs->gumr);
+
+	/* Set Virtual Fifo registers */
+	iowrite16be(uf_info->urfs, &uf_regs->urfs);
+	iowrite16be(uf_info->urfet, &uf_regs->urfet);
+	iowrite16be(uf_info->urfset, &uf_regs->urfset);
+	iowrite16be(uf_info->utfs, &uf_regs->utfs);
+	iowrite16be(uf_info->utfet, &uf_regs->utfet);
+	iowrite16be(uf_info->utftt, &uf_regs->utftt);
+	/* utfb, urfb are offsets from MURAM base */
+	iowrite32be(uccf->ucc_fast_tx_virtual_fifo_base_offset, &uf_regs->utfb);
+	iowrite32be(uccf->ucc_fast_rx_virtual_fifo_base_offset, &uf_regs->urfb);
+
+	/* Rx Tx and sync clock routing */
+	resume_clk_config(priv);
+
+	iowrite32be(uf_info->uccm_mask, &uf_regs->uccm);
+	iowrite32be(0xffffffff, &uf_regs->ucce);
+
+	ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX);
+
+	/* rebuild SIRAM */
+	if (priv->tsa)
+		ucc_tdm_init(priv->utdm, priv->ut_info);
+
+	/* Write to QE CECR, UCCx channel to Stop Transmission */
+	cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num);
+	ret = qe_issue_cmd(QE_STOP_TX, cecr_subblock,
+			   (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0);
+
+	/* Set UPSMR normal mode */
+	iowrite32be(0, &uf_regs->upsmr);
+
+	/* init parameter base */
+	cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num);
+	ret = qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock,
+			   QE_CR_PROTOCOL_UNSPECIFIED, priv->ucc_pram_offset);
+
+	priv->ucc_pram = (struct ucc_hdlc_param __iomem *)
+				qe_muram_addr(priv->ucc_pram_offset);
+
+	/* restore ucc parameter */
+	memcpy_toio(priv->ucc_pram, priv->ucc_pram_bak,
+		    sizeof(struct ucc_hdlc_param));
+	kfree(priv->ucc_pram_bak);
+
+	/* rebuild BD entry */
+	for (i = 0; i < RX_BD_RING_LEN; i++) {
+		if (i < (RX_BD_RING_LEN - 1))
+			bd_status = R_E | R_I;
+		else
+			bd_status = R_E | R_I | R_W;
+
+		iowrite32be(bd_status, (u32 *)(priv->rx_bd_base + i));
+		iowrite32be(priv->dma_rx_addr + i * MAX_RX_BUF_LENGTH,
+			    &priv->rx_bd_base[i].buf);
+	}
+
+	for (i = 0; i < TX_BD_RING_LEN; i++) {
+		if (i < (TX_BD_RING_LEN - 1))
+			bd_status =  T_I | T_TC;
+		else
+			bd_status =  T_I | T_TC | T_W;
+
+		iowrite32be(bd_status, (u32 *)(priv->tx_bd_base + i));
+		iowrite32be(priv->dma_tx_addr + i * MAX_RX_BUF_LENGTH,
+			    &priv->tx_bd_base[i].buf);
+	}
+
+	/* if hdlc is busy enable TX and RX */
+	if (priv->hdlc_busy == 1) {
+		cecr_subblock = ucc_fast_get_qe_cr_subblock(
+					priv->ut_info->uf_info.ucc_num);
+
+		qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock,
+			     (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0);
+
+		ucc_fast_enable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX);
+
+		/* Enable the TDM port */
+		if (priv->tsa)
+			utdm->si_regs->siglmr1_h |= (0x1 << utdm->tdm_port);
+	}
+
+	napi_enable(&priv->napi);
+	netif_device_attach(priv->ndev);
+
+	return 0;
+}
+
+static const struct dev_pm_ops uhdlc_pm_ops = {
+	.suspend = uhdlc_suspend,
+	.resume = uhdlc_resume,
+	.freeze = uhdlc_suspend,
+	.thaw = uhdlc_resume,
+};
+
+#define HDLC_PM_OPS (&uhdlc_pm_ops)
+
+#else
+
+#define HDLC_PM_OPS NULL
+
+#endif
+static const struct net_device_ops uhdlc_ops = {
+	.ndo_open       = uhdlc_open,
+	.ndo_stop       = uhdlc_close,
+	.ndo_change_mtu = hdlc_change_mtu,
+	.ndo_start_xmit = hdlc_start_xmit,
+	.ndo_do_ioctl   = uhdlc_ioctl,
+};
+
+static int ucc_hdlc_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct ucc_hdlc_private *uhdlc_priv = NULL;
+	struct ucc_tdm_info *ut_info;
+	struct ucc_tdm *utdm;
+	struct resource res;
+	struct net_device *dev;
+	hdlc_device *hdlc;
+	int ucc_num;
+	const char *sprop;
+	int ret;
+	u32 val;
+
+	ret = of_property_read_u32_index(np, "cell-index", 0, &val);
+	if (ret) {
+		dev_err(&pdev->dev, "Invalid ucc property\n");
+		return -ENODEV;
+	}
+
+	ucc_num = val - 1;
+	if ((ucc_num > 3) || (ucc_num < 0)) {
+		dev_err(&pdev->dev, ": Invalid UCC num\n");
+		return -EINVAL;
+	}
+
+	memcpy(&utdm_info[ucc_num], &utdm_primary_info,
+	       sizeof(utdm_primary_info));
+
+	ut_info = &utdm_info[ucc_num];
+	ut_info->uf_info.ucc_num = ucc_num;
+
+	sprop = of_get_property(np, "rx-clock-name", NULL);
+	if (sprop) {
+		ut_info->uf_info.rx_clock = qe_clock_source(sprop);
+		if ((ut_info->uf_info.rx_clock < QE_CLK_NONE) ||
+		    (ut_info->uf_info.rx_clock > QE_CLK24)) {
+			dev_err(&pdev->dev, "Invalid rx-clock-name property\n");
+			return -EINVAL;
+		}
+	} else {
+		dev_err(&pdev->dev, "Invalid rx-clock-name property\n");
+		return -EINVAL;
+	}
+
+	sprop = of_get_property(np, "tx-clock-name", NULL);
+	if (sprop) {
+		ut_info->uf_info.tx_clock = qe_clock_source(sprop);
+		if ((ut_info->uf_info.tx_clock < QE_CLK_NONE) ||
+		    (ut_info->uf_info.tx_clock > QE_CLK24)) {
+			dev_err(&pdev->dev, "Invalid tx-clock-name property\n");
+			return -EINVAL;
+		}
+	} else {
+		dev_err(&pdev->dev, "Invalid tx-clock-name property\n");
+		return -EINVAL;
+	}
+
+	/* use the same clock when work in loopback */
+	if (ut_info->uf_info.rx_clock == ut_info->uf_info.tx_clock)
+		qe_setbrg(ut_info->uf_info.rx_clock, 20000000, 1);
+
+	ret = of_address_to_resource(np, 0, &res);
+	if (ret)
+		return -EINVAL;
+
+	ut_info->uf_info.regs = res.start;
+	ut_info->uf_info.irq = irq_of_parse_and_map(np, 0);
+
+	uhdlc_priv = kzalloc(sizeof(*uhdlc_priv), GFP_KERNEL);
+	if (!uhdlc_priv) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "No mem to alloc hdlc private data\n");
+		goto err_alloc_priv;
+	}
+
+	dev_set_drvdata(&pdev->dev, uhdlc_priv);
+	uhdlc_priv->dev = &pdev->dev;
+	uhdlc_priv->ut_info = ut_info;
+
+	if (of_get_property(np, "fsl,tdm-interface", NULL))
+		uhdlc_priv->tsa = 1;
+
+	if (of_get_property(np, "fsl,ucc-internal-loopback", NULL))
+		uhdlc_priv->loopback = 1;
+
+	if (uhdlc_priv->tsa == 1) {
+		utdm = kzalloc(sizeof(*utdm), GFP_KERNEL);
+		if (!utdm) {
+			ret = -ENOMEM;
+			dev_err(&pdev->dev, "No mem to alloc ucc tdm data\n");
+			goto err_alloc_utdm;
+		}
+		uhdlc_priv->utdm = utdm;
+		ret = ucc_of_parse_tdm(np, utdm, ut_info);
+		if (ret)
+			goto err_miss_tsa_property;
+	}
+
+	ret = uhdlc_init(uhdlc_priv);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to init uhdlc\n");
+		goto err_hdlc_init;
+	}
+
+	dev = alloc_hdlcdev(uhdlc_priv);
+	if (!dev) {
+		ret = -ENOMEM;
+		pr_err("ucc_hdlc: unable to allocate memory\n");
+		goto err_hdlc_init;
+	}
+
+	uhdlc_priv->ndev = dev;
+	hdlc = dev_to_hdlc(dev);
+	dev->tx_queue_len = 16;
+	dev->netdev_ops = &uhdlc_ops;
+	hdlc->attach = ucc_hdlc_attach;
+	hdlc->xmit = ucc_hdlc_tx;
+	netif_napi_add(dev, &uhdlc_priv->napi, ucc_hdlc_poll, 32);
+	if (register_hdlc_device(dev)) {
+		ret = -ENOBUFS;
+		pr_err("ucc_hdlc: unable to register hdlc device\n");
+		free_netdev(dev);
+		goto err_hdlc_init;
+	}
+
+#ifdef DEBUG
+	dump_priv(uhdlc_priv);
+	dump_ucc(uhdlc_priv);
+	dump_bds(uhdlc_priv);
+	if (uhdlc_priv->tsa)
+		mem_disp((u8 *)uhdlc_priv->utdm->si_regs, 0x20);
+#endif
+
+	return 0;
+
+err_hdlc_init:
+err_miss_tsa_property:
+	kfree(uhdlc_priv);
+	if (uhdlc_priv->tsa)
+		kfree(utdm);
+err_alloc_utdm:
+	kfree(uhdlc_priv);
+err_alloc_priv:
+	return ret;
+}
+
+static int ucc_hdlc_remove(struct platform_device *pdev)
+{
+	struct ucc_hdlc_private *priv = dev_get_drvdata(&pdev->dev);
+
+	uhdlc_memclean(priv);
+
+	if (priv->utdm->si_regs) {
+		iounmap(priv->utdm->si_regs);
+		priv->utdm->si_regs = NULL;
+	}
+
+	if (priv->utdm->siram) {
+		iounmap(priv->utdm->siram);
+		priv->utdm->siram = NULL;
+	}
+	kfree(priv);
+
+	dev_info(&pdev->dev, "UCC based hdlc module removed\n");
+
+	return 0;
+}
+
+static const struct of_device_id fsl_ucc_hdlc_of_match[] = {
+	{
+	.compatible = "fsl,ucc-hdlc",
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, fsl_ucc_hdlc_of_match);
+
+static struct platform_driver ucc_hdlc_driver = {
+	.probe	= ucc_hdlc_probe,
+	.remove	= ucc_hdlc_remove,
+	.driver	= {
+		.owner		= THIS_MODULE,
+		.name		= DRV_NAME,
+		.pm		= HDLC_PM_OPS,
+		.of_match_table	= fsl_ucc_hdlc_of_match,
+	},
+};
+
+static int __init ucc_hdlc_init(void)
+{
+	return platform_driver_register(&ucc_hdlc_driver);
+}
+
+static void __exit ucc_hdlc_exit(void)
+{
+	platform_driver_unregister(&ucc_hdlc_driver);
+}
+
+module_init(ucc_hdlc_init);
+module_exit(ucc_hdlc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Freescale Semiconductor Inc.");
+MODULE_DESCRIPTION("Driver For Freescale QE UCC HDLC controller");
+MODULE_VERSION("1.0");
diff --git a/drivers/net/wan/fsl_ucc_hdlc.h b/drivers/net/wan/fsl_ucc_hdlc.h
new file mode 100644
index 0000000..ded03d6
--- /dev/null
+++ b/drivers/net/wan/fsl_ucc_hdlc.h
@@ -0,0 +1,140 @@
+/* Freescale QUICC Engine HDLC Device Driver
+ *
+ * Copyright 2014 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef CONFIG_UCC_HDLC_H
+#define CONFIG_UCC_HDLC_H
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+
+#include <soc/fsl/qe/immap_qe.h>
+#include <soc/fsl/qe/qe.h>
+
+#include <soc/fsl/qe/ucc.h>
+#include <soc/fsl/qe/ucc_fast.h>
+
+/* UCC HDLC event register */
+#define UCCE_HDLC_RX_EVENTS	\
+(UCC_HDLC_UCCE_RXF | UCC_HDLC_UCCE_RXB | UCC_HDLC_UCCE_BSY)
+#define UCCE_HDLC_TX_EVENTS	(UCC_HDLC_UCCE_TXB | UCC_HDLC_UCCE_TXE)
+
+struct ucc_hdlc_param {
+	__be16 riptr;
+	__be16 tiptr;
+	__be16 res0;
+	__be16 mrblr;
+	__be32 rstate;
+	__be32 rbase;
+	__be16 rbdstat;
+	__be16 rbdlen;
+	__be32 rdptr;
+	__be32 tstate;
+	__be32 tbase;
+	__be16 tbdstat;
+	__be16 tbdlen;
+	__be32 tdptr;
+	__be32 rbptr;
+	__be32 tbptr;
+	__be32 rcrc;
+	__be32 res1;
+	__be32 tcrc;
+	__be32 res2;
+	__be32 res3;
+	__be32 c_mask;
+	__be32 c_pres;
+	__be16 disfc;
+	__be16 crcec;
+	__be16 abtsc;
+	__be16 nmarc;
+	__be32 max_cnt;
+	__be16 mflr;
+	__be16 rfthr;
+	__be16 rfcnt;
+	__be16 hmask;
+	__be16 haddr1;
+	__be16 haddr2;
+	__be16 haddr3;
+	__be16 haddr4;
+	__be16 ts_tmp;
+	__be16 tmp_mb;
+} __attribute__ ((__packed__));
+
+struct ucc_hdlc_private {
+	struct ucc_tdm	*utdm;
+	struct ucc_tdm_info *ut_info;
+	struct ucc_fast_private *uccf;
+	struct device *dev;
+	struct net_device *ndev;
+	struct napi_struct napi;
+	struct ucc_fast __iomem *uf_regs;	/* UCC Fast registers */
+	struct ucc_hdlc_param __iomem *ucc_pram;
+	u16 tsa;
+	bool hdlc_busy;
+	u8 loopback;
+
+	u8 *tx_buffer;		/* buffer used for Tx by the HDLC */
+	u8 *rx_buffer;		/* buffer used for Rx by the HDLC */
+	dma_addr_t dma_tx_addr;	/* dma mapped buffer for HDLC Tx */
+	dma_addr_t dma_rx_addr;	/* dma mapped buffer for HDLC Rx */
+
+	struct qe_bd *tx_bd_base;
+	struct qe_bd *rx_bd_base;
+	dma_addr_t dma_tx_bd;
+	dma_addr_t dma_rx_bd;
+	struct qe_bd *curtx_bd;
+	struct qe_bd *currx_bd;
+	struct qe_bd *dirty_tx;
+	u16 currx_bdnum;
+
+	struct sk_buff **tx_skbuff;
+	struct sk_buff **rx_skbuff;
+	u16 skb_curtx;
+	u16 skb_currx;
+	unsigned short skb_dirtytx;
+
+	unsigned short tx_ring_size;
+	unsigned short rx_ring_size;
+	u32 ucc_pram_offset;
+
+	unsigned short encoding;
+	unsigned short parity;
+	u32 clocking;
+	spinlock_t lock;	/* lock for Tx BD and Tx buffer */
+#ifdef CONFIG_PM
+	struct ucc_hdlc_param *ucc_pram_bak;
+	u32 gumr;
+	u8 guemr;
+	u32 cmxsi1cr_l, cmxsi1cr_h;
+	u32 cmxsi1syr;
+	u32 cmxucr[4];
+#endif
+};
+
+#define TX_BD_RING_LEN	0x10
+#define RX_BD_RING_LEN	0x20
+#define RX_CLEAN_MAX	0x10
+#define NUM_OF_BUF	4
+#define MAX_RX_BUF_LENGTH	(48 * 0x20)
+#define ALIGNMENT_OF_UCC_HDLC_PRAM	64
+#define SI_BANK_SIZE	128
+#define MAX_HDLC_NUM	4
+#define HDLC_HEAD_LEN	2
+#define HDLC_CRC_SIZE	2
+#define TX_RING_MOD_MASK(size) (size - 1)
+#define RX_RING_MOD_MASK(size) (size - 1)
+
+#define HDLC_HEAD_MASK		0x0000
+#define DEFAULT_HDLC_HEAD	0xff44
+#define DEFAULT_ADDR_MASK	0x00ff
+#define DEFAULT_HDLC_ADDR	0x00ff
+
+#define DEFAULT_PPP_HEAD    0xff03
+
+#endif
diff --git a/include/soc/fsl/qe/ucc_fast.h b/include/soc/fsl/qe/ucc_fast.h
index e898895..d775550 100644
--- a/include/soc/fsl/qe/ucc_fast.h
+++ b/include/soc/fsl/qe/ucc_fast.h
@@ -27,12 +27,16 @@
 #define R_I	0x10000000	/* interrupt on reception */
 #define R_L	0x08000000	/* last */
 #define R_F	0x04000000	/* first */
+#define R_CM	0x02000000	/* first */
+#define R_CR	0x00040000	/* first */
 
 /* transmit BD's status */
 #define T_R	0x80000000	/* ready bit */
 #define T_W	0x20000000	/* wrap bit */
 #define T_I	0x10000000	/* interrupt on completion */
 #define T_L	0x08000000	/* last */
+#define T_TC	0x04000000	/* crc */
+#define T_TM	0x02000000	/* crc */
 
 /* Rx Data buffer must be 4 bytes aligned in most cases */
 #define UCC_FAST_RX_ALIGN			4
-- 
2.1.0.27.g96db324

^ permalink raw reply related

* Re: Question on rhashtable in worst-case scenario.
From: Johannes Berg @ 2016-03-30  9:14 UTC (permalink / raw)
  To: Ben Greear, Linux Kernel Mailing List, Herbert Xu,
	linux-wireless@vger.kernel.org, netdev
  Cc: Thomas Graf
In-Reply-To: <56FAAA6D.3070806@candelatech.com>

On Tue, 2016-03-29 at 09:16 -0700, Ben Greear wrote:
> Looks like rhashtable has too much policy in it to properly deal with
> cases where there are too many hash collisions, so I am going to work
> on reverting it's use in mac80211.

I'm not really all that happy with that approach - can't we fix the
rhashtable? It's a pretty rare corner case that many keys really are
identical and no kind of hash algorithm, but it seems much better to
still deal with it than to remove the rhashtable usage and go back to
hand-rolling something.

johannes

^ permalink raw reply

* Re: [PATCH] bus: mvebu-mbus: use %pad to print phys_addr_t
From: Gregory CLEMENT @ 2016-03-30  9:37 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: David S . Miller, Marcin Wojtas, Evan Wang, netdev,
	Thomas Petazzoni, Nicolas Schichan, linux-kernel
In-Reply-To: <3820422.5Ogyd0Xvfq@wuerfel>

Hi Arnd,
 
 On mar., mars 29 2016, Arnd Bergmann <arnd@arndb.de> wrote:

> On Tuesday 29 March 2016 18:04:47 Gregory CLEMENT wrote:
>> 
>> What is the status of this patch?
>> 
>> Do you plan to send a second version with the title fixed as suggested
>> by Joe Perches?
>> 
>> Also do you expect that I collect this patch in the mvebu subsystem?
>
> Right now, it's on my long-term todo list along with some 70 other patches
> I need to revisit. If you want to fix up the title and apply it now,
> that would be great, otherwise I'll get to it in a few weeks after coming
> back from ELC/vacation.


So I fixed the title and applied it on mvebu/fixes

Thanks,

Gregory

>
> 	ARnd

-- 
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

^ permalink raw reply

* RE: [PATCH] sctp: avoid refreshing heartbeat timer too often
From: David Laight @ 2016-03-30  9:37 UTC (permalink / raw)
  To: 'Marcelo Ricardo Leitner', netdev@vger.kernel.org
  Cc: Neil Horman, Vlad Yasevich, linux-sctp@vger.kernel.org
In-Reply-To: <1459258897-21607-1-git-send-email-marcelo.leitner@gmail.com>

From: Marcelo Ricardo Leitner
> Sent: 29 March 2016 14:42
>
> Currently on high rate SCTP streams the heartbeat timer refresh can
> consume quite a lot of resources as timer updates are costly and it
> contains a random factor, which a) is also costly and b) invalidates
> mod_timer() optimization for not editing a timer to the same value.
> It may even cause the timer to be slightly advanced, for no good reason.

Interesting thoughts:
1) Is it necessary to use a different 'random factor' until the timer actually
   expires?
2) It might be better to allow the heartbeat timer to expire, on expiry work
   out the new interval based on when the last 'refresh' was done.

	David

^ permalink raw reply

* Re: bpf: net/core/filter.c:2115 suspicious rcu_dereference_protected() usage!
From: Michal Kubecek @ 2016-03-30  9:42 UTC (permalink / raw)
  To: Daniel Borkmann
  Cc: Sasha Levin, Jiri Slaby, David S. Miller, ast,
	netdev@vger.kernel.org, LKML
In-Reply-To: <56FA93AF.8060001@iogearbox.net>

On Tue, Mar 29, 2016 at 04:39:43PM +0200, Daniel Borkmann wrote:
> >
> >>diff --git a/drivers/net/tun.c b/drivers/net/tun.c
> >>index afdf950617c3..7417d7c20bab 100644
> >>--- a/drivers/net/tun.c
> >>+++ b/drivers/net/tun.c
> >>@@ -1818,11 +1818,13 @@ static int set_offload(struct tun_struct *tun, unsigned long arg)
> >>  static void tun_detach_filter(struct tun_struct *tun, int n)
> >>  {
> >>      int i;
> >>-    struct tun_file *tfile;
> >>
> >>      for (i = 0; i < n; i++) {
> >>-        tfile = rtnl_dereference(tun->tfiles[i]);
> >>-        sk_detach_filter(tfile->socket.sk);
> >>+        struct sock *sk = rtnl_dereference(tun->tfiles[i])->socket.sk;
> >>+
> >>+        lock_sock(sk);
> >>+        sk_detach_filter(sk);
> >>+        release_sock(sk);
> >>      }
> >>
> >>      tun->filter_attached = false;
> >>
> >
> >In tun case, the control path for tun_attach_filter() and tun_detach_filter()
> >is under RTNL lock (held in __tun_chr_ioctl()).
> >
> >So in the BPF core the rcu_dereference_protected(<sk_filter>, sock_owned_by_user(sk))
> >looks like a false positive in this specific use case to me, that we should probably
> >just silence.
> >
> >Running the filter via sk_filter() in tun device happens under rcu_read_lock(),
> >so the dereference and assignment pair seems okay to me.
> >
> >Was wondering whether we should convert this to unattached BPF filter, but this
> >would break with existing expectations from sk_filter() (e.g. security modules).
> 
> If we want to silence it, could be something like the below (only compile-tested):
> 
>  drivers/net/tun.c      |  8 +++++---
>  include/linux/filter.h |  4 ++++
>  net/core/filter.c      | 33 +++++++++++++++++++++------------
>  3 files changed, 30 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/net/tun.c b/drivers/net/tun.c
> index afdf950..510e90a 100644
> --- a/drivers/net/tun.c
> +++ b/drivers/net/tun.c
> @@ -622,7 +622,8 @@ static int tun_attach(struct tun_struct *tun, struct file *file, bool skip_filte
> 
>  	/* Re-attach the filter to persist device */
>  	if (!skip_filter && (tun->filter_attached == true)) {
> -		err = sk_attach_filter(&tun->fprog, tfile->socket.sk);
> +		err = __sk_attach_filter(&tun->fprog, tfile->socket.sk,
> +					 lockdep_rtnl_is_held());
>  		if (!err)
>  			goto out;
>  	}
> @@ -1822,7 +1823,7 @@ static void tun_detach_filter(struct tun_struct *tun, int n)
> 
>  	for (i = 0; i < n; i++) {
>  		tfile = rtnl_dereference(tun->tfiles[i]);
> -		sk_detach_filter(tfile->socket.sk);
> +		__sk_detach_filter(tfile->socket.sk, lockdep_rtnl_is_held());
>  	}
> 
>  	tun->filter_attached = false;
> @@ -1835,7 +1836,8 @@ static int tun_attach_filter(struct tun_struct *tun)
> 
>  	for (i = 0; i < tun->numqueues; i++) {
>  		tfile = rtnl_dereference(tun->tfiles[i]);
> -		ret = sk_attach_filter(&tun->fprog, tfile->socket.sk);
> +		ret = __sk_attach_filter(&tun->fprog, tfile->socket.sk,
> +					 lockdep_rtnl_is_held());
>  		if (ret) {
>  			tun_detach_filter(tun, i);
>  			return ret;
> diff --git a/include/linux/filter.h b/include/linux/filter.h
> index 43aa1f8..a51a536 100644
> --- a/include/linux/filter.h
> +++ b/include/linux/filter.h
> @@ -465,10 +465,14 @@ int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog,
>  void bpf_prog_destroy(struct bpf_prog *fp);
> 
>  int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
> +int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk,
> +		       bool locked);
>  int sk_attach_bpf(u32 ufd, struct sock *sk);
>  int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk);
>  int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk);
>  int sk_detach_filter(struct sock *sk);
> +int __sk_detach_filter(struct sock *sk, bool locked);
> +
>  int sk_get_filter(struct sock *sk, struct sock_filter __user *filter,
>  		  unsigned int len);
> 
> diff --git a/net/core/filter.c b/net/core/filter.c
> index 2429918..02f2f6c 100644
> --- a/net/core/filter.c
> +++ b/net/core/filter.c
> @@ -1149,7 +1149,8 @@ void bpf_prog_destroy(struct bpf_prog *fp)
>  }
>  EXPORT_SYMBOL_GPL(bpf_prog_destroy);
> 
> -static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk)
> +static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk,
> +			    bool locked)
>  {
>  	struct sk_filter *fp, *old_fp;
> 
> @@ -1165,10 +1166,8 @@ static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk)
>  		return -ENOMEM;
>  	}
> 
> -	old_fp = rcu_dereference_protected(sk->sk_filter,
> -					   sock_owned_by_user(sk));
> +	old_fp = rcu_dereference_protected(sk->sk_filter, locked);
>  	rcu_assign_pointer(sk->sk_filter, fp);
> -
>  	if (old_fp)
>  		sk_filter_uncharge(sk, old_fp);
> 
> @@ -1247,7 +1246,8 @@ struct bpf_prog *__get_filter(struct sock_fprog *fprog, struct sock *sk)
>   * occurs or there is insufficient memory for the filter a negative
>   * errno code is returned. On success the return is zero.
>   */
> -int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
> +int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk,
> +		       bool locked)
>  {
>  	struct bpf_prog *prog = __get_filter(fprog, sk);
>  	int err;
> @@ -1255,7 +1255,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
>  	if (IS_ERR(prog))
>  		return PTR_ERR(prog);
> 
> -	err = __sk_attach_prog(prog, sk);
> +	err = __sk_attach_prog(prog, sk, locked);
>  	if (err < 0) {
>  		__bpf_prog_release(prog);
>  		return err;
> @@ -1263,7 +1263,12 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
> 
>  	return 0;
>  }
> -EXPORT_SYMBOL_GPL(sk_attach_filter);
> +EXPORT_SYMBOL_GPL(__sk_attach_filter);
> +
> +int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
> +{
> +	return __sk_attach_filter(fprog, sk, sock_owned_by_user(sk));
> +}
> 
>  int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk)
>  {
> @@ -1309,7 +1314,7 @@ int sk_attach_bpf(u32 ufd, struct sock *sk)
>  	if (IS_ERR(prog))
>  		return PTR_ERR(prog);
> 
> -	err = __sk_attach_prog(prog, sk);
> +	err = __sk_attach_prog(prog, sk, sock_owned_by_user(sk));
>  	if (err < 0) {
>  		bpf_prog_put(prog);
>  		return err;
> @@ -2445,7 +2450,7 @@ static int __init register_sk_filter_ops(void)
>  }
>  late_initcall(register_sk_filter_ops);
> 
> -int sk_detach_filter(struct sock *sk)
> +int __sk_detach_filter(struct sock *sk, bool locked)
>  {
>  	int ret = -ENOENT;
>  	struct sk_filter *filter;
> @@ -2453,8 +2458,7 @@ int sk_detach_filter(struct sock *sk)
>  	if (sock_flag(sk, SOCK_FILTER_LOCKED))
>  		return -EPERM;
> 
> -	filter = rcu_dereference_protected(sk->sk_filter,
> -					   sock_owned_by_user(sk));
> +	filter = rcu_dereference_protected(sk->sk_filter, locked);
>  	if (filter) {
>  		RCU_INIT_POINTER(sk->sk_filter, NULL);
>  		sk_filter_uncharge(sk, filter);
> @@ -2463,7 +2467,12 @@ int sk_detach_filter(struct sock *sk)
> 
>  	return ret;
>  }
> -EXPORT_SYMBOL_GPL(sk_detach_filter);
> +EXPORT_SYMBOL_GPL(__sk_detach_filter);
> +
> +int sk_detach_filter(struct sock *sk)
> +{
> +	return __sk_detach_filter(sk, sock_owned_by_user(sk));
> +}
> 
>  int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf,
>  		  unsigned int len)
> -- 
> 1.9.3

Looks good to me.

I'm just not sure checking if we hold the right lock depending on caller
is worth the extra complexity. After all, what is really needed is to
hold _some_ lock guaranteeing sk_attach_prog() and sk_detach_filter()
are safe so that just changing the condition in both to

  sock_owned_by_user(sk) || lockdep_rtnl_is_held()

could suffice.

                                                        Michal Kubecek

^ permalink raw reply

* Re: [PATCH 5/5] drivers/net: support hdlc function for QE-UCC
From: kbuild test robot @ 2016-03-30  9:53 UTC (permalink / raw)
  To: Zhao Qiang
  Cc: kbuild-all, davem, akpm, gregkh, oss, xiaobo.xie, linux-kernel,
	netdev, linuxppc-dev, Zhao Qiang
In-Reply-To: <1459327830-19829-5-git-send-email-qiang.zhao@nxp.com>

[-- Attachment #1: Type: text/plain, Size: 928 bytes --]

Hi Zhao,

[auto build test WARNING on net/master]
[also build test WARNING on v4.6-rc1 next-20160330]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Zhao-Qiang/fsl-qe-add-rx_sync-and-tx_sync-for-TDM-mode/20160330-170411
config: xtensa-allyesconfig (attached as .config)
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=xtensa 

All warnings (new ones prefixed by >>):

warning: (FSL_UCC_HDLC) selects QUICC_ENGINE which has unmet direct dependencies (FSL_SOC && PPC32)

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 44940 bytes --]

^ permalink raw reply

* Re: [PATCH net-next] net: hns: add support of pause frame ctrl for HNS V2
From: Andy Shevchenko @ 2016-03-30 10:51 UTC (permalink / raw)
  To: Yisen Zhuang, davem, salil.mehta, liguozhu, huangdaode, arnd,
	andrew, geliangtang, ivecera, lisheng011, fengguang.wu
  Cc: charles.chenxin, haifeng.wei, netdev, linux-kernel,
	linux-arm-kernel, linuxarm
In-Reply-To: <1459235041-91766-1-git-send-email-Yisen.Zhuang@huawei.com>

On Tue, 2016-03-29 at 15:04 +0800, Yisen Zhuang wrote:
> From: Lisheng <lisheng011@huawei.com>
> 
> The patch adds support of pause ctrl for HNS V2, and this feature is
> lost
> by HNS V1:
>        1) service ports can disable rx pause frame,
>        2) debug ports can open tx/rx pause frame.
> 
> And this patch updates the REGs about the pause ctrl when updated
> status function called by upper layer routine.

> --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c
> +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c
> 

> +void hns_dsaf_get_rx_mac_pause_en(struct dsaf_device *dsaf_dev, int
> mac_id,
> +				  u32 *en)
> +{
> +	if (AE_IS_VER1(dsaf_dev->dsaf_ver))
> +		*en = 1;
>  	else
>  		dsaf_write_dev(dsaf_dev, DSAF_PFC_EN_0_REG + mac_id
> * 4, 0xff);
> +		*en = dsaf_get_dev_bit(dsaf_dev,
> +				       DSAF_PAUSE_CFG_REG + mac_id *
> 4,
> +				       DSAF_MAC_PAUSE_RX_EN_B);

And what the point of if branch then? I think it's an obvious misfix,
you must replace else to } else { and add }.

How did you test this part?

>  }


-- 
Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Intel Finland Oy

^ permalink raw reply

* Re: bpf: net/core/filter.c:2115 suspicious rcu_dereference_protected() usage!
From: Daniel Borkmann @ 2016-03-30 11:33 UTC (permalink / raw)
  To: Michal Kubecek
  Cc: Sasha Levin, Jiri Slaby, David S. Miller, ast,
	netdev@vger.kernel.org, LKML
In-Reply-To: <20160330094224.GC15048@unicorn.suse.cz>

On 03/30/2016 11:42 AM, Michal Kubecek wrote:
> On Tue, Mar 29, 2016 at 04:39:43PM +0200, Daniel Borkmann wrote:
>>>
>>>> diff --git a/drivers/net/tun.c b/drivers/net/tun.c
>>>> index afdf950617c3..7417d7c20bab 100644
>>>> --- a/drivers/net/tun.c
>>>> +++ b/drivers/net/tun.c
>>>> @@ -1818,11 +1818,13 @@ static int set_offload(struct tun_struct *tun, unsigned long arg)
>>>>   static void tun_detach_filter(struct tun_struct *tun, int n)
>>>>   {
>>>>       int i;
>>>> -    struct tun_file *tfile;
>>>>
>>>>       for (i = 0; i < n; i++) {
>>>> -        tfile = rtnl_dereference(tun->tfiles[i]);
>>>> -        sk_detach_filter(tfile->socket.sk);
>>>> +        struct sock *sk = rtnl_dereference(tun->tfiles[i])->socket.sk;
>>>> +
>>>> +        lock_sock(sk);
>>>> +        sk_detach_filter(sk);
>>>> +        release_sock(sk);
>>>>       }
>>>>
>>>>       tun->filter_attached = false;
>>>>
>>>
>>> In tun case, the control path for tun_attach_filter() and tun_detach_filter()
>>> is under RTNL lock (held in __tun_chr_ioctl()).
>>>
>>> So in the BPF core the rcu_dereference_protected(<sk_filter>, sock_owned_by_user(sk))
>>> looks like a false positive in this specific use case to me, that we should probably
>>> just silence.
>>>
>>> Running the filter via sk_filter() in tun device happens under rcu_read_lock(),
>>> so the dereference and assignment pair seems okay to me.
>>>
>>> Was wondering whether we should convert this to unattached BPF filter, but this
>>> would break with existing expectations from sk_filter() (e.g. security modules).
>>
>> If we want to silence it, could be something like the below (only compile-tested):
>>
>>   drivers/net/tun.c      |  8 +++++---
>>   include/linux/filter.h |  4 ++++
>>   net/core/filter.c      | 33 +++++++++++++++++++++------------
>>   3 files changed, 30 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/net/tun.c b/drivers/net/tun.c
>> index afdf950..510e90a 100644
>> --- a/drivers/net/tun.c
>> +++ b/drivers/net/tun.c
>> @@ -622,7 +622,8 @@ static int tun_attach(struct tun_struct *tun, struct file *file, bool skip_filte
>>
>>   	/* Re-attach the filter to persist device */
>>   	if (!skip_filter && (tun->filter_attached == true)) {
>> -		err = sk_attach_filter(&tun->fprog, tfile->socket.sk);
>> +		err = __sk_attach_filter(&tun->fprog, tfile->socket.sk,
>> +					 lockdep_rtnl_is_held());
>>   		if (!err)
>>   			goto out;
>>   	}
>> @@ -1822,7 +1823,7 @@ static void tun_detach_filter(struct tun_struct *tun, int n)
>>
>>   	for (i = 0; i < n; i++) {
>>   		tfile = rtnl_dereference(tun->tfiles[i]);
>> -		sk_detach_filter(tfile->socket.sk);
>> +		__sk_detach_filter(tfile->socket.sk, lockdep_rtnl_is_held());
>>   	}
>>
>>   	tun->filter_attached = false;
>> @@ -1835,7 +1836,8 @@ static int tun_attach_filter(struct tun_struct *tun)
>>
>>   	for (i = 0; i < tun->numqueues; i++) {
>>   		tfile = rtnl_dereference(tun->tfiles[i]);
>> -		ret = sk_attach_filter(&tun->fprog, tfile->socket.sk);
>> +		ret = __sk_attach_filter(&tun->fprog, tfile->socket.sk,
>> +					 lockdep_rtnl_is_held());
>>   		if (ret) {
>>   			tun_detach_filter(tun, i);
>>   			return ret;
>> diff --git a/include/linux/filter.h b/include/linux/filter.h
>> index 43aa1f8..a51a536 100644
>> --- a/include/linux/filter.h
>> +++ b/include/linux/filter.h
>> @@ -465,10 +465,14 @@ int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog,
>>   void bpf_prog_destroy(struct bpf_prog *fp);
>>
>>   int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
>> +int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk,
>> +		       bool locked);
>>   int sk_attach_bpf(u32 ufd, struct sock *sk);
>>   int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk);
>>   int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk);
>>   int sk_detach_filter(struct sock *sk);
>> +int __sk_detach_filter(struct sock *sk, bool locked);
>> +
>>   int sk_get_filter(struct sock *sk, struct sock_filter __user *filter,
>>   		  unsigned int len);
>>
>> diff --git a/net/core/filter.c b/net/core/filter.c
>> index 2429918..02f2f6c 100644
>> --- a/net/core/filter.c
>> +++ b/net/core/filter.c
>> @@ -1149,7 +1149,8 @@ void bpf_prog_destroy(struct bpf_prog *fp)
>>   }
>>   EXPORT_SYMBOL_GPL(bpf_prog_destroy);
>>
>> -static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk)
>> +static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk,
>> +			    bool locked)
>>   {
>>   	struct sk_filter *fp, *old_fp;
>>
>> @@ -1165,10 +1166,8 @@ static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk)
>>   		return -ENOMEM;
>>   	}
>>
>> -	old_fp = rcu_dereference_protected(sk->sk_filter,
>> -					   sock_owned_by_user(sk));
>> +	old_fp = rcu_dereference_protected(sk->sk_filter, locked);
>>   	rcu_assign_pointer(sk->sk_filter, fp);
>> -
>>   	if (old_fp)
>>   		sk_filter_uncharge(sk, old_fp);
>>
>> @@ -1247,7 +1246,8 @@ struct bpf_prog *__get_filter(struct sock_fprog *fprog, struct sock *sk)
>>    * occurs or there is insufficient memory for the filter a negative
>>    * errno code is returned. On success the return is zero.
>>    */
>> -int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
>> +int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk,
>> +		       bool locked)
>>   {
>>   	struct bpf_prog *prog = __get_filter(fprog, sk);
>>   	int err;
>> @@ -1255,7 +1255,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
>>   	if (IS_ERR(prog))
>>   		return PTR_ERR(prog);
>>
>> -	err = __sk_attach_prog(prog, sk);
>> +	err = __sk_attach_prog(prog, sk, locked);
>>   	if (err < 0) {
>>   		__bpf_prog_release(prog);
>>   		return err;
>> @@ -1263,7 +1263,12 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
>>
>>   	return 0;
>>   }
>> -EXPORT_SYMBOL_GPL(sk_attach_filter);
>> +EXPORT_SYMBOL_GPL(__sk_attach_filter);
>> +
>> +int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
>> +{
>> +	return __sk_attach_filter(fprog, sk, sock_owned_by_user(sk));
>> +}
>>
>>   int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk)
>>   {
>> @@ -1309,7 +1314,7 @@ int sk_attach_bpf(u32 ufd, struct sock *sk)
>>   	if (IS_ERR(prog))
>>   		return PTR_ERR(prog);
>>
>> -	err = __sk_attach_prog(prog, sk);
>> +	err = __sk_attach_prog(prog, sk, sock_owned_by_user(sk));
>>   	if (err < 0) {
>>   		bpf_prog_put(prog);
>>   		return err;
>> @@ -2445,7 +2450,7 @@ static int __init register_sk_filter_ops(void)
>>   }
>>   late_initcall(register_sk_filter_ops);
>>
>> -int sk_detach_filter(struct sock *sk)
>> +int __sk_detach_filter(struct sock *sk, bool locked)
>>   {
>>   	int ret = -ENOENT;
>>   	struct sk_filter *filter;
>> @@ -2453,8 +2458,7 @@ int sk_detach_filter(struct sock *sk)
>>   	if (sock_flag(sk, SOCK_FILTER_LOCKED))
>>   		return -EPERM;
>>
>> -	filter = rcu_dereference_protected(sk->sk_filter,
>> -					   sock_owned_by_user(sk));
>> +	filter = rcu_dereference_protected(sk->sk_filter, locked);
>>   	if (filter) {
>>   		RCU_INIT_POINTER(sk->sk_filter, NULL);
>>   		sk_filter_uncharge(sk, filter);
>> @@ -2463,7 +2467,12 @@ int sk_detach_filter(struct sock *sk)
>>
>>   	return ret;
>>   }
>> -EXPORT_SYMBOL_GPL(sk_detach_filter);
>> +EXPORT_SYMBOL_GPL(__sk_detach_filter);
>> +
>> +int sk_detach_filter(struct sock *sk)
>> +{
>> +	return __sk_detach_filter(sk, sock_owned_by_user(sk));
>> +}
>>
>>   int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf,
>>   		  unsigned int len)
>> --
>> 1.9.3
>
> Looks good to me.
>
> I'm just not sure checking if we hold the right lock depending on caller
> is worth the extra complexity. After all, what is really needed is to
> hold _some_ lock guaranteeing sk_attach_prog() and sk_detach_filter()
> are safe so that just changing the condition in both to
>
>    sock_owned_by_user(sk) || lockdep_rtnl_is_held()

It would certainly silence it, but would be less accurate in terms of lock
proving as opposed to the diff above. E.g. rntl could be held elsewhere,
while someone attaches a socket filter w/o having locked the socket (currently
not the case, but it would kind of defeat the purpose of rcu_dereference_protected()
here). Was thinking about using a extra socket flag to indicate it's
externally managed, but it's not really worth wasting sk's flags bit
space just for this corner case.

> could suffice.
>
>                                                          Michal Kubecek
>

^ permalink raw reply

* Re: [PATCH] sctp: really allow using GFP_KERNEL on sctp_packet_transmit
From: Neil Horman @ 2016-03-30 11:45 UTC (permalink / raw)
  To: Marcelo Ricardo Leitner; +Cc: netdev, Vlad Yasevich, linux-sctp
In-Reply-To: <1459258863-21351-1-git-send-email-marcelo.leitner@gmail.com>

On Tue, Mar 29, 2016 at 10:41:03AM -0300, Marcelo Ricardo Leitner wrote:
> Somehow my patch for commit cea8768f333e ("sctp: allow
> sctp_transmit_packet and others to use gfp") missed two important
> chunks, which are now added.
> 
> Fixes: cea8768f333e ("sctp: allow sctp_transmit_packet and others to use gfp")
> Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
> ---
>  net/sctp/output.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/net/sctp/output.c b/net/sctp/output.c
> index 736c004abfbc2787a3c50fa85168ebdf3b112787..97745351d58c2fb32b9f9b57d61831d7724d83b2 100644
> --- a/net/sctp/output.c
> +++ b/net/sctp/output.c
> @@ -401,7 +401,7 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
>  	sk = chunk->skb->sk;
>  
>  	/* Allocate the new skb.  */
> -	nskb = alloc_skb(packet->size + MAX_HEADER, GFP_ATOMIC);
> +	nskb = alloc_skb(packet->size + MAX_HEADER, gfp);
>  	if (!nskb)
>  		goto nomem;
>  
> @@ -523,8 +523,8 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
>  	 */
>  	if (auth)
>  		sctp_auth_calculate_hmac(asoc, nskb,
> -					(struct sctp_auth_chunk *)auth,
> -					GFP_ATOMIC);
> +					 (struct sctp_auth_chunk *)auth,
> +					 gfp);
>  
>  	/* 2) Calculate the Adler-32 checksum of the whole packet,
>  	 *    including the SCTP common header and all the
> -- 
> 2.5.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-sctp" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
Acked-By: Neil Horman <nhorman@tuxdriver.com>

^ permalink raw reply

* Re: [PATCH 4/5] fsl/qe: Add QE TDM lib
From: Joakim Tjernlund @ 2016-03-30 11:50 UTC (permalink / raw)
  To: davem@davemloft.net, qiang.zhao@nxp.com
  Cc: linuxppc-dev@lists.ozlabs.org, linux-kernel@vger.kernel.org,
	xiaobo.xie@nxp.com, oss@buserror.net, gregkh@linuxfoundation.org,
	akpm@linux-foundation.org, netdev@vger.kernel.org
In-Reply-To: <1459327830-19829-4-git-send-email-qiang.zhao@nxp.com>

On Wed, 2016-03-30 at 16:50 +0800, Zhao Qiang wrote:
> QE has module to support TDM, some other protocols
> supported by QE are based on TDM.
> add a qe-tdm lib, this lib provides functions to the protocols
> using TDM to configurate QE-TDM.
> 
> Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com>
> ---
>  drivers/soc/fsl/qe/Kconfig    |   4 +
>  drivers/soc/fsl/qe/Makefile   |   1 +
>  drivers/soc/fsl/qe/qe_tdm.c   | 271 ++++++++++++++++++++++++++++++++++++++++++
>  include/soc/fsl/qe/immap_qe.h |   5 +-
>  include/soc/fsl/qe/qe_tdm.h   |  94 +++++++++++++++
>  5 files changed, 371 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/soc/fsl/qe/qe_tdm.c
>  create mode 100644 include/soc/fsl/qe/qe_tdm.h
> 
> diff --git a/drivers/soc/fsl/qe/Kconfig b/drivers/soc/fsl/qe/Kconfig
> index 20978f2..463cf29 100644
> --- a/drivers/soc/fsl/qe/Kconfig
> +++ b/drivers/soc/fsl/qe/Kconfig
> @@ -31,6 +31,10 @@ config UCC
>  	bool
>  	default y if UCC_FAST || UCC_SLOW
>  
> +config QE_TDM
> +	bool
> +	select UCC_FAST
> +
>  config QE_USB
>  	bool
>  	default y if USB_FSL_QE
> diff --git a/drivers/soc/fsl/qe/Makefile b/drivers/soc/fsl/qe/Makefile
> index ffac541..2031d38 100644
> --- a/drivers/soc/fsl/qe/Makefile
> +++ b/drivers/soc/fsl/qe/Makefile
> @@ -6,5 +6,6 @@ obj-$(CONFIG_CPM)	+= qe_common.o
>  obj-$(CONFIG_UCC)	+= ucc.o
>  obj-$(CONFIG_UCC_SLOW)	+= ucc_slow.o
>  obj-$(CONFIG_UCC_FAST)	+= ucc_fast.o
> +obj-$(CONFIG_QE_TDM)	+= qe_tdm.o
>  obj-$(CONFIG_QE_USB)	+= usb.o
>  obj-$(CONFIG_QE_GPIO)	+= gpio.o
> diff --git a/drivers/soc/fsl/qe/qe_tdm.c b/drivers/soc/fsl/qe/qe_tdm.c
> new file mode 100644
> index 0000000..9a2374d
> --- /dev/null
> +++ b/drivers/soc/fsl/qe/qe_tdm.c
> @@ -0,0 +1,271 @@
> +/*
> + * Copyright (C) 2015 Freescale Semiconductor, Inc. All rights reserved.
> + *
> + * Authors:	Zhao Qiang <qiang.zhao@nxp.com>
> + *
> + * Description:
> + * QE TDM API Set - TDM specific routines implementations.
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <soc/fsl/qe/qe_tdm.h>
> +
> +static enum tdm_framer_t set_tdm_framer(const char *tdm_framer_type)
> +{
> +	if (strcmp(tdm_framer_type, "e1") == 0)
> +		return TDM_FRAMER_E1;
> +	else
> +		return TDM_FRAMER_T1;
> +}
> +
> +static void set_si_param(struct ucc_tdm *utdm, struct ucc_tdm_info *ut_info)
> +{
> +	struct si_mode_info *si_info = &ut_info->si_info;
> +
> +	if (utdm->tdm_mode == TDM_INTERNAL_LOOPBACK) {
> +		si_info->simr_crt = 1;
> +		si_info->simr_rfsd = 0;
> +	}
> +}
> +
> +int ucc_of_parse_tdm(struct device_node *np, struct ucc_tdm *utdm,
> +		     struct ucc_tdm_info *ut_info)
> +{
> +	const char *sprop;
> +	int ret = 0;
> +	u32 val;
> +	struct resource *res;
> +	struct device_node *np2;
> +	static int siram_init_flag;
> +	struct platform_device *pdev;
> +
> +	sprop = of_get_property(np, "fsl,rx-sync-clock", NULL);
> +	if (sprop) {
> +		ut_info->uf_info.rx_sync = qe_clock_source(sprop);
> +		if ((ut_info->uf_info.rx_sync < QE_CLK_NONE) ||
> +		    (ut_info->uf_info.rx_sync > QE_RSYNC_PIN)) {
> +			pr_err("QE-TDM: Invalid rx-sync-clock property\n");
> +			return -EINVAL;
> +		}
> +	} else {
> +		pr_err("QE-TDM: Invalid rx-sync-clock property\n");
> +		return -EINVAL;
> +	}
> +
> +	sprop = of_get_property(np, "fsl,tx-sync-clock", NULL);
> +	if (sprop) {
> +		ut_info->uf_info.tx_sync = qe_clock_source(sprop);
> +		if ((ut_info->uf_info.tx_sync < QE_CLK_NONE) ||
> +		    (ut_info->uf_info.tx_sync > QE_TSYNC_PIN)) {
> +			pr_err("QE-TDM: Invalid tx-sync-clock property\n");
> +		return -EINVAL;
> +		}
> +	} else {
> +		pr_err("QE-TDM: Invalid tx-sync-clock property\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = of_property_read_u32_index(np, "fsl,tx-timeslot-mask", 0, &val);
> +	if (ret) {
> +		pr_err("QE-TDM: Invalid tx-timeslot-mask property\n");
> +		return -EINVAL;
> +	}
> +	utdm->tx_ts_mask = val;
> +
> +	ret = of_property_read_u32_index(np, "fsl,rx-timeslot-mask", 0, &val);
> +	if (ret) {
> +		ret = -EINVAL;
> +		pr_err("QE-TDM: Invalid rx-timeslot-mask property\n");
> +		return ret;
> +	}
> +	utdm->rx_ts_mask = val;
> +
> +	ret = of_property_read_u32_index(np, "fsl,tdm-id", 0, &val);
> +	if (ret) {
> +		ret = -EINVAL;
> +		pr_err("QE-TDM: No fsl,tdm-id property for this UCC\n");
> +		return ret;
> +	}
> +	utdm->tdm_port = val;
> +	ut_info->uf_info.tdm_num = utdm->tdm_port;
> +
> +	if (of_get_property(np, "fsl,tdm-internal-loopback", NULL))
> +		utdm->tdm_mode = TDM_INTERNAL_LOOPBACK;
> +	else
> +		utdm->tdm_mode = TDM_NORMAL;
> +
> +	sprop = of_get_property(np, "fsl,tdm-framer-type", NULL);
> +	if (!sprop) {
> +		ret = -EINVAL;
> +		pr_err("QE-TDM: No tdm-framer-type property for UCC\n");
> +		return ret;
> +	}
> +	utdm->tdm_framer_type = set_tdm_framer(sprop);
> +
> +	ret = of_property_read_u32_index(np, "fsl,siram-entry-id", 0, &val);
> +	if (ret) {
> +		ret = -EINVAL;
> +		pr_err("QE-TDM: No siram entry id for UCC\n");
> +		return ret;
> +	}
> +	utdm->siram_entry_id = val;
> +
> +	set_si_param(utdm, ut_info);
> +
> +	np2 = of_find_compatible_node(NULL, NULL, "fsl,t1040-qe-si");

fsl,t1040-qe-si only? What about mpc83xx?
I recall QE is a little bit different compared to T1040 or will this work(including the hdlc driver)
on 83xx as well?

 Jocke 

^ permalink raw reply

* Re: [PATCH 0/5] wireless: ti: Convert specialized logging macros to kernel style
From: Kalle Valo @ 2016-03-30 11:51 UTC (permalink / raw)
  To: Joe Perches; +Cc: linux-kernel, linux-wireless, netdev
In-Reply-To: <cover.1457395071.git.joe@perches.com>

Joe Perches <joe@perches.com> writes:

> Using the normal kernel logging mechanisms makes this code
> a bit more like other wireless drivers.

Personally I don't see the point but I don't have any strong opinions. A
bigger problem is that TI drivers are not really in active development
and that's I'm not thrilled to take big patches like this for dormant
drivers.

-- 
Kalle Valo

^ permalink raw reply

* [PATCH] net: mvneta: replace MVNETA_CPU_D_CACHE_LINE_SIZE with L1_CACHE_BYTES
From: Jisheng Zhang @ 2016-03-30 11:55 UTC (permalink / raw)
  To: thomas.petazzoni, davem
  Cc: netdev, linux-kernel, linux-arm-kernel, Jisheng Zhang

The mvneta is also used in some Marvell berlin family SoCs which may
have 64bytes cacheline size. Replace the MVNETA_CPU_D_CACHE_LINE_SIZE
usage with L1_CACHE_BYTES.

And since dma_alloc_coherent() is always cacheline size aligned, so
remove the align checks.

Signed-off-by: Jisheng Zhang <jszhang@marvell.com>
---
 drivers/net/ethernet/marvell/mvneta.c | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 577f7ca..5880871 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -260,7 +260,6 @@
 
 #define MVNETA_VLAN_TAG_LEN             4
 
-#define MVNETA_CPU_D_CACHE_LINE_SIZE    32
 #define MVNETA_TX_CSUM_DEF_SIZE		1600
 #define MVNETA_TX_CSUM_MAX_SIZE		9800
 #define MVNETA_ACC_MODE_EXT1		1
@@ -300,7 +299,7 @@
 #define MVNETA_RX_PKT_SIZE(mtu) \
 	ALIGN((mtu) + MVNETA_MH_SIZE + MVNETA_VLAN_TAG_LEN + \
 	      ETH_HLEN + ETH_FCS_LEN,			     \
-	      MVNETA_CPU_D_CACHE_LINE_SIZE)
+	      L1_CACHE_BYTES)
 
 #define IS_TSO_HEADER(txq, addr) \
 	((addr >= txq->tso_hdrs_phys) && \
@@ -2764,9 +2763,6 @@ static int mvneta_rxq_init(struct mvneta_port *pp,
 	if (rxq->descs == NULL)
 		return -ENOMEM;
 
-	BUG_ON(rxq->descs !=
-	       PTR_ALIGN(rxq->descs, MVNETA_CPU_D_CACHE_LINE_SIZE));
-
 	rxq->last_desc = rxq->size - 1;
 
 	/* Set Rx descriptors queue starting address */
@@ -2837,10 +2833,6 @@ static int mvneta_txq_init(struct mvneta_port *pp,
 	if (txq->descs == NULL)
 		return -ENOMEM;
 
-	/* Make sure descriptor address is cache line size aligned  */
-	BUG_ON(txq->descs !=
-	       PTR_ALIGN(txq->descs, MVNETA_CPU_D_CACHE_LINE_SIZE));
-
 	txq->last_desc = txq->size - 1;
 
 	/* Set maximum bandwidth for enabled TXQs */
-- 
2.8.0.rc3

^ permalink raw reply related

* [PATCH] net: mvpp2: replace MVPP2_CPU_D_CACHE_LINE_SIZE with L1_CACHE_BYTES
From: Jisheng Zhang @ 2016-03-30 11:53 UTC (permalink / raw)
  To: davem; +Cc: netdev, linux-kernel, linux-arm-kernel, Jisheng Zhang

The mvpp2 ip maybe used in SoCs which may have have 64bytes cacheline
size. Replace the MVPP2_CPU_D_CACHE_LINE_SIZE with L1_CACHE_BYTES.

And since dma_alloc_coherent() is always cacheline size aligned, so
remove the align checks.

Signed-off-by: Jisheng Zhang <jszhang@marvell.com>
---
 drivers/net/ethernet/marvell/mvpp2.c | 14 +-------------
 1 file changed, 1 insertion(+), 13 deletions(-)

diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index c797971a..05f358b 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -321,7 +321,6 @@
 /* Lbtd 802.3 type */
 #define MVPP2_IP_LBDT_TYPE		0xfffa
 
-#define MVPP2_CPU_D_CACHE_LINE_SIZE	32
 #define MVPP2_TX_CSUM_MAX_SIZE		9800
 
 /* Timeout constants */
@@ -377,7 +376,7 @@
 
 #define MVPP2_RX_PKT_SIZE(mtu) \
 	ALIGN((mtu) + MVPP2_MH_SIZE + MVPP2_VLAN_TAG_LEN + \
-	      ETH_HLEN + ETH_FCS_LEN, MVPP2_CPU_D_CACHE_LINE_SIZE)
+	      ETH_HLEN + ETH_FCS_LEN, L1_CACHE_BYTES)
 
 #define MVPP2_RX_BUF_SIZE(pkt_size)	((pkt_size) + NET_SKB_PAD)
 #define MVPP2_RX_TOTAL_SIZE(buf_size)	((buf_size) + MVPP2_SKB_SHINFO_SIZE)
@@ -4493,10 +4492,6 @@ static int mvpp2_aggr_txq_init(struct platform_device *pdev,
 	if (!aggr_txq->descs)
 		return -ENOMEM;
 
-	/* Make sure descriptor address is cache line size aligned  */
-	BUG_ON(aggr_txq->descs !=
-	       PTR_ALIGN(aggr_txq->descs, MVPP2_CPU_D_CACHE_LINE_SIZE));
-
 	aggr_txq->last_desc = aggr_txq->size - 1;
 
 	/* Aggr TXQ no reset WA */
@@ -4526,9 +4521,6 @@ static int mvpp2_rxq_init(struct mvpp2_port *port,
 	if (!rxq->descs)
 		return -ENOMEM;
 
-	BUG_ON(rxq->descs !=
-	       PTR_ALIGN(rxq->descs, MVPP2_CPU_D_CACHE_LINE_SIZE));
-
 	rxq->last_desc = rxq->size - 1;
 
 	/* Zero occupied and non-occupied counters - direct access */
@@ -4616,10 +4608,6 @@ static int mvpp2_txq_init(struct mvpp2_port *port,
 	if (!txq->descs)
 		return -ENOMEM;
 
-	/* Make sure descriptor address is cache line size aligned  */
-	BUG_ON(txq->descs !=
-	       PTR_ALIGN(txq->descs, MVPP2_CPU_D_CACHE_LINE_SIZE));
-
 	txq->last_desc = txq->size - 1;
 
 	/* Set Tx descriptors queue starting address - indirect access */
-- 
2.8.0.rc3

^ permalink raw reply related

* Re: [PATCH] sctp: flush if we can't fit another DATA chunk
From: Neil Horman @ 2016-03-30 12:03 UTC (permalink / raw)
  To: Marcelo Ricardo Leitner; +Cc: netdev, Vlad Yasevich, linux-sctp
In-Reply-To: <1459258885-21502-1-git-send-email-marcelo.leitner@gmail.com>

On Tue, Mar 29, 2016 at 10:41:25AM -0300, Marcelo Ricardo Leitner wrote:
> There is no point in delaying the packet if we can't fit a single byte
> of data on it anymore. So lets just reduce the threshold by the amount
> that a data chunk with 4 bytes (rounding) would use.
> 
> Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
> ---
>  net/sctp/output.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/net/sctp/output.c b/net/sctp/output.c
> index 97745351d58c2fb32b9f9b57d61831d7724d83b2..c518569123ce42a8f21f80754756306c39875013 100644
> --- a/net/sctp/output.c
> +++ b/net/sctp/output.c
> @@ -705,7 +705,8 @@ static sctp_xmit_t sctp_packet_can_append_data(struct sctp_packet *packet,
>  	/* Check whether this chunk and all the rest of pending data will fit
>  	 * or delay in hopes of bundling a full sized packet.
>  	 */
> -	if (chunk->skb->len + q->out_qlen >= transport->pathmtu - packet->overhead)
> +	if (chunk->skb->len + q->out_qlen >
> +		maxsize - packet->overhead - sizeof(sctp_data_chunk_t) - 4)
>  		/* Enough data queued to fill a packet */
>  		return SCTP_XMIT_OK;
>  
> -- 
> 2.5.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-sctp" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

Acked-By: Neil Horman <nhorman@tuxdriver.com>

^ permalink raw reply

* Re: [PATCH] sctp: avoid refreshing heartbeat timer too often
From: Marcelo Ricardo Leitner @ 2016-03-30 12:13 UTC (permalink / raw)
  To: David Laight, netdev@vger.kernel.org
  Cc: Neil Horman, Vlad Yasevich, linux-sctp@vger.kernel.org
In-Reply-To: <063D6719AE5E284EB5DD2968C1650D6D54DBFFCD@AcuExch.aculab.com>

Em 30-03-2016 06:37, David Laight escreveu:
> From: Marcelo Ricardo Leitner
>> Sent: 29 March 2016 14:42
>>
>> Currently on high rate SCTP streams the heartbeat timer refresh can
>> consume quite a lot of resources as timer updates are costly and it
>> contains a random factor, which a) is also costly and b) invalidates
>> mod_timer() optimization for not editing a timer to the same value.
>> It may even cause the timer to be slightly advanced, for no good reason.
>
> Interesting thoughts:
> 1) Is it necessary to use a different 'random factor' until the timer actually
>     expires?

I don't understand you fully here, but we have to have a random factor 
on timer expire. As noted by Daniel Borkmann on his commit 8f61059a96c2 
("net: sctp: improve timer slack calculation for transport HBs"):

     RFC4960, section 8.3 says:

       On an idle destination address that is allowed to heartbeat,
       it is recommended that a HEARTBEAT chunk is sent once per RTO
       of that destination address plus the protocol parameter
       'HB.interval', with jittering of +/- 50% of the RTO value,
       and exponential backoff of the RTO if the previous HEARTBEAT
       is unanswered.

Previous to his commit, it was using a random factor based on jiffies.

This patch then assumes that random_A+2 is just as random as random_B as 
long as it is within the allowed range, avoiding the unnecessary updates.

> 2) It might be better to allow the heartbeat timer to expire, on expiry work
>     out the new interval based on when the last 'refresh' was done.

Cool, I thought about this too. It would introduce some extra complexity 
that is not really worth I think, specially because now we may be doing 
more timer updates even with this patch but it's not triggering any wake 
ups and we would need at least 2 wake ups then: one for the first 
timeout event, and then re-schedule the timer for the next updated one, 
and maybe again, and again.. less timer updates but more wake ups, one 
at every heartbeat interval even on a busy transport. Seems it's cheaper 
to just update the timer then.

Thanks,
Marcelo

^ permalink raw reply

* Re: bpf: net/core/filter.c:2115 suspicious rcu_dereference_protected() usage!
From: Michal Kubecek @ 2016-03-30 12:24 UTC (permalink / raw)
  To: Daniel Borkmann
  Cc: Sasha Levin, Jiri Slaby, David S. Miller, ast,
	netdev@vger.kernel.org, LKML
In-Reply-To: <56FBB998.5090504@iogearbox.net>

On Wed, Mar 30, 2016 at 01:33:44PM +0200, Daniel Borkmann wrote:
> On 03/30/2016 11:42 AM, Michal Kubecek wrote:
> >
> >I'm just not sure checking if we hold the right lock depending on caller
> >is worth the extra complexity. After all, what is really needed is to
> >hold _some_ lock guaranteeing sk_attach_prog() and sk_detach_filter()
> >are safe so that just changing the condition in both to
> >
> >   sock_owned_by_user(sk) || lockdep_rtnl_is_held()
> 
> It would certainly silence it, but would be less accurate in terms of lock
> proving as opposed to the diff above. E.g. rntl could be held elsewhere,
> while someone attaches a socket filter w/o having locked the socket (currently
> not the case, but it would kind of defeat the purpose of rcu_dereference_protected()
> here). Was thinking about using a extra socket flag to indicate it's
> externally managed, but it's not really worth wasting sk's flags bit
> space just for this corner case.

Originally my reasoning was that to actually hide a locking issue from
lockdep, this would have to happen every time we get down into the
function which is unlikely. But thinking about it again, this code path
is not so frequent and the fuzzers tend to do strange things so that it
could really happen.

Sasha/Jiri, could you test the patch with your testcases? I received it
corrupted (strange leading whitespaces) so I better add cleaned up
version below.

                                                         Michal Kubecek


diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index afdf950617c3..510e90a6bb26 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -622,7 +622,8 @@ static int tun_attach(struct tun_struct *tun, struct file *file, bool skip_filte
 
 	/* Re-attach the filter to persist device */
 	if (!skip_filter && (tun->filter_attached == true)) {
-		err = sk_attach_filter(&tun->fprog, tfile->socket.sk);
+		err = __sk_attach_filter(&tun->fprog, tfile->socket.sk,
+					 lockdep_rtnl_is_held());
 		if (!err)
 			goto out;
 	}
@@ -1822,7 +1823,7 @@ static void tun_detach_filter(struct tun_struct *tun, int n)
 
 	for (i = 0; i < n; i++) {
 		tfile = rtnl_dereference(tun->tfiles[i]);
-		sk_detach_filter(tfile->socket.sk);
+		__sk_detach_filter(tfile->socket.sk, lockdep_rtnl_is_held());
 	}
 
 	tun->filter_attached = false;
@@ -1835,7 +1836,8 @@ static int tun_attach_filter(struct tun_struct *tun)
 
 	for (i = 0; i < tun->numqueues; i++) {
 		tfile = rtnl_dereference(tun->tfiles[i]);
-		ret = sk_attach_filter(&tun->fprog, tfile->socket.sk);
+		ret = __sk_attach_filter(&tun->fprog, tfile->socket.sk,
+					 lockdep_rtnl_is_held());
 		if (ret) {
 			tun_detach_filter(tun, i);
 			return ret;
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 43aa1f8855c7..a51a5361695f 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -465,10 +465,14 @@ int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog,
 void bpf_prog_destroy(struct bpf_prog *fp);
 
 int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
+int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk,
+		       bool locked);
 int sk_attach_bpf(u32 ufd, struct sock *sk);
 int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk);
 int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk);
 int sk_detach_filter(struct sock *sk);
+int __sk_detach_filter(struct sock *sk, bool locked);
+
 int sk_get_filter(struct sock *sk, struct sock_filter __user *filter,
 		  unsigned int len);
 
diff --git a/net/core/filter.c b/net/core/filter.c
index b7177d01ecb0..dfb4561a2247 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -1149,7 +1149,8 @@ void bpf_prog_destroy(struct bpf_prog *fp)
 }
 EXPORT_SYMBOL_GPL(bpf_prog_destroy);
 
-static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk)
+static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk,
+			    bool locked)
 {
 	struct sk_filter *fp, *old_fp;
 
@@ -1165,10 +1166,8 @@ static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk)
 		return -ENOMEM;
 	}
 
-	old_fp = rcu_dereference_protected(sk->sk_filter,
-					   sock_owned_by_user(sk));
+	old_fp = rcu_dereference_protected(sk->sk_filter, locked);
 	rcu_assign_pointer(sk->sk_filter, fp);
-
 	if (old_fp)
 		sk_filter_uncharge(sk, old_fp);
 
@@ -1247,7 +1246,8 @@ struct bpf_prog *__get_filter(struct sock_fprog *fprog, struct sock *sk)
  * occurs or there is insufficient memory for the filter a negative
  * errno code is returned. On success the return is zero.
  */
-int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
+int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk,
+		       bool locked)
 {
 	struct bpf_prog *prog = __get_filter(fprog, sk);
 	int err;
@@ -1255,7 +1255,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
 	if (IS_ERR(prog))
 		return PTR_ERR(prog);
 
-	err = __sk_attach_prog(prog, sk);
+	err = __sk_attach_prog(prog, sk, locked);
 	if (err < 0) {
 		__bpf_prog_release(prog);
 		return err;
@@ -1263,7 +1263,12 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(sk_attach_filter);
+EXPORT_SYMBOL_GPL(__sk_attach_filter);
+
+int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
+{
+	return __sk_attach_filter(fprog, sk, sock_owned_by_user(sk));
+}
 
 int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk)
 {
@@ -1309,7 +1314,7 @@ int sk_attach_bpf(u32 ufd, struct sock *sk)
 	if (IS_ERR(prog))
 		return PTR_ERR(prog);
 
-	err = __sk_attach_prog(prog, sk);
+	err = __sk_attach_prog(prog, sk, sock_owned_by_user(sk));
 	if (err < 0) {
 		bpf_prog_put(prog);
 		return err;
@@ -2247,7 +2252,7 @@ static int __init register_sk_filter_ops(void)
 }
 late_initcall(register_sk_filter_ops);
 
-int sk_detach_filter(struct sock *sk)
+int __sk_detach_filter(struct sock *sk, bool locked)
 {
 	int ret = -ENOENT;
 	struct sk_filter *filter;
@@ -2255,8 +2260,7 @@ int sk_detach_filter(struct sock *sk)
 	if (sock_flag(sk, SOCK_FILTER_LOCKED))
 		return -EPERM;
 
-	filter = rcu_dereference_protected(sk->sk_filter,
-					   sock_owned_by_user(sk));
+	filter = rcu_dereference_protected(sk->sk_filter, locked);
 	if (filter) {
 		RCU_INIT_POINTER(sk->sk_filter, NULL);
 		sk_filter_uncharge(sk, filter);
@@ -2265,7 +2269,12 @@ int sk_detach_filter(struct sock *sk)
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(sk_detach_filter);
+EXPORT_SYMBOL_GPL(__sk_detach_filter);
+
+int sk_detach_filter(struct sock *sk)
+{
+	return __sk_detach_filter(sk, sock_owned_by_user(sk));
+}
 
 int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf,
 		  unsigned int len)

^ permalink raw reply related

* Re: [PATCH 5/5] drivers/net: support hdlc function for QE-UCC
From: kbuild test robot @ 2016-03-30 12:27 UTC (permalink / raw)
  To: Zhao Qiang
  Cc: kbuild-all, davem, akpm, gregkh, oss, xiaobo.xie, linux-kernel,
	netdev, linuxppc-dev, Zhao Qiang
In-Reply-To: <1459327830-19829-5-git-send-email-qiang.zhao@nxp.com>

[-- Attachment #1: Type: text/plain, Size: 941 bytes --]

Hi Zhao,

[auto build test WARNING on net/master]
[also build test WARNING on v4.6-rc1 next-20160330]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Zhao-Qiang/fsl-qe-add-rx_sync-and-tx_sync-for-TDM-mode/20160330-170411
config: powerpc-allyesconfig (attached as .config)
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=powerpc 

All warnings (new ones prefixed by >>):

warning: (KMETER1 && FSL_UCC_HDLC) selects QUICC_ENGINE which has unmet direct dependencies (FSL_SOC && PPC32)

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 48751 bytes --]

^ permalink raw reply

* Re: bpf: net/core/filter.c:2115 suspicious rcu_dereference_protected() usage!
From: Daniel Borkmann @ 2016-03-30 12:38 UTC (permalink / raw)
  To: Michal Kubecek
  Cc: Sasha Levin, Jiri Slaby, David S. Miller, ast,
	netdev@vger.kernel.org, LKML
In-Reply-To: <20160330122418.GD15048@unicorn.suse.cz>

On 03/30/2016 02:24 PM, Michal Kubecek wrote:
> On Wed, Mar 30, 2016 at 01:33:44PM +0200, Daniel Borkmann wrote:
>> On 03/30/2016 11:42 AM, Michal Kubecek wrote:
>>>
>>> I'm just not sure checking if we hold the right lock depending on caller
>>> is worth the extra complexity. After all, what is really needed is to
>>> hold _some_ lock guaranteeing sk_attach_prog() and sk_detach_filter()
>>> are safe so that just changing the condition in both to
>>>
>>>    sock_owned_by_user(sk) || lockdep_rtnl_is_held()
>>
>> It would certainly silence it, but would be less accurate in terms of lock
>> proving as opposed to the diff above. E.g. rntl could be held elsewhere,
>> while someone attaches a socket filter w/o having locked the socket (currently
>> not the case, but it would kind of defeat the purpose of rcu_dereference_protected()
>> here). Was thinking about using a extra socket flag to indicate it's
>> externally managed, but it's not really worth wasting sk's flags bit
>> space just for this corner case.
>
> Originally my reasoning was that to actually hide a locking issue from
> lockdep, this would have to happen every time we get down into the
> function which is unlikely. But thinking about it again, this code path
> is not so frequent and the fuzzers tend to do strange things so that it
> could really happen.

In this case actually nothing too fancy, just seems that filters on tap devices
might not be really used by anyone (issue is already couple of years old).

> Sasha/Jiri, could you test the patch with your testcases? I received it
> corrupted (strange leading whitespaces) so I better add cleaned up
> version below.

Tested this yesterday night on my machine with PROVE_RCU + PROVE_RCU_REPEATEDLY
enabled, and it can easily be triggered with a simple ioctl(tun_fd,
TUN{ATTACH,DETACH}FILTER, ...) on a tap device, and the patch now silences
it. Sorry for the white space damage (should have just attached it), I'd send
it later today.

Thanks,
Daniel

^ permalink raw reply

* Re: [PATCH 5/5] drivers/net: support hdlc function for QE-UCC
From: kbuild test robot @ 2016-03-30 12:53 UTC (permalink / raw)
  To: Zhao Qiang
  Cc: kbuild-all, davem, akpm, gregkh, oss, xiaobo.xie, linux-kernel,
	netdev, linuxppc-dev, Zhao Qiang
In-Reply-To: <1459327830-19829-5-git-send-email-qiang.zhao@nxp.com>

[-- Attachment #1: Type: text/plain, Size: 5695 bytes --]

Hi Zhao,

[auto build test ERROR on net/master]
[also build test ERROR on v4.6-rc1 next-20160330]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Zhao-Qiang/fsl-qe-add-rx_sync-and-tx_sync-for-TDM-mode/20160330-170411
config: i386-allmodconfig (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All error/warnings (new ones prefixed by >>):

   In file included from include/soc/fsl/qe/ucc_slow.h:21:0,
                    from drivers/tty/serial/ucc_uart.c:34:
>> include/soc/fsl/qe/qe.h:24:21: fatal error: asm/cpm.h: No such file or directory
   compilation terminated.
--
>> drivers/net/ethernet/freescale/gianfar_ptp.c:75:0: warning: "FS" redefined
    #define FS                    (1<<28) /* FIPER start indication */
    ^
   In file included from arch/x86/include/uapi/asm/ptrace.h:5:0,
                    from arch/x86/include/asm/ptrace.h:6,
                    from arch/x86/include/asm/alternative.h:8,
                    from arch/x86/include/asm/bitops.h:16,
                    from include/linux/bitops.h:36,
                    from include/linux/kernel.h:10,
                    from include/linux/list.h:8,
                    from include/linux/kobject.h:20,
                    from include/linux/device.h:17,
                    from drivers/net/ethernet/freescale/gianfar_ptp.c:23:
   arch/x86/include/uapi/asm/ptrace-abi.h:15:0: note: this is the location of the previous definition
    #define FS 9
    ^
--
   In file included from drivers/soc/fsl/qe/qe_ic.c:31:0:
   include/soc/fsl/qe/qe_ic.h: In function 'qe_ic_cascade_low_ipic':
>> include/soc/fsl/qe/qe_ic.h:86:21: error: 'NO_IRQ' undeclared (first use in this function)
     if (cascade_irq != NO_IRQ)
                        ^
   include/soc/fsl/qe/qe_ic.h:86:21: note: each undeclared identifier is reported only once for each function it appears in
   include/soc/fsl/qe/qe_ic.h: In function 'qe_ic_cascade_high_ipic':
   include/soc/fsl/qe/qe_ic.h:95:21: error: 'NO_IRQ' undeclared (first use in this function)
     if (cascade_irq != NO_IRQ)
                        ^
   include/soc/fsl/qe/qe_ic.h: In function 'qe_ic_cascade_low_mpic':
   include/soc/fsl/qe/qe_ic.h:105:21: error: 'NO_IRQ' undeclared (first use in this function)
     if (cascade_irq != NO_IRQ)
                        ^
   include/soc/fsl/qe/qe_ic.h: In function 'qe_ic_cascade_high_mpic':
   include/soc/fsl/qe/qe_ic.h:117:21: error: 'NO_IRQ' undeclared (first use in this function)
     if (cascade_irq != NO_IRQ)
                        ^
   include/soc/fsl/qe/qe_ic.h: In function 'qe_ic_cascade_muxed_mpic':
   include/soc/fsl/qe/qe_ic.h:130:21: error: 'NO_IRQ' undeclared (first use in this function)
     if (cascade_irq == NO_IRQ)
                        ^
   drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_read':
>> drivers/soc/fsl/qe/qe_ic.c:180:9: error: implicit declaration of function 'in_be32' [-Werror=implicit-function-declaration]
     return in_be32(base + (reg >> 2));
            ^
   drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_write':
>> drivers/soc/fsl/qe/qe_ic.c:186:2: error: implicit declaration of function 'out_be32' [-Werror=implicit-function-declaration]
     out_be32(base + (reg >> 2), value);
     ^
   drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_get_low_irq':
>> drivers/soc/fsl/qe/qe_ic.c:299:10: error: 'NO_IRQ' undeclared (first use in this function)
      return NO_IRQ;
             ^
   drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_get_high_irq':
   drivers/soc/fsl/qe/qe_ic.c:315:10: error: 'NO_IRQ' undeclared (first use in this function)
      return NO_IRQ;
             ^
   drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_init':
   drivers/soc/fsl/qe/qe_ic.c:350:25: error: 'NO_IRQ' undeclared (first use in this function)
     if (qe_ic->virq_low == NO_IRQ) {
                            ^
   drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_set_highest_priority':
>> drivers/soc/fsl/qe/qe_ic.c:392:21: error: implicit declaration of function 'virq_to_hw' [-Werror=implicit-function-declaration]
     unsigned int src = virq_to_hw(virq);
                        ^
   cc1: some warnings being treated as errors

vim +24 include/soc/fsl/qe/qe.h

98658538 include/asm-powerpc/qe.h      Li Yang         2006-10-03  18  
1291e49e arch/powerpc/include/asm/qe.h Zhao Qiang      2015-11-30  19  #include <linux/compiler.h>
1291e49e arch/powerpc/include/asm/qe.h Zhao Qiang      2015-11-30  20  #include <linux/genalloc.h>
5e41486c include/asm-powerpc/qe.h      Anton Vorontsov 2008-05-23  21  #include <linux/spinlock.h>
1b9e8904 arch/powerpc/include/asm/qe.h Anton Vorontsov 2008-12-03  22  #include <linux/errno.h>
1b9e8904 arch/powerpc/include/asm/qe.h Anton Vorontsov 2008-12-03  23  #include <linux/err.h>
5093bb96 include/asm-powerpc/qe.h      Anton Vorontsov 2008-05-23 @24  #include <asm/cpm.h>
7aa1aa6e include/soc/fsl/qe/qe.h       Zhao Qiang      2015-11-30  25  #include <soc/fsl/qe/immap_qe.h>
1291e49e arch/powerpc/include/asm/qe.h Zhao Qiang      2015-11-30  26  #include <linux/of.h>
1291e49e arch/powerpc/include/asm/qe.h Zhao Qiang      2015-11-30  27  #include <linux/of_address.h>

:::::: The code at line 24 was first introduced by commit
:::::: 5093bb965a163fe288c3e5db0275165f86c895c2 powerpc/QE: switch to the cpm_muram implementation

:::::: TO: Anton Vorontsov <avorontsov@ru.mvista.com>
:::::: CC: Kumar Gala <galak@kernel.crashing.org>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 54451 bytes --]

^ permalink raw reply

* Re: [PATCH RFC] net: decrease the length of backlog queue immediately after it's detached from sk
From: Sergei Shtylyov @ 2016-03-30 12:56 UTC (permalink / raw)
  To: Yang Yingliang, netdev; +Cc: davem, eric.dumazet
In-Reply-To: <1459315001-3448-1-git-send-email-yangyingliang@huawei.com>

Hello.

On 3/30/2016 8:16 AM, Yang Yingliang wrote:

> When task A hold the sk owned in tcp_sendmsg, if lots of packets
> arrive and the packets will be added to backlog queue. The packets
> will be handled in release_sock called from tcp_sendmsg. When the
> sk_backlog is removed from sk, the length will not decrease until
> all the packets in backlog queue are handled. This may leads to the
> new packets be dropped because the lenth is too big. So set the
> lenth to 0 immediately after it's detached from sk.

    Length?

> Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>
[...]

MBR, Sergei

^ permalink raw reply

* [PATCH] wlcore: spi: add wl18xx support
From: Eyal Reizer @ 2016-03-30 12:58 UTC (permalink / raw)
  To: kvalo, linux-wireless, netdev, linux-kernel; +Cc: Eyal Reizer

From: Eyal <eyalr@eyalr-VirtualBox.(none)>

Add support for using with both wl12xx and wl18xx.

- all wilink family needs special init command for entering wspi mode.
  extra clock cycles should be sent after the spi init command while the
  cs pin is high.
- switch to controling the cs pin from the spi driver for achieveing the
  above.
- the selected cs gpio is read from the spi device-tree node using the
  cs-gpios field and setup as a gpio.
- See the example below for specifying the cs gpio using the cs-gpios entry

&spi0	{
 	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&spi0_pins>;
	cs-gpios = <&gpio0 5 0>;
	#address-cells = <1>;
	#size-cells = <0>;
 	wlcore: wlcore@0 {
		compatible = "ti,wl1835";
		vwlan-supply = <&wlan_en_reg>;
		spi-max-frequency = <48000000>;
		reg = <0>;      /* chip select 0 on spi0, ie spi0.0 */
		interrupt-parent = <&gpio0>;
		interrupts = <27 IRQ_TYPE_EDGE_RISING>;
 	};
};

Signed-off-by: Eyal Reizer <eyalr@ti.com>
---
 drivers/net/wireless/ti/wlcore/spi.c |  176 ++++++++++++++++++++++++++++++----
 1 file changed, 157 insertions(+), 19 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c
index 96d9c9d..6c5a369 100644
--- a/drivers/net/wireless/ti/wlcore/spi.c
+++ b/drivers/net/wireless/ti/wlcore/spi.c
@@ -32,6 +32,7 @@
 #include <linux/platform_device.h>
 #include <linux/of_irq.h>
 #include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
 
 #include "wlcore.h"
 #include "wl12xx_80211.h"
@@ -70,16 +71,30 @@
 #define WSPI_MAX_CHUNK_SIZE    4092
 
 /*
- * only support SPI for 12xx - this code should be reworked when 18xx
- * support is introduced
+ * wl18xx driver aggregation buffer size is (13 * PAGE_SIZE) compared to
+ * (4 * PAGE_SIZE) for wl12xx, so use the larger buffer needed for wl18xx
  */
-#define SPI_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
+#define SPI_AGGR_BUFFER_SIZE (13 * PAGE_SIZE)
 
 /* Maximum number of SPI write chunks */
 #define WSPI_MAX_NUM_OF_CHUNKS \
 	((SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) + 1)
 
 
+struct wilink_familiy_data {
+	char name[8];
+};
+
+const struct wilink_familiy_data *wilink_data;
+
+static const struct wilink_familiy_data wl18xx_data = {
+	.name = "wl18xx",
+};
+
+static const struct wilink_familiy_data wl12xx_data = {
+	.name = "wl12xx",
+};
+
 struct wl12xx_spi_glue {
 	struct device *dev;
 	struct platform_device *core;
@@ -120,6 +135,8 @@ static void wl12xx_spi_init(struct device *child)
 	struct spi_transfer t;
 	struct spi_message m;
 	u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
+	struct spi_device *spi = (struct spi_device *)glue->dev;
+	struct spi_master *master = spi->master;
 
 	if (!cmd) {
 		dev_err(child->parent,
@@ -127,6 +144,15 @@ static void wl12xx_spi_init(struct device *child)
 		return;
 	}
 
+	if (!master->cs_gpios) {
+		dev_err(child->parent,
+			"spi chip select pin missing in platform data!\n");
+		return;
+	}
+
+	/* Drive CS line low */
+	gpio_direction_output(master->cs_gpios[0], 0);
+
 	memset(&t, 0, sizeof(t));
 	spi_message_init(&m);
 
@@ -163,6 +189,26 @@ static void wl12xx_spi_init(struct device *child)
 	spi_message_add_tail(&t, &m);
 
 	spi_sync(to_spi_device(glue->dev), &m);
+
+	/* Send extra clocks with CS high. this is required by the wilink
+	 * family in order for successfully enter WSPI mode
+	 */
+	gpio_direction_output(master->cs_gpios[0], 1);
+
+	memset(&m, 0, sizeof(m));
+	spi_message_init(&m);
+
+	cmd[0] = 0xff;
+	cmd[1] = 0xff;
+	cmd[2] = 0xff;
+	cmd[3] = 0xff;
+	swab32s((u32 *)cmd);
+
+	t.tx_buf = cmd;
+	t.len = 4;
+	spi_message_add_tail(&t, &m);
+	spi_sync(to_spi_device(glue->dev), &m);
+
 	kfree(cmd);
 }
 
@@ -213,6 +259,16 @@ static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
 	u32 *busy_buf;
 	u32 *cmd;
 	u32 chunk_len;
+	struct spi_device *spi = (struct spi_device *)glue->dev;
+	struct spi_master *master = spi->master;
+
+	if (!master->cs_gpios) {
+		dev_err(child->parent,
+			"spi chip select pin missing in platform data!\n");
+		return -EINVAL;
+	}
+	/* Drive CS line low */
+	gpio_direction_output(master->cs_gpios[0], 0);
 
 	while (len > 0) {
 		chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len);
@@ -267,25 +323,44 @@ static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
 		len -= chunk_len;
 	}
 
+	/* Drive CS line high */
+	gpio_direction_output(master->cs_gpios[0], 1);
 	return 0;
 }
 
-static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
-					     void *buf, size_t len, bool fixed)
+static int __wl12xx_spi_raw_write(struct device *child, int addr,
+				  void *buf, size_t len, bool fixed)
 {
 	struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
-	/* SPI write buffers - 2 for each chunk */
-	struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS];
+	struct spi_transfer *t;
 	struct spi_message m;
 	u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; /* 1 command per chunk */
 	u32 *cmd;
 	u32 chunk_len;
 	int i;
+	struct spi_device *spi = (struct spi_device *)glue->dev;
+	struct spi_master *master = spi->master;
+
+	if (!master->cs_gpios) {
+		dev_err(child->parent,
+			"spi chip select pin missing in platform data!\n");
+		return -EINVAL;
+	}
+
+	/* SPI write buffers - 2 for each chunk */
+	t = kzalloc(sizeof(*t) * 2 * WSPI_MAX_NUM_OF_CHUNKS, GFP_KERNEL);
+	if (!t) {
+		dev_err(child->parent,
+			"could not allocate spi write buffer\n");
+		return -ENOMEM;
+	}
+
+	/* Drive CS line low */
+	gpio_direction_output(master->cs_gpios[0], 0);
 
 	WARN_ON(len > SPI_AGGR_BUFFER_SIZE);
 
 	spi_message_init(&m);
-	memset(t, 0, sizeof(t));
 
 	cmd = &commands[0];
 	i = 0;
@@ -318,9 +393,29 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
 
 	spi_sync(to_spi_device(glue->dev), &m);
 
+	/* Drive CS line high */
+	gpio_direction_output(master->cs_gpios[0], 1);
+
+	kfree(t);
 	return 0;
 }
 
+static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
+					     void *buf, size_t len, bool fixed)
+{
+	int ret;
+
+	/* The ELP wakeup write may fail the first time due to internal
+	 * hardware latency. It is safer to send the wakeup command twice to
+	 * avoid unexpected failures.
+	 */
+	if (addr == HW_ACCESS_ELP_CTRL_REG)
+		ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
+	ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
+
+	return ret;
+}
+
 /**
  * wl12xx_spi_set_power - power on/off the wl12xx unit
  * @child: wl12xx device handle.
@@ -349,17 +444,38 @@ static int wl12xx_spi_set_power(struct device *child, bool enable)
 	return ret;
 }
 
+/**
+ * wl12xx_spi_set_block_size
+ *
+ * This function is not needed for spi mode, but need to be present.
+ * Without it defined the wlcore fallback to use the wrong packet
+ * allignment on tx.
+ */
+static void wl12xx_spi_set_block_size(struct device *child,
+				      unsigned int blksz)
+{
+}
+
 static struct wl1271_if_operations spi_ops = {
 	.read		= wl12xx_spi_raw_read,
 	.write		= wl12xx_spi_raw_write,
 	.reset		= wl12xx_spi_reset,
 	.init		= wl12xx_spi_init,
 	.power		= wl12xx_spi_set_power,
-	.set_block_size = NULL,
+	.set_block_size = wl12xx_spi_set_block_size,
 };
 
 static const struct of_device_id wlcore_spi_of_match_table[] = {
-	{ .compatible = "ti,wl1271" },
+	{ .compatible = "ti,wl1271", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1273", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1281", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1283", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1801", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1805", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1807", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1831", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1835", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1837", .data = &wl18xx_data},
 	{ }
 };
 MODULE_DEVICE_TABLE(of, wlcore_spi_of_match_table);
@@ -375,18 +491,24 @@ static int wlcore_probe_of(struct spi_device *spi, struct wl12xx_spi_glue *glue,
 			   struct wlcore_platdev_data *pdev_data)
 {
 	struct device_node *dt_node = spi->dev.of_node;
-	int ret;
+	const struct of_device_id *of_id;
+
+	of_id = of_match_node(wlcore_spi_of_match_table, dt_node);
+	if (!of_id)
+		return -ENODEV;
+
+	wilink_data = of_id->data;
+	dev_info(&spi->dev, "selected chip familiy is %s\n",
+		 wilink_data->name);
 
 	if (of_find_property(dt_node, "clock-xtal", NULL))
 		pdev_data->ref_clock_xtal = true;
 
-	ret = of_property_read_u32(dt_node, "ref-clock-frequency",
-				   &pdev_data->ref_clock_freq);
-	if (IS_ERR_VALUE(ret)) {
-		dev_err(glue->dev,
-			"can't get reference clock frequency (%d)\n", ret);
-		return ret;
-	}
+	/* optional clock frequency params */
+	of_property_read_u32(dt_node, "ref-clock-frequency",
+			     &pdev_data->ref_clock_freq);
+	of_property_read_u32(dt_node, "tcxo-clock-frequency",
+			     &pdev_data->tcxo_clock_freq);
 
 	return 0;
 }
@@ -397,6 +519,7 @@ static int wl1271_probe(struct spi_device *spi)
 	struct wlcore_platdev_data pdev_data;
 	struct resource res[1];
 	int ret;
+	struct spi_master *master = spi->master;
 
 	memset(&pdev_data, 0x00, sizeof(pdev_data));
 
@@ -410,6 +533,12 @@ static int wl1271_probe(struct spi_device *spi)
 
 	glue->dev = &spi->dev;
 
+	if (!master->cs_gpios) {
+		dev_err(glue->dev,
+			"spi chip select pin missing in platform data!\n");
+		return -EINVAL;
+	}
+
 	spi_set_drvdata(spi, glue);
 
 	/* This is the only SPI value that we need to set here, the rest
@@ -431,15 +560,21 @@ static int wl1271_probe(struct spi_device *spi)
 		return ret;
 	}
 
+	if (gpio_request(master->cs_gpios[0], "spi1-cs0"))
+		return -EINVAL;
+
 	ret = spi_setup(spi);
 	if (ret < 0) {
 		dev_err(glue->dev, "spi_setup failed\n");
+		gpio_free(master->cs_gpios[0]);
 		return ret;
 	}
 
-	glue->core = platform_device_alloc("wl12xx", PLATFORM_DEVID_AUTO);
+	glue->core = platform_device_alloc(wilink_data->name,
+					   PLATFORM_DEVID_AUTO);
 	if (!glue->core) {
 		dev_err(glue->dev, "can't allocate platform_device\n");
+		gpio_free(master->cs_gpios[0]);
 		return -ENOMEM;
 	}
 
@@ -474,14 +609,17 @@ static int wl1271_probe(struct spi_device *spi)
 
 out_dev_put:
 	platform_device_put(glue->core);
+	gpio_free(master->cs_gpios[0]);
 	return ret;
 }
 
 static int wl1271_remove(struct spi_device *spi)
 {
 	struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
+	struct spi_master *master = spi->master;
 
 	platform_device_unregister(glue->core);
+	gpio_free(master->cs_gpios[0]);
 
 	return 0;
 }
-- 
1.7.9.5

^ permalink raw reply related

* Re: [PATCH] wlcore: spi: add wl18xx support
From: Emmanuel Grumbach @ 2016-03-30 13:02 UTC (permalink / raw)
  To: Eyal Reizer
  Cc: Kalle Valo, linux-wireless,
	netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Eyal Reizer
In-Reply-To: <1459342694-24461-1-git-send-email-eyalr-l0cyMroinI0@public.gmane.org>

On Wed, Mar 30, 2016 at 3:58 PM, Eyal Reizer <eyalreizer-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>
> From: Eyal <eyalr@eyalr-VirtualBox.(none)>

Are you trying to hide or something? :)

>
> Add support for using with both wl12xx and wl18xx.
>
> - all wilink family needs special init command for entering wspi mode.
>   extra clock cycles should be sent after the spi init command while the
>   cs pin is high.
> - switch to controling the cs pin from the spi driver for achieveing the
>   above.
> - the selected cs gpio is read from the spi device-tree node using the
>   cs-gpios field and setup as a gpio.
> - See the example below for specifying the cs gpio using the cs-gpios entry
>
> &spi0   {
>         status = "okay";
>         pinctrl-names = "default";
>         pinctrl-0 = <&spi0_pins>;
>         cs-gpios = <&gpio0 5 0>;
>         #address-cells = <1>;
>         #size-cells = <0>;
>         wlcore: wlcore@0 {
>                 compatible = "ti,wl1835";
>                 vwlan-supply = <&wlan_en_reg>;
>                 spi-max-frequency = <48000000>;
>                 reg = <0>;      /* chip select 0 on spi0, ie spi0.0 */
>                 interrupt-parent = <&gpio0>;
>                 interrupts = <27 IRQ_TYPE_EDGE_RISING>;
>         };
> };
>
> Signed-off-by: Eyal Reizer <eyalr-l0cyMroinI0@public.gmane.org>
> ---
>  drivers/net/wireless/ti/wlcore/spi.c |  176 ++++++++++++++++++++++++++++++----
>  1 file changed, 157 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c
> index 96d9c9d..6c5a369 100644
> --- a/drivers/net/wireless/ti/wlcore/spi.c
> +++ b/drivers/net/wireless/ti/wlcore/spi.c
> @@ -32,6 +32,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/of_irq.h>
>  #include <linux/regulator/consumer.h>
> +#include <linux/gpio.h>
>
>  #include "wlcore.h"
>  #include "wl12xx_80211.h"
> @@ -70,16 +71,30 @@
>  #define WSPI_MAX_CHUNK_SIZE    4092
>
>  /*
> - * only support SPI for 12xx - this code should be reworked when 18xx
> - * support is introduced
> + * wl18xx driver aggregation buffer size is (13 * PAGE_SIZE) compared to
> + * (4 * PAGE_SIZE) for wl12xx, so use the larger buffer needed for wl18xx
>   */
> -#define SPI_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
> +#define SPI_AGGR_BUFFER_SIZE (13 * PAGE_SIZE)
>
>  /* Maximum number of SPI write chunks */
>  #define WSPI_MAX_NUM_OF_CHUNKS \
>         ((SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) + 1)
>
>
> +struct wilink_familiy_data {
> +       char name[8];
> +};
> +
> +const struct wilink_familiy_data *wilink_data;
> +
> +static const struct wilink_familiy_data wl18xx_data = {
> +       .name = "wl18xx",
> +};
> +
> +static const struct wilink_familiy_data wl12xx_data = {
> +       .name = "wl12xx",
> +};
> +
>  struct wl12xx_spi_glue {
>         struct device *dev;
>         struct platform_device *core;
> @@ -120,6 +135,8 @@ static void wl12xx_spi_init(struct device *child)
>         struct spi_transfer t;
>         struct spi_message m;
>         u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
> +       struct spi_device *spi = (struct spi_device *)glue->dev;
> +       struct spi_master *master = spi->master;
>
>         if (!cmd) {
>                 dev_err(child->parent,
> @@ -127,6 +144,15 @@ static void wl12xx_spi_init(struct device *child)
>                 return;
>         }
>
> +       if (!master->cs_gpios) {
> +               dev_err(child->parent,
> +                       "spi chip select pin missing in platform data!\n");
> +               return;
> +       }
> +
> +       /* Drive CS line low */
> +       gpio_direction_output(master->cs_gpios[0], 0);
> +
>         memset(&t, 0, sizeof(t));
>         spi_message_init(&m);
>
> @@ -163,6 +189,26 @@ static void wl12xx_spi_init(struct device *child)
>         spi_message_add_tail(&t, &m);
>
>         spi_sync(to_spi_device(glue->dev), &m);
> +
> +       /* Send extra clocks with CS high. this is required by the wilink
> +        * family in order for successfully enter WSPI mode
> +        */
> +       gpio_direction_output(master->cs_gpios[0], 1);
> +
> +       memset(&m, 0, sizeof(m));
> +       spi_message_init(&m);
> +
> +       cmd[0] = 0xff;
> +       cmd[1] = 0xff;
> +       cmd[2] = 0xff;
> +       cmd[3] = 0xff;
> +       swab32s((u32 *)cmd);
> +
> +       t.tx_buf = cmd;
> +       t.len = 4;
> +       spi_message_add_tail(&t, &m);
> +       spi_sync(to_spi_device(glue->dev), &m);
> +
>         kfree(cmd);
>  }
>
> @@ -213,6 +259,16 @@ static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
>         u32 *busy_buf;
>         u32 *cmd;
>         u32 chunk_len;
> +       struct spi_device *spi = (struct spi_device *)glue->dev;
> +       struct spi_master *master = spi->master;
> +
> +       if (!master->cs_gpios) {
> +               dev_err(child->parent,
> +                       "spi chip select pin missing in platform data!\n");
> +               return -EINVAL;
> +       }
> +       /* Drive CS line low */
> +       gpio_direction_output(master->cs_gpios[0], 0);
>
>         while (len > 0) {
>                 chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len);
> @@ -267,25 +323,44 @@ static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
>                 len -= chunk_len;
>         }
>
> +       /* Drive CS line high */
> +       gpio_direction_output(master->cs_gpios[0], 1);
>         return 0;
>  }
>
> -static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
> -                                            void *buf, size_t len, bool fixed)
> +static int __wl12xx_spi_raw_write(struct device *child, int addr,
> +                                 void *buf, size_t len, bool fixed)
>  {
>         struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
> -       /* SPI write buffers - 2 for each chunk */
> -       struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS];
> +       struct spi_transfer *t;
>         struct spi_message m;
>         u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; /* 1 command per chunk */
>         u32 *cmd;
>         u32 chunk_len;
>         int i;
> +       struct spi_device *spi = (struct spi_device *)glue->dev;
> +       struct spi_master *master = spi->master;
> +
> +       if (!master->cs_gpios) {
> +               dev_err(child->parent,
> +                       "spi chip select pin missing in platform data!\n");
> +               return -EINVAL;
> +       }
> +
> +       /* SPI write buffers - 2 for each chunk */
> +       t = kzalloc(sizeof(*t) * 2 * WSPI_MAX_NUM_OF_CHUNKS, GFP_KERNEL);
> +       if (!t) {
> +               dev_err(child->parent,
> +                       "could not allocate spi write buffer\n");
> +               return -ENOMEM;
> +       }
> +
> +       /* Drive CS line low */
> +       gpio_direction_output(master->cs_gpios[0], 0);
>
>         WARN_ON(len > SPI_AGGR_BUFFER_SIZE);
>
>         spi_message_init(&m);
> -       memset(t, 0, sizeof(t));
>
>         cmd = &commands[0];
>         i = 0;
> @@ -318,9 +393,29 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
>
>         spi_sync(to_spi_device(glue->dev), &m);
>
> +       /* Drive CS line high */
> +       gpio_direction_output(master->cs_gpios[0], 1);
> +
> +       kfree(t);
>         return 0;
>  }
>
> +static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
> +                                            void *buf, size_t len, bool fixed)
> +{
> +       int ret;
> +
> +       /* The ELP wakeup write may fail the first time due to internal
> +        * hardware latency. It is safer to send the wakeup command twice to
> +        * avoid unexpected failures.
> +        */
> +       if (addr == HW_ACCESS_ELP_CTRL_REG)
> +               ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
> +       ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
> +
> +       return ret;
> +}
> +
>  /**
>   * wl12xx_spi_set_power - power on/off the wl12xx unit
>   * @child: wl12xx device handle.
> @@ -349,17 +444,38 @@ static int wl12xx_spi_set_power(struct device *child, bool enable)
>         return ret;
>  }
>
> +/**
> + * wl12xx_spi_set_block_size
> + *
> + * This function is not needed for spi mode, but need to be present.
> + * Without it defined the wlcore fallback to use the wrong packet
> + * allignment on tx.
> + */
> +static void wl12xx_spi_set_block_size(struct device *child,
> +                                     unsigned int blksz)
> +{
> +}
> +
>  static struct wl1271_if_operations spi_ops = {
>         .read           = wl12xx_spi_raw_read,
>         .write          = wl12xx_spi_raw_write,
>         .reset          = wl12xx_spi_reset,
>         .init           = wl12xx_spi_init,
>         .power          = wl12xx_spi_set_power,
> -       .set_block_size = NULL,
> +       .set_block_size = wl12xx_spi_set_block_size,
>  };
>
>  static const struct of_device_id wlcore_spi_of_match_table[] = {
> -       { .compatible = "ti,wl1271" },
> +       { .compatible = "ti,wl1271", .data = &wl12xx_data},
> +       { .compatible = "ti,wl1273", .data = &wl12xx_data},
> +       { .compatible = "ti,wl1281", .data = &wl12xx_data},
> +       { .compatible = "ti,wl1283", .data = &wl12xx_data},
> +       { .compatible = "ti,wl1801", .data = &wl18xx_data},
> +       { .compatible = "ti,wl1805", .data = &wl18xx_data},
> +       { .compatible = "ti,wl1807", .data = &wl18xx_data},
> +       { .compatible = "ti,wl1831", .data = &wl18xx_data},
> +       { .compatible = "ti,wl1835", .data = &wl18xx_data},
> +       { .compatible = "ti,wl1837", .data = &wl18xx_data},
>         { }
>  };
>  MODULE_DEVICE_TABLE(of, wlcore_spi_of_match_table);
> @@ -375,18 +491,24 @@ static int wlcore_probe_of(struct spi_device *spi, struct wl12xx_spi_glue *glue,
>                            struct wlcore_platdev_data *pdev_data)
>  {
>         struct device_node *dt_node = spi->dev.of_node;
> -       int ret;
> +       const struct of_device_id *of_id;
> +
> +       of_id = of_match_node(wlcore_spi_of_match_table, dt_node);
> +       if (!of_id)
> +               return -ENODEV;
> +
> +       wilink_data = of_id->data;
> +       dev_info(&spi->dev, "selected chip familiy is %s\n",
> +                wilink_data->name);
>
>         if (of_find_property(dt_node, "clock-xtal", NULL))
>                 pdev_data->ref_clock_xtal = true;
>
> -       ret = of_property_read_u32(dt_node, "ref-clock-frequency",
> -                                  &pdev_data->ref_clock_freq);
> -       if (IS_ERR_VALUE(ret)) {
> -               dev_err(glue->dev,
> -                       "can't get reference clock frequency (%d)\n", ret);
> -               return ret;
> -       }
> +       /* optional clock frequency params */
> +       of_property_read_u32(dt_node, "ref-clock-frequency",
> +                            &pdev_data->ref_clock_freq);
> +       of_property_read_u32(dt_node, "tcxo-clock-frequency",
> +                            &pdev_data->tcxo_clock_freq);
>
>         return 0;
>  }
> @@ -397,6 +519,7 @@ static int wl1271_probe(struct spi_device *spi)
>         struct wlcore_platdev_data pdev_data;
>         struct resource res[1];
>         int ret;
> +       struct spi_master *master = spi->master;
>
>         memset(&pdev_data, 0x00, sizeof(pdev_data));
>
> @@ -410,6 +533,12 @@ static int wl1271_probe(struct spi_device *spi)
>
>         glue->dev = &spi->dev;
>
> +       if (!master->cs_gpios) {
> +               dev_err(glue->dev,
> +                       "spi chip select pin missing in platform data!\n");
> +               return -EINVAL;
> +       }
> +
>         spi_set_drvdata(spi, glue);
>
>         /* This is the only SPI value that we need to set here, the rest
> @@ -431,15 +560,21 @@ static int wl1271_probe(struct spi_device *spi)
>                 return ret;
>         }
>
> +       if (gpio_request(master->cs_gpios[0], "spi1-cs0"))
> +               return -EINVAL;
> +
>         ret = spi_setup(spi);
>         if (ret < 0) {
>                 dev_err(glue->dev, "spi_setup failed\n");
> +               gpio_free(master->cs_gpios[0]);
>                 return ret;
>         }
>
> -       glue->core = platform_device_alloc("wl12xx", PLATFORM_DEVID_AUTO);
> +       glue->core = platform_device_alloc(wilink_data->name,
> +                                          PLATFORM_DEVID_AUTO);
>         if (!glue->core) {
>                 dev_err(glue->dev, "can't allocate platform_device\n");
> +               gpio_free(master->cs_gpios[0]);
>                 return -ENOMEM;
>         }
>
> @@ -474,14 +609,17 @@ static int wl1271_probe(struct spi_device *spi)
>
>  out_dev_put:
>         platform_device_put(glue->core);
> +       gpio_free(master->cs_gpios[0]);
>         return ret;
>  }
>
>  static int wl1271_remove(struct spi_device *spi)
>  {
>         struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
> +       struct spi_master *master = spi->master;
>
>         platform_device_unregister(glue->core);
> +       gpio_free(master->cs_gpios[0]);
>
>         return 0;
>  }
> --
> 1.7.9.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH] wlcore: spi: add wl18xx support
From: Eyal Reizer @ 2016-03-30 13:06 UTC (permalink / raw)
  To: kvalo, linux-wireless, netdev, linux-kernel; +Cc: Eyal Reizer

Add support for using with both wl12xx and wl18xx.

- all wilink family needs special init command for entering wspi mode.
  extra clock cycles should be sent after the spi init command while the
  cs pin is high.
- switch to controling the cs pin from the spi driver for achieveing the
  above.
- the selected cs gpio is read from the spi device-tree node using the
  cs-gpios field and setup as a gpio.
- See the example below for specifying the cs gpio using the cs-gpios entry

&spi0	{
 	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&spi0_pins>;
	cs-gpios = <&gpio0 5 0>;
	#address-cells = <1>;
	#size-cells = <0>;
 	wlcore: wlcore@0 {
		compatible = "ti,wl1835";
		vwlan-supply = <&wlan_en_reg>;
		spi-max-frequency = <48000000>;
		reg = <0>;      /* chip select 0 on spi0, ie spi0.0 */
		interrupt-parent = <&gpio0>;
		interrupts = <27 IRQ_TYPE_EDGE_RISING>;
 	};
};

Signed-off-by: Eyal Reizer <eyalr@ti.com>
---
 drivers/net/wireless/ti/wlcore/spi.c |  176 ++++++++++++++++++++++++++++++----
 1 file changed, 157 insertions(+), 19 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c
index 96d9c9d..6c5a369 100644
--- a/drivers/net/wireless/ti/wlcore/spi.c
+++ b/drivers/net/wireless/ti/wlcore/spi.c
@@ -32,6 +32,7 @@
 #include <linux/platform_device.h>
 #include <linux/of_irq.h>
 #include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
 
 #include "wlcore.h"
 #include "wl12xx_80211.h"
@@ -70,16 +71,30 @@
 #define WSPI_MAX_CHUNK_SIZE    4092
 
 /*
- * only support SPI for 12xx - this code should be reworked when 18xx
- * support is introduced
+ * wl18xx driver aggregation buffer size is (13 * PAGE_SIZE) compared to
+ * (4 * PAGE_SIZE) for wl12xx, so use the larger buffer needed for wl18xx
  */
-#define SPI_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
+#define SPI_AGGR_BUFFER_SIZE (13 * PAGE_SIZE)
 
 /* Maximum number of SPI write chunks */
 #define WSPI_MAX_NUM_OF_CHUNKS \
 	((SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) + 1)
 
 
+struct wilink_familiy_data {
+	char name[8];
+};
+
+const struct wilink_familiy_data *wilink_data;
+
+static const struct wilink_familiy_data wl18xx_data = {
+	.name = "wl18xx",
+};
+
+static const struct wilink_familiy_data wl12xx_data = {
+	.name = "wl12xx",
+};
+
 struct wl12xx_spi_glue {
 	struct device *dev;
 	struct platform_device *core;
@@ -120,6 +135,8 @@ static void wl12xx_spi_init(struct device *child)
 	struct spi_transfer t;
 	struct spi_message m;
 	u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
+	struct spi_device *spi = (struct spi_device *)glue->dev;
+	struct spi_master *master = spi->master;
 
 	if (!cmd) {
 		dev_err(child->parent,
@@ -127,6 +144,15 @@ static void wl12xx_spi_init(struct device *child)
 		return;
 	}
 
+	if (!master->cs_gpios) {
+		dev_err(child->parent,
+			"spi chip select pin missing in platform data!\n");
+		return;
+	}
+
+	/* Drive CS line low */
+	gpio_direction_output(master->cs_gpios[0], 0);
+
 	memset(&t, 0, sizeof(t));
 	spi_message_init(&m);
 
@@ -163,6 +189,26 @@ static void wl12xx_spi_init(struct device *child)
 	spi_message_add_tail(&t, &m);
 
 	spi_sync(to_spi_device(glue->dev), &m);
+
+	/* Send extra clocks with CS high. this is required by the wilink
+	 * family in order for successfully enter WSPI mode
+	 */
+	gpio_direction_output(master->cs_gpios[0], 1);
+
+	memset(&m, 0, sizeof(m));
+	spi_message_init(&m);
+
+	cmd[0] = 0xff;
+	cmd[1] = 0xff;
+	cmd[2] = 0xff;
+	cmd[3] = 0xff;
+	swab32s((u32 *)cmd);
+
+	t.tx_buf = cmd;
+	t.len = 4;
+	spi_message_add_tail(&t, &m);
+	spi_sync(to_spi_device(glue->dev), &m);
+
 	kfree(cmd);
 }
 
@@ -213,6 +259,16 @@ static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
 	u32 *busy_buf;
 	u32 *cmd;
 	u32 chunk_len;
+	struct spi_device *spi = (struct spi_device *)glue->dev;
+	struct spi_master *master = spi->master;
+
+	if (!master->cs_gpios) {
+		dev_err(child->parent,
+			"spi chip select pin missing in platform data!\n");
+		return -EINVAL;
+	}
+	/* Drive CS line low */
+	gpio_direction_output(master->cs_gpios[0], 0);
 
 	while (len > 0) {
 		chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len);
@@ -267,25 +323,44 @@ static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
 		len -= chunk_len;
 	}
 
+	/* Drive CS line high */
+	gpio_direction_output(master->cs_gpios[0], 1);
 	return 0;
 }
 
-static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
-					     void *buf, size_t len, bool fixed)
+static int __wl12xx_spi_raw_write(struct device *child, int addr,
+				  void *buf, size_t len, bool fixed)
 {
 	struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
-	/* SPI write buffers - 2 for each chunk */
-	struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS];
+	struct spi_transfer *t;
 	struct spi_message m;
 	u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; /* 1 command per chunk */
 	u32 *cmd;
 	u32 chunk_len;
 	int i;
+	struct spi_device *spi = (struct spi_device *)glue->dev;
+	struct spi_master *master = spi->master;
+
+	if (!master->cs_gpios) {
+		dev_err(child->parent,
+			"spi chip select pin missing in platform data!\n");
+		return -EINVAL;
+	}
+
+	/* SPI write buffers - 2 for each chunk */
+	t = kzalloc(sizeof(*t) * 2 * WSPI_MAX_NUM_OF_CHUNKS, GFP_KERNEL);
+	if (!t) {
+		dev_err(child->parent,
+			"could not allocate spi write buffer\n");
+		return -ENOMEM;
+	}
+
+	/* Drive CS line low */
+	gpio_direction_output(master->cs_gpios[0], 0);
 
 	WARN_ON(len > SPI_AGGR_BUFFER_SIZE);
 
 	spi_message_init(&m);
-	memset(t, 0, sizeof(t));
 
 	cmd = &commands[0];
 	i = 0;
@@ -318,9 +393,29 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
 
 	spi_sync(to_spi_device(glue->dev), &m);
 
+	/* Drive CS line high */
+	gpio_direction_output(master->cs_gpios[0], 1);
+
+	kfree(t);
 	return 0;
 }
 
+static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
+					     void *buf, size_t len, bool fixed)
+{
+	int ret;
+
+	/* The ELP wakeup write may fail the first time due to internal
+	 * hardware latency. It is safer to send the wakeup command twice to
+	 * avoid unexpected failures.
+	 */
+	if (addr == HW_ACCESS_ELP_CTRL_REG)
+		ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
+	ret = __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
+
+	return ret;
+}
+
 /**
  * wl12xx_spi_set_power - power on/off the wl12xx unit
  * @child: wl12xx device handle.
@@ -349,17 +444,38 @@ static int wl12xx_spi_set_power(struct device *child, bool enable)
 	return ret;
 }
 
+/**
+ * wl12xx_spi_set_block_size
+ *
+ * This function is not needed for spi mode, but need to be present.
+ * Without it defined the wlcore fallback to use the wrong packet
+ * allignment on tx.
+ */
+static void wl12xx_spi_set_block_size(struct device *child,
+				      unsigned int blksz)
+{
+}
+
 static struct wl1271_if_operations spi_ops = {
 	.read		= wl12xx_spi_raw_read,
 	.write		= wl12xx_spi_raw_write,
 	.reset		= wl12xx_spi_reset,
 	.init		= wl12xx_spi_init,
 	.power		= wl12xx_spi_set_power,
-	.set_block_size = NULL,
+	.set_block_size = wl12xx_spi_set_block_size,
 };
 
 static const struct of_device_id wlcore_spi_of_match_table[] = {
-	{ .compatible = "ti,wl1271" },
+	{ .compatible = "ti,wl1271", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1273", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1281", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1283", .data = &wl12xx_data},
+	{ .compatible = "ti,wl1801", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1805", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1807", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1831", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1835", .data = &wl18xx_data},
+	{ .compatible = "ti,wl1837", .data = &wl18xx_data},
 	{ }
 };
 MODULE_DEVICE_TABLE(of, wlcore_spi_of_match_table);
@@ -375,18 +491,24 @@ static int wlcore_probe_of(struct spi_device *spi, struct wl12xx_spi_glue *glue,
 			   struct wlcore_platdev_data *pdev_data)
 {
 	struct device_node *dt_node = spi->dev.of_node;
-	int ret;
+	const struct of_device_id *of_id;
+
+	of_id = of_match_node(wlcore_spi_of_match_table, dt_node);
+	if (!of_id)
+		return -ENODEV;
+
+	wilink_data = of_id->data;
+	dev_info(&spi->dev, "selected chip familiy is %s\n",
+		 wilink_data->name);
 
 	if (of_find_property(dt_node, "clock-xtal", NULL))
 		pdev_data->ref_clock_xtal = true;
 
-	ret = of_property_read_u32(dt_node, "ref-clock-frequency",
-				   &pdev_data->ref_clock_freq);
-	if (IS_ERR_VALUE(ret)) {
-		dev_err(glue->dev,
-			"can't get reference clock frequency (%d)\n", ret);
-		return ret;
-	}
+	/* optional clock frequency params */
+	of_property_read_u32(dt_node, "ref-clock-frequency",
+			     &pdev_data->ref_clock_freq);
+	of_property_read_u32(dt_node, "tcxo-clock-frequency",
+			     &pdev_data->tcxo_clock_freq);
 
 	return 0;
 }
@@ -397,6 +519,7 @@ static int wl1271_probe(struct spi_device *spi)
 	struct wlcore_platdev_data pdev_data;
 	struct resource res[1];
 	int ret;
+	struct spi_master *master = spi->master;
 
 	memset(&pdev_data, 0x00, sizeof(pdev_data));
 
@@ -410,6 +533,12 @@ static int wl1271_probe(struct spi_device *spi)
 
 	glue->dev = &spi->dev;
 
+	if (!master->cs_gpios) {
+		dev_err(glue->dev,
+			"spi chip select pin missing in platform data!\n");
+		return -EINVAL;
+	}
+
 	spi_set_drvdata(spi, glue);
 
 	/* This is the only SPI value that we need to set here, the rest
@@ -431,15 +560,21 @@ static int wl1271_probe(struct spi_device *spi)
 		return ret;
 	}
 
+	if (gpio_request(master->cs_gpios[0], "spi1-cs0"))
+		return -EINVAL;
+
 	ret = spi_setup(spi);
 	if (ret < 0) {
 		dev_err(glue->dev, "spi_setup failed\n");
+		gpio_free(master->cs_gpios[0]);
 		return ret;
 	}
 
-	glue->core = platform_device_alloc("wl12xx", PLATFORM_DEVID_AUTO);
+	glue->core = platform_device_alloc(wilink_data->name,
+					   PLATFORM_DEVID_AUTO);
 	if (!glue->core) {
 		dev_err(glue->dev, "can't allocate platform_device\n");
+		gpio_free(master->cs_gpios[0]);
 		return -ENOMEM;
 	}
 
@@ -474,14 +609,17 @@ static int wl1271_probe(struct spi_device *spi)
 
 out_dev_put:
 	platform_device_put(glue->core);
+	gpio_free(master->cs_gpios[0]);
 	return ret;
 }
 
 static int wl1271_remove(struct spi_device *spi)
 {
 	struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
+	struct spi_master *master = spi->master;
 
 	platform_device_unregister(glue->core);
+	gpio_free(master->cs_gpios[0]);
 
 	return 0;
 }
-- 
1.7.9.5

^ permalink raw reply related

* Re: [PATCH RFC net-next] net: core: Pass XPS select queue decision to skb_tx_hash
From: Saeed Mahameed @ 2016-03-30 13:23 UTC (permalink / raw)
  To: John Fastabend, Saeed Mahameed, netdev
  Cc: Eric Dumazet, Tom Herbert, Jiri Pirko, David S. Miller,
	John Fastabend
In-Reply-To: <56FB1B64.40301@gmail.com>



On 3/30/2016 3:18 AM, John Fastabend wrote:
> I would prefer to not have another strange quirk users have to 
> remember in order to do tx classification. So with this change 
> depending on the driver the queue selection precedence changes. 
This change doesn't depend on the driver it affects all drivers that 
implement the select queue ndo and use the default fallback 
"pick_tx_queue" which this patch came to fix, or any driver that doesn't 
implement the ndo (the fallback is the default in this case).
> In short I agree with the problem statement but think we can find a 
> better solution. One idea that comes to mind is we can have a tc 
> action to force the queue selection? Now that we have the egress tc 
> hook it would probably be fairly cheap to implement and if users want 
> this behavior they can ask for it explicitly. If your thinking about 
> tc stuff we could fix the tooling to set this action when ever dcb is 
> turned on or hardware rate limiting is enabled, etc. And even if we 
> wanted we could have the driver add the rule in the cases where 
> firmware protocols are configuring the QOS/etc. 
Why would you ask for a bug fix explicitly ? IMHO this how I expect the 
pick _tx_queue routine to behave, why would I disable XPS in order for 
select queue to choose according TC QoS ?
as this patch suggests we can benefit from both without any additional 
tooling !

>>   	if (skb_vlan_tag_present(skb))
>>   		up = skb_vlan_tag_get(skb) >> VLAN_PRIO_SHIFT;
>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>> index cb0d5d0..ad81ffe 100644
>> --- a/include/linux/netdevice.h
>> +++ b/include/linux/netdevice.h
>> @@ -3130,16 +3130,16 @@ static inline int netif_set_xps_queue(struct net_device *dev,
>>   #endif
>>   
>>   u16 __skb_tx_hash(const struct net_device *dev, struct sk_buff *skb,
>> -		  unsigned int num_tx_queues);
>> +		  unsigned int num_tx_queues, int txq_hint);
>>   
> [...]
>
> And all this seems like it would only ever be called by drivers select
> queue routines which I really wish we could kill off one of these days
> instead of add to. Now if the signal is something higher in the stack
> and not the driver I think it is OK.
I agree, drivers shouldn't call this function, the only reason drivers 
call this function is to bypass get_xps_queue
and after this patch i don't think driver will need to call it, since it 
will be called even if XPS is configured.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox