Rx_sync and tx_sync are used by QE-TDM mode,
add them to struct ucc_fast_info.
Signed-off-by: Zhao Qiang <[email protected]>
---
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
Add tdm clock configuration in both qe clock system and ucc
fast controller.
Signed-off-by: Zhao Qiang <[email protected]>
---
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
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 <[email protected]>
---
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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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
The driver add hdlc support for Freescale QUICC Engine.
It support NMSI and TSA mode.
Signed-off-by: Zhao Qiang <[email protected]>
---
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 <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/net/wan/fsl_ucc_hdlc*
+
FREESCALE QUICC ENGINE UCC UART DRIVER
M: Timur Tabi <[email protected]>
L: [email protected]
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
Signed-off-by: Zhao Qiang <[email protected]>
---
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
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
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 <[email protected]>
> ---
> ?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 <[email protected]>
> + *
> + * Description:
> + * QE TDM API Set - TDM specific routines implementations.
> + *
> + * This program is free software; you can redistribute??it and/or modify it
> + * under??the terms of??the GNU General??Public License as published by the
> + * Free Software Foundation;??either version 2 of the??License, or (at your
> + * option) any later version.
> + */
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <soc/fsl/qe/qe_tdm.h>
> +
> +static enum tdm_framer_t set_tdm_framer(const char *tdm_framer_type)
> +{
> + if (strcmp(tdm_framer_type, "e1") == 0)
> + return TDM_FRAMER_E1;
> + else
> + return TDM_FRAMER_T1;
> +}
> +
> +static void set_si_param(struct ucc_tdm *utdm, struct ucc_tdm_info *ut_info)
> +{
> + struct si_mode_info *si_info = &ut_info->si_info;
> +
> + if (utdm->tdm_mode == TDM_INTERNAL_LOOPBACK) {
> + si_info->simr_crt = 1;
> + si_info->simr_rfsd = 0;
> + }
> +}
> +
> +int ucc_of_parse_tdm(struct device_node *np, struct ucc_tdm *utdm,
> + ?????struct ucc_tdm_info *ut_info)
> +{
> + const char *sprop;
> + int ret = 0;
> + u32 val;
> + struct resource *res;
> + struct device_node *np2;
> + static int siram_init_flag;
> + struct platform_device *pdev;
> +
> + sprop = of_get_property(np, "fsl,rx-sync-clock", NULL);
> + if (sprop) {
> + ut_info->uf_info.rx_sync = qe_clock_source(sprop);
> + if ((ut_info->uf_info.rx_sync < QE_CLK_NONE) ||
> + ????(ut_info->uf_info.rx_sync > QE_RSYNC_PIN)) {
> + pr_err("QE-TDM: Invalid rx-sync-clock property\n");
> + return -EINVAL;
> + }
> + } else {
> + pr_err("QE-TDM: Invalid rx-sync-clock property\n");
> + return -EINVAL;
> + }
> +
> + sprop = of_get_property(np, "fsl,tx-sync-clock", NULL);
> + if (sprop) {
> + ut_info->uf_info.tx_sync = qe_clock_source(sprop);
> + if ((ut_info->uf_info.tx_sync < QE_CLK_NONE) ||
> + ????(ut_info->uf_info.tx_sync > QE_TSYNC_PIN)) {
> + pr_err("QE-TDM: Invalid tx-sync-clock property\n");
> + return -EINVAL;
> + }
> + } else {
> + pr_err("QE-TDM: Invalid tx-sync-clock property\n");
> + return -EINVAL;
> + }
> +
> + ret = of_property_read_u32_index(np, "fsl,tx-timeslot-mask", 0, &val);
> + if (ret) {
> + pr_err("QE-TDM: Invalid tx-timeslot-mask property\n");
> + return -EINVAL;
> + }
> + utdm->tx_ts_mask = val;
> +
> + ret = of_property_read_u32_index(np, "fsl,rx-timeslot-mask", 0, &val);
> + if (ret) {
> + ret = -EINVAL;
> + pr_err("QE-TDM: Invalid rx-timeslot-mask property\n");
> + return ret;
> + }
> + utdm->rx_ts_mask = val;
> +
> + ret = of_property_read_u32_index(np, "fsl,tdm-id", 0, &val);
> + if (ret) {
> + ret = -EINVAL;
> + pr_err("QE-TDM: No fsl,tdm-id property for this UCC\n");
> + return ret;
> + }
> + utdm->tdm_port = val;
> + ut_info->uf_info.tdm_num = utdm->tdm_port;
> +
> + if (of_get_property(np, "fsl,tdm-internal-loopback", NULL))
> + utdm->tdm_mode = TDM_INTERNAL_LOOPBACK;
> + else
> + utdm->tdm_mode = TDM_NORMAL;
> +
> + sprop = of_get_property(np, "fsl,tdm-framer-type", NULL);
> + if (!sprop) {
> + ret = -EINVAL;
> + pr_err("QE-TDM: No tdm-framer-type property for UCC\n");
> + return ret;
> + }
> + utdm->tdm_framer_type = set_tdm_framer(sprop);
> +
> + ret = of_property_read_u32_index(np, "fsl,siram-entry-id", 0, &val);
> + if (ret) {
> + ret = -EINVAL;
> + pr_err("QE-TDM: No siram entry id for UCC\n");
> + return ret;
> + }
> + utdm->siram_entry_id = val;
> +
> + set_si_param(utdm, ut_info);
> +
> + np2 = of_find_compatible_node(NULL, NULL, "fsl,t1040-qe-si");
fsl,t1040-qe-si only? What about mpc83xx?
I recall QE is a little bit different compared to T1040 or will this work(including the hdlc driver)
on 83xx as well?
?Jocke?
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
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 <[email protected]>
:::::: CC: Kumar Gala <[email protected]>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
On Wed, 2016-03-30 at 07:50PM, Joakim Tjernlund wrote:
> -----Original Message-----
> From: Joakim Tjernlund [mailto:[email protected]]
> Sent: Wednesday, March 30, 2016 7:50 PM
> To: [email protected]; Qiang Zhao <[email protected]>
> Cc: [email protected]; [email protected]; Xiaobo Xie
> <[email protected]>; [email protected]; [email protected];
> [email protected]; [email protected]
> Subject: Re: [PATCH 4/5] fsl/qe: Add QE TDM lib
>
> 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 <[email protected]>
> > + utdm->siram_entry_id = val;
> > +
> > + set_si_param(utdm, ut_info);
> > +
> > + np2 = of_find_compatible_node(NULL, NULL, "fsl,t1040-qe-si");
>
> fsl,t1040-qe-si only? What about mpc83xx?
> I recall QE is a little bit different compared to T1040 or will this work(including
> the hdlc driver) on 83xx as well?
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 :
si1: si@700 {
compatible = "fsl,ls1043-qe-si", "fsl,t1040-qe-si";
reg = <0x700 0x80>;
};
Best Regards
Zhao Qiang
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 <[email protected]>
> ---
> 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 <[email protected]>
> +L: [email protected]
> +S: Maintained
> +F: drivers/net/wan/fsl_ucc_hdlc*
> +
> FREESCALE QUICC ENGINE UCC UART DRIVER
> M: Timur Tabi <[email protected]>
> L: [email protected]
> 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
On 20/04/2016 12:22AM, Christophe Leroy <[email protected]> wrote
> -----Original Message-----
> From: Christophe Leroy [mailto:[email protected]]
> Sent: Wednesday, April 20, 2016 12:22 AM
> To: Qiang Zhao <[email protected]>; [email protected]
> Cc: [email protected]; Xiaobo Xie <[email protected]>; linux-
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]
> Subject: Re: [PATCH 5/5] drivers/net: support hdlc function for QE-UCC
>
> Le 30/03/2016 10:50, Zhao Qiang a écrit :
> > The driver add hdlc support for Freescale QUICC Engine.
> > It support NMSI and TSA mode.
> When using TSA, how does the TSA gets configured ? Especially how do you
> describe which Timeslot is switched to HDLC channels ?
the TSA is configured statically according to device tree node.
For " which Timeslot is switched to HDLC channels ", there is a property
"fsl,tx-timeslot-mask" in device tree to describe it.
> Is it possible to route some Timeslots to one UCC for HDLC, and route some
> others to another UCC for an ALSA sound driver ?
The feature you describe is not supported at present.
> The QE also have a QMC which allows to split all timeslots to a given UCC into
> independant channels that can either be used with HDLC or transparents (for
> audio for instance). Do you intent to also support QMC ?
new QE use UMCC instead of QMC in old QE, we have started to develop UMCC.
> According to the compatible property, it looks like your driver is for freescale
> T1040. The MPC83xx also has a Quick Engine, would it work on it too ?
The driver is common, but tested on t1040, it is needed to add node to MPC83xx
If you want to test on mpc83xx.
-Zhao Qiang