From mboxrd@z Thu Jan 1 00:00:00 1970 From: Christophe Leroy Subject: Re: [PATCH 5/5] drivers/net: support hdlc function for QE-UCC Date: Tue, 19 Apr 2016 18:22:00 +0200 Message-ID: <57165B27.60000@c-s.fr> References: <1459327830-19829-1-git-send-email-qiang.zhao@nxp.com> <1459327830-19829-5-git-send-email-qiang.zhao@nxp.com> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: QUOTED-PRINTABLE Cc: gregkh@linuxfoundation.org, 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 To: Zhao Qiang , davem@davemloft.net Return-path: In-Reply-To: <1459327830-19829-5-git-send-email-qiang.zhao@nxp.com> Sender: linux-kernel-owner@vger.kernel.org List-Id: netdev.vger.kernel.org Le 30/03/2016 10:50, Zhao Qiang a =C3=A9crit : > 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 yo= u=20 describe which Timeslot is switched to HDLC channels ? Is it possible to route some Timeslots to one UCC for HDLC, and route=20 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=20 UCC into independant channels that can either be used with HDLC or=20 transparents (for audio for instance). Do you intent to also support QM= C ? According to the compatible property, it looks like your driver is for=20 freescale T1040. The MPC83xx also has a Quick Engine, would it work on=20 it too ? Christophe > > Signed-off-by: Zhao Qiang > --- > 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 > =20 > +FREESCALE QUICC ENGINE UCC HDLC DRIVER > +M: Zhao Qiang > +L: linuxppc-dev@lists.ozlabs.org > +S: Maintained > +F: drivers/net/wan/fsl_ucc_hdlc* > + > FREESCALE QUICC ENGINE UCC UART DRIVER > M: Timur Tabi > 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. > =20 > +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) +=3D wanxl.o > obj-$(CONFIG_PCI200SYN) +=3D pci200syn.o > obj-$(CONFIG_PC300TOO) +=3D pc300too.o > obj-$(CONFIG_IXP4XX_HSS) +=3D ixp4xx_hss.o > +obj-$(CONFIG_FSL_UCC_HDLC) +=3D fsl_ucc_hdlc.o > =20 > clean-files :=3D 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 mo= dify 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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 =3D { > + .uf_info =3D { > + .tsa =3D 0, > + .cdp =3D 0, > + .cds =3D 1, > + .ctsp =3D 1, > + .ctss =3D 1, > + .revd =3D 0, > + .urfs =3D 256, > + .utfs =3D 256, > + .urfet =3D 128, > + .urfset =3D 192, > + .utfet =3D 128, > + .utftt =3D 0x40, > + .ufpt =3D 256, > + .mode =3D UCC_FAST_PROTOCOL_MODE_HDLC, > + .ttx_trx =3D UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_NORMAL, > + .tenc =3D UCC_FAST_TX_ENCODING_NRZ, > + .renc =3D UCC_FAST_RX_ENCODING_NRZ, > + .tcrc =3D UCC_FAST_16_BIT_CRC, > + .synl =3D UCC_FAST_SYNC_LEN_NOT_USED, > + }, > + > + .si_info =3D { > +#ifdef CONFIG_FSL_PQ_MDS_T1 > + .simr_rfsd =3D 1, /* TDM card need 1 bit delay */ > + .simr_tfsd =3D 0, > +#else > +#ifdef TDM_PPPOHT_SLIC_MAXIN > + .simr_rfsd =3D 1, > + .simr_tfsd =3D 2, > +#else > + .simr_rfsd =3D 0, > + .simr_tfsd =3D 0, > +#endif > +#endif > + .simr_crt =3D 0, > + .simr_sl =3D 0, > + .simr_ce =3D 1, > + .simr_fe =3D 1, > + .simr_gm =3D 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 =3D (size >> 4) << 4; > + int size4_aling =3D (size >> 2) << 2; > + int not_align =3D 0; > + > + if (size % 16) > + not_align =3D 1; > + > + for (i =3D addr; i < addr + size16_aling; i +=3D 16) { > + u32 *i32 =3D 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 =3D=3D 1) > + pr_info("0x%08p: ", i); > + for (; i < addr + size4_aling; i +=3D 4) > + pr_info("%08x ", be32_to_cpu(*((u32 *)(i)))); > + for (; i < addr + size; i++) > + pr_info("%02x", *((u8 *)(i))); > + if (not_align =3D=3D 1) > + pr_info("\r\n"); > +} > + > +static void dump_ucc(struct ucc_hdlc_private *priv) > +{ > + struct ucc_hdlc_param *ucc_pram; > + > + ucc_pram =3D 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 =3D 0x%x\n", ioread32be(&ucc_pram->rbase= )); > + dev_info(priv->dev, "rbptr =3D 0x%x\n", ioread32be(&ucc_pram->rbptr= )); > + dev_info(priv->dev, "mrblr =3D 0x%x\n", ioread16be(&ucc_pram->mrblr= )); > + dev_info(priv->dev, "rbdlen =3D 0x%x\n", ioread16be(&ucc_pram->rbdl= en)); > + dev_info(priv->dev, "rbdstat =3D 0x%x\n", ioread16be(&ucc_pram->rbd= stat)); > + dev_info(priv->dev, "rstate =3D 0x%x\n", ioread32be(&ucc_pram->rsta= te)); > + dev_info(priv->dev, "rdptr =3D 0x%x\n", ioread32be(&ucc_pram->rdptr= )); > + dev_info(priv->dev, "riptr =3D 0x%x\n", ioread16be(&ucc_pram->riptr= )); > + dev_info(priv->dev, "tbase =3D 0x%x\n", ioread32be(&ucc_pram->tbase= )); > + dev_info(priv->dev, "tbptr =3D 0x%x\n", ioread32be(&ucc_pram->tbptr= )); > + dev_info(priv->dev, "tbdlen =3D 0x%x\n", ioread16be(&ucc_pram->tbdl= en)); > + dev_info(priv->dev, "tbdstat =3D 0x%x\n", ioread16be(&ucc_pram->tbd= stat)); > + dev_info(priv->dev, "tstate =3D 0x%x\n", ioread32be(&ucc_pram->tsta= te)); > + dev_info(priv->dev, "tdptr =3D 0x%x\n", ioread32be(&ucc_pram->tdptr= )); > + dev_info(priv->dev, "tiptr =3D 0x%x\n", ioread16be(&ucc_pram->tiptr= )); > + dev_info(priv->dev, "rcrc =3D 0x%x\n", ioread32be(&ucc_pram->rcrc))= ; > + dev_info(priv->dev, "tcrc =3D 0x%x\n", ioread32be(&ucc_pram->tcrc))= ; > + dev_info(priv->dev, "c_mask =3D 0x%x\n", ioread32be(&ucc_pram->c_ma= sk)); > + dev_info(priv->dev, "c_pers =3D 0x%x\n", ioread32be(&ucc_pram->c_pr= es)); > + dev_info(priv->dev, "disfc =3D 0x%x\n", ioread16be(&ucc_pram->disfc= )); > + dev_info(priv->dev, "crcec =3D 0x%x\n", ioread16be(&ucc_pram->crcec= )); > +} > + > +static void dump_bds(struct ucc_hdlc_private *priv) > +{ > + int length; > + > + if (priv->tx_bd_base) { > + length =3D 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 =3D 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 =3D 0x%x\n", (u32)priv->ut_info); > + dev_info(priv->dev, "uccf =3D 0x%x\n", (u32)priv->uccf); > + dev_info(priv->dev, "uf_regs =3D 0x%x\n", (u32)priv->uf_regs); > + dev_info(priv->dev, "si_regs =3D 0x%x\n", (u32)priv->utdm->si_regs)= ; > + dev_info(priv->dev, "ucc_pram =3D 0x%x\n", (u32)priv->ucc_pram); > + dev_info(priv->dev, "tdm_port =3D 0x%x\n", (u32)priv->utdm->tdm_por= t); > + dev_info(priv->dev, "siram_entry_id =3D 0x%x\n", > + priv->utdm->siram_entry_id); > + dev_info(priv->dev, "siram =3D 0x%x\n", (u32)priv->utdm->siram); > + dev_info(priv->dev, "tdm_mode =3D 0x%x\n", (u32)priv->utdm->tdm_mod= e); > + dev_info(priv->dev, "tdm_framer_type; =3D 0x%x\n", > + (u32)priv->utdm->tdm_framer_type); > + dev_info(priv->dev, "rx_buffer; =3D 0x%x\n", (u32)priv->rx_buffer); > + dev_info(priv->dev, "tx_buffer; =3D 0x%x\n", (u32)priv->tx_buffer); > + dev_info(priv->dev, "dma_rx_addr; =3D 0x%x\n", (u32)priv->dma_rx_ad= dr); > + dev_info(priv->dev, "tx_bd; =3D 0x%x\n", (u32)priv->tx_bd_base); > + dev_info(priv->dev, "rx_bd; =3D 0x%x\n", (u32)priv->rx_bd_base); > + dev_info(priv->dev, "curtx_bd =3D 0x%x\n", (u32)priv->curtx_bd); > + dev_info(priv->dev, "currx_bd =3D 0x%x\n", (u32)priv->currx_bd); > + dev_info(priv->dev, "ucc_pram_offset =3D 0x%x\n", priv->ucc_pram_of= fset); > +} > + > +#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 =3D priv->ut_info; > + uf_info =3D &ut_info->uf_info; > + > + if (priv->tsa) { > + uf_info->tsa =3D 1; > + uf_info->ctsp =3D 1; > + } > + uf_info->uccm_mask =3D (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 =3D 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 =3D ioread32be(&priv->uf_regs->gumr); > + gumr |=3D (0x40000000 | UCC_FAST_GUMR_CDS | UCC_FAST_GUMR_TCI); > + gumr &=3D ~(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 =3D ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); > + ret =3D 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 =3D RX_BD_RING_LEN; > + priv->tx_ring_size =3D TX_BD_RING_LEN; > + /* Alloc Rx BD */ > + priv->rx_bd_base =3D 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 =3D -ENOMEM; > + goto rxbd_alloc_error; > + } > + > + /* Alloc Tx BD */ > + priv->tx_bd_base =3D 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 =3D -ENOMEM; > + goto txbd_alloc_error; > + } > + > + /* Alloc parameter ram for ucc hdlc */ > + priv->ucc_pram_offset =3D 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 =3D -ENOMEM; > + goto pram_alloc_error; > + } > + > + priv->rx_skbuff =3D kmalloc_array(priv->rx_ring_size, > + sizeof(*priv->rx_skbuff), GFP_KERNEL); > + if (!priv->rx_skbuff) > + goto rx_skb_alloc_error; > + for (i =3D 0; i < priv->rx_ring_size; i++) > + priv->rx_skbuff[i] =3D NULL; > + > + priv->tx_skbuff =3D kmalloc_array(priv->tx_ring_size, > + sizeof(*priv->tx_skbuff), GFP_KERNEL); > + if (!priv->tx_skbuff) > + goto tx_skb_alloc_error; > + for (i =3D 0; i < priv->tx_ring_size; i++) > + priv->tx_skbuff[i] =3D NULL; > + > + priv->skb_curtx =3D 0; > + priv->skb_dirtytx =3D 0; > + priv->curtx_bd =3D priv->tx_bd_base; > + priv->dirty_tx =3D priv->tx_bd_base; > + priv->currx_bd =3D priv->rx_bd_base; > + priv->currx_bdnum =3D 0; > + > + /* init parameter base */ > + cecr_subblock =3D ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); > + ret =3D qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock, > + QE_CR_PROTOCOL_UNSPECIFIED, priv->ucc_pram_offset); > + > + priv->ucc_pram =3D (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 =3D 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 =3D -ENOMEM; > + goto riptr_alloc_error; > + } > + > + tiptr =3D qe_muram_alloc(32, 32); > + if (IS_ERR_VALUE(tiptr)) { > + dev_err(priv->dev, "Cannot allocate MURAM mem for Transmit interna= l temp data pointer\n"); > + ret =3D -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 =3D 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 =3D bd_buffer; > + priv->tx_buffer =3D bd_buffer + RX_BD_RING_LEN * MAX_RX_BUF_LENGTH; > + > + priv->dma_rx_addr =3D bd_dma_addr; > + priv->dma_tx_addr =3D bd_dma_addr + RX_BD_RING_LEN * MAX_RX_BUF_LEN= GTH; > + > + for (i =3D 0; i < RX_BD_RING_LEN; i++) { > + if (i < (RX_BD_RING_LEN - 1)) > + bd_status =3D R_E | R_I; > + else > + bd_status =3D 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 =3D 0; i < TX_BD_RING_LEN; i++) { > + if (i < (TX_BD_RING_LEN - 1)) > + bd_status =3D T_I | T_TC; > + else > + bd_status =3D 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_devic= e *dev) > +{ > + hdlc_device *hdlc =3D dev_to_hdlc(dev); > + struct ucc_hdlc_private *priv =3D (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 =3D (u16 *)skb->data; > + tmp_head =3D *proto_head; > + tmp_head =3D (tmp_head & HDLC_HEAD_MASK) | > + htons(DEFAULT_HDLC_HEAD); > + *proto_head =3D tmp_head; > + > + dev->stats.tx_bytes +=3D skb->len; > + break; > + > + case ARPHRD_PPP: > + proto_head =3D (u16 *)skb->data; > + if (*proto_head !=3D 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 +=3D 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 =3D (u8 *)skb->data; > + pr_info("\nTransmitted data:\n"); > + for (i =3D 0; (i < 16); i++) { > + if (i =3D=3D 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 =3D priv->curtx_bd; > + bd_status =3D ioread32be((u32 __iomem *)bd); > + /* Save the skb pointer so we can free it later */ > + priv->tx_skbuff[priv->skb_curtx] =3D skb; > + > + /* Update the current skb pointer (wrapping if this was the last) *= / > + priv->skb_curtx =3D > + (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 =3D (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 +=3D 1; > + else > + bd =3D priv->tx_bd_base; > + > + if (bd =3D=3D priv->dirty_tx) { > + if (!netif_queue_stopped(dev)) > + netif_stop_queue(dev); > + } > + > + priv->curtx_bd =3D 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 =3D priv->ndev; > + struct qe_bd *bd; /* BD pointer */ > + u32 bd_status; > + > + bd =3D priv->dirty_tx; > + bd_status =3D ioread32be((u32 __iomem *)bd); > + > + /* Normal processing. */ > + while ((bd_status & T_R) =3D=3D 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 =3D 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] =3D NULL; > + priv->skb_dirtytx =3D > + (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 +=3D 1; > + else > + bd =3D priv->tx_bd_base; > + bd_status =3D ioread32be((u32 __iomem *)bd); > + } > + priv->dirty_tx =3D bd; > + > + return 0; > +} > + > +static int hdlc_rx_done(struct ucc_hdlc_private *priv, int rx_work_l= imit) > +{ > + struct net_device *dev =3D priv->ndev; > + struct sk_buff *skb; > + hdlc_device *hdlc =3D dev_to_hdlc(dev); > + struct qe_bd *bd; > + u32 bd_status; > + u16 length, howmany =3D 0; > + u8 *bdbuffer; > +#ifdef QE_HDLC_TEST > + int i; > + static int entry; > +#endif > + > + bd =3D priv->currx_bd; > + bd_status =3D 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 =3D priv->rx_buffer + > + (priv->currx_bdnum * MAX_RX_BUF_LENGTH); > + length =3D (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 =3D 0; (i < 16); i++) { > + if (i =3D=3D length) > + pr_info("++++"); > + else > + pr_info("%02x\n", bdbuffer[i]); > + } > +#endif > + > + switch (dev->type) { > + case ARPHRD_RAWHDLC: > + bdbuffer +=3D HDLC_HEAD_LEN; > + length -=3D (HDLC_HEAD_LEN + HDLC_CRC_SIZE); > + > + skb =3D dev_alloc_skb(length); > + if (!skb) { > + dev->stats.rx_dropped++; > + return -ENOMEM; > + } > + > + skb_put(skb, length); > + skb->len =3D length; > + skb->dev =3D dev; > + memcpy(skb->data, bdbuffer, length); > + break; > + > + case ARPHRD_PPP: > + length -=3D HDLC_CRC_SIZE; > + > + skb =3D dev_alloc_skb(length); > + if (!skb) { > + dev->stats.rx_dropped++; > + return -ENOMEM; > + } > + > + skb_put(skb, length); > + skb->len =3D length; > + skb->dev =3D dev; > + memcpy(skb->data, bdbuffer, length); > + break; > + } > + > + dev->stats.rx_packets++; > + dev->stats.rx_bytes +=3D skb->len; > + howmany++; > + if (hdlc->proto) > + skb->protocol =3D 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 =3D 0; > + bd =3D priv->rx_bd_base; > + } else { > + if (priv->currx_bdnum < (RX_BD_RING_LEN - 1)) > + priv->currx_bdnum +=3D 1; > + else > + priv->currx_bdnum =3D RX_BD_RING_LEN - 1; > + > + bd +=3D 1; > + } > + > + bd_status =3D ioread32be((u32 __iomem *)bd); > + } > + > + priv->currx_bd =3D bd; > + return howmany; > +} > + > +static int ucc_hdlc_poll(struct napi_struct *napi, int budget) > +{ > + struct ucc_hdlc_private *priv =3D 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 =3D 0; > + howmany +=3D 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 =3D (struct ucc_hdlc_private *)dev_id= ; > + struct net_device *dev =3D priv->ndev; > + struct ucc_fast_private *uccf; > + struct ucc_tdm_info *ut_info; > + u32 ucce; > + u32 uccm; > + > + ut_info =3D priv->ut_info; > + uccf =3D priv->uccf; > + > + ucce =3D ioread32be(uccf->p_ucce); > + uccm =3D ioread32be(uccf->p_uccm); > + ucce &=3D 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 &=3D ~((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, in= t cmd) > +{ > + const size_t size =3D sizeof(te1_settings); > + te1_settings line; > + struct ucc_hdlc_private *priv =3D netdev_priv(dev); > + > + if (cmd !=3D SIOCWANDEV) > + return hdlc_ioctl(dev, ifr, cmd); > + > + switch (ifr->ifr_settings.type) { > + case IF_GET_IFACE: > + ifr->ifr_settings.type =3D IF_IFACE_E1; > + if (ifr->ifr_settings.size < size) { > + ifr->ifr_settings.size =3D size; /* data size wanted */ > + return -ENOBUFS; > + } > + line.clock_type =3D priv->clocking; > + line.clock_rate =3D 0; > + line.loopback =3D 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 =3D dev_to_hdlc(dev); > + struct ucc_hdlc_private *priv =3D hdlc->priv; > + struct ucc_tdm *utdm =3D priv->utdm; > + > + if (priv->hdlc_busy !=3D 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 =3D 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 |=3D (0x1 << utdm->tdm_port); > + > + priv->hdlc_busy =3D 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 =3D NULL; > + priv->dma_rx_bd =3D 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 =3D NULL; > + priv->dma_tx_bd =3D 0; > + } > + > + if (priv->ucc_pram) { > + qe_muram_free(priv->ucc_pram_offset); > + priv->ucc_pram =3D NULL; > + priv->ucc_pram_offset =3D 0; > + } > + > + kfree(priv->rx_skbuff); > + priv->rx_skbuff =3D NULL; > + > + kfree(priv->tx_skbuff); > + priv->tx_skbuff =3D NULL; > + > + if (priv->uf_regs) { > + iounmap(priv->uf_regs); > + priv->uf_regs =3D NULL; > + } > + > + if (priv->uccf) { > + ucc_fast_free(priv->uccf); > + priv->uccf =3D 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 =3D NULL; > + priv->dma_rx_addr =3D 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 =3D NULL; > + priv->dma_tx_addr =3D 0; > + } > +} > + > +static int uhdlc_close(struct net_device *dev) > +{ > + struct ucc_hdlc_private *priv =3D dev_to_hdlc(dev)->priv; > + struct ucc_tdm *utdm =3D priv->utdm; > + u32 cecr_subblock; > + > + napi_disable(&priv->napi); > + cecr_subblock =3D 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 &=3D ~(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 =3D 0; > + > + return 0; > +} > + > +static int ucc_hdlc_attach(struct net_device *dev, unsigned short en= coding, > + unsigned short parity) > +{ > + struct ucc_hdlc_private *priv =3D dev_to_hdlc(dev)->priv; > + > + if (encoding !=3D ENCODING_NRZ && > + encoding !=3D ENCODING_NRZI) > + return -EINVAL; > + > + if (parity !=3D PARITY_NONE && > + parity !=3D PARITY_CRC32_PR1_CCITT && > + parity !=3D PARITY_CRC16_PR1_CCITT) > + return -EINVAL; > + > + priv->encoding =3D encoding; > + priv->parity =3D parity; > + > + return 0; > +} > + > +#ifdef CONFIG_PM > +static void store_clk_config(struct ucc_hdlc_private *priv) > +{ > + struct qe_mux *qe_mux_reg =3D &qe_immr->qmx; > + > + /* store si clk */ > + priv->cmxsi1cr_h =3D ioread32be(&qe_mux_reg->cmxsi1cr_h); > + priv->cmxsi1cr_l =3D ioread32be(&qe_mux_reg->cmxsi1cr_l); > + > + /* store si sync */ > + priv->cmxsi1syr =3D 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 =3D &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 =3D 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 =3D priv->ut_info; > + uf_regs =3D priv->uf_regs; > + > + /* backup gumr guemr*/ > + priv->gumr =3D ioread32be(&uf_regs->gumr); > + priv->guemr =3D ioread8(&uf_regs->guemr); > + > + priv->ucc_pram_bak =3D 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 =3D dev_get_drvdata(dev); > + struct ucc_tdm *utdm =3D 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 =3D priv->ut_info; > + uf_info =3D &ut_info->uf_info; > + uf_regs =3D priv->uf_regs; > + uccf =3D 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->u= tfb); > + iowrite32be(uccf->ucc_fast_rx_virtual_fifo_base_offset, &uf_regs->u= rfb); > + > + /* 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 =3D ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); > + ret =3D 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 =3D ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); > + ret =3D qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock, > + QE_CR_PROTOCOL_UNSPECIFIED, priv->ucc_pram_offset); > + > + priv->ucc_pram =3D (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 =3D 0; i < RX_BD_RING_LEN; i++) { > + if (i < (RX_BD_RING_LEN - 1)) > + bd_status =3D R_E | R_I; > + else > + bd_status =3D 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 =3D 0; i < TX_BD_RING_LEN; i++) { > + if (i < (TX_BD_RING_LEN - 1)) > + bd_status =3D T_I | T_TC; > + else > + bd_status =3D 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 =3D=3D 1) { > + cecr_subblock =3D 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 |=3D (0x1 << utdm->tdm_port); > + } > + > + napi_enable(&priv->napi); > + netif_device_attach(priv->ndev); > + > + return 0; > +} > + > +static const struct dev_pm_ops uhdlc_pm_ops =3D { > + .suspend =3D uhdlc_suspend, > + .resume =3D uhdlc_resume, > + .freeze =3D uhdlc_suspend, > + .thaw =3D uhdlc_resume, > +}; > + > +#define HDLC_PM_OPS (&uhdlc_pm_ops) > + > +#else > + > +#define HDLC_PM_OPS NULL > + > +#endif > +static const struct net_device_ops uhdlc_ops =3D { > + .ndo_open =3D uhdlc_open, > + .ndo_stop =3D uhdlc_close, > + .ndo_change_mtu =3D hdlc_change_mtu, > + .ndo_start_xmit =3D hdlc_start_xmit, > + .ndo_do_ioctl =3D uhdlc_ioctl, > +}; > + > +static int ucc_hdlc_probe(struct platform_device *pdev) > +{ > + struct device_node *np =3D pdev->dev.of_node; > + struct ucc_hdlc_private *uhdlc_priv =3D 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 =3D of_property_read_u32_index(np, "cell-index", 0, &val); > + if (ret) { > + dev_err(&pdev->dev, "Invalid ucc property\n"); > + return -ENODEV; > + } > + > + ucc_num =3D 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 =3D &utdm_info[ucc_num]; > + ut_info->uf_info.ucc_num =3D ucc_num; > + > + sprop =3D of_get_property(np, "rx-clock-name", NULL); > + if (sprop) { > + ut_info->uf_info.rx_clock =3D 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 =3D of_get_property(np, "tx-clock-name", NULL); > + if (sprop) { > + ut_info->uf_info.tx_clock =3D 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 =3D=3D ut_info->uf_info.tx_clock) > + qe_setbrg(ut_info->uf_info.rx_clock, 20000000, 1); > + > + ret =3D of_address_to_resource(np, 0, &res); > + if (ret) > + return -EINVAL; > + > + ut_info->uf_info.regs =3D res.start; > + ut_info->uf_info.irq =3D irq_of_parse_and_map(np, 0); > + > + uhdlc_priv =3D kzalloc(sizeof(*uhdlc_priv), GFP_KERNEL); > + if (!uhdlc_priv) { > + ret =3D -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 =3D &pdev->dev; > + uhdlc_priv->ut_info =3D ut_info; > + > + if (of_get_property(np, "fsl,tdm-interface", NULL)) > + uhdlc_priv->tsa =3D 1; > + > + if (of_get_property(np, "fsl,ucc-internal-loopback", NULL)) > + uhdlc_priv->loopback =3D 1; > + > + if (uhdlc_priv->tsa =3D=3D 1) { > + utdm =3D kzalloc(sizeof(*utdm), GFP_KERNEL); > + if (!utdm) { > + ret =3D -ENOMEM; > + dev_err(&pdev->dev, "No mem to alloc ucc tdm data\n"); > + goto err_alloc_utdm; > + } > + uhdlc_priv->utdm =3D utdm; > + ret =3D ucc_of_parse_tdm(np, utdm, ut_info); > + if (ret) > + goto err_miss_tsa_property; > + } > + > + ret =3D uhdlc_init(uhdlc_priv); > + if (ret) { > + dev_err(&pdev->dev, "Failed to init uhdlc\n"); > + goto err_hdlc_init; > + } > + > + dev =3D alloc_hdlcdev(uhdlc_priv); > + if (!dev) { > + ret =3D -ENOMEM; > + pr_err("ucc_hdlc: unable to allocate memory\n"); > + goto err_hdlc_init; > + } > + > + uhdlc_priv->ndev =3D dev; > + hdlc =3D dev_to_hdlc(dev); > + dev->tx_queue_len =3D 16; > + dev->netdev_ops =3D &uhdlc_ops; > + hdlc->attach =3D ucc_hdlc_attach; > + hdlc->xmit =3D ucc_hdlc_tx; > + netif_napi_add(dev, &uhdlc_priv->napi, ucc_hdlc_poll, 32); > + if (register_hdlc_device(dev)) { > + ret =3D -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 =3D dev_get_drvdata(&pdev->dev); > + > + uhdlc_memclean(priv); > + > + if (priv->utdm->si_regs) { > + iounmap(priv->utdm->si_regs); > + priv->utdm->si_regs =3D NULL; > + } > + > + if (priv->utdm->siram) { > + iounmap(priv->utdm->siram); > + priv->utdm->siram =3D 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[] =3D { > + { > + .compatible =3D "fsl,ucc-hdlc", > + }, > + {}, > +}; > + > +MODULE_DEVICE_TABLE(of, fsl_ucc_hdlc_of_match); > + > +static struct platform_driver ucc_hdlc_driver =3D { > + .probe =3D ucc_hdlc_probe, > + .remove =3D ucc_hdlc_remove, > + .driver =3D { > + .owner =3D THIS_MODULE, > + .name =3D DRV_NAME, > + .pm =3D HDLC_PM_OPS, > + .of_match_table =3D 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 mo= dify 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 > +#include > + > +#include > +#include > + > +#include > +#include > + > +/* 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_f= ast.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 */ > =20 > /* 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 */ > =20 > /* Rx Data buffer must be 4 bytes aligned in most cases */ > #define UCC_FAST_RX_ALIGN 4