diff mbox

[5/5] drivers/net: support hdlc function for QE-UCC

Message ID 1459327830-19829-5-git-send-email-qiang.zhao@nxp.com (mailing list archive)
State Superseded
Delegated to: Scott Wood
Headers show

Commit Message

Qiang Zhao March 30, 2016, 8:50 a.m. UTC
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

Comments

kernel test robot March 30, 2016, 9:53 a.m. UTC | #1
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
kernel test robot March 30, 2016, 12:27 p.m. UTC | #2
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
kernel test robot March 30, 2016, 12:53 p.m. UTC | #3
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
Christophe Leroy April 19, 2016, 4:22 p.m. UTC | #4
Le 30/03/2016 10:50, Zhao Qiang a écrit :
> The driver add hdlc support for Freescale QUICC Engine.
> It support NMSI and TSA mode.
When using TSA, how does the TSA gets configured ? Especially how do you 
describe which Timeslot is switched to HDLC channels ?
Is it possible to route some Timeslots to one UCC for HDLC, and route 
some others to another UCC for an ALSA sound driver ?

The QE also have a QMC which allows to split all timeslots to a given 
UCC into independant channels that can either be used with HDLC or 
transparents (for audio for instance). Do you intent to also support QMC ?

According to the compatible property, it looks like your driver is for 
freescale T1040. The MPC83xx also has a Quick Engine, would it work on 
it too ?

Christophe

>
> 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
Qiang Zhao April 20, 2016, 2:28 a.m. UTC | #5
On 20/04/2016 12:22AM, Christophe Leroy <christophe.leroy@c-s.fr> wrote
> -----Original Message-----

> From: Christophe Leroy [mailto:christophe.leroy@c-s.fr]

> Sent: Wednesday, April 20, 2016 12:22 AM

> To: Qiang Zhao <qiang.zhao@nxp.com>; davem@davemloft.net

> Cc: gregkh@linuxfoundation.org; Xiaobo Xie <xiaobo.xie@nxp.com>; linux-

> kernel@vger.kernel.org; oss@buserror.net; netdev@vger.kernel.org;

> akpm@linux-foundation.org; linuxppc-dev@lists.ozlabs.org

> Subject: Re: [PATCH 5/5] drivers/net: support hdlc function for QE-UCC

> 

> Le 30/03/2016 10:50, Zhao Qiang a écrit :

> > The driver add hdlc support for Freescale QUICC Engine.

> > It support NMSI and TSA mode.

> When using TSA, how does the TSA gets configured ? Especially how do you

> describe which Timeslot is switched to HDLC channels ?


the TSA is configured statically according to device tree node. 
For " which Timeslot is switched to HDLC channels ", there is a property 
"fsl,tx-timeslot-mask" in device tree to describe it.

> Is it possible to route some Timeslots to one UCC for HDLC, and route some

> others to another UCC for an ALSA sound driver ?


The feature you describe is not supported at present.

> The QE also have a QMC which allows to split all timeslots to a given UCC into

> independant channels that can either be used with HDLC or transparents (for

> audio for instance). Do you intent to also support QMC ?


new QE use UMCC instead of QMC in old QE, we have started to develop UMCC.
 
> According to the compatible property, it looks like your driver is for freescale

> T1040. The MPC83xx also has a Quick Engine, would it work on it too ?


The driver is common, but tested on t1040, it is needed to add node to MPC83xx
If you want to test on mpc83xx.

-Zhao Qiang
diff mbox

Patch

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