* [PATCH 1/5] fsl/qe: add rx_sync and tx_sync for TDM mode @ 2016-03-30 8:50 Zhao Qiang 2016-03-30 8:50 ` [PATCH 2/5] fsl/qe: setup clock source " Zhao Qiang ` (3 more replies) 0 siblings, 4 replies; 12+ messages in thread From: Zhao Qiang @ 2016-03-30 8:50 UTC (permalink / raw) To: davem Cc: akpm, gregkh, oss, xiaobo.xie, linux-kernel, netdev, linuxppc-dev, Zhao Qiang Rx_sync and tx_sync are used by QE-TDM mode, add them to struct ucc_fast_info. Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com> --- drivers/soc/fsl/qe/qe.c | 6 ++++++ include/soc/fsl/qe/qe.h | 2 ++ include/soc/fsl/qe/ucc_fast.h | 2 ++ 3 files changed, 10 insertions(+) diff --git a/drivers/soc/fsl/qe/qe.c b/drivers/soc/fsl/qe/qe.c index 709fc63..7026507 100644 --- a/drivers/soc/fsl/qe/qe.c +++ b/drivers/soc/fsl/qe/qe.c @@ -239,6 +239,12 @@ enum qe_clock qe_clock_source(const char *source) if (strcasecmp(source, "none") == 0) return QE_CLK_NONE; + if (strcmp(source, "tsync_pin") == 0) + return QE_TSYNC_PIN; + + if (strcmp(source, "rsync_pin") == 0) + return QE_RSYNC_PIN; + if (strncasecmp(source, "brg", 3) == 0) { i = simple_strtoul(source + 3, NULL, 10); if ((i >= 1) && (i <= 16)) diff --git a/include/soc/fsl/qe/qe.h b/include/soc/fsl/qe/qe.h index 33b29ea..f918745 100644 --- a/include/soc/fsl/qe/qe.h +++ b/include/soc/fsl/qe/qe.h @@ -80,6 +80,8 @@ enum qe_clock { QE_CLK22, /* Clock 22 */ QE_CLK23, /* Clock 23 */ QE_CLK24, /* Clock 24 */ + QE_RSYNC_PIN, /* RSYNC from pin */ + QE_TSYNC_PIN, /* TSYNC from pin */ QE_CLK_DUMMY }; diff --git a/include/soc/fsl/qe/ucc_fast.h b/include/soc/fsl/qe/ucc_fast.h index df8ea79..31548b7 100644 --- a/include/soc/fsl/qe/ucc_fast.h +++ b/include/soc/fsl/qe/ucc_fast.h @@ -120,6 +120,8 @@ struct ucc_fast_info { int ucc_num; enum qe_clock rx_clock; enum qe_clock tx_clock; + enum qe_clock rx_sync; + enum qe_clock tx_sync; u32 regs; int irq; u32 uccm_mask; -- 2.1.0.27.g96db324 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 2/5] fsl/qe: setup clock source for TDM mode 2016-03-30 8:50 [PATCH 1/5] fsl/qe: add rx_sync and tx_sync for TDM mode Zhao Qiang @ 2016-03-30 8:50 ` Zhao Qiang 2016-03-30 8:50 ` [PATCH 3/5] fsl/qe: Make regs resouce_size_t Zhao Qiang ` (2 subsequent siblings) 3 siblings, 0 replies; 12+ messages in thread From: Zhao Qiang @ 2016-03-30 8:50 UTC (permalink / raw) To: davem Cc: akpm, gregkh, oss, xiaobo.xie, linux-kernel, netdev, linuxppc-dev, Zhao Qiang Add tdm clock configuration in both qe clock system and ucc fast controller. Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com> --- drivers/soc/fsl/qe/ucc.c | 450 ++++++++++++++++++++++++++++++++++++++++++ drivers/soc/fsl/qe/ucc_fast.c | 36 ++++ include/soc/fsl/qe/qe.h | 16 ++ include/soc/fsl/qe/ucc.h | 4 + include/soc/fsl/qe/ucc_fast.h | 1 + 5 files changed, 507 insertions(+) diff --git a/drivers/soc/fsl/qe/ucc.c b/drivers/soc/fsl/qe/ucc.c index b59d335..5e1a850 100644 --- a/drivers/soc/fsl/qe/ucc.c +++ b/drivers/soc/fsl/qe/ucc.c @@ -25,6 +25,12 @@ #include <soc/fsl/qe/qe.h> #include <soc/fsl/qe/ucc.h> +#define UCC_TDM_NUM 8 +#define RX_SYNC_SHIFT_BASE 30 +#define TX_SYNC_SHIFT_BASE 14 +#define RX_CLK_SHIFT_BASE 28 +#define TX_CLK_SHIFT_BASE 12 + int ucc_set_qe_mux_mii_mng(unsigned int ucc_num) { unsigned long flags; @@ -210,3 +216,447 @@ int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock, return 0; } + +static int ucc_get_tdm_common_clk(u32 tdm_num, enum qe_clock clock) +{ + int clock_bits = -EINVAL; + + /* + * for TDM[0, 1, 2, 3], TX and RX use common + * clock source BRG3,4 and CLK1,2 + * for TDM[4, 5, 6, 7], TX and RX use common + * clock source BRG12,13 and CLK23,24 + */ + switch (tdm_num) { + case 0: + case 1: + case 2: + case 3: + switch (clock) { + case QE_BRG3: + clock_bits = 1; + break; + case QE_BRG4: + clock_bits = 2; + break; + case QE_CLK1: + clock_bits = 4; + break; + case QE_CLK2: + clock_bits = 5; + break; + default: + break; + } + break; + case 4: + case 5: + case 6: + case 7: + switch (clock) { + case QE_BRG12: + clock_bits = 1; + break; + case QE_BRG13: + clock_bits = 2; + break; + case QE_CLK23: + clock_bits = 4; + break; + case QE_CLK24: + clock_bits = 5; + break; + default: + break; + } + break; + default: + break; + } + + return clock_bits; +} + +static int ucc_get_tdm_rx_clk(u32 tdm_num, enum qe_clock clock) +{ + int clock_bits = -EINVAL; + + switch (tdm_num) { + case 0: + switch (clock) { + case QE_CLK3: + clock_bits = 6; + break; + case QE_CLK8: + clock_bits = 7; + break; + default: + break; + } + break; + case 1: + switch (clock) { + case QE_CLK5: + clock_bits = 6; + break; + case QE_CLK10: + clock_bits = 7; + break; + default: + break; + } + break; + case 2: + switch (clock) { + case QE_CLK7: + clock_bits = 6; + break; + case QE_CLK12: + clock_bits = 7; + break; + default: + break; + } + break; + case 3: + switch (clock) { + case QE_CLK9: + clock_bits = 6; + break; + case QE_CLK14: + clock_bits = 7; + break; + default: + break; + } + break; + case 4: + switch (clock) { + case QE_CLK11: + clock_bits = 6; + break; + case QE_CLK16: + clock_bits = 7; + break; + default: + break; + } + break; + case 5: + switch (clock) { + case QE_CLK13: + clock_bits = 6; + break; + case QE_CLK18: + clock_bits = 7; + break; + default: + break; + } + break; + case 6: + switch (clock) { + case QE_CLK15: + clock_bits = 6; + break; + case QE_CLK20: + clock_bits = 7; + break; + default: + break; + } + break; + case 7: + switch (clock) { + case QE_CLK17: + clock_bits = 6; + break; + case QE_CLK22: + clock_bits = 7; + break; + default: + break; + } + break; + } + + return clock_bits; +} + +static int ucc_get_tdm_tx_clk(u32 tdm_num, enum qe_clock clock) +{ + int clock_bits = -EINVAL; + + switch (tdm_num) { + case 0: + switch (clock) { + case QE_CLK4: + clock_bits = 6; + break; + case QE_CLK9: + clock_bits = 7; + break; + default: + break; + } + break; + case 1: + switch (clock) { + case QE_CLK6: + clock_bits = 6; + break; + case QE_CLK11: + clock_bits = 7; + break; + default: + break; + } + break; + case 2: + switch (clock) { + case QE_CLK8: + clock_bits = 6; + break; + case QE_CLK13: + clock_bits = 7; + break; + default: + break; + } + break; + case 3: + switch (clock) { + case QE_CLK10: + clock_bits = 6; + break; + case QE_CLK15: + clock_bits = 7; + break; + default: + break; + } + break; + case 4: + switch (clock) { + case QE_CLK12: + clock_bits = 6; + break; + case QE_CLK17: + clock_bits = 7; + break; + default: + break; + } + break; + case 5: + switch (clock) { + case QE_CLK14: + clock_bits = 6; + break; + case QE_CLK19: + clock_bits = 7; + break; + default: + break; + } + break; + case 6: + switch (clock) { + case QE_CLK16: + clock_bits = 6; + break; + case QE_CLK21: + clock_bits = 7; + break; + default: + break; + } + break; + case 7: + switch (clock) { + case QE_CLK18: + clock_bits = 6; + break; + case QE_CLK3: + clock_bits = 7; + break; + default: + break; + } + break; + } + + return clock_bits; +} + +/* tdm_num: TDM A-H port num is 0-7 */ +static int ucc_get_tdm_rxtx_clk(enum comm_dir mode, u32 tdm_num, + enum qe_clock clock) +{ + int clock_bits; + + clock_bits = ucc_get_tdm_common_clk(tdm_num, clock); + if (clock_bits > 0) + return clock_bits; + if (mode == COMM_DIR_RX) + clock_bits = ucc_get_tdm_rx_clk(tdm_num, clock); + if (mode == COMM_DIR_TX) + clock_bits = ucc_get_tdm_tx_clk(tdm_num, clock); + return clock_bits; +} + +static u32 ucc_get_tdm_clk_shift(enum comm_dir mode, u32 tdm_num) +{ + u32 shift; + + shift = (mode == COMM_DIR_RX) ? RX_CLK_SHIFT_BASE : TX_CLK_SHIFT_BASE; + if (tdm_num < 4) + shift -= tdm_num * 4; + else + shift -= (tdm_num - 4) * 4; + + return shift; +} + +int ucc_set_tdm_rxtx_clk(u32 tdm_num, enum qe_clock clock, + enum comm_dir mode) +{ + int clock_bits; + u32 shift; + struct qe_mux __iomem *qe_mux_reg; + __be32 __iomem *cmxs1cr; + + qe_mux_reg = &qe_immr->qmx; + + if (tdm_num > 7 || tdm_num < 0) + return -EINVAL; + + /* The communications direction must be RX or TX */ + if (mode != COMM_DIR_RX && mode != COMM_DIR_TX) + return -EINVAL; + + clock_bits = ucc_get_tdm_rxtx_clk(mode, tdm_num, clock); + if (clock_bits < 0) + return -EINVAL; + + shift = ucc_get_tdm_clk_shift(mode, tdm_num); + + cmxs1cr = (tdm_num < 4) ? &qe_mux_reg->cmxsi1cr_l : + &qe_mux_reg->cmxsi1cr_h; + + qe_clrsetbits32(cmxs1cr, QE_CMXUCR_TX_CLK_SRC_MASK << shift, + clock_bits << shift); + + return 0; +} + +static int ucc_get_tdm_sync_source(u32 tdm_num, enum qe_clock clock, + enum comm_dir mode) +{ + int source = -EINVAL; + + if (mode == COMM_DIR_RX && clock == QE_RSYNC_PIN) { + source = 0; + return source; + } + if (mode == COMM_DIR_TX && clock == QE_TSYNC_PIN) { + source = 0; + return source; + } + + switch (tdm_num) { + case 0: + case 1: + switch (clock) { + case QE_BRG9: + source = 1; + break; + case QE_BRG10: + source = 2; + break; + default: + break; + } + break; + case 2: + case 3: + switch (clock) { + case QE_BRG9: + source = 1; + break; + case QE_BRG11: + source = 2; + break; + default: + break; + } + break; + case 4: + case 5: + switch (clock) { + case QE_BRG13: + source = 1; + break; + case QE_BRG14: + source = 2; + break; + default: + break; + } + break; + case 6: + case 7: + switch (clock) { + case QE_BRG13: + source = 1; + break; + case QE_BRG15: + source = 2; + break; + default: + break; + } + break; + } + + return source; +} + +static u32 ucc_get_tdm_sync_shift(enum comm_dir mode, u32 tdm_num) +{ + u32 shift; + + shift = (mode == COMM_DIR_RX) ? RX_SYNC_SHIFT_BASE : RX_SYNC_SHIFT_BASE; + shift -= tdm_num * 2; + + return shift; +} + +int ucc_set_tdm_rxtx_sync(u32 tdm_num, enum qe_clock clock, + enum comm_dir mode) +{ + int source; + u32 shift; + struct qe_mux *qe_mux_reg; + + qe_mux_reg = &qe_immr->qmx; + + if (tdm_num >= UCC_TDM_NUM) + return -EINVAL; + + /* The communications direction must be RX or TX */ + if (mode != COMM_DIR_RX && mode != COMM_DIR_TX) + return -EINVAL; + + source = ucc_get_tdm_sync_source(tdm_num, clock, mode); + if (source < 0) + return -EINVAL; + + shift = ucc_get_tdm_sync_shift(mode, tdm_num); + + qe_clrsetbits32(&qe_mux_reg->cmxsi1syr, + QE_CMXUCR_TX_CLK_SRC_MASK << shift, + source << shift); + + return 0; +} diff --git a/drivers/soc/fsl/qe/ucc_fast.c b/drivers/soc/fsl/qe/ucc_fast.c index a768931..83d8d16 100644 --- a/drivers/soc/fsl/qe/ucc_fast.c +++ b/drivers/soc/fsl/qe/ucc_fast.c @@ -327,6 +327,42 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc ucc_fast_free(uccf); return -EINVAL; } + } else { + /* tdm Rx clock routing */ + if ((uf_info->rx_clock != QE_CLK_NONE) && + ucc_set_tdm_rxtx_clk(uf_info->tdm_num, uf_info->rx_clock, + COMM_DIR_RX)) { + pr_err("%s: illegal value for RX clock", __func__); + ucc_fast_free(uccf); + return -EINVAL; + } + + /* tdm Tx clock routing */ + if ((uf_info->tx_clock != QE_CLK_NONE) && + ucc_set_tdm_rxtx_clk(uf_info->tdm_num, uf_info->tx_clock, + COMM_DIR_TX)) { + pr_err("%s: illegal value for TX clock", __func__); + ucc_fast_free(uccf); + return -EINVAL; + } + + /* tdm Rx sync clock routing */ + if ((uf_info->rx_sync != QE_CLK_NONE) && + ucc_set_tdm_rxtx_sync(uf_info->tdm_num, uf_info->rx_sync, + COMM_DIR_RX)) { + pr_err("%s: illegal value for RX clock", __func__); + ucc_fast_free(uccf); + return -EINVAL; + } + + /* tdm Tx sync clock routing */ + if ((uf_info->tx_sync != QE_CLK_NONE) && + ucc_set_tdm_rxtx_sync(uf_info->tdm_num, uf_info->tx_sync, + COMM_DIR_TX)) { + pr_err("%s: illegal value for TX clock", __func__); + ucc_fast_free(uccf); + return -EINVAL; + } } /* Set interrupt mask register at UCC level. */ diff --git a/include/soc/fsl/qe/qe.h b/include/soc/fsl/qe/qe.h index f918745..c3b1dc8 100644 --- a/include/soc/fsl/qe/qe.h +++ b/include/soc/fsl/qe/qe.h @@ -244,6 +244,22 @@ static inline int qe_alive_during_sleep(void) #define qe_muram_addr cpm_muram_addr #define qe_muram_offset cpm_muram_offset +#define qe_setbits32(_addr, _v) iowrite32be(ioread32be(_addr) | (_v), (_addr)) +#define qe_clrbits32(_addr, _v) iowrite32be(ioread32be(_addr) & ~(_v), (_addr)) + +#define qe_setbits16(_addr, _v) iowrite16be(ioread16be(_addr) | (_v), (_addr)) +#define qe_clrbits16(_addr, _v) iowrite16be(ioread16be(_addr) & ~(_v), (_addr)) + +#define qe_setbits8(_addr, _v) iowrite8(ioread8(_addr) | (_v), (_addr)) +#define qe_clrbits8(_addr, _v) iowrite8(ioread8(_addr) & ~(_v), (_addr)) + +#define qe_clrsetbits32(addr, clear, set) \ + iowrite32be((ioread32be(addr) & ~(clear)) | (set), (addr)) +#define qe_clrsetbits16(addr, clear, set) \ + iowrite16be((ioread16be(addr) & ~(clear)) | (set), (addr)) +#define qe_clrsetbits8(addr, clear, set) \ + iowrite8((ioread8(addr) & ~(clear)) | (set), (addr)) + /* Structure that defines QE firmware binary files. * * See Documentation/powerpc/qe_firmware.txt for a description of these diff --git a/include/soc/fsl/qe/ucc.h b/include/soc/fsl/qe/ucc.h index 894f14c..6bbbb59 100644 --- a/include/soc/fsl/qe/ucc.h +++ b/include/soc/fsl/qe/ucc.h @@ -41,6 +41,10 @@ int ucc_set_qe_mux_mii_mng(unsigned int ucc_num); int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock, enum comm_dir mode); +int ucc_set_tdm_rxtx_clk(unsigned int tdm_num, enum qe_clock clock, + enum comm_dir mode); +int ucc_set_tdm_rxtx_sync(unsigned int tdm_num, enum qe_clock clock, + enum comm_dir mode); int ucc_mux_set_grant_tsa_bkpt(unsigned int ucc_num, int set, u32 mask); diff --git a/include/soc/fsl/qe/ucc_fast.h b/include/soc/fsl/qe/ucc_fast.h index 31548b7..b2633b7 100644 --- a/include/soc/fsl/qe/ucc_fast.h +++ b/include/soc/fsl/qe/ucc_fast.h @@ -118,6 +118,7 @@ enum ucc_fast_transparent_tcrc { /* Fast UCC initialization structure */ struct ucc_fast_info { int ucc_num; + int tdm_num; enum qe_clock rx_clock; enum qe_clock tx_clock; enum qe_clock rx_sync; -- 2.1.0.27.g96db324 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 3/5] fsl/qe: Make regs resouce_size_t 2016-03-30 8:50 [PATCH 1/5] fsl/qe: add rx_sync and tx_sync for TDM mode Zhao Qiang 2016-03-30 8:50 ` [PATCH 2/5] fsl/qe: setup clock source " Zhao Qiang @ 2016-03-30 8:50 ` Zhao Qiang 2016-03-30 8:50 ` [PATCH 4/5] fsl/qe: Add QE TDM lib Zhao Qiang 2016-03-30 8:50 ` [PATCH 5/5] drivers/net: support hdlc function for QE-UCC Zhao Qiang 3 siblings, 0 replies; 12+ messages in thread From: Zhao Qiang @ 2016-03-30 8:50 UTC (permalink / raw) To: davem Cc: akpm, gregkh, oss, xiaobo.xie, linux-kernel, netdev, linuxppc-dev, Zhao Qiang Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com> --- include/soc/fsl/qe/ucc_fast.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/soc/fsl/qe/ucc_fast.h b/include/soc/fsl/qe/ucc_fast.h index b2633b7..e898895 100644 --- a/include/soc/fsl/qe/ucc_fast.h +++ b/include/soc/fsl/qe/ucc_fast.h @@ -123,7 +123,7 @@ struct ucc_fast_info { enum qe_clock tx_clock; enum qe_clock rx_sync; enum qe_clock tx_sync; - u32 regs; + resource_size_t regs; int irq; u32 uccm_mask; int bd_mem_part; -- 2.1.0.27.g96db324 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 4/5] fsl/qe: Add QE TDM lib 2016-03-30 8:50 [PATCH 1/5] fsl/qe: add rx_sync and tx_sync for TDM mode Zhao Qiang 2016-03-30 8:50 ` [PATCH 2/5] fsl/qe: setup clock source " Zhao Qiang 2016-03-30 8:50 ` [PATCH 3/5] fsl/qe: Make regs resouce_size_t Zhao Qiang @ 2016-03-30 8:50 ` Zhao Qiang 2016-03-30 11:50 ` Joakim Tjernlund 2016-03-30 8:50 ` [PATCH 5/5] drivers/net: support hdlc function for QE-UCC Zhao Qiang 3 siblings, 1 reply; 12+ messages in thread From: Zhao Qiang @ 2016-03-30 8:50 UTC (permalink / raw) To: davem Cc: akpm, gregkh, oss, xiaobo.xie, linux-kernel, netdev, linuxppc-dev, Zhao Qiang QE has module to support TDM, some other protocols supported by QE are based on TDM. add a qe-tdm lib, this lib provides functions to the protocols using TDM to configurate QE-TDM. Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com> --- drivers/soc/fsl/qe/Kconfig | 4 + drivers/soc/fsl/qe/Makefile | 1 + drivers/soc/fsl/qe/qe_tdm.c | 271 ++++++++++++++++++++++++++++++++++++++++++ include/soc/fsl/qe/immap_qe.h | 5 +- include/soc/fsl/qe/qe_tdm.h | 94 +++++++++++++++ 5 files changed, 371 insertions(+), 4 deletions(-) create mode 100644 drivers/soc/fsl/qe/qe_tdm.c create mode 100644 include/soc/fsl/qe/qe_tdm.h diff --git a/drivers/soc/fsl/qe/Kconfig b/drivers/soc/fsl/qe/Kconfig index 20978f2..463cf29 100644 --- a/drivers/soc/fsl/qe/Kconfig +++ b/drivers/soc/fsl/qe/Kconfig @@ -31,6 +31,10 @@ config UCC bool default y if UCC_FAST || UCC_SLOW +config QE_TDM + bool + select UCC_FAST + config QE_USB bool default y if USB_FSL_QE diff --git a/drivers/soc/fsl/qe/Makefile b/drivers/soc/fsl/qe/Makefile index ffac541..2031d38 100644 --- a/drivers/soc/fsl/qe/Makefile +++ b/drivers/soc/fsl/qe/Makefile @@ -6,5 +6,6 @@ obj-$(CONFIG_CPM) += qe_common.o obj-$(CONFIG_UCC) += ucc.o obj-$(CONFIG_UCC_SLOW) += ucc_slow.o obj-$(CONFIG_UCC_FAST) += ucc_fast.o +obj-$(CONFIG_QE_TDM) += qe_tdm.o obj-$(CONFIG_QE_USB) += usb.o obj-$(CONFIG_QE_GPIO) += gpio.o diff --git a/drivers/soc/fsl/qe/qe_tdm.c b/drivers/soc/fsl/qe/qe_tdm.c new file mode 100644 index 0000000..9a2374d --- /dev/null +++ b/drivers/soc/fsl/qe/qe_tdm.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2015 Freescale Semiconductor, Inc. All rights reserved. + * + * Authors: Zhao Qiang <qiang.zhao@nxp.com> + * + * Description: + * QE TDM API Set - TDM specific routines implementations. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <soc/fsl/qe/qe_tdm.h> + +static enum tdm_framer_t set_tdm_framer(const char *tdm_framer_type) +{ + if (strcmp(tdm_framer_type, "e1") == 0) + return TDM_FRAMER_E1; + else + return TDM_FRAMER_T1; +} + +static void set_si_param(struct ucc_tdm *utdm, struct ucc_tdm_info *ut_info) +{ + struct si_mode_info *si_info = &ut_info->si_info; + + if (utdm->tdm_mode == TDM_INTERNAL_LOOPBACK) { + si_info->simr_crt = 1; + si_info->simr_rfsd = 0; + } +} + +int ucc_of_parse_tdm(struct device_node *np, struct ucc_tdm *utdm, + struct ucc_tdm_info *ut_info) +{ + const char *sprop; + int ret = 0; + u32 val; + struct resource *res; + struct device_node *np2; + static int siram_init_flag; + struct platform_device *pdev; + + sprop = of_get_property(np, "fsl,rx-sync-clock", NULL); + if (sprop) { + ut_info->uf_info.rx_sync = qe_clock_source(sprop); + if ((ut_info->uf_info.rx_sync < QE_CLK_NONE) || + (ut_info->uf_info.rx_sync > QE_RSYNC_PIN)) { + pr_err("QE-TDM: Invalid rx-sync-clock property\n"); + return -EINVAL; + } + } else { + pr_err("QE-TDM: Invalid rx-sync-clock property\n"); + return -EINVAL; + } + + sprop = of_get_property(np, "fsl,tx-sync-clock", NULL); + if (sprop) { + ut_info->uf_info.tx_sync = qe_clock_source(sprop); + if ((ut_info->uf_info.tx_sync < QE_CLK_NONE) || + (ut_info->uf_info.tx_sync > QE_TSYNC_PIN)) { + pr_err("QE-TDM: Invalid tx-sync-clock property\n"); + return -EINVAL; + } + } else { + pr_err("QE-TDM: Invalid tx-sync-clock property\n"); + return -EINVAL; + } + + ret = of_property_read_u32_index(np, "fsl,tx-timeslot-mask", 0, &val); + if (ret) { + pr_err("QE-TDM: Invalid tx-timeslot-mask property\n"); + return -EINVAL; + } + utdm->tx_ts_mask = val; + + ret = of_property_read_u32_index(np, "fsl,rx-timeslot-mask", 0, &val); + if (ret) { + ret = -EINVAL; + pr_err("QE-TDM: Invalid rx-timeslot-mask property\n"); + return ret; + } + utdm->rx_ts_mask = val; + + ret = of_property_read_u32_index(np, "fsl,tdm-id", 0, &val); + if (ret) { + ret = -EINVAL; + pr_err("QE-TDM: No fsl,tdm-id property for this UCC\n"); + return ret; + } + utdm->tdm_port = val; + ut_info->uf_info.tdm_num = utdm->tdm_port; + + if (of_get_property(np, "fsl,tdm-internal-loopback", NULL)) + utdm->tdm_mode = TDM_INTERNAL_LOOPBACK; + else + utdm->tdm_mode = TDM_NORMAL; + + sprop = of_get_property(np, "fsl,tdm-framer-type", NULL); + if (!sprop) { + ret = -EINVAL; + pr_err("QE-TDM: No tdm-framer-type property for UCC\n"); + return ret; + } + utdm->tdm_framer_type = set_tdm_framer(sprop); + + ret = of_property_read_u32_index(np, "fsl,siram-entry-id", 0, &val); + if (ret) { + ret = -EINVAL; + pr_err("QE-TDM: No siram entry id for UCC\n"); + return ret; + } + utdm->siram_entry_id = val; + + set_si_param(utdm, ut_info); + + np2 = of_find_compatible_node(NULL, NULL, "fsl,t1040-qe-si"); + if (!np2) + return -EINVAL; + + pdev = of_find_device_by_node(np2); + if (!pdev) { + pr_err("%s: failed to lookup pdev\n", np2->name); + of_node_put(np2); + return -EINVAL; + } + + of_node_put(np2); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + utdm->si_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(utdm->si_regs)) { + ret = PTR_ERR(utdm->si_regs); + goto err_miss_siram_property; + } + + np2 = of_find_compatible_node(NULL, NULL, "fsl,t1040-qe-siram"); + if (!np2) { + ret = -EINVAL; + goto err_miss_siram_property; + } + + pdev = of_find_device_by_node(np2); + if (!pdev) { + ret = -EINVAL; + pr_err("%s: failed to lookup pdev\n", np2->name); + of_node_put(np2); + goto err_miss_siram_property; + } + + of_node_put(np2); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + utdm->siram = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(utdm->siram)) { + ret = PTR_ERR(utdm->siram); + goto err_miss_siram_property; + } + + if (siram_init_flag == 0) { + memset_io(utdm->siram, 0, res->end - res->start + 1); + siram_init_flag = 1; + } + + return ret; + +err_miss_siram_property: + devm_iounmap(&pdev->dev, utdm->si_regs); + return ret; +} + +void ucc_tdm_init(struct ucc_tdm *utdm, struct ucc_tdm_info *ut_info) +{ + struct si1 __iomem *si_regs; + u16 __iomem *siram; + u16 siram_entry_valid; + u16 siram_entry_closed; + u16 ucc_num; + u8 csel; + u16 sixmr; + u16 tdm_port; + u32 siram_entry_id; + u32 mask; + int i; + + si_regs = utdm->si_regs; + siram = utdm->siram; + ucc_num = ut_info->uf_info.ucc_num; + tdm_port = utdm->tdm_port; + siram_entry_id = utdm->siram_entry_id; + + if (utdm->tdm_framer_type == TDM_FRAMER_T1) + utdm->num_of_ts = 24; + if (utdm->tdm_framer_type == TDM_FRAMER_E1) + utdm->num_of_ts = 32; + + /* set siram table */ + csel = (ucc_num < 4) ? ucc_num + 9 : ucc_num - 3; + + siram_entry_valid = SIR_CSEL(csel) | SIR_BYTE | SIR_CNT(0); + siram_entry_closed = SIR_IDLE | SIR_BYTE | SIR_CNT(0); + + for (i = 0; i < utdm->num_of_ts; i++) { + mask = 0x01 << i; + + if (utdm->tx_ts_mask & mask) + iowrite16be(siram_entry_valid, + &siram[siram_entry_id * 32 + i]); + else + iowrite16be(siram_entry_closed, + &siram[siram_entry_id * 32 + i]); + + if (utdm->rx_ts_mask & mask) + iowrite16be(siram_entry_valid, + &siram[siram_entry_id * 32 + 0x200 + i]); + else + iowrite16be(siram_entry_closed, + &siram[siram_entry_id * 32 + 0x200 + i]); + } + + setbits16(&siram[(siram_entry_id * 32) + (utdm->num_of_ts - 1)], + SIR_LAST); + setbits16(&siram[(siram_entry_id * 32) + 0x200 + (utdm->num_of_ts - 1)], + SIR_LAST); + + /* Set SIxMR register */ + sixmr = SIMR_SAD(siram_entry_id); + + sixmr &= ~SIMR_SDM_MASK; + + if (utdm->tdm_mode == TDM_INTERNAL_LOOPBACK) + sixmr |= SIMR_SDM_INTERNAL_LOOPBACK; + else + sixmr |= SIMR_SDM_NORMAL; + + sixmr |= SIMR_RFSD(ut_info->si_info.simr_rfsd) | + SIMR_TFSD(ut_info->si_info.simr_tfsd); + + if (ut_info->si_info.simr_crt) + sixmr |= SIMR_CRT; + if (ut_info->si_info.simr_sl) + sixmr |= SIMR_SL; + if (ut_info->si_info.simr_ce) + sixmr |= SIMR_CE; + if (ut_info->si_info.simr_fe) + sixmr |= SIMR_FE; + if (ut_info->si_info.simr_gm) + sixmr |= SIMR_GM; + + switch (tdm_port) { + case 0: + iowrite16be(sixmr, &si_regs->sixmr1[0]); + break; + case 1: + iowrite16be(sixmr, &si_regs->sixmr1[1]); + break; + case 2: + iowrite16be(sixmr, &si_regs->sixmr1[2]); + break; + case 3: + iowrite16be(sixmr, &si_regs->sixmr1[3]); + break; + default: + pr_err("QE-TDM: can not find tdm sixmr reg\n"); + break; + } +} diff --git a/include/soc/fsl/qe/immap_qe.h b/include/soc/fsl/qe/immap_qe.h index bedbff8..c76ef30 100644 --- a/include/soc/fsl/qe/immap_qe.h +++ b/include/soc/fsl/qe/immap_qe.h @@ -159,10 +159,7 @@ struct spi { /* SI */ struct si1 { - __be16 siamr1; /* SI1 TDMA mode register */ - __be16 sibmr1; /* SI1 TDMB mode register */ - __be16 sicmr1; /* SI1 TDMC mode register */ - __be16 sidmr1; /* SI1 TDMD mode register */ + __be16 sixmr1[4]; /* SI1 TDMx (x = A B C D) mode register */ u8 siglmr1_h; /* SI1 global mode register high */ u8 res0[0x1]; u8 sicmdr1_h; /* SI1 command register high */ diff --git a/include/soc/fsl/qe/qe_tdm.h b/include/soc/fsl/qe/qe_tdm.h new file mode 100644 index 0000000..e7ae93a --- /dev/null +++ b/include/soc/fsl/qe/qe_tdm.h @@ -0,0 +1,94 @@ +/* + * Internal header file for QE TDM mode routines. + * + * Copyright (C) 2015 Freescale Semiconductor, Inc. All rights reserved. + * + * Authors: Zhao Qiang <qiang.zhao@nxp.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version + */ + +#ifndef CONFIG_QE_TDM_H +#define CONFIG_QE_TDM_H + +#include <linux/kernel.h> +#include <linux/list.h> + +#include <soc/fsl/qe/immap_qe.h> +#include <soc/fsl/qe/qe.h> + +#include <soc/fsl/qe/ucc.h> +#include <soc/fsl/qe/ucc_fast.h> + +/* SI RAM entries */ +#define SIR_LAST 0x0001 +#define SIR_BYTE 0x0002 +#define SIR_CNT(x) ((x) << 2) +#define SIR_CSEL(x) ((x) << 5) +#define SIR_SGS 0x0200 +#define SIR_SWTR 0x4000 +#define SIR_MCC 0x8000 +#define SIR_IDLE 0 + +/* SIxMR fields */ +#define SIMR_SAD(x) ((x) << 12) +#define SIMR_SDM_NORMAL 0x0000 +#define SIMR_SDM_INTERNAL_LOOPBACK 0x0800 +#define SIMR_SDM_MASK 0x0c00 +#define SIMR_CRT 0x0040 +#define SIMR_SL 0x0020 +#define SIMR_CE 0x0010 +#define SIMR_FE 0x0008 +#define SIMR_GM 0x0004 +#define SIMR_TFSD(n) (n) +#define SIMR_RFSD(n) ((n) << 8) + +enum tdm_ts_t { + TDM_TX_TS, + TDM_RX_TS +}; + +enum tdm_framer_t { + TDM_FRAMER_T1, + TDM_FRAMER_E1 +}; + +enum tdm_mode_t { + TDM_INTERNAL_LOOPBACK, + TDM_NORMAL +}; + +struct si_mode_info { + u8 simr_rfsd; + u8 simr_tfsd; + u8 simr_crt; + u8 simr_sl; + u8 simr_ce; + u8 simr_fe; + u8 simr_gm; +}; + +struct ucc_tdm_info { + struct ucc_fast_info uf_info; + struct si_mode_info si_info; +}; + +struct ucc_tdm { + u16 tdm_port; /* port for this tdm:TDMA,TDMB */ + u32 siram_entry_id; + u16 __iomem *siram; + struct si1 __iomem *si_regs; + enum tdm_framer_t tdm_framer_type; + enum tdm_mode_t tdm_mode; + u8 num_of_ts; /* the number of timeslots in this tdm frame */ + u32 tx_ts_mask; /* tx time slot mask */ + u32 rx_ts_mask; /* rx time slot mask */ +}; + +int ucc_of_parse_tdm(struct device_node *np, struct ucc_tdm *utdm, + struct ucc_tdm_info *ut_info); +void ucc_tdm_init(struct ucc_tdm *utdm, struct ucc_tdm_info *ut_info); +#endif -- 2.1.0.27.g96db324 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 4/5] fsl/qe: Add QE TDM lib 2016-03-30 8:50 ` [PATCH 4/5] fsl/qe: Add QE TDM lib Zhao Qiang @ 2016-03-30 11:50 ` Joakim Tjernlund 2016-03-31 1:25 ` Qiang Zhao 0 siblings, 1 reply; 12+ messages in thread From: Joakim Tjernlund @ 2016-03-30 11:50 UTC (permalink / raw) To: davem@davemloft.net, qiang.zhao@nxp.com Cc: linuxppc-dev@lists.ozlabs.org, linux-kernel@vger.kernel.org, xiaobo.xie@nxp.com, oss@buserror.net, gregkh@linuxfoundation.org, akpm@linux-foundation.org, netdev@vger.kernel.org On Wed, 2016-03-30 at 16:50 +0800, Zhao Qiang wrote: > QE has module to support TDM, some other protocols > supported by QE are based on TDM. > add a qe-tdm lib, this lib provides functions to the protocols > using TDM to configurate QE-TDM. >=20 > Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com> > --- > =A0drivers/soc/fsl/qe/Kconfig=A0=A0=A0=A0|=A0=A0=A04 + > =A0drivers/soc/fsl/qe/Makefile=A0=A0=A0|=A0=A0=A01 + > =A0drivers/soc/fsl/qe/qe_tdm.c=A0=A0=A0| 271 ++++++++++++++++++++++++++++= ++++++++++++++ > =A0include/soc/fsl/qe/immap_qe.h |=A0=A0=A05 +- > =A0include/soc/fsl/qe/qe_tdm.h=A0=A0=A0|=A0=A094 +++++++++++++++ > =A05 files changed, 371 insertions(+), 4 deletions(-) > =A0create mode 100644 drivers/soc/fsl/qe/qe_tdm.c > =A0create mode 100644 include/soc/fsl/qe/qe_tdm.h >=20 > diff --git a/drivers/soc/fsl/qe/Kconfig b/drivers/soc/fsl/qe/Kconfig > index 20978f2..463cf29 100644 > --- a/drivers/soc/fsl/qe/Kconfig > +++ b/drivers/soc/fsl/qe/Kconfig > @@ -31,6 +31,10 @@ config UCC > =A0 bool > =A0 default y if UCC_FAST || UCC_SLOW > =A0 > +config QE_TDM > + bool > + select UCC_FAST > + > =A0config QE_USB > =A0 bool > =A0 default y if USB_FSL_QE > diff --git a/drivers/soc/fsl/qe/Makefile b/drivers/soc/fsl/qe/Makefile > index ffac541..2031d38 100644 > --- a/drivers/soc/fsl/qe/Makefile > +++ b/drivers/soc/fsl/qe/Makefile > @@ -6,5 +6,6 @@ obj-$(CONFIG_CPM) +=3D qe_common.o > =A0obj-$(CONFIG_UCC) +=3D ucc.o > =A0obj-$(CONFIG_UCC_SLOW) +=3D ucc_slow.o > =A0obj-$(CONFIG_UCC_FAST) +=3D ucc_fast.o > +obj-$(CONFIG_QE_TDM) +=3D qe_tdm.o > =A0obj-$(CONFIG_QE_USB) +=3D usb.o > =A0obj-$(CONFIG_QE_GPIO) +=3D gpio.o > diff --git a/drivers/soc/fsl/qe/qe_tdm.c b/drivers/soc/fsl/qe/qe_tdm.c > new file mode 100644 > index 0000000..9a2374d > --- /dev/null > +++ b/drivers/soc/fsl/qe/qe_tdm.c > @@ -0,0 +1,271 @@ > +/* > + * Copyright (C) 2015 Freescale Semiconductor, Inc. All rights reserved. > + * > + * Authors: Zhao Qiang <qiang.zhao@nxp.com> > + * > + * Description: > + * QE TDM API Set - TDM specific routines implementations. > + * > + * This program is free software; you can redistribute=A0=A0it and/or mo= dify it > + * under=A0=A0the terms of=A0=A0the GNU General=A0=A0Public License as p= ublished by the > + * Free Software Foundation;=A0=A0either version 2 of the=A0=A0License, = or (at your > + * option) any later version. > + */ > +#include <linux/io.h> > +#include <linux/kernel.h> > +#include <linux/of_address.h> > +#include <linux/of_irq.h> > +#include <linux/of_platform.h> > +#include <soc/fsl/qe/qe_tdm.h> > + > +static enum tdm_framer_t set_tdm_framer(const char *tdm_framer_type) > +{ > + if (strcmp(tdm_framer_type, "e1") =3D=3D 0) > + return TDM_FRAMER_E1; > + else > + return TDM_FRAMER_T1; > +} > + > +static void set_si_param(struct ucc_tdm *utdm, struct ucc_tdm_info *ut_i= nfo) > +{ > + struct si_mode_info *si_info =3D &ut_info->si_info; > + > + if (utdm->tdm_mode =3D=3D TDM_INTERNAL_LOOPBACK) { > + si_info->simr_crt =3D 1; > + si_info->simr_rfsd =3D 0; > + } > +} > + > +int ucc_of_parse_tdm(struct device_node *np, struct ucc_tdm *utdm, > + =A0=A0=A0=A0=A0struct ucc_tdm_info *ut_info) > +{ > + const char *sprop; > + int ret =3D 0; > + u32 val; > + struct resource *res; > + struct device_node *np2; > + static int siram_init_flag; > + struct platform_device *pdev; > + > + sprop =3D of_get_property(np, "fsl,rx-sync-clock", NULL); > + if (sprop) { > + ut_info->uf_info.rx_sync =3D qe_clock_source(sprop); > + if ((ut_info->uf_info.rx_sync < QE_CLK_NONE) || > + =A0=A0=A0=A0(ut_info->uf_info.rx_sync > QE_RSYNC_PIN)) { > + pr_err("QE-TDM: Invalid rx-sync-clock property\n"); > + return -EINVAL; > + } > + } else { > + pr_err("QE-TDM: Invalid rx-sync-clock property\n"); > + return -EINVAL; > + } > + > + sprop =3D of_get_property(np, "fsl,tx-sync-clock", NULL); > + if (sprop) { > + ut_info->uf_info.tx_sync =3D qe_clock_source(sprop); > + if ((ut_info->uf_info.tx_sync < QE_CLK_NONE) || > + =A0=A0=A0=A0(ut_info->uf_info.tx_sync > QE_TSYNC_PIN)) { > + pr_err("QE-TDM: Invalid tx-sync-clock property\n"); > + return -EINVAL; > + } > + } else { > + pr_err("QE-TDM: Invalid tx-sync-clock property\n"); > + return -EINVAL; > + } > + > + ret =3D of_property_read_u32_index(np, "fsl,tx-timeslot-mask", 0, &val)= ; > + if (ret) { > + pr_err("QE-TDM: Invalid tx-timeslot-mask property\n"); > + return -EINVAL; > + } > + utdm->tx_ts_mask =3D val; > + > + ret =3D of_property_read_u32_index(np, "fsl,rx-timeslot-mask", 0, &val)= ; > + if (ret) { > + ret =3D -EINVAL; > + pr_err("QE-TDM: Invalid rx-timeslot-mask property\n"); > + return ret; > + } > + utdm->rx_ts_mask =3D val; > + > + ret =3D of_property_read_u32_index(np, "fsl,tdm-id", 0, &val); > + if (ret) { > + ret =3D -EINVAL; > + pr_err("QE-TDM: No fsl,tdm-id property for this UCC\n"); > + return ret; > + } > + utdm->tdm_port =3D val; > + ut_info->uf_info.tdm_num =3D utdm->tdm_port; > + > + if (of_get_property(np, "fsl,tdm-internal-loopback", NULL)) > + utdm->tdm_mode =3D TDM_INTERNAL_LOOPBACK; > + else > + utdm->tdm_mode =3D TDM_NORMAL; > + > + sprop =3D of_get_property(np, "fsl,tdm-framer-type", NULL); > + if (!sprop) { > + ret =3D -EINVAL; > + pr_err("QE-TDM: No tdm-framer-type property for UCC\n"); > + return ret; > + } > + utdm->tdm_framer_type =3D set_tdm_framer(sprop); > + > + ret =3D of_property_read_u32_index(np, "fsl,siram-entry-id", 0, &val); > + if (ret) { > + ret =3D -EINVAL; > + pr_err("QE-TDM: No siram entry id for UCC\n"); > + return ret; > + } > + utdm->siram_entry_id =3D val; > + > + set_si_param(utdm, ut_info); > + > + np2 =3D of_find_compatible_node(NULL, NULL, "fsl,t1040-qe-si"); fsl,t1040-qe-si only? What about mpc83xx? I recall QE is a little bit different compared to T1040 or will this work(i= ncluding the hdlc driver) on 83xx as well? =A0Jocke=A0= ^ permalink raw reply [flat|nested] 12+ messages in thread
* RE: [PATCH 4/5] fsl/qe: Add QE TDM lib 2016-03-30 11:50 ` Joakim Tjernlund @ 2016-03-31 1:25 ` Qiang Zhao 0 siblings, 0 replies; 12+ messages in thread From: Qiang Zhao @ 2016-03-31 1:25 UTC (permalink / raw) To: Joakim Tjernlund, davem@davemloft.net Cc: linuxppc-dev@lists.ozlabs.org, linux-kernel@vger.kernel.org, Xiaobo Xie, oss@buserror.net, gregkh@linuxfoundation.org, akpm@linux-foundation.org, netdev@vger.kernel.org On Wed, 2016-03-30 at 07:50PM, Joakim Tjernlund wrote: > -----Original Message----- > From: Joakim Tjernlund [mailto:Joakim.Tjernlund@infinera.com] > Sent: Wednesday, March 30, 2016 7:50 PM > To: davem@davemloft.net; Qiang Zhao <qiang.zhao@nxp.com> > Cc: linuxppc-dev@lists.ozlabs.org; linux-kernel@vger.kernel.org; Xiaobo X= ie > <xiaobo.xie@nxp.com>; oss@buserror.net; gregkh@linuxfoundation.org; > akpm@linux-foundation.org; netdev@vger.kernel.org > Subject: Re: [PATCH 4/5] fsl/qe: Add QE TDM lib >=20 > On Wed, 2016-03-30 at 16:50 +0800, Zhao Qiang wrote: > > QE has module to support TDM, some other protocols supported by QE are > > based on TDM. > > add a qe-tdm lib, this lib provides functions to the protocols using > > TDM to configurate QE-TDM. > > > > Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com> > > + utdm->siram_entry_id =3D val; > > + > > + set_si_param(utdm, ut_info); > > + > > + np2 =3D of_find_compatible_node(NULL, NULL, "fsl,t1040-qe-si"); >=20 > fsl,t1040-qe-si only? What about mpc83xx? > I recall QE is a little bit different compared to T1040 or will this work= (including > the hdlc driver) on 83xx as well? The " fsl,t1040-qe-si " is new added to dts and bindings, it is required to= have SoC specific compatible strings. mpc83xx will not use qe-si node. If there will be other soc useing qe-si, "= fsl,t1040-qe-si " will follow the soc specific compatible, like :=20 si1: si@700 { compatible =3D "fsl,ls1043-qe-si", "fsl,t1040-qe-si= "; reg =3D <0x700 0x80>; }; Best Regards Zhao Qiang ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 5/5] drivers/net: support hdlc function for QE-UCC 2016-03-30 8:50 [PATCH 1/5] fsl/qe: add rx_sync and tx_sync for TDM mode Zhao Qiang ` (2 preceding siblings ...) 2016-03-30 8:50 ` [PATCH 4/5] fsl/qe: Add QE TDM lib Zhao Qiang @ 2016-03-30 8:50 ` Zhao Qiang 2016-03-30 9:53 ` kbuild test robot ` (3 more replies) 3 siblings, 4 replies; 12+ messages in thread From: Zhao Qiang @ 2016-03-30 8:50 UTC (permalink / raw) To: davem Cc: akpm, gregkh, oss, xiaobo.xie, linux-kernel, netdev, linuxppc-dev, Zhao Qiang The driver add hdlc support for Freescale QUICC Engine. It support NMSI and TSA mode. Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com> --- MAINTAINERS | 6 + drivers/net/wan/Kconfig | 12 + drivers/net/wan/Makefile | 1 + drivers/net/wan/fsl_ucc_hdlc.c | 1339 ++++++++++++++++++++++++++++++++++++++++ drivers/net/wan/fsl_ucc_hdlc.h | 140 +++++ include/soc/fsl/qe/ucc_fast.h | 4 + 6 files changed, 1502 insertions(+) create mode 100644 drivers/net/wan/fsl_ucc_hdlc.c create mode 100644 drivers/net/wan/fsl_ucc_hdlc.h diff --git a/MAINTAINERS b/MAINTAINERS index 74bbff3..428d6ed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4572,6 +4572,12 @@ F: drivers/net/ethernet/freescale/gianfar* X: drivers/net/ethernet/freescale/gianfar_ptp.c F: Documentation/devicetree/bindings/net/fsl-tsec-phy.txt +FREESCALE QUICC ENGINE UCC HDLC DRIVER +M: Zhao Qiang <qiang.zhao@nxp.com> +L: linuxppc-dev@lists.ozlabs.org +S: Maintained +F: drivers/net/wan/fsl_ucc_hdlc* + FREESCALE QUICC ENGINE UCC UART DRIVER M: Timur Tabi <timur@tabi.org> L: linuxppc-dev@lists.ozlabs.org diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig index a2fdd15..cc424b2 100644 --- a/drivers/net/wan/Kconfig +++ b/drivers/net/wan/Kconfig @@ -280,6 +280,18 @@ config DSCC4 To compile this driver as a module, choose M here: the module will be called dscc4. +config FSL_UCC_HDLC + tristate "Freescale QUICC Engine HDLC support" + depends on HDLC + select QE_TDM + select QUICC_ENGINE + help + Driver for Freescale QUICC Engine HDLC controller. The driver + support HDLC run on NMSI and TDM mode. + + To compile this driver as a module, choose M here: the + module will be called fsl_ucc_hdlc. + config DSCC4_PCISYNC bool "Etinc PCISYNC features" depends on DSCC4 diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile index c135ef4..25fec40 100644 --- a/drivers/net/wan/Makefile +++ b/drivers/net/wan/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_WANXL) += wanxl.o obj-$(CONFIG_PCI200SYN) += pci200syn.o obj-$(CONFIG_PC300TOO) += pc300too.o obj-$(CONFIG_IXP4XX_HSS) += ixp4xx_hss.o +obj-$(CONFIG_FSL_UCC_HDLC) += fsl_ucc_hdlc.o clean-files := wanxlfw.inc $(obj)/wanxl.o: $(obj)/wanxlfw.inc diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c new file mode 100644 index 0000000..9958ec1 --- /dev/null +++ b/drivers/net/wan/fsl_ucc_hdlc.c @@ -0,0 +1,1339 @@ +/* Freescale QUICC Engine HDLC Device Driver + * + * Copyright 2014 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/hdlc.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/skbuff.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/stddef.h> +#include <soc/fsl/qe/qe_tdm.h> +#include <uapi/linux/if_arp.h> + +#include "fsl_ucc_hdlc.h" + +#define DRV_DESC "Freescale QE UCC HDLC Driver" +#define DRV_NAME "ucc_hdlc" + +#define TDM_PPPOHT_SLIC_MAXIN +/* #define DEBUG */ +/* #define QE_HDLC_TEST */ +#define BROKEN_FRAME_INFO + +static struct ucc_tdm_info utdm_primary_info = { + .uf_info = { + .tsa = 0, + .cdp = 0, + .cds = 1, + .ctsp = 1, + .ctss = 1, + .revd = 0, + .urfs = 256, + .utfs = 256, + .urfet = 128, + .urfset = 192, + .utfet = 128, + .utftt = 0x40, + .ufpt = 256, + .mode = UCC_FAST_PROTOCOL_MODE_HDLC, + .ttx_trx = UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_NORMAL, + .tenc = UCC_FAST_TX_ENCODING_NRZ, + .renc = UCC_FAST_RX_ENCODING_NRZ, + .tcrc = UCC_FAST_16_BIT_CRC, + .synl = UCC_FAST_SYNC_LEN_NOT_USED, + }, + + .si_info = { +#ifdef CONFIG_FSL_PQ_MDS_T1 + .simr_rfsd = 1, /* TDM card need 1 bit delay */ + .simr_tfsd = 0, +#else +#ifdef TDM_PPPOHT_SLIC_MAXIN + .simr_rfsd = 1, + .simr_tfsd = 2, +#else + .simr_rfsd = 0, + .simr_tfsd = 0, +#endif +#endif + .simr_crt = 0, + .simr_sl = 0, + .simr_ce = 1, + .simr_fe = 1, + .simr_gm = 0, + }, +}; + +static struct ucc_tdm_info utdm_info[MAX_HDLC_NUM]; + +#ifdef DEBUG +static void mem_disp(u8 *addr, int size) +{ + void *i; + int size16_aling = (size >> 4) << 4; + int size4_aling = (size >> 2) << 2; + int not_align = 0; + + if (size % 16) + not_align = 1; + + for (i = addr; i < addr + size16_aling; i += 16) { + u32 *i32 = i; + + pr_info("0x%08p: %08x %08x %08x %08x\r\n", + i32, be32_to_cpu(i32[0]), be32_to_cpu(i32[1]), + be32_to_cpu(i32[2]), be32_to_cpu(i32[3])); + } + + if (not_align == 1) + pr_info("0x%08p: ", i); + for (; i < addr + size4_aling; i += 4) + pr_info("%08x ", be32_to_cpu(*((u32 *)(i)))); + for (; i < addr + size; i++) + pr_info("%02x", *((u8 *)(i))); + if (not_align == 1) + pr_info("\r\n"); +} + +static void dump_ucc(struct ucc_hdlc_private *priv) +{ + struct ucc_hdlc_param *ucc_pram; + + ucc_pram = priv->ucc_pram; + + dev_info(priv->dev, "DumpiniCC %d Registers\n", + priv->ut_info->uf_info.ucc_num); + ucc_fast_dump_regs(priv->uccf); + dev_info(priv->dev, "Dumping UCC %d Parameter RAM\n", + priv->ut_info->uf_info.ucc_num); + dev_info(priv->dev, "rbase = 0x%x\n", ioread32be(&ucc_pram->rbase)); + dev_info(priv->dev, "rbptr = 0x%x\n", ioread32be(&ucc_pram->rbptr)); + dev_info(priv->dev, "mrblr = 0x%x\n", ioread16be(&ucc_pram->mrblr)); + dev_info(priv->dev, "rbdlen = 0x%x\n", ioread16be(&ucc_pram->rbdlen)); + dev_info(priv->dev, "rbdstat = 0x%x\n", ioread16be(&ucc_pram->rbdstat)); + dev_info(priv->dev, "rstate = 0x%x\n", ioread32be(&ucc_pram->rstate)); + dev_info(priv->dev, "rdptr = 0x%x\n", ioread32be(&ucc_pram->rdptr)); + dev_info(priv->dev, "riptr = 0x%x\n", ioread16be(&ucc_pram->riptr)); + dev_info(priv->dev, "tbase = 0x%x\n", ioread32be(&ucc_pram->tbase)); + dev_info(priv->dev, "tbptr = 0x%x\n", ioread32be(&ucc_pram->tbptr)); + dev_info(priv->dev, "tbdlen = 0x%x\n", ioread16be(&ucc_pram->tbdlen)); + dev_info(priv->dev, "tbdstat = 0x%x\n", ioread16be(&ucc_pram->tbdstat)); + dev_info(priv->dev, "tstate = 0x%x\n", ioread32be(&ucc_pram->tstate)); + dev_info(priv->dev, "tdptr = 0x%x\n", ioread32be(&ucc_pram->tdptr)); + dev_info(priv->dev, "tiptr = 0x%x\n", ioread16be(&ucc_pram->tiptr)); + dev_info(priv->dev, "rcrc = 0x%x\n", ioread32be(&ucc_pram->rcrc)); + dev_info(priv->dev, "tcrc = 0x%x\n", ioread32be(&ucc_pram->tcrc)); + dev_info(priv->dev, "c_mask = 0x%x\n", ioread32be(&ucc_pram->c_mask)); + dev_info(priv->dev, "c_pers = 0x%x\n", ioread32be(&ucc_pram->c_pres)); + dev_info(priv->dev, "disfc = 0x%x\n", ioread16be(&ucc_pram->disfc)); + dev_info(priv->dev, "crcec = 0x%x\n", ioread16be(&ucc_pram->crcec)); +} + +static void dump_bds(struct ucc_hdlc_private *priv) +{ + int length; + + if (priv->tx_bd_base) { + length = sizeof(struct qe_bd) * TX_BD_RING_LEN; + dev_info(priv->dev, " Dump tx BDs\n"); + mem_disp((u8 *)priv->tx_bd_base, length); + } + + if (priv->rx_bd_base) { + length = sizeof(struct qe_bd) * RX_BD_RING_LEN; + dev_info(priv->dev, " Dump rx BDs\n"); + mem_disp((u8 *)priv->rx_bd_base, length); + } +} + +static void dump_priv(struct ucc_hdlc_private *priv) +{ + dev_info(priv->dev, "ut_info = 0x%x\n", (u32)priv->ut_info); + dev_info(priv->dev, "uccf = 0x%x\n", (u32)priv->uccf); + dev_info(priv->dev, "uf_regs = 0x%x\n", (u32)priv->uf_regs); + dev_info(priv->dev, "si_regs = 0x%x\n", (u32)priv->utdm->si_regs); + dev_info(priv->dev, "ucc_pram = 0x%x\n", (u32)priv->ucc_pram); + dev_info(priv->dev, "tdm_port = 0x%x\n", (u32)priv->utdm->tdm_port); + dev_info(priv->dev, "siram_entry_id = 0x%x\n", + priv->utdm->siram_entry_id); + dev_info(priv->dev, "siram = 0x%x\n", (u32)priv->utdm->siram); + dev_info(priv->dev, "tdm_mode = 0x%x\n", (u32)priv->utdm->tdm_mode); + dev_info(priv->dev, "tdm_framer_type; = 0x%x\n", + (u32)priv->utdm->tdm_framer_type); + dev_info(priv->dev, "rx_buffer; = 0x%x\n", (u32)priv->rx_buffer); + dev_info(priv->dev, "tx_buffer; = 0x%x\n", (u32)priv->tx_buffer); + dev_info(priv->dev, "dma_rx_addr; = 0x%x\n", (u32)priv->dma_rx_addr); + dev_info(priv->dev, "tx_bd; = 0x%x\n", (u32)priv->tx_bd_base); + dev_info(priv->dev, "rx_bd; = 0x%x\n", (u32)priv->rx_bd_base); + dev_info(priv->dev, "curtx_bd = 0x%x\n", (u32)priv->curtx_bd); + dev_info(priv->dev, "currx_bd = 0x%x\n", (u32)priv->currx_bd); + dev_info(priv->dev, "ucc_pram_offset = 0x%x\n", priv->ucc_pram_offset); +} + +#endif /* DEBUG */ + +static int uhdlc_init(struct ucc_hdlc_private *priv) +{ + struct ucc_tdm_info *ut_info; + struct ucc_fast_info *uf_info; + u32 cecr_subblock; + u32 bd_status; + int ret, i; + void *bd_buffer; + dma_addr_t bd_dma_addr; + u32 riptr; + u32 tiptr; + u32 gumr; + + ut_info = priv->ut_info; + uf_info = &ut_info->uf_info; + + if (priv->tsa) { + uf_info->tsa = 1; + uf_info->ctsp = 1; + } + uf_info->uccm_mask = (u32)((UCC_HDLC_UCCE_RXB | UCC_HDLC_UCCE_RXF | + UCC_HDLC_UCCE_TXB) << 16); + + if (ucc_fast_init(uf_info, &priv->uccf)) { + dev_err(priv->dev, "Failed to init uccf."); + return -ENOMEM; + } + + priv->uf_regs = priv->uccf->uf_regs; + ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); + + /* Loopback mode */ + if (priv->loopback) { + pr_info("TDM Mode: Loopback Mode\n"); + gumr = ioread32be(&priv->uf_regs->gumr); + gumr |= (0x40000000 | UCC_FAST_GUMR_CDS | UCC_FAST_GUMR_TCI); + gumr &= ~(UCC_FAST_GUMR_CTSP | UCC_FAST_GUMR_RSYN); + iowrite32be(gumr, &priv->uf_regs->gumr); + } + + /* Initialize SI */ + if (priv->tsa) + ucc_tdm_init(priv->utdm, priv->ut_info); + + /* Write to QE CECR, UCCx channel to Stop Transmission */ + cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); + ret = qe_issue_cmd(QE_STOP_TX, cecr_subblock, + (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); + + /* Set UPSMR normal mode (need fixed)*/ + iowrite32be(0, &priv->uf_regs->upsmr); + + priv->rx_ring_size = RX_BD_RING_LEN; + priv->tx_ring_size = TX_BD_RING_LEN; + /* Alloc Rx BD */ + priv->rx_bd_base = dma_alloc_coherent(priv->dev, + RX_BD_RING_LEN * sizeof(struct qe_bd *), + &priv->dma_rx_bd, GFP_KERNEL); + + if (IS_ERR_VALUE((unsigned long)priv->rx_bd_base)) { + dev_err(priv->dev, "Cannot allocate MURAM memory for RxBDs\n"); + ret = -ENOMEM; + goto rxbd_alloc_error; + } + + /* Alloc Tx BD */ + priv->tx_bd_base = dma_alloc_coherent(priv->dev, + TX_BD_RING_LEN * sizeof(struct qe_bd *), + &priv->dma_tx_bd, GFP_KERNEL); + + if (IS_ERR_VALUE((unsigned long)priv->tx_bd_base)) { + dev_err(priv->dev, "Cannot allocate MURAM memory for TxBDs\n"); + ret = -ENOMEM; + goto txbd_alloc_error; + } + + /* Alloc parameter ram for ucc hdlc */ + priv->ucc_pram_offset = qe_muram_alloc(sizeof(priv->ucc_pram), + ALIGNMENT_OF_UCC_HDLC_PRAM); + + if (IS_ERR_VALUE(priv->ucc_pram_offset)) { + dev_err(priv->dev, "Can not allocate MURAM for hdlc prameter.\n"); + ret = -ENOMEM; + goto pram_alloc_error; + } + + priv->rx_skbuff = kmalloc_array(priv->rx_ring_size, + sizeof(*priv->rx_skbuff), GFP_KERNEL); + if (!priv->rx_skbuff) + goto rx_skb_alloc_error; + for (i = 0; i < priv->rx_ring_size; i++) + priv->rx_skbuff[i] = NULL; + + priv->tx_skbuff = kmalloc_array(priv->tx_ring_size, + sizeof(*priv->tx_skbuff), GFP_KERNEL); + if (!priv->tx_skbuff) + goto tx_skb_alloc_error; + for (i = 0; i < priv->tx_ring_size; i++) + priv->tx_skbuff[i] = NULL; + + priv->skb_curtx = 0; + priv->skb_dirtytx = 0; + priv->curtx_bd = priv->tx_bd_base; + priv->dirty_tx = priv->tx_bd_base; + priv->currx_bd = priv->rx_bd_base; + priv->currx_bdnum = 0; + + /* init parameter base */ + cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); + ret = qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock, + QE_CR_PROTOCOL_UNSPECIFIED, priv->ucc_pram_offset); + + priv->ucc_pram = (struct ucc_hdlc_param __iomem *) + qe_muram_addr(priv->ucc_pram_offset); + + /* Zero out parameter ram */ + memset_io(priv->ucc_pram, 0, sizeof(struct ucc_hdlc_param)); + + /* Alloc riptr, tiptr */ + riptr = qe_muram_alloc(32, 32); + if (IS_ERR_VALUE(riptr)) { + dev_err(priv->dev, "Cannot allocate MURAM mem for Receive internal temp data pointer\n"); + ret = -ENOMEM; + goto riptr_alloc_error; + } + + tiptr = qe_muram_alloc(32, 32); + if (IS_ERR_VALUE(tiptr)) { + dev_err(priv->dev, "Cannot allocate MURAM mem for Transmit internal temp data pointer\n"); + ret = -ENOMEM; + goto tiptr_alloc_error; + } + + /* Set RIPTR, TIPTR */ + iowrite16be((u16)riptr, &priv->ucc_pram->riptr); + iowrite16be((u16)tiptr, &priv->ucc_pram->tiptr); + + /* Set MRBLR */ + iowrite16be((u16)MAX_RX_BUF_LENGTH, &priv->ucc_pram->mrblr); + + /* Set RBASE, TBASE */ + iowrite32be((u32)priv->dma_rx_bd, &priv->ucc_pram->rbase); + iowrite32be((u32)priv->dma_tx_bd, &priv->ucc_pram->tbase); + + /* Set RSTATE, TSTATE */ + iowrite32be(0x30000000, &priv->ucc_pram->rstate); + iowrite32be(0x30000000, &priv->ucc_pram->tstate); + + /* Set C_MASK, C_PRES for 16bit CRC */ + iowrite32be(0x0000F0B8, &priv->ucc_pram->c_mask); + iowrite32be(0x0000FFFF, &priv->ucc_pram->c_pres); + + iowrite16be(MAX_RX_BUF_LENGTH + 8, &priv->ucc_pram->mflr); + iowrite16be(1, &priv->ucc_pram->rfthr); + iowrite16be(1, &priv->ucc_pram->rfcnt); + iowrite16be(DEFAULT_ADDR_MASK, &priv->ucc_pram->hmask); + iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr1); + iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr2); + iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr3); + iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr4); + + /* Get BD buffer */ + bd_buffer = dma_alloc_coherent(priv->dev, + (RX_BD_RING_LEN + TX_BD_RING_LEN) * + MAX_RX_BUF_LENGTH, + &bd_dma_addr, GFP_KERNEL); + + if (!bd_buffer) { + dev_err(priv->dev, "Could not allocate buffer descriptors\n"); + return -ENOMEM; + } + + memset(bd_buffer, 0, (RX_BD_RING_LEN + TX_BD_RING_LEN) + * MAX_RX_BUF_LENGTH); + + priv->rx_buffer = bd_buffer; + priv->tx_buffer = bd_buffer + RX_BD_RING_LEN * MAX_RX_BUF_LENGTH; + + priv->dma_rx_addr = bd_dma_addr; + priv->dma_tx_addr = bd_dma_addr + RX_BD_RING_LEN * MAX_RX_BUF_LENGTH; + + for (i = 0; i < RX_BD_RING_LEN; i++) { + if (i < (RX_BD_RING_LEN - 1)) + bd_status = R_E | R_I; + else + bd_status = R_E | R_I | R_W; + + iowrite32be(bd_status, (u32 *)(priv->rx_bd_base + i)); + iowrite32be(priv->dma_rx_addr + i * MAX_RX_BUF_LENGTH, + &priv->rx_bd_base[i].buf); + } + + for (i = 0; i < TX_BD_RING_LEN; i++) { + if (i < (TX_BD_RING_LEN - 1)) + bd_status = T_I | T_TC; + else + bd_status = T_I | T_TC | T_W; + + iowrite32be(bd_status, (u32 *)(priv->tx_bd_base + i)); + iowrite32be(priv->dma_tx_addr + i * MAX_RX_BUF_LENGTH, + &priv->tx_bd_base[i].buf); + } + + return 0; + +tiptr_alloc_error: + qe_muram_free(riptr); +riptr_alloc_error: + kfree(priv->tx_skbuff); +tx_skb_alloc_error: + kfree(priv->rx_skbuff); +rx_skb_alloc_error: + qe_muram_free(priv->ucc_pram_offset); +pram_alloc_error: + dma_free_coherent(priv->dev, + TX_BD_RING_LEN * sizeof(struct qe_bd), + priv->tx_bd_base, priv->dma_tx_bd); +txbd_alloc_error: + dma_free_coherent(priv->dev, + RX_BD_RING_LEN * sizeof(struct qe_bd), + priv->rx_bd_base, priv->dma_rx_bd); +rxbd_alloc_error: + ucc_fast_free(priv->uccf); + + return ret; +} + +static netdev_tx_t ucc_hdlc_tx(struct sk_buff *skb, struct net_device *dev) +{ + hdlc_device *hdlc = dev_to_hdlc(dev); + struct ucc_hdlc_private *priv = (struct ucc_hdlc_private *)hdlc->priv; + struct qe_bd __iomem *bd; + u32 bd_status; + unsigned long flags; +#ifdef QE_HDLC_TEST + u8 *send_buf; + int i; +#endif + u16 *proto_head, tmp_head; + + switch (dev->type) { + case ARPHRD_RAWHDLC: + if (skb_headroom(skb) < HDLC_HEAD_LEN) { + dev->stats.tx_dropped++; + dev_kfree_skb(skb); + netdev_err(dev, "No enough space for hdlc head\n"); + return -ENOMEM; + } + + skb_push(skb, HDLC_HEAD_LEN); + + proto_head = (u16 *)skb->data; + tmp_head = *proto_head; + tmp_head = (tmp_head & HDLC_HEAD_MASK) | + htons(DEFAULT_HDLC_HEAD); + *proto_head = tmp_head; + + dev->stats.tx_bytes += skb->len; + break; + + case ARPHRD_PPP: + proto_head = (u16 *)skb->data; + if (*proto_head != ntohs(DEFAULT_PPP_HEAD)) { + dev->stats.tx_dropped++; + dev_kfree_skb(skb); + netdev_err(dev, "Wrong ppp header\n"); + return -ENOMEM; + } + + dev->stats.tx_bytes += skb->len; + break; + + default: + dev->stats.tx_dropped++; + dev_kfree_skb(skb); + netdev_err(dev, "Protocol not supported!\n"); + return -ENOMEM; + + } /*switch right bracket*/ + +#ifdef QE_HDLC_TEST + pr_info("Tx data skb->len:%d ", skb->len); + send_buf = (u8 *)skb->data; + pr_info("\nTransmitted data:\n"); + for (i = 0; (i < 16); i++) { + if (i == skb->len) + pr_info("++++"); + else + pr_info("%02x\n", send_buf[i]); + } +#endif + spin_lock_irqsave(&priv->lock, flags); + + /* Start from the next BD that should be filled */ + bd = priv->curtx_bd; + bd_status = ioread32be((u32 __iomem *)bd); + /* Save the skb pointer so we can free it later */ + priv->tx_skbuff[priv->skb_curtx] = skb; + + /* Update the current skb pointer (wrapping if this was the last) */ + priv->skb_curtx = + (priv->skb_curtx + 1) & TX_RING_MOD_MASK(TX_BD_RING_LEN); + + /* copy skb data to tx buffer for sdma processing */ + memcpy(priv->tx_buffer + (be32_to_cpu(bd->buf) - priv->dma_tx_addr), + skb->data, skb->len); + + /* set bd status and length */ + bd_status = (bd_status & T_W) | T_R | T_I | T_L | T_TC | skb->len; + + iowrite32be(bd_status, (u32 __iomem *)bd); + + /* Move to next BD in the ring */ + if (!(bd_status & T_W)) + bd += 1; + else + bd = priv->tx_bd_base; + + if (bd == priv->dirty_tx) { + if (!netif_queue_stopped(dev)) + netif_stop_queue(dev); + } + + priv->curtx_bd = bd; + + spin_unlock_irqrestore(&priv->lock, flags); + + return NETDEV_TX_OK; +} + +static int hdlc_tx_done(struct ucc_hdlc_private *priv) +{ + /* Start from the next BD that should be filled */ + struct net_device *dev = priv->ndev; + struct qe_bd *bd; /* BD pointer */ + u32 bd_status; + + bd = priv->dirty_tx; + bd_status = ioread32be((u32 __iomem *)bd); + + /* Normal processing. */ + while ((bd_status & T_R) == 0) { + struct sk_buff *skb; + + /* BD contains already transmitted buffer. */ + /* Handle the transmitted buffer and release */ + /* the BD to be used with the current frame */ + + skb = priv->tx_skbuff[priv->skb_dirtytx]; + if (!skb) + break; +#ifdef QE_HDLC_TEST + pr_info("TxBD: %x\n", bd_status); +#endif + dev->stats.tx_packets++; + memset(priv->tx_buffer + + (be32_to_cpu(bd->buf) - priv->dma_tx_addr), + 0, skb->len); + dev_kfree_skb_irq(skb); + + priv->tx_skbuff[priv->skb_dirtytx] = NULL; + priv->skb_dirtytx = + (priv->skb_dirtytx + + 1) & TX_RING_MOD_MASK(TX_BD_RING_LEN); + + /* We freed a buffer, so now we can restart transmission */ + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + + /* Advance the confirmation BD pointer */ + if (!(bd_status & T_W)) + bd += 1; + else + bd = priv->tx_bd_base; + bd_status = ioread32be((u32 __iomem *)bd); + } + priv->dirty_tx = bd; + + return 0; +} + +static int hdlc_rx_done(struct ucc_hdlc_private *priv, int rx_work_limit) +{ + struct net_device *dev = priv->ndev; + struct sk_buff *skb; + hdlc_device *hdlc = dev_to_hdlc(dev); + struct qe_bd *bd; + u32 bd_status; + u16 length, howmany = 0; + u8 *bdbuffer; +#ifdef QE_HDLC_TEST + int i; + static int entry; +#endif + + bd = priv->currx_bd; + bd_status = ioread32be((u32 __iomem *)bd); + + /* while there are received buffers and BD is full (~R_E) */ + while (!((bd_status & (R_E)) || (--rx_work_limit < 0))) { + if (bd_status & R_CR) { +#ifdef BROKEN_FRAME_INFO + pr_info("Broken Frame with RxBD: %x\n", bd_status); +#endif + dev->stats.rx_dropped++; + goto recycle; + } + bdbuffer = priv->rx_buffer + + (priv->currx_bdnum * MAX_RX_BUF_LENGTH); + length = (u16)(bd_status & BD_LENGTH_MASK); + +#ifdef QE_HDLC_TEST + pr_info("Received data length:%d", length); + pr_info("while entry times:%d", entry++); + + pr_info("\nReceived data:\n"); + for (i = 0; (i < 16); i++) { + if (i == length) + pr_info("++++"); + else + pr_info("%02x\n", bdbuffer[i]); + } +#endif + + switch (dev->type) { + case ARPHRD_RAWHDLC: + bdbuffer += HDLC_HEAD_LEN; + length -= (HDLC_HEAD_LEN + HDLC_CRC_SIZE); + + skb = dev_alloc_skb(length); + if (!skb) { + dev->stats.rx_dropped++; + return -ENOMEM; + } + + skb_put(skb, length); + skb->len = length; + skb->dev = dev; + memcpy(skb->data, bdbuffer, length); + break; + + case ARPHRD_PPP: + length -= HDLC_CRC_SIZE; + + skb = dev_alloc_skb(length); + if (!skb) { + dev->stats.rx_dropped++; + return -ENOMEM; + } + + skb_put(skb, length); + skb->len = length; + skb->dev = dev; + memcpy(skb->data, bdbuffer, length); + break; + } + + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + howmany++; + if (hdlc->proto) + skb->protocol = hdlc_type_trans(skb, dev); +#ifdef QE_HDLC_TEST + pr_info("skb->protocol:%x\n", skb->protocol); +#endif + netif_receive_skb(skb); + +recycle: + iowrite32be((bd_status & ~BD_LENGTH_MASK) | R_E | R_I, + (u32 *)bd); + + /* update to point at the next bd */ + if (bd_status & R_W) { + priv->currx_bdnum = 0; + bd = priv->rx_bd_base; + } else { + if (priv->currx_bdnum < (RX_BD_RING_LEN - 1)) + priv->currx_bdnum += 1; + else + priv->currx_bdnum = RX_BD_RING_LEN - 1; + + bd += 1; + } + + bd_status = ioread32be((u32 __iomem *)bd); + } + + priv->currx_bd = bd; + return howmany; +} + +static int ucc_hdlc_poll(struct napi_struct *napi, int budget) +{ + struct ucc_hdlc_private *priv = container_of(napi, + struct ucc_hdlc_private, + napi); + int howmany; + + /* Tx event processing */ + spin_lock(&priv->lock); + hdlc_tx_done(priv); + spin_unlock(&priv->lock); + + howmany = 0; + howmany += hdlc_rx_done(priv, budget - howmany); + + if (howmany < budget) { + napi_complete(napi); + qe_setbits32(priv->uccf->p_uccm, + (UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS) << 16); + } + + return howmany; +} + +static irqreturn_t ucc_hdlc_irq_handler(int irq, void *dev_id) +{ + struct ucc_hdlc_private *priv = (struct ucc_hdlc_private *)dev_id; + struct net_device *dev = priv->ndev; + struct ucc_fast_private *uccf; + struct ucc_tdm_info *ut_info; + u32 ucce; + u32 uccm; + + ut_info = priv->ut_info; + uccf = priv->uccf; + + ucce = ioread32be(uccf->p_ucce); + uccm = ioread32be(uccf->p_uccm); + ucce &= uccm; + iowrite32be(ucce, uccf->p_ucce); +#ifdef QE_HDLC_TEST + pr_info("irq ucce:%x\n", ucce); +#endif + + if ((ucce >> 16) & (UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS)) { + if (napi_schedule_prep(&priv->napi)) { + uccm &= ~((UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS) + << 16); + iowrite32be(uccm, uccf->p_uccm); + __napi_schedule(&priv->napi); + } + } + + /* Errors and other events */ + if (ucce >> 16 & UCC_HDLC_UCCE_BSY) + dev->stats.rx_errors++; + if (ucce >> 16 & UCC_HDLC_UCCE_TXE) + dev->stats.tx_errors++; + + return IRQ_HANDLED; +} + +static int uhdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + const size_t size = sizeof(te1_settings); + te1_settings line; + struct ucc_hdlc_private *priv = netdev_priv(dev); + + if (cmd != SIOCWANDEV) + return hdlc_ioctl(dev, ifr, cmd); + + switch (ifr->ifr_settings.type) { + case IF_GET_IFACE: + ifr->ifr_settings.type = IF_IFACE_E1; + if (ifr->ifr_settings.size < size) { + ifr->ifr_settings.size = size; /* data size wanted */ + return -ENOBUFS; + } + line.clock_type = priv->clocking; + line.clock_rate = 0; + line.loopback = 0; + + if (copy_to_user(ifr->ifr_settings.ifs_ifsu.sync, &line, size)) + return -EFAULT; + return 0; + + default: + return hdlc_ioctl(dev, ifr, cmd); + } +} + +static int uhdlc_open(struct net_device *dev) +{ + u32 cecr_subblock; + hdlc_device *hdlc = dev_to_hdlc(dev); + struct ucc_hdlc_private *priv = hdlc->priv; + struct ucc_tdm *utdm = priv->utdm; + + if (priv->hdlc_busy != 1) { + if (request_irq(priv->ut_info->uf_info.irq, + ucc_hdlc_irq_handler, 0, + "hdlc", (void *)priv)) { + dev_err(priv->dev, "request_irq for ucc hdlc failed\n"); + return -ENODEV; + } + cecr_subblock = ucc_fast_get_qe_cr_subblock( + priv->ut_info->uf_info.ucc_num); + + qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock, + (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); + + ucc_fast_enable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); + + /* Enable the TDM port */ + if (priv->tsa) + utdm->si_regs->siglmr1_h |= (0x1 << utdm->tdm_port); + + priv->hdlc_busy = 1; + netif_device_attach(priv->ndev); + napi_enable(&priv->napi); + netif_start_queue(dev); + hdlc_open(dev); + } else { + dev_err(priv->dev, "HDLC IS RUNNING!\n"); + } + +#ifdef DEBUG + dump_priv(priv); + dump_ucc(priv); + dump_bds(priv); +#endif + return 0; +} + +static void uhdlc_memclean(struct ucc_hdlc_private *priv) +{ + qe_muram_free(priv->ucc_pram->riptr); + qe_muram_free(priv->ucc_pram->tiptr); + + if (priv->rx_bd_base) { + dma_free_coherent(priv->dev, + RX_BD_RING_LEN * sizeof(struct qe_bd), + priv->rx_bd_base, priv->dma_rx_bd); + + priv->rx_bd_base = NULL; + priv->dma_rx_bd = 0; + } + + if (priv->tx_bd_base) { + dma_free_coherent(priv->dev, + TX_BD_RING_LEN * sizeof(struct qe_bd), + priv->tx_bd_base, priv->dma_tx_bd); + + priv->tx_bd_base = NULL; + priv->dma_tx_bd = 0; + } + + if (priv->ucc_pram) { + qe_muram_free(priv->ucc_pram_offset); + priv->ucc_pram = NULL; + priv->ucc_pram_offset = 0; + } + + kfree(priv->rx_skbuff); + priv->rx_skbuff = NULL; + + kfree(priv->tx_skbuff); + priv->tx_skbuff = NULL; + + if (priv->uf_regs) { + iounmap(priv->uf_regs); + priv->uf_regs = NULL; + } + + if (priv->uccf) { + ucc_fast_free(priv->uccf); + priv->uccf = NULL; + } + + if (priv->rx_buffer) { + dma_free_coherent(priv->dev, + RX_BD_RING_LEN * MAX_RX_BUF_LENGTH, + priv->rx_buffer, priv->dma_rx_addr); + priv->rx_buffer = NULL; + priv->dma_rx_addr = 0; + } + + if (priv->tx_buffer) { + dma_free_coherent(priv->dev, + TX_BD_RING_LEN * MAX_RX_BUF_LENGTH, + priv->tx_buffer, priv->dma_tx_addr); + priv->tx_buffer = NULL; + priv->dma_tx_addr = 0; + } +} + +static int uhdlc_close(struct net_device *dev) +{ + struct ucc_hdlc_private *priv = dev_to_hdlc(dev)->priv; + struct ucc_tdm *utdm = priv->utdm; + u32 cecr_subblock; + + napi_disable(&priv->napi); + cecr_subblock = ucc_fast_get_qe_cr_subblock( + priv->ut_info->uf_info.ucc_num); + + qe_issue_cmd(QE_GRACEFUL_STOP_TX, cecr_subblock, + (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); + qe_issue_cmd(QE_CLOSE_RX_BD, cecr_subblock, + (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); + + if (priv->tsa) + utdm->si_regs->siglmr1_h &= ~(0x1 << utdm->tdm_port); + + ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); + + free_irq(priv->ut_info->uf_info.irq, priv); + netif_stop_queue(dev); + priv->hdlc_busy = 0; + + return 0; +} + +static int ucc_hdlc_attach(struct net_device *dev, unsigned short encoding, + unsigned short parity) +{ + struct ucc_hdlc_private *priv = dev_to_hdlc(dev)->priv; + + if (encoding != ENCODING_NRZ && + encoding != ENCODING_NRZI) + return -EINVAL; + + if (parity != PARITY_NONE && + parity != PARITY_CRC32_PR1_CCITT && + parity != PARITY_CRC16_PR1_CCITT) + return -EINVAL; + + priv->encoding = encoding; + priv->parity = parity; + + return 0; +} + +#ifdef CONFIG_PM +static void store_clk_config(struct ucc_hdlc_private *priv) +{ + struct qe_mux *qe_mux_reg = &qe_immr->qmx; + + /* store si clk */ + priv->cmxsi1cr_h = ioread32be(&qe_mux_reg->cmxsi1cr_h); + priv->cmxsi1cr_l = ioread32be(&qe_mux_reg->cmxsi1cr_l); + + /* store si sync */ + priv->cmxsi1syr = ioread32be(&qe_mux_reg->cmxsi1syr); + + /* store ucc clk */ + memcpy_fromio(priv->cmxucr, qe_mux_reg->cmxucr, 4 * sizeof(u32)); +} + +static void resume_clk_config(struct ucc_hdlc_private *priv) +{ + struct qe_mux *qe_mux_reg = &qe_immr->qmx; + + memcpy_toio(qe_mux_reg->cmxucr, priv->cmxucr, 4 * sizeof(u32)); + + iowrite32be(priv->cmxsi1cr_h, &qe_mux_reg->cmxsi1cr_h); + iowrite32be(priv->cmxsi1cr_l, &qe_mux_reg->cmxsi1cr_l); + + iowrite32be(priv->cmxsi1syr, &qe_mux_reg->cmxsi1syr); +} + +static int uhdlc_suspend(struct device *dev) +{ + struct ucc_hdlc_private *priv = dev_get_drvdata(dev); + struct ucc_tdm_info *ut_info; + struct ucc_fast __iomem *uf_regs; + + if (!priv) + return -EINVAL; + + if (!netif_running(priv->ndev)) + return 0; + + netif_device_detach(priv->ndev); + napi_disable(&priv->napi); + + ut_info = priv->ut_info; + uf_regs = priv->uf_regs; + + /* backup gumr guemr*/ + priv->gumr = ioread32be(&uf_regs->gumr); + priv->guemr = ioread8(&uf_regs->guemr); + + priv->ucc_pram_bak = kmalloc(sizeof(*priv->ucc_pram_bak), + GFP_KERNEL); + if (!priv->ucc_pram_bak) + return -ENOMEM; + + /* backup HDLC parameter */ + memcpy_fromio(priv->ucc_pram_bak, priv->ucc_pram, + sizeof(struct ucc_hdlc_param)); + + /* store the clk configuration */ + store_clk_config(priv); + + /* save power */ + ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); + + dev_dbg(dev, "ucc hdlc suspend\n"); + return 0; +} + +static int uhdlc_resume(struct device *dev) +{ + struct ucc_hdlc_private *priv = dev_get_drvdata(dev); + struct ucc_tdm *utdm = priv->utdm; + struct ucc_tdm_info *ut_info; + struct ucc_fast __iomem *uf_regs; + struct ucc_fast_private *uccf; + struct ucc_fast_info *uf_info; + int ret, i; + u32 cecr_subblock, bd_status; + + if (!priv) + return -EINVAL; + + if (!netif_running(priv->ndev)) + return 0; + + ut_info = priv->ut_info; + uf_info = &ut_info->uf_info; + uf_regs = priv->uf_regs; + uccf = priv->uccf; + + /* restore gumr guemr */ + iowrite8(priv->guemr, &uf_regs->guemr); + iowrite32be(priv->gumr, &uf_regs->gumr); + + /* Set Virtual Fifo registers */ + iowrite16be(uf_info->urfs, &uf_regs->urfs); + iowrite16be(uf_info->urfet, &uf_regs->urfet); + iowrite16be(uf_info->urfset, &uf_regs->urfset); + iowrite16be(uf_info->utfs, &uf_regs->utfs); + iowrite16be(uf_info->utfet, &uf_regs->utfet); + iowrite16be(uf_info->utftt, &uf_regs->utftt); + /* utfb, urfb are offsets from MURAM base */ + iowrite32be(uccf->ucc_fast_tx_virtual_fifo_base_offset, &uf_regs->utfb); + iowrite32be(uccf->ucc_fast_rx_virtual_fifo_base_offset, &uf_regs->urfb); + + /* Rx Tx and sync clock routing */ + resume_clk_config(priv); + + iowrite32be(uf_info->uccm_mask, &uf_regs->uccm); + iowrite32be(0xffffffff, &uf_regs->ucce); + + ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); + + /* rebuild SIRAM */ + if (priv->tsa) + ucc_tdm_init(priv->utdm, priv->ut_info); + + /* Write to QE CECR, UCCx channel to Stop Transmission */ + cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); + ret = qe_issue_cmd(QE_STOP_TX, cecr_subblock, + (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); + + /* Set UPSMR normal mode */ + iowrite32be(0, &uf_regs->upsmr); + + /* init parameter base */ + cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); + ret = qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock, + QE_CR_PROTOCOL_UNSPECIFIED, priv->ucc_pram_offset); + + priv->ucc_pram = (struct ucc_hdlc_param __iomem *) + qe_muram_addr(priv->ucc_pram_offset); + + /* restore ucc parameter */ + memcpy_toio(priv->ucc_pram, priv->ucc_pram_bak, + sizeof(struct ucc_hdlc_param)); + kfree(priv->ucc_pram_bak); + + /* rebuild BD entry */ + for (i = 0; i < RX_BD_RING_LEN; i++) { + if (i < (RX_BD_RING_LEN - 1)) + bd_status = R_E | R_I; + else + bd_status = R_E | R_I | R_W; + + iowrite32be(bd_status, (u32 *)(priv->rx_bd_base + i)); + iowrite32be(priv->dma_rx_addr + i * MAX_RX_BUF_LENGTH, + &priv->rx_bd_base[i].buf); + } + + for (i = 0; i < TX_BD_RING_LEN; i++) { + if (i < (TX_BD_RING_LEN - 1)) + bd_status = T_I | T_TC; + else + bd_status = T_I | T_TC | T_W; + + iowrite32be(bd_status, (u32 *)(priv->tx_bd_base + i)); + iowrite32be(priv->dma_tx_addr + i * MAX_RX_BUF_LENGTH, + &priv->tx_bd_base[i].buf); + } + + /* if hdlc is busy enable TX and RX */ + if (priv->hdlc_busy == 1) { + cecr_subblock = ucc_fast_get_qe_cr_subblock( + priv->ut_info->uf_info.ucc_num); + + qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock, + (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); + + ucc_fast_enable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); + + /* Enable the TDM port */ + if (priv->tsa) + utdm->si_regs->siglmr1_h |= (0x1 << utdm->tdm_port); + } + + napi_enable(&priv->napi); + netif_device_attach(priv->ndev); + + return 0; +} + +static const struct dev_pm_ops uhdlc_pm_ops = { + .suspend = uhdlc_suspend, + .resume = uhdlc_resume, + .freeze = uhdlc_suspend, + .thaw = uhdlc_resume, +}; + +#define HDLC_PM_OPS (&uhdlc_pm_ops) + +#else + +#define HDLC_PM_OPS NULL + +#endif +static const struct net_device_ops uhdlc_ops = { + .ndo_open = uhdlc_open, + .ndo_stop = uhdlc_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = uhdlc_ioctl, +}; + +static int ucc_hdlc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct ucc_hdlc_private *uhdlc_priv = NULL; + struct ucc_tdm_info *ut_info; + struct ucc_tdm *utdm; + struct resource res; + struct net_device *dev; + hdlc_device *hdlc; + int ucc_num; + const char *sprop; + int ret; + u32 val; + + ret = of_property_read_u32_index(np, "cell-index", 0, &val); + if (ret) { + dev_err(&pdev->dev, "Invalid ucc property\n"); + return -ENODEV; + } + + ucc_num = val - 1; + if ((ucc_num > 3) || (ucc_num < 0)) { + dev_err(&pdev->dev, ": Invalid UCC num\n"); + return -EINVAL; + } + + memcpy(&utdm_info[ucc_num], &utdm_primary_info, + sizeof(utdm_primary_info)); + + ut_info = &utdm_info[ucc_num]; + ut_info->uf_info.ucc_num = ucc_num; + + sprop = of_get_property(np, "rx-clock-name", NULL); + if (sprop) { + ut_info->uf_info.rx_clock = qe_clock_source(sprop); + if ((ut_info->uf_info.rx_clock < QE_CLK_NONE) || + (ut_info->uf_info.rx_clock > QE_CLK24)) { + dev_err(&pdev->dev, "Invalid rx-clock-name property\n"); + return -EINVAL; + } + } else { + dev_err(&pdev->dev, "Invalid rx-clock-name property\n"); + return -EINVAL; + } + + sprop = of_get_property(np, "tx-clock-name", NULL); + if (sprop) { + ut_info->uf_info.tx_clock = qe_clock_source(sprop); + if ((ut_info->uf_info.tx_clock < QE_CLK_NONE) || + (ut_info->uf_info.tx_clock > QE_CLK24)) { + dev_err(&pdev->dev, "Invalid tx-clock-name property\n"); + return -EINVAL; + } + } else { + dev_err(&pdev->dev, "Invalid tx-clock-name property\n"); + return -EINVAL; + } + + /* use the same clock when work in loopback */ + if (ut_info->uf_info.rx_clock == ut_info->uf_info.tx_clock) + qe_setbrg(ut_info->uf_info.rx_clock, 20000000, 1); + + ret = of_address_to_resource(np, 0, &res); + if (ret) + return -EINVAL; + + ut_info->uf_info.regs = res.start; + ut_info->uf_info.irq = irq_of_parse_and_map(np, 0); + + uhdlc_priv = kzalloc(sizeof(*uhdlc_priv), GFP_KERNEL); + if (!uhdlc_priv) { + ret = -ENOMEM; + dev_err(&pdev->dev, "No mem to alloc hdlc private data\n"); + goto err_alloc_priv; + } + + dev_set_drvdata(&pdev->dev, uhdlc_priv); + uhdlc_priv->dev = &pdev->dev; + uhdlc_priv->ut_info = ut_info; + + if (of_get_property(np, "fsl,tdm-interface", NULL)) + uhdlc_priv->tsa = 1; + + if (of_get_property(np, "fsl,ucc-internal-loopback", NULL)) + uhdlc_priv->loopback = 1; + + if (uhdlc_priv->tsa == 1) { + utdm = kzalloc(sizeof(*utdm), GFP_KERNEL); + if (!utdm) { + ret = -ENOMEM; + dev_err(&pdev->dev, "No mem to alloc ucc tdm data\n"); + goto err_alloc_utdm; + } + uhdlc_priv->utdm = utdm; + ret = ucc_of_parse_tdm(np, utdm, ut_info); + if (ret) + goto err_miss_tsa_property; + } + + ret = uhdlc_init(uhdlc_priv); + if (ret) { + dev_err(&pdev->dev, "Failed to init uhdlc\n"); + goto err_hdlc_init; + } + + dev = alloc_hdlcdev(uhdlc_priv); + if (!dev) { + ret = -ENOMEM; + pr_err("ucc_hdlc: unable to allocate memory\n"); + goto err_hdlc_init; + } + + uhdlc_priv->ndev = dev; + hdlc = dev_to_hdlc(dev); + dev->tx_queue_len = 16; + dev->netdev_ops = &uhdlc_ops; + hdlc->attach = ucc_hdlc_attach; + hdlc->xmit = ucc_hdlc_tx; + netif_napi_add(dev, &uhdlc_priv->napi, ucc_hdlc_poll, 32); + if (register_hdlc_device(dev)) { + ret = -ENOBUFS; + pr_err("ucc_hdlc: unable to register hdlc device\n"); + free_netdev(dev); + goto err_hdlc_init; + } + +#ifdef DEBUG + dump_priv(uhdlc_priv); + dump_ucc(uhdlc_priv); + dump_bds(uhdlc_priv); + if (uhdlc_priv->tsa) + mem_disp((u8 *)uhdlc_priv->utdm->si_regs, 0x20); +#endif + + return 0; + +err_hdlc_init: +err_miss_tsa_property: + kfree(uhdlc_priv); + if (uhdlc_priv->tsa) + kfree(utdm); +err_alloc_utdm: + kfree(uhdlc_priv); +err_alloc_priv: + return ret; +} + +static int ucc_hdlc_remove(struct platform_device *pdev) +{ + struct ucc_hdlc_private *priv = dev_get_drvdata(&pdev->dev); + + uhdlc_memclean(priv); + + if (priv->utdm->si_regs) { + iounmap(priv->utdm->si_regs); + priv->utdm->si_regs = NULL; + } + + if (priv->utdm->siram) { + iounmap(priv->utdm->siram); + priv->utdm->siram = NULL; + } + kfree(priv); + + dev_info(&pdev->dev, "UCC based hdlc module removed\n"); + + return 0; +} + +static const struct of_device_id fsl_ucc_hdlc_of_match[] = { + { + .compatible = "fsl,ucc-hdlc", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, fsl_ucc_hdlc_of_match); + +static struct platform_driver ucc_hdlc_driver = { + .probe = ucc_hdlc_probe, + .remove = ucc_hdlc_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .pm = HDLC_PM_OPS, + .of_match_table = fsl_ucc_hdlc_of_match, + }, +}; + +static int __init ucc_hdlc_init(void) +{ + return platform_driver_register(&ucc_hdlc_driver); +} + +static void __exit ucc_hdlc_exit(void) +{ + platform_driver_unregister(&ucc_hdlc_driver); +} + +module_init(ucc_hdlc_init); +module_exit(ucc_hdlc_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Freescale Semiconductor Inc."); +MODULE_DESCRIPTION("Driver For Freescale QE UCC HDLC controller"); +MODULE_VERSION("1.0"); diff --git a/drivers/net/wan/fsl_ucc_hdlc.h b/drivers/net/wan/fsl_ucc_hdlc.h new file mode 100644 index 0000000..ded03d6 --- /dev/null +++ b/drivers/net/wan/fsl_ucc_hdlc.h @@ -0,0 +1,140 @@ +/* Freescale QUICC Engine HDLC Device Driver + * + * Copyright 2014 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef CONFIG_UCC_HDLC_H +#define CONFIG_UCC_HDLC_H + +#include <linux/kernel.h> +#include <linux/list.h> + +#include <soc/fsl/qe/immap_qe.h> +#include <soc/fsl/qe/qe.h> + +#include <soc/fsl/qe/ucc.h> +#include <soc/fsl/qe/ucc_fast.h> + +/* UCC HDLC event register */ +#define UCCE_HDLC_RX_EVENTS \ +(UCC_HDLC_UCCE_RXF | UCC_HDLC_UCCE_RXB | UCC_HDLC_UCCE_BSY) +#define UCCE_HDLC_TX_EVENTS (UCC_HDLC_UCCE_TXB | UCC_HDLC_UCCE_TXE) + +struct ucc_hdlc_param { + __be16 riptr; + __be16 tiptr; + __be16 res0; + __be16 mrblr; + __be32 rstate; + __be32 rbase; + __be16 rbdstat; + __be16 rbdlen; + __be32 rdptr; + __be32 tstate; + __be32 tbase; + __be16 tbdstat; + __be16 tbdlen; + __be32 tdptr; + __be32 rbptr; + __be32 tbptr; + __be32 rcrc; + __be32 res1; + __be32 tcrc; + __be32 res2; + __be32 res3; + __be32 c_mask; + __be32 c_pres; + __be16 disfc; + __be16 crcec; + __be16 abtsc; + __be16 nmarc; + __be32 max_cnt; + __be16 mflr; + __be16 rfthr; + __be16 rfcnt; + __be16 hmask; + __be16 haddr1; + __be16 haddr2; + __be16 haddr3; + __be16 haddr4; + __be16 ts_tmp; + __be16 tmp_mb; +} __attribute__ ((__packed__)); + +struct ucc_hdlc_private { + struct ucc_tdm *utdm; + struct ucc_tdm_info *ut_info; + struct ucc_fast_private *uccf; + struct device *dev; + struct net_device *ndev; + struct napi_struct napi; + struct ucc_fast __iomem *uf_regs; /* UCC Fast registers */ + struct ucc_hdlc_param __iomem *ucc_pram; + u16 tsa; + bool hdlc_busy; + u8 loopback; + + u8 *tx_buffer; /* buffer used for Tx by the HDLC */ + u8 *rx_buffer; /* buffer used for Rx by the HDLC */ + dma_addr_t dma_tx_addr; /* dma mapped buffer for HDLC Tx */ + dma_addr_t dma_rx_addr; /* dma mapped buffer for HDLC Rx */ + + struct qe_bd *tx_bd_base; + struct qe_bd *rx_bd_base; + dma_addr_t dma_tx_bd; + dma_addr_t dma_rx_bd; + struct qe_bd *curtx_bd; + struct qe_bd *currx_bd; + struct qe_bd *dirty_tx; + u16 currx_bdnum; + + struct sk_buff **tx_skbuff; + struct sk_buff **rx_skbuff; + u16 skb_curtx; + u16 skb_currx; + unsigned short skb_dirtytx; + + unsigned short tx_ring_size; + unsigned short rx_ring_size; + u32 ucc_pram_offset; + + unsigned short encoding; + unsigned short parity; + u32 clocking; + spinlock_t lock; /* lock for Tx BD and Tx buffer */ +#ifdef CONFIG_PM + struct ucc_hdlc_param *ucc_pram_bak; + u32 gumr; + u8 guemr; + u32 cmxsi1cr_l, cmxsi1cr_h; + u32 cmxsi1syr; + u32 cmxucr[4]; +#endif +}; + +#define TX_BD_RING_LEN 0x10 +#define RX_BD_RING_LEN 0x20 +#define RX_CLEAN_MAX 0x10 +#define NUM_OF_BUF 4 +#define MAX_RX_BUF_LENGTH (48 * 0x20) +#define ALIGNMENT_OF_UCC_HDLC_PRAM 64 +#define SI_BANK_SIZE 128 +#define MAX_HDLC_NUM 4 +#define HDLC_HEAD_LEN 2 +#define HDLC_CRC_SIZE 2 +#define TX_RING_MOD_MASK(size) (size - 1) +#define RX_RING_MOD_MASK(size) (size - 1) + +#define HDLC_HEAD_MASK 0x0000 +#define DEFAULT_HDLC_HEAD 0xff44 +#define DEFAULT_ADDR_MASK 0x00ff +#define DEFAULT_HDLC_ADDR 0x00ff + +#define DEFAULT_PPP_HEAD 0xff03 + +#endif diff --git a/include/soc/fsl/qe/ucc_fast.h b/include/soc/fsl/qe/ucc_fast.h index e898895..d775550 100644 --- a/include/soc/fsl/qe/ucc_fast.h +++ b/include/soc/fsl/qe/ucc_fast.h @@ -27,12 +27,16 @@ #define R_I 0x10000000 /* interrupt on reception */ #define R_L 0x08000000 /* last */ #define R_F 0x04000000 /* first */ +#define R_CM 0x02000000 /* first */ +#define R_CR 0x00040000 /* first */ /* transmit BD's status */ #define T_R 0x80000000 /* ready bit */ #define T_W 0x20000000 /* wrap bit */ #define T_I 0x10000000 /* interrupt on completion */ #define T_L 0x08000000 /* last */ +#define T_TC 0x04000000 /* crc */ +#define T_TM 0x02000000 /* crc */ /* Rx Data buffer must be 4 bytes aligned in most cases */ #define UCC_FAST_RX_ALIGN 4 -- 2.1.0.27.g96db324 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 5/5] drivers/net: support hdlc function for QE-UCC 2016-03-30 8:50 ` [PATCH 5/5] drivers/net: support hdlc function for QE-UCC Zhao Qiang @ 2016-03-30 9:53 ` kbuild test robot 2016-03-30 12:27 ` kbuild test robot ` (2 subsequent siblings) 3 siblings, 0 replies; 12+ messages in thread From: kbuild test robot @ 2016-03-30 9:53 UTC (permalink / raw) To: Zhao Qiang Cc: kbuild-all, davem, akpm, gregkh, oss, xiaobo.xie, linux-kernel, netdev, linuxppc-dev, Zhao Qiang [-- Attachment #1: Type: text/plain, Size: 928 bytes --] Hi Zhao, [auto build test WARNING on net/master] [also build test WARNING on v4.6-rc1 next-20160330] [if your patch is applied to the wrong git tree, please drop us a note to help improving the system] url: https://github.com/0day-ci/linux/commits/Zhao-Qiang/fsl-qe-add-rx_sync-and-tx_sync-for-TDM-mode/20160330-170411 config: xtensa-allyesconfig (attached as .config) reproduce: wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree make.cross ARCH=xtensa All warnings (new ones prefixed by >>): warning: (FSL_UCC_HDLC) selects QUICC_ENGINE which has unmet direct dependencies (FSL_SOC && PPC32) --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation [-- Attachment #2: .config.gz --] [-- Type: application/octet-stream, Size: 44940 bytes --] ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 5/5] drivers/net: support hdlc function for QE-UCC 2016-03-30 8:50 ` [PATCH 5/5] drivers/net: support hdlc function for QE-UCC Zhao Qiang 2016-03-30 9:53 ` kbuild test robot @ 2016-03-30 12:27 ` kbuild test robot 2016-03-30 12:53 ` kbuild test robot 2016-04-19 16:22 ` Christophe Leroy 3 siblings, 0 replies; 12+ messages in thread From: kbuild test robot @ 2016-03-30 12:27 UTC (permalink / raw) To: Zhao Qiang Cc: kbuild-all, davem, akpm, gregkh, oss, xiaobo.xie, linux-kernel, netdev, linuxppc-dev, Zhao Qiang [-- Attachment #1: Type: text/plain, Size: 941 bytes --] Hi Zhao, [auto build test WARNING on net/master] [also build test WARNING on v4.6-rc1 next-20160330] [if your patch is applied to the wrong git tree, please drop us a note to help improving the system] url: https://github.com/0day-ci/linux/commits/Zhao-Qiang/fsl-qe-add-rx_sync-and-tx_sync-for-TDM-mode/20160330-170411 config: powerpc-allyesconfig (attached as .config) reproduce: wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree make.cross ARCH=powerpc All warnings (new ones prefixed by >>): warning: (KMETER1 && FSL_UCC_HDLC) selects QUICC_ENGINE which has unmet direct dependencies (FSL_SOC && PPC32) --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation [-- Attachment #2: .config.gz --] [-- Type: application/octet-stream, Size: 48751 bytes --] ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 5/5] drivers/net: support hdlc function for QE-UCC 2016-03-30 8:50 ` [PATCH 5/5] drivers/net: support hdlc function for QE-UCC Zhao Qiang 2016-03-30 9:53 ` kbuild test robot 2016-03-30 12:27 ` kbuild test robot @ 2016-03-30 12:53 ` kbuild test robot 2016-04-19 16:22 ` Christophe Leroy 3 siblings, 0 replies; 12+ messages in thread From: kbuild test robot @ 2016-03-30 12:53 UTC (permalink / raw) To: Zhao Qiang Cc: kbuild-all, davem, akpm, gregkh, oss, xiaobo.xie, linux-kernel, netdev, linuxppc-dev, Zhao Qiang [-- Attachment #1: Type: text/plain, Size: 5695 bytes --] Hi Zhao, [auto build test ERROR on net/master] [also build test ERROR on v4.6-rc1 next-20160330] [if your patch is applied to the wrong git tree, please drop us a note to help improving the system] url: https://github.com/0day-ci/linux/commits/Zhao-Qiang/fsl-qe-add-rx_sync-and-tx_sync-for-TDM-mode/20160330-170411 config: i386-allmodconfig (attached as .config) reproduce: # save the attached .config to linux build tree make ARCH=i386 All error/warnings (new ones prefixed by >>): In file included from include/soc/fsl/qe/ucc_slow.h:21:0, from drivers/tty/serial/ucc_uart.c:34: >> include/soc/fsl/qe/qe.h:24:21: fatal error: asm/cpm.h: No such file or directory compilation terminated. -- >> drivers/net/ethernet/freescale/gianfar_ptp.c:75:0: warning: "FS" redefined #define FS (1<<28) /* FIPER start indication */ ^ In file included from arch/x86/include/uapi/asm/ptrace.h:5:0, from arch/x86/include/asm/ptrace.h:6, from arch/x86/include/asm/alternative.h:8, from arch/x86/include/asm/bitops.h:16, from include/linux/bitops.h:36, from include/linux/kernel.h:10, from include/linux/list.h:8, from include/linux/kobject.h:20, from include/linux/device.h:17, from drivers/net/ethernet/freescale/gianfar_ptp.c:23: arch/x86/include/uapi/asm/ptrace-abi.h:15:0: note: this is the location of the previous definition #define FS 9 ^ -- In file included from drivers/soc/fsl/qe/qe_ic.c:31:0: include/soc/fsl/qe/qe_ic.h: In function 'qe_ic_cascade_low_ipic': >> include/soc/fsl/qe/qe_ic.h:86:21: error: 'NO_IRQ' undeclared (first use in this function) if (cascade_irq != NO_IRQ) ^ include/soc/fsl/qe/qe_ic.h:86:21: note: each undeclared identifier is reported only once for each function it appears in include/soc/fsl/qe/qe_ic.h: In function 'qe_ic_cascade_high_ipic': include/soc/fsl/qe/qe_ic.h:95:21: error: 'NO_IRQ' undeclared (first use in this function) if (cascade_irq != NO_IRQ) ^ include/soc/fsl/qe/qe_ic.h: In function 'qe_ic_cascade_low_mpic': include/soc/fsl/qe/qe_ic.h:105:21: error: 'NO_IRQ' undeclared (first use in this function) if (cascade_irq != NO_IRQ) ^ include/soc/fsl/qe/qe_ic.h: In function 'qe_ic_cascade_high_mpic': include/soc/fsl/qe/qe_ic.h:117:21: error: 'NO_IRQ' undeclared (first use in this function) if (cascade_irq != NO_IRQ) ^ include/soc/fsl/qe/qe_ic.h: In function 'qe_ic_cascade_muxed_mpic': include/soc/fsl/qe/qe_ic.h:130:21: error: 'NO_IRQ' undeclared (first use in this function) if (cascade_irq == NO_IRQ) ^ drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_read': >> drivers/soc/fsl/qe/qe_ic.c:180:9: error: implicit declaration of function 'in_be32' [-Werror=implicit-function-declaration] return in_be32(base + (reg >> 2)); ^ drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_write': >> drivers/soc/fsl/qe/qe_ic.c:186:2: error: implicit declaration of function 'out_be32' [-Werror=implicit-function-declaration] out_be32(base + (reg >> 2), value); ^ drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_get_low_irq': >> drivers/soc/fsl/qe/qe_ic.c:299:10: error: 'NO_IRQ' undeclared (first use in this function) return NO_IRQ; ^ drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_get_high_irq': drivers/soc/fsl/qe/qe_ic.c:315:10: error: 'NO_IRQ' undeclared (first use in this function) return NO_IRQ; ^ drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_init': drivers/soc/fsl/qe/qe_ic.c:350:25: error: 'NO_IRQ' undeclared (first use in this function) if (qe_ic->virq_low == NO_IRQ) { ^ drivers/soc/fsl/qe/qe_ic.c: In function 'qe_ic_set_highest_priority': >> drivers/soc/fsl/qe/qe_ic.c:392:21: error: implicit declaration of function 'virq_to_hw' [-Werror=implicit-function-declaration] unsigned int src = virq_to_hw(virq); ^ cc1: some warnings being treated as errors vim +24 include/soc/fsl/qe/qe.h 98658538 include/asm-powerpc/qe.h Li Yang 2006-10-03 18 1291e49e arch/powerpc/include/asm/qe.h Zhao Qiang 2015-11-30 19 #include <linux/compiler.h> 1291e49e arch/powerpc/include/asm/qe.h Zhao Qiang 2015-11-30 20 #include <linux/genalloc.h> 5e41486c include/asm-powerpc/qe.h Anton Vorontsov 2008-05-23 21 #include <linux/spinlock.h> 1b9e8904 arch/powerpc/include/asm/qe.h Anton Vorontsov 2008-12-03 22 #include <linux/errno.h> 1b9e8904 arch/powerpc/include/asm/qe.h Anton Vorontsov 2008-12-03 23 #include <linux/err.h> 5093bb96 include/asm-powerpc/qe.h Anton Vorontsov 2008-05-23 @24 #include <asm/cpm.h> 7aa1aa6e include/soc/fsl/qe/qe.h Zhao Qiang 2015-11-30 25 #include <soc/fsl/qe/immap_qe.h> 1291e49e arch/powerpc/include/asm/qe.h Zhao Qiang 2015-11-30 26 #include <linux/of.h> 1291e49e arch/powerpc/include/asm/qe.h Zhao Qiang 2015-11-30 27 #include <linux/of_address.h> :::::: The code at line 24 was first introduced by commit :::::: 5093bb965a163fe288c3e5db0275165f86c895c2 powerpc/QE: switch to the cpm_muram implementation :::::: TO: Anton Vorontsov <avorontsov@ru.mvista.com> :::::: CC: Kumar Gala <galak@kernel.crashing.org> --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation [-- Attachment #2: .config.gz --] [-- Type: application/octet-stream, Size: 54451 bytes --] ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 5/5] drivers/net: support hdlc function for QE-UCC 2016-03-30 8:50 ` [PATCH 5/5] drivers/net: support hdlc function for QE-UCC Zhao Qiang ` (2 preceding siblings ...) 2016-03-30 12:53 ` kbuild test robot @ 2016-04-19 16:22 ` Christophe Leroy 2016-04-20 2:28 ` Qiang Zhao 3 siblings, 1 reply; 12+ messages in thread From: Christophe Leroy @ 2016-04-19 16:22 UTC (permalink / raw) To: Zhao Qiang, davem Cc: gregkh, xiaobo.xie, linux-kernel, oss, netdev, akpm, linuxppc-dev 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 ^ permalink raw reply [flat|nested] 12+ messages in thread
* RE: [PATCH 5/5] drivers/net: support hdlc function for QE-UCC 2016-04-19 16:22 ` Christophe Leroy @ 2016-04-20 2:28 ` Qiang Zhao 0 siblings, 0 replies; 12+ messages in thread From: Qiang Zhao @ 2016-04-20 2:28 UTC (permalink / raw) To: Christophe Leroy, davem@davemloft.net Cc: gregkh@linuxfoundation.org, Xiaobo Xie, linux-kernel@vger.kernel.org, oss@buserror.net, netdev@vger.kernel.org, akpm@linux-foundation.org, linuxppc-dev@lists.ozlabs.org T24gMjAvMDQvMjAxNiAxMjoyMkFNLCBDaHJpc3RvcGhlIExlcm95IDxjaHJpc3RvcGhlLmxlcm95 QGMtcy5mcj4gd3JvdGUNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJvbTogQ2hy aXN0b3BoZSBMZXJveSBbbWFpbHRvOmNocmlzdG9waGUubGVyb3lAYy1zLmZyXQ0KPiBTZW50OiBX ZWRuZXNkYXksIEFwcmlsIDIwLCAyMDE2IDEyOjIyIEFNDQo+IFRvOiBRaWFuZyBaaGFvIDxxaWFu Zy56aGFvQG54cC5jb20+OyBkYXZlbUBkYXZlbWxvZnQubmV0DQo+IENjOiBncmVna2hAbGludXhm b3VuZGF0aW9uLm9yZzsgWGlhb2JvIFhpZSA8eGlhb2JvLnhpZUBueHAuY29tPjsgbGludXgtDQo+ IGtlcm5lbEB2Z2VyLmtlcm5lbC5vcmc7IG9zc0BidXNlcnJvci5uZXQ7IG5ldGRldkB2Z2VyLmtl cm5lbC5vcmc7DQo+IGFrcG1AbGludXgtZm91bmRhdGlvbi5vcmc7IGxpbnV4cHBjLWRldkBsaXN0 cy5vemxhYnMub3JnDQo+IFN1YmplY3Q6IFJlOiBbUEFUQ0ggNS81XSBkcml2ZXJzL25ldDogc3Vw cG9ydCBoZGxjIGZ1bmN0aW9uIGZvciBRRS1VQ0MNCj4gDQo+IExlIDMwLzAzLzIwMTYgMTA6NTAs IFpoYW8gUWlhbmcgYSDDqWNyaXQgOg0KPiA+IFRoZSBkcml2ZXIgYWRkIGhkbGMgc3VwcG9ydCBm b3IgRnJlZXNjYWxlIFFVSUNDIEVuZ2luZS4NCj4gPiBJdCBzdXBwb3J0IE5NU0kgYW5kIFRTQSBt b2RlLg0KPiBXaGVuIHVzaW5nIFRTQSwgaG93IGRvZXMgdGhlIFRTQSBnZXRzIGNvbmZpZ3VyZWQg PyBFc3BlY2lhbGx5IGhvdyBkbyB5b3UNCj4gZGVzY3JpYmUgd2hpY2ggVGltZXNsb3QgaXMgc3dp dGNoZWQgdG8gSERMQyBjaGFubmVscyA/DQoNCnRoZSBUU0EgaXMgY29uZmlndXJlZCBzdGF0aWNh bGx5IGFjY29yZGluZyB0byBkZXZpY2UgdHJlZSBub2RlLiANCkZvciAiIHdoaWNoIFRpbWVzbG90 IGlzIHN3aXRjaGVkIHRvIEhETEMgY2hhbm5lbHMgIiwgdGhlcmUgaXMgYSBwcm9wZXJ0eSANCiJm c2wsdHgtdGltZXNsb3QtbWFzayIgaW4gZGV2aWNlIHRyZWUgdG8gZGVzY3JpYmUgaXQuDQoNCj4g SXMgaXQgcG9zc2libGUgdG8gcm91dGUgc29tZSBUaW1lc2xvdHMgdG8gb25lIFVDQyBmb3IgSERM QywgYW5kIHJvdXRlIHNvbWUNCj4gb3RoZXJzIHRvIGFub3RoZXIgVUNDIGZvciBhbiBBTFNBIHNv dW5kIGRyaXZlciA/DQoNClRoZSBmZWF0dXJlIHlvdSBkZXNjcmliZSBpcyBub3Qgc3VwcG9ydGVk IGF0IHByZXNlbnQuDQoNCj4gVGhlIFFFIGFsc28gaGF2ZSBhIFFNQyB3aGljaCBhbGxvd3MgdG8g c3BsaXQgYWxsIHRpbWVzbG90cyB0byBhIGdpdmVuIFVDQyBpbnRvDQo+IGluZGVwZW5kYW50IGNo YW5uZWxzIHRoYXQgY2FuIGVpdGhlciBiZSB1c2VkIHdpdGggSERMQyBvciB0cmFuc3BhcmVudHMg KGZvcg0KPiBhdWRpbyBmb3IgaW5zdGFuY2UpLiBEbyB5b3UgaW50ZW50IHRvIGFsc28gc3VwcG9y dCBRTUMgPw0KDQpuZXcgUUUgdXNlIFVNQ0MgaW5zdGVhZCBvZiBRTUMgaW4gb2xkIFFFLCB3ZSBo YXZlIHN0YXJ0ZWQgdG8gZGV2ZWxvcCBVTUNDLg0KIA0KPiBBY2NvcmRpbmcgdG8gdGhlIGNvbXBh dGlibGUgcHJvcGVydHksIGl0IGxvb2tzIGxpa2UgeW91ciBkcml2ZXIgaXMgZm9yIGZyZWVzY2Fs ZQ0KPiBUMTA0MC4gVGhlIE1QQzgzeHggYWxzbyBoYXMgYSBRdWljayBFbmdpbmUsIHdvdWxkIGl0 IHdvcmsgb24gaXQgdG9vID8NCg0KVGhlIGRyaXZlciBpcyBjb21tb24sIGJ1dCB0ZXN0ZWQgb24g dDEwNDAsIGl0IGlzIG5lZWRlZCB0byBhZGQgbm9kZSB0byBNUEM4M3h4DQpJZiB5b3Ugd2FudCB0 byB0ZXN0IG9uIG1wYzgzeHguDQoNCi1aaGFvIFFpYW5nDQo= ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2016-04-20 2:44 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2016-03-30 8:50 [PATCH 1/5] fsl/qe: add rx_sync and tx_sync for TDM mode Zhao Qiang 2016-03-30 8:50 ` [PATCH 2/5] fsl/qe: setup clock source " Zhao Qiang 2016-03-30 8:50 ` [PATCH 3/5] fsl/qe: Make regs resouce_size_t Zhao Qiang 2016-03-30 8:50 ` [PATCH 4/5] fsl/qe: Add QE TDM lib Zhao Qiang 2016-03-30 11:50 ` Joakim Tjernlund 2016-03-31 1:25 ` Qiang Zhao 2016-03-30 8:50 ` [PATCH 5/5] drivers/net: support hdlc function for QE-UCC Zhao Qiang 2016-03-30 9:53 ` kbuild test robot 2016-03-30 12:27 ` kbuild test robot 2016-03-30 12:53 ` kbuild test robot 2016-04-19 16:22 ` Christophe Leroy 2016-04-20 2:28 ` Qiang Zhao
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).