MLK-11442 mlb: imx: add mlb support on imx_4.1.y
authorGao Pan <b54642@freescale.com>
Thu, 13 Aug 2015 09:32:47 +0000 (17:32 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:48:16 +0000 (14:48 -0500)
Add mlb support on imx_4.1.y. The files are copied from imx_3.14.y.

Signed-off-by: Gao Pan <b54642@freescale.com>
drivers/mxc/Kconfig
drivers/mxc/Makefile
drivers/mxc/mlb/Kconfig [new file with mode: 0644]
drivers/mxc/mlb/Makefile [new file with mode: 0644]
drivers/mxc/mlb/mxc_mlb.c [new file with mode: 0755]
include/linux/mxc_mlb.h [new file with mode: 0644]
include/uapi/linux/mxc_mlb.h [new file with mode: 0644]

index 6533d2e..d52baf1 100644 (file)
@@ -4,8 +4,9 @@ if ARCH_MXC
 
 menu "MXC support drivers"
 
-source "drivers/mxc/vpu/Kconfig"
+source "drivers/mxc/mlb/Kconfig"
 source "drivers/mxc/sim/Kconfig"
+source "drivers/mxc/vpu/Kconfig"
 
 endmenu
 
index ea4ee9d..060dc06 100644 (file)
@@ -1,2 +1,3 @@
-obj-$(CONFIG_MXC_VPU) += vpu/
+obj-$(CONFIG_MXC_MLB) += mlb/
 obj-$(CONFIG_MXC_SIM) += sim/
+obj-$(CONFIG_MXC_VPU) += vpu/
diff --git a/drivers/mxc/mlb/Kconfig b/drivers/mxc/mlb/Kconfig
new file mode 100644 (file)
index 0000000..667226e
--- /dev/null
@@ -0,0 +1,17 @@
+#
+# MLB150 configuration
+#
+
+menu "MXC Media Local Bus Driver"
+
+config MXC_MLB
+       boolean
+
+config MXC_MLB150
+       tristate "MLB150 support"
+       depends on SOC_IMX6Q
+       select MXC_MLB
+       ---help---
+       Say Y to get the MLB150 support.
+
+endmenu
diff --git a/drivers/mxc/mlb/Makefile b/drivers/mxc/mlb/Makefile
new file mode 100644 (file)
index 0000000..e71005a
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for the i.MX6Q/DL MLB150 driver
+#
+
+obj-$(CONFIG_MXC_MLB150) += mxc_mlb.o
diff --git a/drivers/mxc/mlb/mxc_mlb.c b/drivers/mxc/mlb/mxc_mlb.c
new file mode 100755 (executable)
index 0000000..02485f1
--- /dev/null
@@ -0,0 +1,2809 @@
+/*
+ * Copyright (C) 2011-2014 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/cdev.h>
+#include <linux/circ_buf.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/genalloc.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mxc_mlb.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+
+#define DRIVER_NAME "mxc_mlb150"
+
+/*
+ * MLB module memory map registers define
+ */
+#define REG_MLBC0              0x0
+#define MLBC0_MLBEN            (0x1)
+#define MLBC0_MLBCLK_MASK      (0x7 << 2)
+#define MLBC0_MLBCLK_SHIFT     (2)
+#define MLBC0_MLBPEN           (0x1 << 5)
+#define MLBC0_MLBLK            (0x1 << 7)
+#define MLBC0_ASYRETRY         (0x1 << 12)
+#define MLBC0_CTLRETRY         (0x1 << 12)
+#define MLBC0_FCNT_MASK                (0x7 << 15)
+#define MLBC0_FCNT_SHIFT       (15)
+
+#define REG_MLBPC0             0x8
+#define MLBPC0_MCLKHYS         (0x1 << 11)
+
+#define REG_MS0                        0xC
+#define REG_MS1                        0x14
+
+#define REG_MSS                        0x20
+#define MSS_RSTSYSCMD          (0x1)
+#define MSS_LKSYSCMD           (0x1 << 1)
+#define MSS_ULKSYSCMD          (0x1 << 2)
+#define MSS_CSSYSCMD           (0x1 << 3)
+#define MSS_SWSYSCMD           (0x1 << 4)
+#define MSS_SERVREQ            (0x1 << 5)
+
+#define REG_MSD                        0x24
+
+#define REG_MIEN               0x2C
+#define MIEN_ISOC_PE           (0x1)
+#define MIEN_ISOC_BUFO         (0x1 << 1)
+#define MIEN_SYNC_PE           (0x1 << 16)
+#define MIEN_ARX_DONE          (0x1 << 17)
+#define MIEN_ARX_PE            (0x1 << 18)
+#define MIEN_ARX_BREAK         (0x1 << 19)
+#define MIEN_ATX_DONE          (0x1 << 20)
+#define MIEN_ATX_PE            (0x1 << 21)
+#define MIEN_ATX_BREAK         (0x1 << 22)
+#define MIEN_CRX_DONE          (0x1 << 24)
+#define MIEN_CRX_PE            (0x1 << 25)
+#define MIEN_CRX_BREAK         (0x1 << 26)
+#define MIEN_CTX_DONE          (0x1 << 27)
+#define MIEN_CTX_PE            (0x1 << 28)
+#define MIEN_CTX_BREAK         (0x1 << 29)
+
+#define REG_MLBPC2             0x34
+#define REG_MLBPC1             0x38
+#define MLBPC1_VAL             (0x00000888)
+
+#define REG_MLBC1              0x3C
+#define MLBC1_LOCK             (0x1 << 6)
+#define MLBC1_CLKM             (0x1 << 7)
+#define MLBC1_NDA_MASK         (0xFF << 8)
+#define MLBC1_NDA_SHIFT                (8)
+
+#define REG_HCTL               0x80
+#define HCTL_RST0              (0x1)
+#define HCTL_RST1              (0x1 << 1)
+#define HCTL_EN                        (0x1 << 15)
+
+#define REG_HCMR0              0x88
+#define REG_HCMR1              0x8C
+#define REG_HCER0              0x90
+#define REG_HCER1              0x94
+#define REG_HCBR0              0x98
+#define REG_HCBR1              0x9C
+
+#define REG_MDAT0              0xC0
+#define REG_MDAT1              0xC4
+#define REG_MDAT2              0xC8
+#define REG_MDAT3              0xCC
+
+#define REG_MDWE0              0xD0
+#define REG_MDWE1              0xD4
+#define REG_MDWE2              0xD8
+#define REG_MDWE3              0xDC
+
+#define REG_MCTL               0xE0
+#define MCTL_XCMP              (0x1)
+
+#define REG_MADR               0xE4
+#define MADR_WNR               (0x1 << 31)
+#define MADR_TB                        (0x1 << 30)
+#define MADR_ADDR_MASK         (0x7f << 8)
+#define MADR_ADDR_SHIFT                (0)
+
+#define REG_ACTL               0x3C0
+#define ACTL_MPB               (0x1 << 4)
+#define ACTL_DMAMODE           (0x1 << 2)
+#define ACTL_SMX               (0x1 << 1)
+#define ACTL_SCE               (0x1)
+
+#define REG_ACSR0              0x3D0
+#define REG_ACSR1              0x3D4
+#define REG_ACMR0              0x3D8
+#define REG_ACMR1              0x3DC
+
+#define REG_CAT_MDATn(ch) (REG_MDAT0 + ((ch % 8) >> 1) * 4)
+#define REG_CAT_MDWEn(ch) (REG_MDWE0 + ((ch % 8) >> 1) * 4)
+
+#define INT_AHB0_CH_START      (0)
+#define INT_AHB1_CH_START      (32)
+
+#define LOGIC_CH_NUM           (64)
+#define BUF_CDT_OFFSET         (0x0)
+#define BUF_ADT_OFFSET         (0x40)
+#define BUF_CAT_MLB_OFFSET     (0x80)
+#define BUF_CAT_HBI_OFFSET     (0x88)
+#define BUF_CTR_END_OFFSET     (0x8F)
+
+#define CAT_MODE_RX            (0x1 << 0)
+#define CAT_MODE_TX            (0x1 << 1)
+#define CAT_MODE_INBOUND_DMA   (0x1 << 8)
+#define CAT_MODE_OUTBOUND_DMA  (0x1 << 9)
+
+#define CH_SYNC_DEFAULT_QUAD   (1)
+#define CH_SYNC_MAX_QUAD       (15)
+#define CH_SYNC_CDT_BUF_DEP    (CH_SYNC_DEFAULT_QUAD * 4 * 4)
+#define CH_SYNC_ADT_BUF_MULTI  (4)
+#define CH_SYNC_ADT_BUF_DEP    (CH_SYNC_CDT_BUF_DEP * CH_SYNC_ADT_BUF_MULTI)
+#define CH_SYNC_BUF_SZ         (CH_SYNC_MAX_QUAD * 4 * 4 * \
+                               CH_SYNC_ADT_BUF_MULTI)
+#define CH_CTRL_CDT_BUF_DEP    (64)
+#define CH_CTRL_ADT_BUF_DEP    (CH_CTRL_CDT_BUF_DEP)
+#define CH_CTRL_BUF_SZ         (CH_CTRL_ADT_BUF_DEP)
+#define CH_ASYNC_MDP_PACKET_LEN        (1024)
+#define CH_ASYNC_MEP_PACKET_LEN        (1536)
+#define CH_ASYNC_CDT_BUF_DEP   (CH_ASYNC_MEP_PACKET_LEN)
+#define CH_ASYNC_ADT_BUF_DEP   (CH_ASYNC_CDT_BUF_DEP)
+#define CH_ASYNC_BUF_SZ                (CH_ASYNC_ADT_BUF_DEP)
+#define CH_ISOC_BLK_SIZE_188   (188)
+#define CH_ISOC_BLK_SIZE_196   (196)
+#define CH_ISOC_BLK_SIZE       (CH_ISOC_BLK_SIZE_188)
+#define CH_ISOC_BLK_NUM                (1)
+#define CH_ISOC_CDT_BUF_DEP    (CH_ISOC_BLK_SIZE * CH_ISOC_BLK_NUM)
+#define CH_ISOC_ADT_BUF_DEP    (CH_ISOC_CDT_BUF_DEP)
+#define CH_ISOC_BUF_SZ         (1024)
+
+#define CH_SYNC_DBR_BUF_OFFSET (0x0)
+#define CH_CTRL_DBR_BUF_OFFSET (CH_SYNC_DBR_BUF_OFFSET + \
+                               2 * (CH_SYNC_MAX_QUAD * 4 * 4))
+#define CH_ASYNC_DBR_BUF_OFFSET        (CH_CTRL_DBR_BUF_OFFSET + \
+                               2 * CH_CTRL_CDT_BUF_DEP)
+#define CH_ISOC_DBR_BUF_OFFSET (CH_ASYNC_DBR_BUF_OFFSET + \
+                               2 * CH_ASYNC_CDT_BUF_DEP)
+
+#define DBR_BUF_START 0x00000
+
+#define CDT_LEN                        (16)
+#define ADT_LEN                        (16)
+#define CAT_LEN                        (2)
+
+#define CDT_SZ                 (CDT_LEN * LOGIC_CH_NUM)
+#define ADT_SZ                 (ADT_LEN * LOGIC_CH_NUM)
+#define CAT_SZ                 (CAT_LEN * LOGIC_CH_NUM * 2)
+
+#define CDT_BASE(base)         (base + BUF_CDT_OFFSET)
+#define ADT_BASE(base)         (base + BUF_ADT_OFFSET)
+#define CAT_MLB_BASE(base)     (base + BUF_CAT_MLB_OFFSET)
+#define CAT_HBI_BASE(base)     (base + BUF_CAT_HBI_OFFSET)
+
+#define CDTn_ADDR(base, n)     (base + BUF_CDT_OFFSET + n * CDT_LEN)
+#define ADTn_ADDR(base, n)     (base + BUF_ADT_OFFSET + n * ADT_LEN)
+#define CATn_MLB_ADDR(base, n) (base + BUF_CAT_MLB_OFFSET + n * CAT_LEN)
+#define CATn_HBI_ADDR(base, n) (base + BUF_CAT_HBI_OFFSET + n * CAT_LEN)
+
+#define CAT_CL_SHIFT           (0x0)
+#define CAT_CT_SHIFT           (8)
+#define CAT_CE                 (0x1 << 11)
+#define CAT_RNW                        (0x1 << 12)
+#define CAT_MT                 (0x1 << 13)
+#define CAT_FCE                        (0x1 << 14)
+#define CAT_MFE                        (0x1 << 14)
+
+#define CDT_WSBC_SHIFT         (14)
+#define CDT_WPC_SHIFT          (11)
+#define CDT_RSBC_SHIFT         (30)
+#define CDT_RPC_SHIFT          (27)
+#define CDT_WPC_1_SHIFT                (12)
+#define CDT_RPC_1_SHIFT                (28)
+#define CDT_WPTR_SHIFT         (0)
+#define CDT_SYNC_WSTS_MASK     (0x0000f000)
+#define CDT_SYNC_WSTS_SHIFT    (12)
+#define CDT_CTRL_ASYNC_WSTS_MASK       (0x0000f000)
+#define CDT_CTRL_ASYNC_WSTS_SHIFT      (12)
+#define CDT_ISOC_WSTS_MASK     (0x0000e000)
+#define CDT_ISOC_WSTS_SHIFT    (13)
+#define CDT_RPTR_SHIFT         (16)
+#define CDT_SYNC_RSTS_MASK     (0xf0000000)
+#define CDT_SYNC_RSTS_SHIFT    (28)
+#define CDT_CTRL_ASYNC_RSTS_MASK       (0xf0000000)
+#define CDT_CTRL_ASYNC_RSTS_SHIFT      (28)
+#define CDT_ISOC_RSTS_MASK     (0xe0000000)
+#define CDT_ISOC_RSTS_SHIFT    (29)
+#define CDT_CTRL_ASYNC_WSTS_1  (0x1 << 14)
+#define CDT_CTRL_ASYNC_RSTS_1  (0x1 << 15)
+#define CDT_BD_SHIFT           (0)
+#define CDT_BA_SHIFT           (16)
+#define CDT_BS_SHIFT           (0)
+#define CDT_BF_SHIFT           (31)
+
+#define ADT_PG                 (0x1 << 13)
+#define ADT_LE                 (0x1 << 14)
+#define ADT_CE                 (0x1 << 15)
+#define ADT_BD1_SHIFT          (0)
+#define ADT_ERR1               (0x1 << 13)
+#define ADT_DNE1               (0x1 << 14)
+#define ADT_RDY1               (0x1 << 15)
+#define ADT_BD2_SHIFT          (16)
+#define ADT_ERR2               (0x1 << 29)
+#define ADT_DNE2               (0x1 << 30)
+#define ADT_RDY2               (0x1 << 31)
+#define ADT_BA1_SHIFT          (0x0)
+#define ADT_BA2_SHIFT          (0x0)
+#define ADT_PS1                        (0x1 << 12)
+#define ADT_PS2                        (0x1 << 28)
+#define ADT_MEP1               (0x1 << 11)
+#define ADT_MEP2               (0x1 << 27)
+
+#define MLB_MINOR_DEVICES      4
+#define MLB_CONTROL_DEV_NAME   "ctrl"
+#define MLB_ASYNC_DEV_NAME     "async"
+#define MLB_SYNC_DEV_NAME      "sync"
+#define MLB_ISOC_DEV_NAME      "isoc"
+
+#define TX_CHANNEL             0
+#define RX_CHANNEL             1
+
+#define TRANS_RING_NODES       (1 << 3)
+#define MLB_QUIRK_MLB150       (1 << 0)
+
+enum MLB_CTYPE {
+       MLB_CTYPE_SYNC,
+       MLB_CTYPE_CTRL,
+       MLB_CTYPE_ASYNC,
+       MLB_CTYPE_ISOC,
+};
+
+enum CLK_SPEED {
+       CLK_256FS,
+       CLK_512FS,
+       CLK_1024FS,
+       CLK_2048FS,
+       CLK_3072FS,
+       CLK_4096FS,
+       CLK_6144FS,
+       CLK_8192FS,
+};
+
+enum MLB_INDEX {
+       IMX6Q_MLB = 0,
+       IMX6SX_MLB,
+};
+
+struct mlb_ringbuf {
+       s8 *virt_bufs[TRANS_RING_NODES];
+       u32 phy_addrs[TRANS_RING_NODES];
+       s32 head;
+       s32 tail;
+       s32 unit_size;
+       s32 total_size;
+       rwlock_t rb_lock ____cacheline_aligned; /* ring index lock */
+};
+
+struct mlb_channel_info {
+       /* Input MLB channel address */
+       u32 address;
+       /* Internal AHB channel label */
+       u32 cl;
+       /* DBR buf head */
+       u32 dbr_buf_head;
+};
+
+struct mlb_dev_info {
+       /* device node name */
+       const char dev_name[20];
+       /* channel type */
+       const unsigned int channel_type;
+       /* ch fps */
+       enum CLK_SPEED fps;
+       /* channel info for tx/rx */
+       struct mlb_channel_info channels[2];
+       /* ring buffer */
+       u8 *rbuf_base_virt;
+       u32 rbuf_base_phy;
+       struct mlb_ringbuf rx_rbuf;
+       struct mlb_ringbuf tx_rbuf;
+       /* exception event */
+       unsigned long ex_event;
+       /* tx busy indicator */
+       unsigned long tx_busy;
+       /* channel started up or not */
+       atomic_t on;
+       /* device open count */
+       atomic_t opencnt;
+       /* wait queue head for channel */
+       wait_queue_head_t rx_wq;
+       wait_queue_head_t tx_wq;
+       /* TX OK */
+       s32 tx_ok;
+       /* spinlock for event access */
+       spinlock_t event_lock;
+       /*
+        * Block size for isoc mode
+        * This variable can be configured in ioctl
+        */
+       u32 isoc_blksz;
+       /*
+        * Quads number for sync mode
+        * This variable can be confifured in ioctl
+        */
+       u32 sync_quad;
+       /* Buffer depth in cdt */
+       u32 cdt_buf_dep;
+       /* Buffer depth in adt */
+       u32 adt_buf_dep;
+       /* Buffer size to hold data */
+       u32 buf_size;
+};
+
+struct mlb_data {
+       struct mlb_dev_info *devinfo;
+       struct clk *clk_mlb3p;
+       struct clk *clk_mlb6p;
+       struct cdev cdev;
+       struct class *class;    /* device class */
+       dev_t firstdev;
+#ifdef CONFIG_REGULATOR
+       struct regulator *nvcc;
+#endif
+       void __iomem *membase;  /* mlb module base address */
+       struct gen_pool *iram_pool;
+       u32 iram_size;
+       u32 irq_ahb0;
+       u32 irq_ahb1;
+       u32 irq_mlb;
+       u32 quirk_flag;
+};
+
+/*
+ * For optimization, we use fixed channel label for
+ * input channels of each mode
+ * SYNC: CL = 0 for RX, CL = 64 for TX
+ * CTRL: CL = 1 for RX, CL = 65 for TX
+ * ASYNC: CL = 2 for RX, CL = 66 for TX
+ * ISOC: CL = 3 for RX, CL = 67 for TX
+ */
+#define SYNC_RX_CL_AHB0                0
+#define CTRL_RX_CL_AHB0                1
+#define ASYNC_RX_CL_AHB0       2
+#define ISOC_RX_CL_AHB0                3
+#define SYNC_TX_CL_AHB0                4
+#define CTRL_TX_CL_AHB0                5
+#define ASYNC_TX_CL_AHB0       6
+#define ISOC_TX_CL_AHB0                7
+
+#define SYNC_RX_CL_AHB1                32
+#define CTRL_RX_CL_AHB1                33
+#define ASYNC_RX_CL_AHB1       34
+#define ISOC_RX_CL_AHB1                35
+#define SYNC_TX_CL_AHB1                36
+#define CTRL_TX_CL_AHB1                37
+#define ASYNC_TX_CL_AHB1       38
+#define ISOC_TX_CL_AHB1                39
+
+#define SYNC_RX_CL     SYNC_RX_CL_AHB0
+#define CTRL_RX_CL     CTRL_RX_CL_AHB0
+#define ASYNC_RX_CL    ASYNC_RX_CL_AHB0
+#define ISOC_RX_CL     ISOC_RX_CL_AHB0
+
+#define SYNC_TX_CL     SYNC_TX_CL_AHB0
+#define CTRL_TX_CL     CTRL_TX_CL_AHB0
+#define ASYNC_TX_CL    ASYNC_TX_CL_AHB0
+#define ISOC_TX_CL     ISOC_TX_CL_AHB0
+
+static struct mlb_dev_info mlb_devinfo[MLB_MINOR_DEVICES] = {
+       {
+       .dev_name = MLB_SYNC_DEV_NAME,
+       .channel_type = MLB_CTYPE_SYNC,
+       .channels = {
+               [0] = {
+                       .cl = SYNC_TX_CL,
+                       .dbr_buf_head = CH_SYNC_DBR_BUF_OFFSET,
+               },
+               [1] = {
+                       .cl = SYNC_RX_CL,
+                       .dbr_buf_head = CH_SYNC_DBR_BUF_OFFSET
+                                       + CH_SYNC_BUF_SZ,
+               },
+       },
+       .rx_rbuf = {
+               .unit_size = CH_SYNC_BUF_SZ,
+               .rb_lock =
+                       __RW_LOCK_UNLOCKED(mlb_devinfo[0].rx_rbuf.rb_lock),
+       },
+       .tx_rbuf = {
+               .unit_size = CH_SYNC_BUF_SZ,
+               .rb_lock =
+                       __RW_LOCK_UNLOCKED(mlb_devinfo[0].tx_rbuf.rb_lock),
+       },
+       .cdt_buf_dep = CH_SYNC_CDT_BUF_DEP,
+       .adt_buf_dep = CH_SYNC_ADT_BUF_DEP,
+       .buf_size = CH_SYNC_BUF_SZ,
+       .on = ATOMIC_INIT(0),
+       .opencnt = ATOMIC_INIT(0),
+       .event_lock = __SPIN_LOCK_UNLOCKED(mlb_devinfo[0].event_lock),
+       },
+       {
+       .dev_name = MLB_CONTROL_DEV_NAME,
+       .channel_type = MLB_CTYPE_CTRL,
+       .channels = {
+               [0] = {
+                       .cl = CTRL_TX_CL,
+                       .dbr_buf_head = CH_CTRL_DBR_BUF_OFFSET,
+               },
+               [1] = {
+                       .cl = CTRL_RX_CL,
+                       .dbr_buf_head = CH_CTRL_DBR_BUF_OFFSET
+                                       + CH_CTRL_BUF_SZ,
+               },
+       },
+       .rx_rbuf = {
+               .unit_size = CH_CTRL_BUF_SZ,
+               .rb_lock =
+                       __RW_LOCK_UNLOCKED(mlb_devinfo[1].rx_rbuf.rb_lock),
+       },
+       .tx_rbuf = {
+               .unit_size = CH_CTRL_BUF_SZ,
+               .rb_lock =
+                       __RW_LOCK_UNLOCKED(mlb_devinfo[1].tx_rbuf.rb_lock),
+       },
+       .cdt_buf_dep = CH_CTRL_CDT_BUF_DEP,
+       .adt_buf_dep = CH_CTRL_ADT_BUF_DEP,
+       .buf_size = CH_CTRL_BUF_SZ,
+       .on = ATOMIC_INIT(0),
+       .opencnt = ATOMIC_INIT(0),
+       .event_lock = __SPIN_LOCK_UNLOCKED(mlb_devinfo[1].event_lock),
+       },
+       {
+       .dev_name = MLB_ASYNC_DEV_NAME,
+       .channel_type = MLB_CTYPE_ASYNC,
+       .channels = {
+               [0] = {
+                       .cl = ASYNC_TX_CL,
+                       .dbr_buf_head = CH_ASYNC_DBR_BUF_OFFSET,
+               },
+               [1] = {
+                       .cl = ASYNC_RX_CL,
+                       .dbr_buf_head = CH_ASYNC_DBR_BUF_OFFSET
+                                       + CH_ASYNC_BUF_SZ,
+               },
+       },
+       .rx_rbuf = {
+               .unit_size = CH_ASYNC_BUF_SZ,
+               .rb_lock =
+                       __RW_LOCK_UNLOCKED(mlb_devinfo[2].rx_rbuf.rb_lock),
+       },
+       .tx_rbuf = {
+               .unit_size = CH_ASYNC_BUF_SZ,
+               .rb_lock =
+                       __RW_LOCK_UNLOCKED(mlb_devinfo[2].tx_rbuf.rb_lock),
+       },
+       .cdt_buf_dep = CH_ASYNC_CDT_BUF_DEP,
+       .adt_buf_dep = CH_ASYNC_ADT_BUF_DEP,
+       .buf_size = CH_ASYNC_BUF_SZ,
+       .on = ATOMIC_INIT(0),
+       .opencnt = ATOMIC_INIT(0),
+       .event_lock = __SPIN_LOCK_UNLOCKED(mlb_devinfo[2].event_lock),
+       },
+       {
+       .dev_name = MLB_ISOC_DEV_NAME,
+       .channel_type = MLB_CTYPE_ISOC,
+       .channels = {
+               [0] = {
+                       .cl = ISOC_TX_CL,
+                       .dbr_buf_head = CH_ISOC_DBR_BUF_OFFSET,
+               },
+               [1] = {
+                       .cl = ISOC_RX_CL,
+                       .dbr_buf_head = CH_ISOC_DBR_BUF_OFFSET
+                                       + CH_ISOC_BUF_SZ,
+               },
+       },
+       .rx_rbuf = {
+               .unit_size = CH_ISOC_BUF_SZ,
+               .rb_lock =
+                       __RW_LOCK_UNLOCKED(mlb_devinfo[3].rx_rbuf.rb_lock),
+       },
+       .tx_rbuf = {
+               .unit_size = CH_ISOC_BUF_SZ,
+               .rb_lock =
+                       __RW_LOCK_UNLOCKED(mlb_devinfo[3].tx_rbuf.rb_lock),
+       },
+       .cdt_buf_dep = CH_ISOC_CDT_BUF_DEP,
+       .adt_buf_dep = CH_ISOC_ADT_BUF_DEP,
+       .buf_size = CH_ISOC_BUF_SZ,
+       .on = ATOMIC_INIT(0),
+       .opencnt = ATOMIC_INIT(0),
+       .event_lock = __SPIN_LOCK_UNLOCKED(mlb_devinfo[3].event_lock),
+       .isoc_blksz = CH_ISOC_BLK_SIZE_188,
+       },
+};
+
+static void __iomem *mlb_base;
+
+DEFINE_SPINLOCK(ctr_lock);
+
+#ifdef DEBUG
+#define DUMP_REG(reg) pr_debug(#reg": 0x%08x\n", __raw_readl(mlb_base + reg))
+
+static void mlb150_dev_dump_reg(void)
+{
+       pr_debug("mxc_mlb150: Dump registers:\n");
+       DUMP_REG(REG_MLBC0);
+       DUMP_REG(REG_MLBPC0);
+       DUMP_REG(REG_MS0);
+       DUMP_REG(REG_MS1);
+       DUMP_REG(REG_MSS);
+       DUMP_REG(REG_MSD);
+       DUMP_REG(REG_MIEN);
+       DUMP_REG(REG_MLBPC2);
+       DUMP_REG(REG_MLBPC1);
+       DUMP_REG(REG_MLBC1);
+       DUMP_REG(REG_HCTL);
+       DUMP_REG(REG_HCMR0);
+       DUMP_REG(REG_HCMR1);
+       DUMP_REG(REG_HCER0);
+       DUMP_REG(REG_HCER1);
+       DUMP_REG(REG_HCBR0);
+       DUMP_REG(REG_HCBR1);
+       DUMP_REG(REG_MDAT0);
+       DUMP_REG(REG_MDAT1);
+       DUMP_REG(REG_MDAT2);
+       DUMP_REG(REG_MDAT3);
+       DUMP_REG(REG_MDWE0);
+       DUMP_REG(REG_MDWE1);
+       DUMP_REG(REG_MDWE2);
+       DUMP_REG(REG_MDWE3);
+       DUMP_REG(REG_MCTL);
+       DUMP_REG(REG_MADR);
+       DUMP_REG(REG_ACTL);
+       DUMP_REG(REG_ACSR0);
+       DUMP_REG(REG_ACSR1);
+       DUMP_REG(REG_ACMR0);
+       DUMP_REG(REG_ACMR1);
+}
+
+static void mlb150_dev_dump_hex(const u8 *buf, u32 len)
+{
+       print_hex_dump(KERN_DEBUG, "CTR DUMP:",
+                       DUMP_PREFIX_OFFSET, 8, 1, buf, len, 0);
+}
+#endif
+
+static inline void mlb150_dev_enable_ctr_write(u32 mdat0_bits_en,
+               u32 mdat1_bits_en, u32 mdat2_bits_en, u32 mdat3_bits_en)
+{
+       __raw_writel(mdat0_bits_en, mlb_base + REG_MDWE0);
+       __raw_writel(mdat1_bits_en, mlb_base + REG_MDWE1);
+       __raw_writel(mdat2_bits_en, mlb_base + REG_MDWE2);
+       __raw_writel(mdat3_bits_en, mlb_base + REG_MDWE3);
+}
+
+#ifdef DEBUG
+static inline u8 mlb150_dev_dbr_read(u32 dbr_addr)
+{
+       s32 timeout = 1000;
+       u8  dbr_val = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ctr_lock, flags);
+       __raw_writel(MADR_TB | dbr_addr,
+               mlb_base + REG_MADR);
+
+       while ((!(__raw_readl(mlb_base + REG_MCTL)
+                       & MCTL_XCMP)) &&
+                       timeout--)
+               ;
+
+       if (0 == timeout) {
+               spin_unlock_irqrestore(&ctr_lock, flags);
+               return -ETIME;
+       }
+
+       dbr_val = __raw_readl(mlb_base + REG_MDAT0) & 0x000000ff;
+
+       __raw_writel(0, mlb_base + REG_MCTL);
+       spin_unlock_irqrestore(&ctr_lock, flags);
+
+       return dbr_val;
+}
+
+static inline s32 mlb150_dev_dbr_write(u32 dbr_addr, u8 dbr_val)
+{
+       s32 timeout = 1000;
+       u32 mdat0 = dbr_val & 0x000000ff;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ctr_lock, flags);
+       __raw_writel(mdat0, mlb_base + REG_MDAT0);
+
+       __raw_writel(MADR_WNR | MADR_TB | dbr_addr,
+                       mlb_base + REG_MADR);
+
+       while ((!(__raw_readl(mlb_base + REG_MCTL)
+                       & MCTL_XCMP)) &&
+                       timeout--)
+               ;
+
+       if (timeout <= 0) {
+               spin_unlock_irqrestore(&ctr_lock, flags);
+               return -ETIME;
+       }
+
+       __raw_writel(0, mlb_base + REG_MCTL);
+       spin_unlock_irqrestore(&ctr_lock, flags);
+
+       return 0;
+}
+
+static inline s32 mlb150_dev_dbr_dump(u32 addr, u32 size)
+{
+       u8 *dump_buf = NULL;
+       u8 *buf_ptr = NULL;
+       s32 i;
+
+       dump_buf = kzalloc(size, GFP_KERNEL);
+       if (!dump_buf) {
+               pr_err("can't allocate enough memory\n");
+               return -ENOMEM;
+       }
+
+       for (i = 0, buf_ptr = dump_buf;
+                       i < size; ++i, ++buf_ptr)
+               *buf_ptr = mlb150_dev_dbr_read(addr + i);
+
+       mlb150_dev_dump_hex(dump_buf, size);
+
+       kfree(dump_buf);
+
+       return 0;
+}
+#endif
+
+static s32 mlb150_dev_ctr_read(u32 ctr_offset, u32 *ctr_val)
+{
+       s32 timeout = 1000;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ctr_lock, flags);
+       __raw_writel(ctr_offset, mlb_base + REG_MADR);
+
+       while ((!(__raw_readl(mlb_base + REG_MCTL)
+                       & MCTL_XCMP)) &&
+                       timeout--)
+               ;
+
+       if (timeout <= 0) {
+               spin_unlock_irqrestore(&ctr_lock, flags);
+               pr_debug("mxc_mlb150: Read CTR timeout\n");
+               return -ETIME;
+       }
+
+       ctr_val[0] = __raw_readl(mlb_base + REG_MDAT0);
+       ctr_val[1] = __raw_readl(mlb_base + REG_MDAT1);
+       ctr_val[2] = __raw_readl(mlb_base + REG_MDAT2);
+       ctr_val[3] = __raw_readl(mlb_base + REG_MDAT3);
+
+       __raw_writel(0, mlb_base + REG_MCTL);
+
+       spin_unlock_irqrestore(&ctr_lock, flags);
+
+       return 0;
+}
+
+static s32 mlb150_dev_ctr_write(u32 ctr_offset, const u32 *ctr_val)
+{
+       s32 timeout = 1000;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ctr_lock, flags);
+
+       __raw_writel(ctr_val[0], mlb_base + REG_MDAT0);
+       __raw_writel(ctr_val[1], mlb_base + REG_MDAT1);
+       __raw_writel(ctr_val[2], mlb_base + REG_MDAT2);
+       __raw_writel(ctr_val[3], mlb_base + REG_MDAT3);
+
+       __raw_writel(MADR_WNR | ctr_offset,
+                       mlb_base + REG_MADR);
+
+       while ((!(__raw_readl(mlb_base + REG_MCTL)
+                       & MCTL_XCMP)) &&
+                       timeout--)
+               ;
+
+       if (timeout <= 0) {
+               spin_unlock_irqrestore(&ctr_lock, flags);
+               pr_debug("mxc_mlb150: Write CTR timeout\n");
+               return -ETIME;
+       }
+
+       __raw_writel(0, mlb_base + REG_MCTL);
+
+       spin_unlock_irqrestore(&ctr_lock, flags);
+
+#ifdef DEBUG_CTR
+       {
+               u32 ctr_rd[4] = { 0 };
+
+               if (!mlb150_dev_ctr_read(ctr_offset, ctr_rd)) {
+                       if (ctr_val[0] == ctr_rd[0] &&
+                               ctr_val[1] == ctr_rd[1] &&
+                               ctr_val[2] == ctr_rd[2] &&
+                               ctr_val[3] == ctr_rd[3])
+                               return 0;
+                       else {
+                               pr_debug("mxc_mlb150: ctr write failed\n");
+                               pr_debug("offset: 0x%x\n", ctr_offset);
+                               pr_debug("Write: 0x%x 0x%x 0x%x 0x%x\n",
+                                               ctr_val[3], ctr_val[2],
+                                               ctr_val[1], ctr_val[0]);
+                               pr_debug("Read: 0x%x 0x%x 0x%x 0x%x\n",
+                                               ctr_rd[3], ctr_rd[2],
+                                               ctr_rd[1], ctr_rd[0]);
+                               return -EBADE;
+                       }
+               } else {
+                       pr_debug("mxc_mlb150: ctr read failed\n");
+                       return -EBADE;
+               }
+       }
+#endif
+
+       return 0;
+}
+
+#ifdef DEBUG
+static s32 mlb150_dev_cat_read(u32 ctr_offset, u32 ch, u16 *cat_val)
+{
+       u16 ctr_val[8] = { 0 };
+
+       if (mlb150_dev_ctr_read(ctr_offset, (u32 *)ctr_val))
+               return -ETIME;
+
+       /*
+        * Use u16 array to get u32 array value,
+        * need to convert
+        */
+       cat_val = ctr_val[ch % 8];
+
+        return 0;
+}
+#endif
+
+static s32 mlb150_dev_cat_write(u32 ctr_offset, u32 ch, const u16 cat_val)
+{
+       u16 ctr_val[8] = { 0 };
+
+       if (mlb150_dev_ctr_read(ctr_offset, (u32 *)ctr_val))
+               return -ETIME;
+
+       ctr_val[ch % 8] = cat_val;
+       if (mlb150_dev_ctr_write(ctr_offset, (u32 *)ctr_val))
+               return -ETIME;
+
+       return 0;
+}
+
+#define mlb150_dev_cat_mlb_read(ch, cat_val)   \
+       mlb150_dev_cat_read(BUF_CAT_MLB_OFFSET + (ch >> 3), ch, cat_val)
+#define mlb150_dev_cat_mlb_write(ch, cat_val)  \
+       mlb150_dev_cat_write(BUF_CAT_MLB_OFFSET + (ch >> 3), ch, cat_val)
+#define mlb150_dev_cat_hbi_read(ch, cat_val)   \
+       mlb150_dev_cat_read(BUF_CAT_HBI_OFFSET + (ch >> 3), ch, cat_val)
+#define mlb150_dev_cat_hbi_write(ch, cat_val)  \
+       mlb150_dev_cat_write(BUF_CAT_HBI_OFFSET + (ch >> 3), ch, cat_val)
+
+#define mlb150_dev_cdt_read(ch, cdt_val)       \
+       mlb150_dev_ctr_read(BUF_CDT_OFFSET + ch, cdt_val)
+#define mlb150_dev_cdt_write(ch, cdt_val)      \
+       mlb150_dev_ctr_write(BUF_CDT_OFFSET + ch, cdt_val)
+#define mlb150_dev_adt_read(ch, adt_val)       \
+       mlb150_dev_ctr_read(BUF_ADT_OFFSET + ch, adt_val)
+#define mlb150_dev_adt_write(ch, adt_val)      \
+       mlb150_dev_ctr_write(BUF_ADT_OFFSET + ch, adt_val)
+
+static s32 mlb150_dev_get_adt_sts(u32 ch)
+{
+       s32 timeout = 1000;
+       unsigned long flags;
+       u32 reg;
+
+       spin_lock_irqsave(&ctr_lock, flags);
+       __raw_writel(BUF_ADT_OFFSET + ch,
+                       mlb_base + REG_MADR);
+
+       while ((!(__raw_readl(mlb_base + REG_MCTL)
+                       & MCTL_XCMP)) &&
+                       timeout--)
+               ;
+
+       if (timeout <= 0) {
+               spin_unlock_irqrestore(&ctr_lock, flags);
+               pr_debug("mxc_mlb150: Read CTR timeout\n");
+               return -ETIME;
+       }
+
+       reg = __raw_readl(mlb_base + REG_MDAT1);
+
+       __raw_writel(0, mlb_base + REG_MCTL);
+       spin_unlock_irqrestore(&ctr_lock, flags);
+
+#ifdef DEBUG_ADT
+       pr_debug("mxc_mlb150: Get ch %d adt sts: 0x%08x\n", ch, reg);
+#endif
+
+       return reg;
+}
+
+#ifdef DEBUG
+static void mlb150_dev_dump_ctr_tbl(u32 ch_start, u32 ch_end)
+{
+       u32 i = 0;
+       u32 ctr_val[4] = { 0 };
+
+       pr_debug("mxc_mlb150: CDT Table");
+       for (i = BUF_CDT_OFFSET + ch_start;
+                       i < BUF_CDT_OFFSET + ch_end;
+                       ++i) {
+               mlb150_dev_ctr_read(i, ctr_val);
+               pr_debug("CTR 0x%02x: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
+                       i, ctr_val[3], ctr_val[2], ctr_val[1], ctr_val[0]);
+       }
+
+       pr_debug("mxc_mlb150: ADT Table");
+       for (i = BUF_ADT_OFFSET + ch_start;
+                       i < BUF_ADT_OFFSET + ch_end;
+                       ++i) {
+               mlb150_dev_ctr_read(i, ctr_val);
+               pr_debug("CTR 0x%02x: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
+                       i, ctr_val[3], ctr_val[2], ctr_val[1], ctr_val[0]);
+       }
+
+       pr_debug("mxc_mlb150: CAT MLB Table");
+       for (i = BUF_CAT_MLB_OFFSET + (ch_start >> 3);
+                       i <= BUF_CAT_MLB_OFFSET + ((ch_end + 8) >> 3);
+                       ++i) {
+               mlb150_dev_ctr_read(i, ctr_val);
+               pr_debug("CTR 0x%02x: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
+                       i, ctr_val[3], ctr_val[2], ctr_val[1], ctr_val[0]);
+       }
+
+       pr_debug("mxc_mlb150: CAT HBI Table");
+       for (i = BUF_CAT_HBI_OFFSET + (ch_start >> 3);
+                       i <= BUF_CAT_HBI_OFFSET + ((ch_end + 8) >> 3);
+                       ++i) {
+               mlb150_dev_ctr_read(i, ctr_val);
+               pr_debug("CTR 0x%02x: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
+                       i, ctr_val[3], ctr_val[2], ctr_val[1], ctr_val[0]);
+       }
+}
+#endif
+
+/*
+ * Initial the MLB module device
+ */
+static inline void  mlb150_dev_enable_dma_irq(u32 enable)
+{
+       u32 ch_rx_mask = (1 << SYNC_RX_CL_AHB0) | (1 << CTRL_RX_CL_AHB0)
+                       | (1 << ASYNC_RX_CL_AHB0) | (1 << ISOC_RX_CL_AHB0)
+                       | (1 << SYNC_TX_CL_AHB0) | (1 << CTRL_TX_CL_AHB0)
+                       | (1 << ASYNC_TX_CL_AHB0) | (1 << ISOC_TX_CL_AHB0);
+       u32 ch_tx_mask = (1 << (SYNC_RX_CL_AHB1 - INT_AHB1_CH_START)) |
+                       (1 << (CTRL_RX_CL_AHB1 - INT_AHB1_CH_START)) |
+                       (1 << (ASYNC_RX_CL_AHB1 - INT_AHB1_CH_START)) |
+                       (1 << (ISOC_RX_CL_AHB1 - INT_AHB1_CH_START)) |
+                       (1 << (SYNC_TX_CL_AHB1 - INT_AHB1_CH_START)) |
+                       (1 << (CTRL_TX_CL_AHB1 - INT_AHB1_CH_START)) |
+                       (1 << (ASYNC_TX_CL_AHB1 - INT_AHB1_CH_START)) |
+                       (1 << (ISOC_TX_CL_AHB1 - INT_AHB1_CH_START));
+
+       if (enable) {
+               __raw_writel(ch_rx_mask, mlb_base + REG_ACMR0);
+               __raw_writel(ch_tx_mask, mlb_base + REG_ACMR1);
+       } else {
+               __raw_writel(0x0, mlb_base + REG_ACMR0);
+               __raw_writel(0x0, mlb_base + REG_ACMR1);
+       }
+}
+
+
+static void mlb150_dev_init_ir_amba_ahb(void)
+{
+       u32 reg = 0;
+
+       /*
+        * Step 1. Program the ACMRn registers to enable interrupts from all
+        * active DMA channels
+        */
+       mlb150_dev_enable_dma_irq(1);
+
+       /*
+        * Step 2. Select the status clear method:
+        * ACTL.SCE = 0, hardware clears on read
+        * ACTL.SCE = 1, software writes a '1' to clear
+        * We only support DMA MODE 1
+        */
+       reg = __raw_readl(mlb_base + REG_ACTL);
+       reg |= ACTL_DMAMODE;
+#ifdef MULTIPLE_PACKAGE_MODE
+       reg |= REG_ACTL_MPB;
+#endif
+
+       /*
+        *  Step 3. Select 1 or 2 interrupt signals:
+        * ACTL.SMX = 0: one interrupt for channels 0 - 31 on ahb_init[0]
+        *      and another interrupt for channels 32 - 63 on ahb_init[1]
+        * ACTL.SMX = 1: singel interrupt all channels on ahb_init[0]
+        */
+       reg &= ~ACTL_SMX;
+
+       __raw_writel(reg, mlb_base + REG_ACTL);
+}
+
+static inline void mlb150_dev_enable_ir_mlb(u32 enable)
+{
+       /*
+        * Step 1, Select the MSn to be cleared by software,
+        * writing a '0' to the appropriate bits
+        */
+       __raw_writel(0, mlb_base + REG_MS0);
+       __raw_writel(0, mlb_base + REG_MS1);
+
+       /*
+        * Step 1, Program MIEN to enable protocol error
+        * interrupts for all active MLB channels
+        */
+       if (enable)
+               __raw_writel(MIEN_CTX_PE |
+                       MIEN_CRX_PE | MIEN_ATX_PE |
+                       MIEN_ARX_PE | MIEN_SYNC_PE |
+                       MIEN_ISOC_PE,
+                       mlb_base + REG_MIEN);
+       else
+               __raw_writel(0, mlb_base + REG_MIEN);
+}
+
+static inline void mlb150_enable_pll(struct mlb_data *drvdata)
+{
+       u32 c0_val;
+
+       __raw_writel(MLBPC1_VAL,
+                       drvdata->membase + REG_MLBPC1);
+
+       c0_val = __raw_readl(drvdata->membase + REG_MLBC0);
+       if (c0_val & MLBC0_MLBPEN) {
+               c0_val &= ~MLBC0_MLBPEN;
+               __raw_writel(c0_val,
+                               drvdata->membase + REG_MLBC0);
+       }
+
+       clk_prepare_enable(drvdata->clk_mlb6p);
+
+       c0_val |= (MLBC0_MLBPEN);
+       __raw_writel(c0_val, drvdata->membase + REG_MLBC0);
+}
+
+static inline void mlb150_disable_pll(struct mlb_data *drvdata)
+{
+       u32 c0_val;
+
+       clk_disable_unprepare(drvdata->clk_mlb6p);
+
+       c0_val = __raw_readl(drvdata->membase + REG_MLBC0);
+
+       __raw_writel(0x0, drvdata->membase + REG_MLBPC1);
+
+       c0_val &= ~MLBC0_MLBPEN;
+       __raw_writel(c0_val, drvdata->membase + REG_MLBC0);
+}
+
+static void mlb150_dev_reset_cdt(void)
+{
+       int i = 0;
+       u32 ctr_val[4] = { 0 };
+
+       mlb150_dev_enable_ctr_write(0xffffffff, 0xffffffff,
+                       0xffffffff, 0xffffffff);
+
+       for (i = 0; i < (LOGIC_CH_NUM); ++i)
+               mlb150_dev_ctr_write(BUF_CDT_OFFSET + i, ctr_val);
+}
+
+static s32 mlb150_dev_init_ch_cdt(struct mlb_dev_info *pdevinfo, u32 ch,
+               enum MLB_CTYPE ctype, u32 ch_func)
+{
+       u32 cdt_val[4] = { 0 };
+
+       /* a. Set the 14-bit base address (BA) */
+       pr_debug("mxc_mlb150: ctype: %d, ch: %d, dbr_buf_head: 0x%08x",
+               ctype, ch, pdevinfo->channels[ch_func].dbr_buf_head);
+       cdt_val[3] = (pdevinfo->channels[ch_func].dbr_buf_head)
+                       << CDT_BA_SHIFT;
+       /*
+        * b. Set the 12-bit or 13-bit buffer depth (BD)
+        * BD = buffer depth in bytes - 1
+        * For synchronous channels: (BD + 1) = 4 * m * bpf
+        * For control channels: (BD + 1) >= max packet length (64)
+        * For asynchronous channels: (BD + 1) >= max packet length
+        * 1024 for a MOST Data packet (MDP);
+        * 1536 for a MOST Ethernet Packet (MEP)
+        * For isochronous channels: (BD + 1) mod (BS + 1) = 0
+        * BS
+        */
+       if (MLB_CTYPE_ISOC == ctype)
+               cdt_val[1] |= (pdevinfo->isoc_blksz - 1);
+       /* BD */
+       cdt_val[3] |= (pdevinfo->cdt_buf_dep - 1) << CDT_BD_SHIFT;
+
+       pr_debug("mxc_mlb150: Set CDT val of channel %d, type: %d: "
+               "0x%08x 0x%08x 0x%08x 0x%08x\n",
+               ch, ctype, cdt_val[3], cdt_val[2], cdt_val[1], cdt_val[0]);
+
+       if (mlb150_dev_cdt_write(ch, cdt_val))
+               return -ETIME;
+
+#ifdef DEBUG_CTR
+       {
+               u32 cdt_rd[4] = { 0 };
+               if (!mlb150_dev_cdt_read(ch, cdt_rd)) {
+                       pr_debug("mxc_mlb150: CDT val of channel %d: "
+                               "0x%08x 0x%08x 0x%08x 0x%08x\n",
+                               ch, cdt_rd[3], cdt_rd[2], cdt_rd[1], cdt_rd[0]);
+                       if (cdt_rd[3] == cdt_val[3] &&
+                               cdt_rd[2] == cdt_val[2] &&
+                               cdt_rd[1] == cdt_val[1] &&
+                               cdt_rd[0] == cdt_val[0]) {
+                               pr_debug("mxc_mlb150: set cdt succeed!\n");
+                               return 0;
+                       } else {
+                               pr_debug("mxc_mlb150: set cdt failed!\n");
+                               return -EBADE;
+                       }
+               } else {
+                       pr_debug("mxc_mlb150: Read CDT val of channel %d failed\n",
+                                       ch);
+                       return -EBADE;
+               }
+       }
+#endif
+
+       return 0;
+}
+
+static s32 mlb150_dev_init_ch_cat(u32 ch, u32 cl,
+               u32 cat_mode, enum MLB_CTYPE ctype)
+{
+       u16 cat_val = 0;
+#ifdef DEBUG_CTR
+       u16 cat_rd = 0;
+#endif
+
+       cat_val = CAT_CE | (ctype << CAT_CT_SHIFT) | cl;
+
+       if (cat_mode & CAT_MODE_OUTBOUND_DMA)
+               cat_val |= CAT_RNW;
+
+       if (MLB_CTYPE_SYNC == ctype)
+               cat_val |= CAT_MT;
+
+       switch (cat_mode) {
+       case CAT_MODE_RX | CAT_MODE_INBOUND_DMA:
+       case CAT_MODE_TX | CAT_MODE_OUTBOUND_DMA:
+               pr_debug("mxc_mlb150: set CAT val of channel %d, type: %d: 0x%04x\n",
+                       ch, ctype, cat_val);
+
+               if (mlb150_dev_cat_mlb_write(ch, cat_val))
+                       return -ETIME;
+#ifdef DEBUG_CTR
+               if (!mlb150_dev_cat_mlb_read(ch, &cat_rd))
+                       pr_debug("mxc_mlb150: CAT val of mlb channel %d: 0x%04x",
+                                       ch, cat_rd);
+               else {
+                       pr_debug("mxc_mlb150: Read CAT of mlb channel %d failed\n",
+                                       ch);
+                               return -EBADE;
+               }
+#endif
+               break;
+       case CAT_MODE_TX | CAT_MODE_INBOUND_DMA:
+       case CAT_MODE_RX | CAT_MODE_OUTBOUND_DMA:
+               pr_debug("mxc_mlb150: set CAT val of channel %d, type: %d: 0x%04x\n",
+                       cl, ctype, cat_val);
+
+               if (mlb150_dev_cat_hbi_write(cl, cat_val))
+                       return -ETIME;
+#ifdef DEBUG_CTR
+               if (!mlb150_dev_cat_hbi_read(cl, &cat_rd))
+                       pr_debug("mxc_mlb150: CAT val of hbi channel %d: 0x%04x",
+                                       cl, cat_rd);
+               else {
+                       pr_debug("mxc_mlb150: Read CAT of hbi channel %d failed\n",
+                                       cl);
+                               return -EBADE;
+               }
+#endif
+               break;
+       default:
+               return EBADRQC;
+       }
+
+#ifdef DEBUG_CTR
+       {
+               if (cat_val == cat_rd) {
+                       pr_debug("mxc_mlb150: set cat succeed!\n");
+                       return 0;
+               } else {
+                       pr_debug("mxc_mlb150: set cat failed!\n");
+                       return -EBADE;
+               }
+       }
+#endif
+       return 0;
+}
+
+static void mlb150_dev_reset_cat(void)
+{
+       int i = 0;
+       u32 ctr_val[4] = { 0 };
+
+       mlb150_dev_enable_ctr_write(0xffffffff, 0xffffffff,
+                       0xffffffff, 0xffffffff);
+
+       for (i = 0; i < (LOGIC_CH_NUM >> 3); ++i) {
+               mlb150_dev_ctr_write(BUF_CAT_MLB_OFFSET + i, ctr_val);
+               mlb150_dev_ctr_write(BUF_CAT_HBI_OFFSET + i, ctr_val);
+       }
+}
+
+static void mlb150_dev_init_rfb(struct mlb_dev_info *pdevinfo, u32 rx_ch,
+               u32 tx_ch, enum MLB_CTYPE ctype)
+{
+       u32 rx_cl = pdevinfo->channels[RX_CHANNEL].cl;
+       u32 tx_cl = pdevinfo->channels[TX_CHANNEL].cl;
+       /* Step 1, Initialize all bits of CAT to '0' */
+       mlb150_dev_reset_cat();
+       mlb150_dev_reset_cdt();
+       /*
+        * Step 2, Initialize logical channel
+        * Step 3, Program the CDT for channel N
+        */
+       mlb150_dev_init_ch_cdt(pdevinfo, rx_cl, ctype, RX_CHANNEL);
+       mlb150_dev_init_ch_cdt(pdevinfo, tx_cl, ctype, TX_CHANNEL);
+
+       /* Step 4&5, Program the CAT for the inbound and outbound DMA */
+       mlb150_dev_init_ch_cat(rx_ch, rx_cl,
+                       CAT_MODE_RX | CAT_MODE_INBOUND_DMA,
+                       ctype);
+       mlb150_dev_init_ch_cat(rx_ch, rx_cl,
+                       CAT_MODE_RX | CAT_MODE_OUTBOUND_DMA,
+                       ctype);
+       mlb150_dev_init_ch_cat(tx_ch, tx_cl,
+                       CAT_MODE_TX | CAT_MODE_INBOUND_DMA,
+                       ctype);
+       mlb150_dev_init_ch_cat(tx_ch, tx_cl,
+                       CAT_MODE_TX | CAT_MODE_OUTBOUND_DMA,
+                       ctype);
+}
+
+static void mlb150_dev_reset_adt(void)
+{
+       int i = 0;
+       u32 ctr_val[4] = { 0 };
+
+       mlb150_dev_enable_ctr_write(0xffffffff, 0xffffffff,
+                       0xffffffff, 0xffffffff);
+
+       for (i = 0; i < (LOGIC_CH_NUM); ++i)
+               mlb150_dev_ctr_write(BUF_ADT_OFFSET + i, ctr_val);
+}
+
+static void mlb150_dev_reset_whole_ctr(void)
+{
+       mlb150_dev_enable_ctr_write(0xffffffff, 0xffffffff,
+                       0xffffffff, 0xffffffff);
+       mlb150_dev_reset_cdt();
+       mlb150_dev_reset_adt();
+       mlb150_dev_reset_cat();
+}
+
+#define CLR_REG(reg)  __raw_writel(0x0, mlb_base + reg)
+
+static void mlb150_dev_reset_all_regs(void)
+{
+       CLR_REG(REG_MLBC0);
+       CLR_REG(REG_MLBPC0);
+       CLR_REG(REG_MS0);
+       CLR_REG(REG_MS1);
+       CLR_REG(REG_MSS);
+       CLR_REG(REG_MSD);
+       CLR_REG(REG_MIEN);
+       CLR_REG(REG_MLBPC2);
+       CLR_REG(REG_MLBPC1);
+       CLR_REG(REG_MLBC1);
+       CLR_REG(REG_HCTL);
+       CLR_REG(REG_HCMR0);
+       CLR_REG(REG_HCMR1);
+       CLR_REG(REG_HCER0);
+       CLR_REG(REG_HCER1);
+       CLR_REG(REG_HCBR0);
+       CLR_REG(REG_HCBR1);
+       CLR_REG(REG_MDAT0);
+       CLR_REG(REG_MDAT1);
+       CLR_REG(REG_MDAT2);
+       CLR_REG(REG_MDAT3);
+       CLR_REG(REG_MDWE0);
+       CLR_REG(REG_MDWE1);
+       CLR_REG(REG_MDWE2);
+       CLR_REG(REG_MDWE3);
+       CLR_REG(REG_MCTL);
+       CLR_REG(REG_MADR);
+       CLR_REG(REG_ACTL);
+       CLR_REG(REG_ACSR0);
+       CLR_REG(REG_ACSR1);
+       CLR_REG(REG_ACMR0);
+       CLR_REG(REG_ACMR1);
+}
+
+static inline s32 mlb150_dev_pipo_start(struct mlb_ringbuf *rbuf,
+                                               u32 ahb_ch, u32 buf_addr)
+{
+       u32 ctr_val[4] = { 0 };
+
+       ctr_val[1] |= ADT_RDY1;
+       ctr_val[2] = buf_addr;
+
+       if (mlb150_dev_adt_write(ahb_ch, ctr_val))
+               return -ETIME;
+
+       return 0;
+}
+
+static inline s32 mlb150_dev_pipo_next(u32 ahb_ch, enum MLB_CTYPE ctype,
+                               u32 dne_sts, u32 buf_addr)
+{
+       u32 ctr_val[4] = { 0 };
+
+       if (MLB_CTYPE_ASYNC == ctype ||
+               MLB_CTYPE_CTRL == ctype) {
+               ctr_val[1] |= ADT_PS1;
+               ctr_val[1] |= ADT_PS2;
+       }
+
+       /*
+        * Clear DNE1 and ERR1
+        * Set the page ready bit (RDY1)
+        */
+       if (dne_sts & ADT_DNE1) {
+               ctr_val[1] |= ADT_RDY2;
+               ctr_val[3] = buf_addr;
+       } else {
+               ctr_val[1] |= ADT_RDY1;
+               ctr_val[2] = buf_addr;
+       }
+
+       if (mlb150_dev_adt_write(ahb_ch, ctr_val))
+               return -ETIME;
+
+       return 0;
+}
+
+static inline s32 mlb150_dev_pipo_stop(struct mlb_ringbuf *rbuf, u32 ahb_ch)
+{
+       u32 ctr_val[4] = { 0 };
+       unsigned long flags;
+
+       write_lock_irqsave(&rbuf->rb_lock, flags);
+       rbuf->head = rbuf->tail = 0;
+       write_unlock_irqrestore(&rbuf->rb_lock, flags);
+
+       if (mlb150_dev_adt_write(ahb_ch, ctr_val))
+               return -ETIME;
+
+       return 0;
+}
+
+static s32 mlb150_dev_init_ch_amba_ahb(struct mlb_dev_info *pdevinfo,
+                                       struct mlb_channel_info *chinfo,
+                                       enum MLB_CTYPE ctype)
+{
+       u32 ctr_val[4] = { 0 };
+
+       /* a. Set the 32-bit base address (BA1) */
+       ctr_val[3] = 0;
+       ctr_val[2] = 0;
+       ctr_val[1] = (pdevinfo->adt_buf_dep - 1) << ADT_BD1_SHIFT;
+       ctr_val[1] |= (pdevinfo->adt_buf_dep - 1) << ADT_BD2_SHIFT;
+       if (MLB_CTYPE_ASYNC == ctype ||
+               MLB_CTYPE_CTRL == ctype) {
+               ctr_val[1] |= ADT_PS1;
+               ctr_val[1] |= ADT_PS2;
+       }
+
+       ctr_val[0] |= (ADT_LE | ADT_CE);
+
+       pr_debug("mxc_mlb150: Set ADT val of channel %d, ctype: %d: "
+               "0x%08x 0x%08x 0x%08x 0x%08x\n",
+               chinfo->cl, ctype, ctr_val[3], ctr_val[2],
+               ctr_val[1], ctr_val[0]);
+
+       if (mlb150_dev_adt_write(chinfo->cl, ctr_val))
+               return -ETIME;
+
+#ifdef DEBUG_CTR
+       {
+               u32 ctr_rd[4] = { 0 };
+               if (!mlb150_dev_adt_read(chinfo->cl, ctr_rd)) {
+                       pr_debug("mxc_mlb150: ADT val of channel %d: "
+                               "0x%08x 0x%08x 0x%08x 0x%08x\n",
+                               chinfo->cl, ctr_rd[3], ctr_rd[2],
+                               ctr_rd[1], ctr_rd[0]);
+                       if (ctr_rd[3] == ctr_val[3] &&
+                               ctr_rd[2] == ctr_val[2] &&
+                               ctr_rd[1] == ctr_val[1] &&
+                               ctr_rd[0] == ctr_val[0]) {
+                               pr_debug("mxc_mlb150: set adt succeed!\n");
+                               return 0;
+                       } else {
+                               pr_debug("mxc_mlb150: set adt failed!\n");
+                               return -EBADE;
+                       }
+               } else {
+                       pr_debug("mxc_mlb150: Read ADT val of channel %d failed\n",
+                                       chinfo->cl);
+                       return -EBADE;
+               }
+       }
+#endif
+
+       return 0;
+}
+
+static void mlb150_dev_init_amba_ahb(struct mlb_dev_info *pdevinfo,
+                                       enum MLB_CTYPE ctype)
+{
+       struct mlb_channel_info *tx_chinfo = &pdevinfo->channels[TX_CHANNEL];
+       struct mlb_channel_info *rx_chinfo = &pdevinfo->channels[RX_CHANNEL];
+
+       /* Step 1, Initialize all bits of the ADT to '0' */
+       mlb150_dev_reset_adt();
+
+       /*
+        * Step 2, Select a logic channel
+        * Step 3, Program the AMBA AHB block ping page for channel N
+        * Step 4, Program the AMBA AHB block pong page for channel N
+        */
+       mlb150_dev_init_ch_amba_ahb(pdevinfo, rx_chinfo, ctype);
+       mlb150_dev_init_ch_amba_ahb(pdevinfo, tx_chinfo, ctype);
+}
+
+static void mlb150_dev_exit(void)
+{
+       u32 c0_val, hctl_val;
+
+       /* Disable EN bits */
+       c0_val = __raw_readl(mlb_base + REG_MLBC0);
+       c0_val &= ~(MLBC0_MLBEN | MLBC0_MLBPEN);
+       __raw_writel(c0_val, mlb_base + REG_MLBC0);
+
+       hctl_val = __raw_readl(mlb_base + REG_HCTL);
+       hctl_val &= ~HCTL_EN;
+       __raw_writel(hctl_val, mlb_base + REG_HCTL);
+
+       __raw_writel(0x0, mlb_base + REG_HCMR0);
+       __raw_writel(0x0, mlb_base + REG_HCMR1);
+
+       mlb150_dev_enable_dma_irq(0);
+       mlb150_dev_enable_ir_mlb(0);
+}
+
+static void mlb150_dev_init(void)
+{
+       u32 c0_val;
+       u32 ch_rx_mask = (1 << SYNC_RX_CL_AHB0) | (1 << CTRL_RX_CL_AHB0)
+                       | (1 << ASYNC_RX_CL_AHB0) | (1 << ISOC_RX_CL_AHB0)
+                       | (1 << SYNC_TX_CL_AHB0) | (1 << CTRL_TX_CL_AHB0)
+                       | (1 << ASYNC_TX_CL_AHB0) | (1 << ISOC_TX_CL_AHB0);
+       u32 ch_tx_mask = (1 << (SYNC_RX_CL_AHB1 - INT_AHB1_CH_START)) |
+                       (1 << (CTRL_RX_CL_AHB1 - INT_AHB1_CH_START)) |
+                       (1 << (ASYNC_RX_CL_AHB1 - INT_AHB1_CH_START)) |
+                       (1 << (ISOC_RX_CL_AHB1 - INT_AHB1_CH_START)) |
+                       (1 << (SYNC_TX_CL_AHB1 - INT_AHB1_CH_START)) |
+                       (1 << (CTRL_TX_CL_AHB1 - INT_AHB1_CH_START)) |
+                       (1 << (ASYNC_TX_CL_AHB1 - INT_AHB1_CH_START)) |
+                       (1 << (ISOC_TX_CL_AHB1 - INT_AHB1_CH_START));
+
+       /* Disable EN bits */
+       mlb150_dev_exit();
+
+       /*
+        * Step 1. Initialize CTR and registers
+        * a. Set all bit of the CTR (CAT, CDT, and ADT) to 0.
+        */
+       mlb150_dev_reset_whole_ctr();
+
+       /* a. Set all bit of the CTR (CAT, CDT, and ADT) to 0. */
+       mlb150_dev_reset_all_regs();
+
+       /*
+        * Step 2, Configure the MediaLB interface
+        * Select pin mode and clock, 3-pin and 256fs
+        */
+       c0_val = __raw_readl(mlb_base + REG_MLBC0);
+       c0_val &= ~(MLBC0_MLBPEN | MLBC0_MLBCLK_MASK);
+       __raw_writel(c0_val, mlb_base + REG_MLBC0);
+
+       c0_val |= MLBC0_MLBEN;
+       __raw_writel(c0_val, mlb_base + REG_MLBC0);
+
+       /* Step 3, Configure the HBI interface */
+       __raw_writel(ch_rx_mask, mlb_base + REG_HCMR0);
+       __raw_writel(ch_tx_mask, mlb_base + REG_HCMR1);
+       __raw_writel(HCTL_EN, mlb_base + REG_HCTL);
+
+       mlb150_dev_init_ir_amba_ahb();
+
+       mlb150_dev_enable_ir_mlb(1);
+}
+
+static s32 mlb150_dev_unmute_syn_ch(u32 rx_ch, u32 rx_cl, u32 tx_ch, u32 tx_cl)
+{
+       u32 timeout = 10000;
+
+       /*
+        * Check that MediaLB clock is running (MLBC1.CLKM = 0)
+        * If MLBC1.CLKM = 1, clear the register bit, wait one
+        * APB or I/O clock cycle and repeat the check
+        */
+       while ((__raw_readl(mlb_base + REG_MLBC1) & MLBC1_CLKM)
+                       && --timeout)
+               __raw_writel(~MLBC1_CLKM, mlb_base + REG_MLBC1);
+
+       if (0 == timeout)
+               return -ETIME;
+
+       timeout = 10000;
+       /* Poll for MLB lock (MLBC0.MLBLK = 1) */
+       while (!(__raw_readl(mlb_base + REG_MLBC0) & MLBC0_MLBLK)
+                       && --timeout)
+               ;
+
+       if (0 == timeout)
+               return -ETIME;
+
+       /* Unmute synchronous channel(s) */
+       mlb150_dev_cat_mlb_write(rx_ch, CAT_CE | rx_cl);
+       mlb150_dev_cat_mlb_write(tx_ch,
+                       CAT_CE | tx_cl | CAT_RNW);
+       mlb150_dev_cat_hbi_write(rx_cl,
+                       CAT_CE | rx_cl | CAT_RNW);
+       mlb150_dev_cat_hbi_write(tx_cl, CAT_CE | tx_cl);
+
+       return 0;
+}
+
+/* In case the user calls channel shutdown, but rx or tx is not completed yet */
+static s32 mlb150_trans_complete_check(struct mlb_dev_info *pdevinfo)
+{
+       struct mlb_ringbuf *rx_rbuf = &pdevinfo->rx_rbuf;
+       struct mlb_ringbuf *tx_rbuf = &pdevinfo->tx_rbuf;
+       s32 timeout = 1024;
+
+       while (timeout--) {
+               read_lock(&tx_rbuf->rb_lock);
+               if (!CIRC_CNT(tx_rbuf->head, tx_rbuf->tail, TRANS_RING_NODES)) {
+                       read_unlock(&tx_rbuf->rb_lock);
+                       break;
+               } else
+                       read_unlock(&tx_rbuf->rb_lock);
+       }
+
+       if (timeout <= 0) {
+               pr_debug("TX complete check timeout!\n");
+               return -ETIME;
+       }
+
+       timeout = 1024;
+       while (timeout--) {
+               read_lock(&rx_rbuf->rb_lock);
+               if (!CIRC_CNT(rx_rbuf->head, rx_rbuf->tail, TRANS_RING_NODES)) {
+                       read_unlock(&rx_rbuf->rb_lock);
+                       break;
+               } else
+                       read_unlock(&rx_rbuf->rb_lock);
+       }
+
+       if (timeout <= 0) {
+               pr_debug("RX complete check timeout!\n");
+               return -ETIME;
+       }
+
+       /*
+        * Interrupt from TX can only inform that the data is sent
+        * to AHB bus, not mean that it is sent to MITB. Thus we add
+        * a delay here for data to be completed sent.
+        */
+       udelay(1000);
+
+       return 0;
+}
+
+/*
+ * Enable/Disable the MLB IRQ
+ */
+static void mxc_mlb150_irq_enable(struct mlb_data *drvdata, u8 enable)
+{
+       if (enable) {
+               enable_irq(drvdata->irq_ahb0);
+               enable_irq(drvdata->irq_ahb1);
+               enable_irq(drvdata->irq_mlb);
+       } else {
+               disable_irq(drvdata->irq_ahb0);
+               disable_irq(drvdata->irq_ahb1);
+               disable_irq(drvdata->irq_mlb);
+       }
+}
+
+/*
+ * Enable the MLB channel
+ */
+static s32 mlb_channel_enable(struct mlb_data *drvdata,
+                               int chan_dev_id, int on)
+{
+       struct mlb_dev_info *pdevinfo = drvdata->devinfo;
+       struct mlb_channel_info *tx_chinfo = &pdevinfo->channels[TX_CHANNEL];
+       struct mlb_channel_info *rx_chinfo = &pdevinfo->channels[RX_CHANNEL];
+       u32 tx_ch = tx_chinfo->address;
+       u32 rx_ch = rx_chinfo->address;
+       u32 tx_cl = tx_chinfo->cl;
+       u32 rx_cl = rx_chinfo->cl;
+       s32 ret = 0;
+
+       /*
+        * setup the direction, enable, channel type,
+        * mode select, channel address and mask buf start
+        */
+       if (on) {
+               u32 ctype = pdevinfo->channel_type;
+
+               mlb150_dev_enable_ctr_write(0xffffffff, 0xffffffff,
+                               0xffffffff, 0xffffffff);
+               mlb150_dev_init_rfb(pdevinfo, rx_ch, tx_ch, ctype);
+
+               mlb150_dev_init_amba_ahb(pdevinfo, ctype);
+
+#ifdef DEBUG
+               mlb150_dev_dump_ctr_tbl(0, tx_chinfo->cl + 1);
+#endif
+               /* Synchronize and unmute synchrouous channel */
+               if (MLB_CTYPE_SYNC == ctype) {
+                       ret = mlb150_dev_unmute_syn_ch(rx_ch, rx_cl,
+                                                       tx_ch, tx_cl);
+                       if (ret)
+                               return ret;
+               }
+
+               mlb150_dev_enable_ctr_write(0x0, ADT_RDY1 | ADT_DNE1 |
+                               ADT_ERR1 | ADT_PS1 |
+                               ADT_RDY2 | ADT_DNE2 | ADT_ERR2 | ADT_PS2,
+                               0xffffffff, 0xffffffff);
+
+               if (pdevinfo->fps >= CLK_2048FS)
+                       mlb150_enable_pll(drvdata);
+
+               atomic_set(&pdevinfo->on, 1);
+
+#ifdef DEBUG
+               mlb150_dev_dump_reg();
+               mlb150_dev_dump_ctr_tbl(0, tx_chinfo->cl + 1);
+#endif
+               /* Init RX ADT */
+               mlb150_dev_pipo_start(&pdevinfo->rx_rbuf, rx_cl,
+                                       pdevinfo->rx_rbuf.phy_addrs[0]);
+       } else {
+               mlb150_dev_pipo_stop(&pdevinfo->rx_rbuf, rx_cl);
+
+               mlb150_dev_enable_dma_irq(0);
+               mlb150_dev_enable_ir_mlb(0);
+
+               mlb150_dev_reset_cat();
+
+               atomic_set(&pdevinfo->on, 0);
+
+               if (pdevinfo->fps >= CLK_2048FS)
+                       mlb150_disable_pll(drvdata);
+       }
+
+       return 0;
+}
+
+/*
+ * MLB interrupt handler
+ */
+static void mlb_rx_isr(s32 ctype, u32 ahb_ch, struct mlb_dev_info *pdevinfo)
+{
+       struct mlb_ringbuf *rx_rbuf = &pdevinfo->rx_rbuf;
+       s32 head, tail, adt_sts;
+       u32 rx_buf_ptr;
+
+#ifdef DEBUG_RX
+       pr_debug("mxc_mlb150: mlb_rx_isr\n");
+#endif
+
+       read_lock(&rx_rbuf->rb_lock);
+
+       head = (rx_rbuf->head + 1) & (TRANS_RING_NODES - 1);
+       tail = ACCESS_ONCE(rx_rbuf->tail);
+       read_unlock(&rx_rbuf->rb_lock);
+
+       if (CIRC_SPACE(head, tail, TRANS_RING_NODES) >= 1) {
+               rx_buf_ptr = rx_rbuf->phy_addrs[head];
+
+               /* commit the item before incrementing the head */
+               smp_wmb();
+
+               write_lock(&rx_rbuf->rb_lock);
+               rx_rbuf->head = head;
+               write_unlock(&rx_rbuf->rb_lock);
+
+               /* wake up the reader */
+               wake_up_interruptible(&pdevinfo->rx_wq);
+       } else {
+               rx_buf_ptr = rx_rbuf->phy_addrs[head];
+               pr_debug("drop RX package, due to no space, (%d,%d)\n",
+                               head, tail);
+       }
+
+       adt_sts = mlb150_dev_get_adt_sts(ahb_ch);
+       /*  Set ADT for RX */
+       mlb150_dev_pipo_next(ahb_ch, ctype, adt_sts, rx_buf_ptr);
+}
+
+static void mlb_tx_isr(s32 ctype, u32 ahb_ch, struct mlb_dev_info *pdevinfo)
+{
+       struct mlb_ringbuf *tx_rbuf = &pdevinfo->tx_rbuf;
+       s32 head, tail, adt_sts;
+       u32 tx_buf_ptr;
+
+       read_lock(&tx_rbuf->rb_lock);
+
+       head = ACCESS_ONCE(tx_rbuf->head);
+       tail = (tx_rbuf->tail + 1) & (TRANS_RING_NODES - 1);
+       read_unlock(&tx_rbuf->rb_lock);
+
+       smp_mb();
+       write_lock(&tx_rbuf->rb_lock);
+       tx_rbuf->tail = tail;
+       write_unlock(&tx_rbuf->rb_lock);
+
+       /* check the current tx buffer is available or not */
+       if (CIRC_CNT(head, tail, TRANS_RING_NODES) >= 1) {
+               /* read index before reading contents at that index */
+               smp_read_barrier_depends();
+
+               tx_buf_ptr = tx_rbuf->phy_addrs[tail];
+
+               wake_up_interruptible(&pdevinfo->tx_wq);
+
+               adt_sts = mlb150_dev_get_adt_sts(ahb_ch);
+               /*  Set ADT for TX */
+               mlb150_dev_pipo_next(ahb_ch, ctype, adt_sts, tx_buf_ptr);
+       }
+}
+
+static irqreturn_t mlb_ahb_isr(int irq, void *dev_id)
+{
+       u32 acsr0, hcer0;
+       u32 ch_mask = (1 << SYNC_RX_CL) | (1 << CTRL_RX_CL)
+                       | (1 << ASYNC_RX_CL) | (1 << ISOC_RX_CL)
+                       | (1 << SYNC_TX_CL) | (1 << CTRL_TX_CL)
+                       | (1 << ASYNC_TX_CL) | (1 << ISOC_TX_CL);
+
+       /*
+        * Step 5, Read the ACSRn registers to determine which channel or
+        * channels are causing the interrupt
+        */
+       acsr0 = __raw_readl(mlb_base + REG_ACSR0);
+
+       hcer0 = __raw_readl(mlb_base + REG_HCER0);
+
+       /*
+        * Step 6, If ACTL.SCE = 1, write the result of step 5 back to ACSR0
+        * and ACSR1 to clear the interrupt
+        * We'll not set ACTL_SCE
+        */
+
+       if (ch_mask & hcer0)
+               pr_err("CH encounters an AHB error: 0x%x\n", hcer0);
+
+       if ((1 << SYNC_RX_CL) & acsr0)
+               mlb_rx_isr(MLB_CTYPE_SYNC, SYNC_RX_CL,
+                               &mlb_devinfo[MLB_CTYPE_SYNC]);
+
+       if ((1 << CTRL_RX_CL) & acsr0)
+               mlb_rx_isr(MLB_CTYPE_CTRL, CTRL_RX_CL,
+                               &mlb_devinfo[MLB_CTYPE_CTRL]);
+
+       if ((1 << ASYNC_RX_CL) & acsr0)
+               mlb_rx_isr(MLB_CTYPE_ASYNC, ASYNC_RX_CL,
+                               &mlb_devinfo[MLB_CTYPE_ASYNC]);
+
+       if ((1 << ISOC_RX_CL) & acsr0)
+               mlb_rx_isr(MLB_CTYPE_ISOC, ISOC_RX_CL,
+                               &mlb_devinfo[MLB_CTYPE_ISOC]);
+
+       if ((1 << SYNC_TX_CL) & acsr0)
+               mlb_tx_isr(MLB_CTYPE_SYNC, SYNC_TX_CL,
+                               &mlb_devinfo[MLB_CTYPE_SYNC]);
+
+       if ((1 << CTRL_TX_CL) & acsr0)
+               mlb_tx_isr(MLB_CTYPE_CTRL, CTRL_TX_CL,
+                               &mlb_devinfo[MLB_CTYPE_CTRL]);
+
+       if ((1 << ASYNC_TX_CL) & acsr0)
+               mlb_tx_isr(MLB_CTYPE_ASYNC, ASYNC_TX_CL,
+                               &mlb_devinfo[MLB_CTYPE_ASYNC]);
+
+       if ((1 << ISOC_TX_CL) & acsr0)
+               mlb_tx_isr(MLB_CTYPE_ASYNC, ISOC_TX_CL,
+                               &mlb_devinfo[MLB_CTYPE_ISOC]);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t mlb_isr(int irq, void *dev_id)
+{
+       u32 rx_int_sts, tx_int_sts, ms0,
+               ms1, tx_cis, rx_cis, ctype;
+       int minor;
+       u32 cdt_val[4] = { 0 };
+
+       /*
+        * Step 4, Read the MSn register to determine which channel(s)
+        * are causing the interrupt
+        */
+       ms0 = __raw_readl(mlb_base + REG_MS0);
+       ms1 = __raw_readl(mlb_base + REG_MS1);
+
+       /*
+        * The MLB150_MS0, MLB150_MS1 registers need to be cleared. In
+        * the spec description, the registers should  be cleared when
+        * enabling interrupt. In fact, we also should clear it in ISR.
+        */
+       __raw_writel(0, mlb_base + REG_MS0);
+       __raw_writel(0, mlb_base + REG_MS1);
+
+       pr_debug("mxc_mlb150: mlb interrupt:0x%08x 0x%08x\n",
+                       (u32)ms0, (u32)ms1);
+
+       for (minor = 0; minor < MLB_MINOR_DEVICES; minor++) {
+               struct mlb_dev_info *pdevinfo = &mlb_devinfo[minor];
+               u32 rx_mlb_ch = pdevinfo->channels[RX_CHANNEL].address;
+               u32 tx_mlb_ch = pdevinfo->channels[TX_CHANNEL].address;
+               u32 rx_mlb_cl = pdevinfo->channels[RX_CHANNEL].cl;
+               u32 tx_mlb_cl = pdevinfo->channels[TX_CHANNEL].cl;
+
+               tx_cis = rx_cis = 0;
+
+               ctype = pdevinfo->channel_type;
+               rx_int_sts = (rx_mlb_ch < 31) ? ms0 : ms1;
+               tx_int_sts = (tx_mlb_ch < 31) ? ms0 : ms1;
+
+               pr_debug("mxc_mlb150: channel interrupt: "
+                               "tx %d: 0x%08x, rx %d: 0x%08x\n",
+                       tx_mlb_ch, (u32)tx_int_sts, rx_mlb_ch, (u32)rx_int_sts);
+
+               /* Get tx channel interrupt status */
+               if (tx_int_sts & (1 << (tx_mlb_ch % 32))) {
+                       mlb150_dev_cdt_read(tx_mlb_cl, cdt_val);
+                       pr_debug("mxc_mlb150: TX_CH: %d, cdt_val[3]: 0x%08x, "
+                                       "cdt_val[2]: 0x%08x, "
+                                       "cdt_val[1]: 0x%08x, "
+                                       "cdt_val[0]: 0x%08x\n",
+                                       tx_mlb_ch, cdt_val[3], cdt_val[2],
+                                       cdt_val[1], cdt_val[0]);
+                       switch (ctype) {
+                       case MLB_CTYPE_SYNC:
+                               tx_cis = (cdt_val[2] & ~CDT_SYNC_WSTS_MASK)
+                                       >> CDT_SYNC_WSTS_SHIFT;
+                               /*
+                                * Clear RSTS/WSTS errors to resume
+                                * channel operation
+                                * a. For synchronous channels: WSTS[3] = 0
+                                */
+                               cdt_val[2] &= ~(0x8 << CDT_SYNC_WSTS_SHIFT);
+                               break;
+                       case MLB_CTYPE_CTRL:
+                       case MLB_CTYPE_ASYNC:
+                               tx_cis = (cdt_val[2] &
+                                       ~CDT_CTRL_ASYNC_WSTS_MASK)
+                                       >> CDT_CTRL_ASYNC_WSTS_SHIFT;
+                               tx_cis = (cdt_val[3] & CDT_CTRL_ASYNC_WSTS_1) ?
+                                       (tx_cis | (0x1 << 4)) : tx_cis;
+                               /*
+                                * b. For async and ctrl channels:
+                                * RSTS[4]/WSTS[4] = 0
+                                * and RSTS[2]/WSTS[2] = 0
+                                */
+                               cdt_val[3] &= ~CDT_CTRL_ASYNC_WSTS_1;
+                               cdt_val[2] &=
+                                       ~(0x4 << CDT_CTRL_ASYNC_WSTS_SHIFT);
+                               break;
+                       case MLB_CTYPE_ISOC:
+                               tx_cis = (cdt_val[2] & ~CDT_ISOC_WSTS_MASK)
+                                       >> CDT_ISOC_WSTS_SHIFT;
+                               /* c. For isoc channels: WSTS[2:1] = 0x00 */
+                               cdt_val[2] &= ~(0x6 << CDT_ISOC_WSTS_SHIFT);
+                               break;
+                       default:
+                               break;
+                       }
+                       mlb150_dev_cdt_write(tx_mlb_ch, cdt_val);
+               }
+
+               /* Get rx channel interrupt status */
+               if (rx_int_sts & (1 << (rx_mlb_ch % 32))) {
+                       mlb150_dev_cdt_read(rx_mlb_cl, cdt_val);
+                       pr_debug("mxc_mlb150: RX_CH: %d, cdt_val[3]: 0x%08x, "
+                                       "cdt_val[2]: 0x%08x, "
+                                       "cdt_val[1]: 0x%08x, "
+                                       "cdt_val[0]: 0x%08x\n",
+                                       rx_mlb_ch, cdt_val[3], cdt_val[2],
+                                       cdt_val[1], cdt_val[0]);
+                       switch (ctype) {
+                       case MLB_CTYPE_SYNC:
+                               tx_cis = (cdt_val[2] & ~CDT_SYNC_RSTS_MASK)
+                                       >> CDT_SYNC_RSTS_SHIFT;
+                               cdt_val[2] &= ~(0x8 << CDT_SYNC_WSTS_SHIFT);
+                               break;
+                       case MLB_CTYPE_CTRL:
+                       case MLB_CTYPE_ASYNC:
+                               tx_cis =
+                                       (cdt_val[2] & ~CDT_CTRL_ASYNC_RSTS_MASK)
+                                       >> CDT_CTRL_ASYNC_RSTS_SHIFT;
+                               tx_cis = (cdt_val[3] & CDT_CTRL_ASYNC_RSTS_1) ?
+                                       (tx_cis | (0x1 << 4)) : tx_cis;
+                               cdt_val[3] &= ~CDT_CTRL_ASYNC_RSTS_1;
+                               cdt_val[2] &=
+                                       ~(0x4 << CDT_CTRL_ASYNC_RSTS_SHIFT);
+                               break;
+                       case MLB_CTYPE_ISOC:
+                               tx_cis = (cdt_val[2] & ~CDT_ISOC_RSTS_MASK)
+                                       >> CDT_ISOC_RSTS_SHIFT;
+                               cdt_val[2] &= ~(0x6 << CDT_ISOC_WSTS_SHIFT);
+                               break;
+                       default:
+                               break;
+                       }
+                       mlb150_dev_cdt_write(rx_mlb_ch, cdt_val);
+               }
+
+               if (!tx_cis && !rx_cis)
+                       continue;
+
+               /* fill exception event */
+               spin_lock(&pdevinfo->event_lock);
+               pdevinfo->ex_event |= (rx_cis << 16) | tx_cis;
+               spin_unlock(&pdevinfo->event_lock);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int mxc_mlb150_open(struct inode *inode, struct file *filp)
+{
+       int minor, ring_buf_size, buf_size, j, ret;
+       void __iomem *buf_addr;
+       ulong phy_addr;
+       struct mlb_dev_info *pdevinfo = NULL;
+       struct mlb_channel_info *pchinfo = NULL;
+       struct mlb_data *drvdata;
+
+       minor = MINOR(inode->i_rdev);
+       drvdata = container_of(inode->i_cdev, struct mlb_data, cdev);
+
+       if (minor < 0 || minor >= MLB_MINOR_DEVICES) {
+               pr_err("no device\n");
+               return -ENODEV;
+       }
+
+       /* open for each channel device */
+       if (atomic_cmpxchg(&mlb_devinfo[minor].opencnt, 0, 1) != 0) {
+               pr_err("busy\n");
+               return -EBUSY;
+       }
+
+       clk_prepare_enable(drvdata->clk_mlb3p);
+
+       /* initial MLB module */
+       mlb150_dev_init();
+
+       pdevinfo = &mlb_devinfo[minor];
+       pchinfo = &pdevinfo->channels[TX_CHANNEL];
+
+       ring_buf_size = pdevinfo->buf_size;
+       buf_size = ring_buf_size * (TRANS_RING_NODES * 2);
+       buf_addr = (void __iomem *)gen_pool_alloc(drvdata->iram_pool, buf_size);
+       if (buf_addr == NULL) {
+               ret = -ENOMEM;
+               pr_err("can not alloc rx/tx buffers: %d\n", buf_size);
+               return ret;
+       }
+       phy_addr = gen_pool_virt_to_phys(drvdata->iram_pool, (ulong)buf_addr);
+       pr_debug("IRAM Range: Virt 0x%p - 0x%p, Phys 0x%x - 0x%x, size: 0x%x\n",
+                       buf_addr, (buf_addr + buf_size - 1), (u32)phy_addr,
+                       (u32)(phy_addr + buf_size - 1), buf_size);
+       pdevinfo->rbuf_base_virt = buf_addr;
+       pdevinfo->rbuf_base_phy = phy_addr;
+       drvdata->iram_size = buf_size;
+
+       memset(buf_addr, 0, buf_size);
+
+       for (j = 0; j < (TRANS_RING_NODES);
+               ++j, buf_addr += ring_buf_size, phy_addr += ring_buf_size) {
+               pdevinfo->rx_rbuf.virt_bufs[j] = buf_addr;
+               pdevinfo->rx_rbuf.phy_addrs[j] = phy_addr;
+               pr_debug("RX Ringbuf[%d]: 0x%p 0x%x\n",
+                       j, buf_addr, (u32)phy_addr);
+       }
+       pdevinfo->rx_rbuf.unit_size = ring_buf_size;
+       pdevinfo->rx_rbuf.total_size = buf_size;
+       for (j = 0; j < (TRANS_RING_NODES);
+               ++j, buf_addr += ring_buf_size, phy_addr += ring_buf_size) {
+               pdevinfo->tx_rbuf.virt_bufs[j] = buf_addr;
+               pdevinfo->tx_rbuf.phy_addrs[j] = phy_addr;
+               pr_debug("TX Ringbuf[%d]: 0x%p 0x%x\n",
+                       j, buf_addr, (u32)phy_addr);
+       }
+
+       pdevinfo->tx_rbuf.unit_size = ring_buf_size;
+       pdevinfo->tx_rbuf.total_size = buf_size;
+
+       /* reset the buffer read/write ptr */
+       pdevinfo->rx_rbuf.head = pdevinfo->rx_rbuf.tail = 0;
+       pdevinfo->tx_rbuf.head = pdevinfo->tx_rbuf.tail = 0;
+       pdevinfo->ex_event = 0;
+       pdevinfo->tx_ok = 0;
+
+       init_waitqueue_head(&pdevinfo->rx_wq);
+       init_waitqueue_head(&pdevinfo->tx_wq);
+
+       drvdata = container_of(inode->i_cdev, struct mlb_data, cdev);
+       drvdata->devinfo = pdevinfo;
+       mxc_mlb150_irq_enable(drvdata, 1);
+       filp->private_data = drvdata;
+
+       return 0;
+}
+
+static int mxc_mlb150_release(struct inode *inode, struct file *filp)
+{
+       int minor;
+       struct mlb_data *drvdata = filp->private_data;
+       struct mlb_dev_info *pdevinfo = drvdata->devinfo;
+
+       minor = MINOR(inode->i_rdev);
+       mxc_mlb150_irq_enable(drvdata, 0);
+
+#ifdef DEBUG
+       mlb150_dev_dump_reg();
+       mlb150_dev_dump_ctr_tbl(0, pdevinfo->channels[TX_CHANNEL].cl + 1);
+#endif
+
+       gen_pool_free(drvdata->iram_pool,
+                       (ulong)pdevinfo->rbuf_base_virt, drvdata->iram_size);
+
+       mlb150_dev_exit();
+
+       if (pdevinfo && atomic_read(&pdevinfo->on)
+               && (pdevinfo->fps >= CLK_2048FS))
+               clk_disable_unprepare(drvdata->clk_mlb6p);
+
+       atomic_set(&pdevinfo->on, 0);
+
+       clk_disable_unprepare(drvdata->clk_mlb3p);
+       /* decrease the open count */
+       atomic_set(&pdevinfo->opencnt, 0);
+
+       drvdata->devinfo = NULL;
+
+       return 0;
+}
+
+static long mxc_mlb150_ioctl(struct file *filp,
+                        unsigned int cmd, unsigned long arg)
+{
+       //struct inode *inode = filp->f_dentry->d_inode;
+       struct inode *inode = file_inode(filp);
+       struct mlb_data *drvdata = filp->private_data;
+       struct mlb_dev_info *pdevinfo = drvdata->devinfo;
+       void __user *argp = (void __user *)arg;
+       unsigned long flags, event;
+       int minor;
+
+       minor = MINOR(inode->i_rdev);
+
+       switch (cmd) {
+       case MLB_CHAN_SETADDR:
+               {
+                       unsigned int caddr;
+                       /* get channel address from user space */
+                       if (copy_from_user(&caddr, argp, sizeof(caddr))) {
+                               pr_err("mxc_mlb150: copy from user failed\n");
+                               return -EFAULT;
+                       }
+                       pdevinfo->channels[TX_CHANNEL].address =
+                                                       (caddr >> 16) & 0xFFFF;
+                       pdevinfo->channels[RX_CHANNEL].address = caddr & 0xFFFF;
+                       pr_debug("mxc_mlb150: set ch addr, tx: %d, rx: %d\n",
+                                       pdevinfo->channels[TX_CHANNEL].address,
+                                       pdevinfo->channels[RX_CHANNEL].address);
+                       break;
+               }
+
+       case MLB_CHAN_STARTUP:
+               if (atomic_read(&pdevinfo->on)) {
+                       pr_debug("mxc_mlb150: channel alreadly startup\n");
+                       break;
+               }
+               if (mlb_channel_enable(drvdata, minor, 1))
+                       return -EFAULT;
+               break;
+       case MLB_CHAN_SHUTDOWN:
+               if (atomic_read(&pdevinfo->on) == 0) {
+                       pr_debug("mxc_mlb150: channel areadly shutdown\n");
+                       break;
+               }
+               mlb150_trans_complete_check(pdevinfo);
+               mlb_channel_enable(drvdata, minor, 0);
+               break;
+       case MLB_CHAN_GETEVENT:
+               /* get and clear the ex_event */
+               spin_lock_irqsave(&pdevinfo->event_lock, flags);
+               event = pdevinfo->ex_event;
+               pdevinfo->ex_event = 0;
+               spin_unlock_irqrestore(&pdevinfo->event_lock, flags);
+
+               if (event) {
+                       if (copy_to_user(argp, &event, sizeof(event))) {
+                               pr_err("mxc_mlb150: copy to user failed\n");
+                               return -EFAULT;
+                       }
+               } else
+                       return -EAGAIN;
+               break;
+       case MLB_SET_ISOC_BLKSIZE_188:
+               pdevinfo->isoc_blksz = 188;
+               pdevinfo->cdt_buf_dep = pdevinfo->adt_buf_dep =
+                                       pdevinfo->isoc_blksz * CH_ISOC_BLK_NUM;
+               break;
+       case MLB_SET_ISOC_BLKSIZE_196:
+               pdevinfo->isoc_blksz = 196;
+               pdevinfo->cdt_buf_dep = pdevinfo->adt_buf_dep =
+                                       pdevinfo->isoc_blksz * CH_ISOC_BLK_NUM;
+               break;
+       case MLB_SET_SYNC_QUAD:
+               {
+                       u32 quad;
+
+                       if (copy_from_user(&quad, argp, sizeof(quad))) {
+                               pr_err("mxc_mlb150: get quad number "
+                                               "from user failed\n");
+                               return -EFAULT;
+                       }
+                       if (quad <= 0 || quad > 3) {
+                               pr_err("mxc_mlb150: Invalid Quadlets!"
+                                       "Quadlets in Sync mode can "
+                                       "only be 1, 2, 3\n");
+                               return -EINVAL;
+                       }
+                       pdevinfo->sync_quad = quad;
+                       /* Each quadlets is 4 bytes */
+                       pdevinfo->cdt_buf_dep = quad * 4 * 4;
+                       pdevinfo->adt_buf_dep =
+                               pdevinfo->cdt_buf_dep * CH_SYNC_ADT_BUF_MULTI;
+               }
+               break;
+       case MLB_SET_FPS:
+               {
+                       u32 fps, c0_val;
+
+                       /* get fps from user space */
+                       if (copy_from_user(&fps, argp, sizeof(fps))) {
+                               pr_err("mxc_mlb150: copy from user failed\n");
+                               return -EFAULT;
+                       }
+
+                       if ((fps > 1024) &&
+                               !(drvdata->quirk_flag & MLB_QUIRK_MLB150)) {
+                               pr_err("mxc_mlb150: not support fps %d\n", fps);
+                               return -EINVAL;
+                       }
+
+                       c0_val = __raw_readl(mlb_base + REG_MLBC0);
+                       c0_val &= ~MLBC0_MLBCLK_MASK;
+
+                       /* check fps value */
+                       switch (fps) {
+                       case 256:
+                       case 512:
+                       case 1024:
+                               pdevinfo->fps = fps >> 9;
+                               c0_val &= ~MLBC0_MLBPEN;
+                               c0_val |= (fps >> 9)
+                                       << MLBC0_MLBCLK_SHIFT;
+
+                               if (1024 == fps) {
+                                       /*
+                                        * Invert output clock phase
+                                        * in 1024 fps
+                                        */
+                                       __raw_writel(0x1,
+                                               mlb_base + REG_MLBPC2);
+                               }
+                               break;
+                       case 2048:
+                       case 3072:
+                       case 4096:
+                               pdevinfo->fps = (fps >> 10) + 1;
+                               c0_val |= ((fps >> 10) + 1)
+                                       << MLBC0_MLBCLK_SHIFT;
+                               break;
+                       case 6144:
+                               pdevinfo->fps = fps >> 10;
+                               c0_val |= ((fps >> 10) + 1)
+                                       << MLBC0_MLBCLK_SHIFT;
+                               break;
+                       case 8192:
+                               pdevinfo->fps = (fps >> 10) - 1;
+                               c0_val |= ((fps >> 10) - 1)
+                                               << MLBC0_MLBCLK_SHIFT;
+                               break;
+                       default:
+                               pr_debug("mxc_mlb150: invalid fps argument: %d\n",
+                                               fps);
+                               return -EINVAL;
+                       }
+
+                       __raw_writel(c0_val, mlb_base + REG_MLBC0);
+
+                       pr_debug("mxc_mlb150: set fps to %d, MLBC0: 0x%08x\n",
+                               fps,
+                               (u32)__raw_readl(mlb_base + REG_MLBC0));
+
+                       break;
+               }
+
+       case MLB_GET_VER:
+               {
+                       u32 version;
+
+                       /* get MLB device module version */
+                       version = 0x03030003;
+
+                       pr_debug("mxc_mlb150: get version: 0x%08x\n",
+                                       version);
+
+                       if (copy_to_user(argp, &version, sizeof(version))) {
+                               pr_err("mxc_mlb150: copy to user failed\n");
+                               return -EFAULT;
+                       }
+                       break;
+               }
+
+       case MLB_SET_DEVADDR:
+               {
+                       u32 c1_val;
+                       u8 devaddr;
+
+                       /* get MLB device address from user space */
+                       if (copy_from_user
+                               (&devaddr, argp, sizeof(unsigned char))) {
+                               pr_err("mxc_mlb150: copy from user failed\n");
+                               return -EFAULT;
+                       }
+
+                       c1_val = __raw_readl(mlb_base + REG_MLBC1);
+                       c1_val &= ~MLBC1_NDA_MASK;
+                       c1_val |= devaddr << MLBC1_NDA_SHIFT;
+                       __raw_writel(c1_val, mlb_base + REG_MLBC1);
+                       pr_debug("mxc_mlb150: set dev addr, dev addr: %d, "
+                               "MLBC1: 0x%08x\n", devaddr,
+                               (u32)__raw_readl(mlb_base + REG_MLBC1));
+
+                       break;
+               }
+
+       case MLB_IRQ_DISABLE:
+               {
+                       disable_irq(drvdata->irq_mlb);
+                       break;
+               }
+
+       case MLB_IRQ_ENABLE:
+               {
+                       enable_irq(drvdata->irq_mlb);
+                       break;
+               }
+       default:
+               pr_info("mxc_mlb150: Invalid ioctl command\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * MLB read routine
+ * Read the current received data from queued buffer,
+ * and free this buffer for hw to fill ingress data.
+ */
+static ssize_t mxc_mlb150_read(struct file *filp, char __user *buf,
+                           size_t count, loff_t *f_pos)
+{
+       int size;
+       struct mlb_data *drvdata = filp->private_data;
+       struct mlb_dev_info *pdevinfo = drvdata->devinfo;
+       struct mlb_ringbuf *rx_rbuf = &pdevinfo->rx_rbuf;
+       int head, tail;
+       unsigned long flags;
+
+       read_lock_irqsave(&rx_rbuf->rb_lock, flags);
+
+       head = ACCESS_ONCE(rx_rbuf->head);
+       tail = rx_rbuf->tail;
+
+       read_unlock_irqrestore(&rx_rbuf->rb_lock, flags);
+
+       /* check the current rx buffer is available or not */
+       if (0 == CIRC_CNT(head, tail, TRANS_RING_NODES)) {
+
+               if (filp->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+
+               do {
+                       DEFINE_WAIT(__wait);
+
+                       for (;;) {
+                               prepare_to_wait(&pdevinfo->rx_wq,
+                                               &__wait, TASK_INTERRUPTIBLE);
+
+                               read_lock_irqsave(&rx_rbuf->rb_lock, flags);
+                               if (CIRC_CNT(rx_rbuf->head, rx_rbuf->tail,
+                                               TRANS_RING_NODES) > 0) {
+                                       read_unlock_irqrestore(&rx_rbuf->rb_lock,
+                                                               flags);
+                                       break;
+                               }
+                               read_unlock_irqrestore(&rx_rbuf->rb_lock,
+                                                       flags);
+
+                               if (!signal_pending(current)) {
+                                       schedule();
+                                       continue;
+                               }
+                               return -ERESTARTSYS;
+                       }
+                       finish_wait(&pdevinfo->rx_wq, &__wait);
+               } while (0);
+       }
+
+       /* read index before reading contents at that index */
+       smp_read_barrier_depends();
+
+       size = pdevinfo->adt_buf_dep;
+       if (size > count) {
+               /* the user buffer is too small */
+               pr_warning
+                       ("mxc_mlb150: received data size is bigger than "
+                       "size: %d, count: %d\n", size, count);
+               return -EINVAL;
+       }
+
+       /* extract one item from the buffer */
+       if (copy_to_user(buf, rx_rbuf->virt_bufs[tail], size)) {
+               pr_err("mxc_mlb150: copy from user failed\n");
+               return -EFAULT;
+       }
+
+       /* finish reading descriptor before incrementing tail */
+       smp_mb();
+
+       write_lock_irqsave(&rx_rbuf->rb_lock, flags);
+       rx_rbuf->tail = (tail + 1) & (TRANS_RING_NODES - 1);
+       write_unlock_irqrestore(&rx_rbuf->rb_lock, flags);
+
+       *f_pos = 0;
+
+       return size;
+}
+
+/*
+ * MLB write routine
+ * Copy the user data to tx channel buffer,
+ * and prepare the channel current/next buffer ptr.
+ */
+static ssize_t mxc_mlb150_write(struct file *filp, const char __user *buf,
+                            size_t count, loff_t *f_pos)
+{
+       s32 ret = 0;
+       struct mlb_channel_info *pchinfo = NULL;
+       struct mlb_data *drvdata = filp->private_data;
+       struct mlb_dev_info *pdevinfo = drvdata->devinfo;
+       struct mlb_ringbuf *tx_rbuf = &pdevinfo->tx_rbuf;
+       int head, tail;
+       unsigned long flags;
+
+       /*
+        * minor = MINOR(filp->f_dentry->d_inode->i_rdev);
+        */
+       pchinfo = &pdevinfo->channels[TX_CHANNEL];
+
+       if (count > pdevinfo->buf_size) {
+               /* too many data to write */
+               pr_warning("mxc_mlb150: overflow write data\n");
+               return -EFBIG;
+       }
+
+       *f_pos = 0;
+
+       read_lock_irqsave(&tx_rbuf->rb_lock, flags);
+
+       head = tx_rbuf->head;
+       tail = ACCESS_ONCE(tx_rbuf->tail);
+       read_unlock_irqrestore(&tx_rbuf->rb_lock, flags);
+
+       if (0 == CIRC_SPACE(head, tail, TRANS_RING_NODES)) {
+               if (filp->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+               do {
+                       DEFINE_WAIT(__wait);
+
+                       for (;;) {
+                               prepare_to_wait(&pdevinfo->tx_wq,
+                                               &__wait, TASK_INTERRUPTIBLE);
+
+                               read_lock_irqsave(&tx_rbuf->rb_lock, flags);
+                               if (CIRC_SPACE(tx_rbuf->head, tx_rbuf->tail,
+                                                       TRANS_RING_NODES) > 0) {
+                                       read_unlock_irqrestore(&tx_rbuf->rb_lock,
+                                                       flags);
+                                       break;
+                               }
+                               read_unlock_irqrestore(&tx_rbuf->rb_lock,
+                                                               flags);
+
+                               if (!signal_pending(current)) {
+                                       schedule();
+                                       continue;
+                               }
+                               return -ERESTARTSYS;
+                       }
+                       finish_wait(&pdevinfo->tx_wq, &__wait);
+               } while (0);
+       }
+
+       if (copy_from_user((void *)tx_rbuf->virt_bufs[head], buf, count)) {
+               read_unlock_irqrestore(&tx_rbuf->rb_lock, flags);
+               pr_err("mxc_mlb: copy from user failed\n");
+               ret = -EFAULT;
+               goto out;
+       }
+
+       write_lock_irqsave(&tx_rbuf->rb_lock, flags);
+       smp_wmb();
+       tx_rbuf->head = (head + 1) & (TRANS_RING_NODES - 1);
+       write_unlock_irqrestore(&tx_rbuf->rb_lock, flags);
+
+       if (0 == CIRC_CNT(head, tail, TRANS_RING_NODES)) {
+               u32 tx_buf_ptr, ahb_ch;
+               s32 adt_sts;
+               u32 ctype = pdevinfo->channel_type;
+
+               /* read index before reading contents at that index */
+               smp_read_barrier_depends();
+
+               tx_buf_ptr = tx_rbuf->phy_addrs[tail];
+
+               ahb_ch = pdevinfo->channels[TX_CHANNEL].cl;
+               adt_sts = mlb150_dev_get_adt_sts(ahb_ch);
+
+               /*  Set ADT for TX */
+               mlb150_dev_pipo_next(ahb_ch, ctype, adt_sts, tx_buf_ptr);
+       }
+
+       ret = count;
+out:
+       return ret;
+}
+
+static unsigned int mxc_mlb150_poll(struct file *filp,
+                                struct poll_table_struct *wait)
+{
+       int minor;
+       unsigned int ret = 0;
+       struct mlb_data *drvdata = filp->private_data;
+       struct mlb_dev_info *pdevinfo = drvdata->devinfo;
+       struct mlb_ringbuf *tx_rbuf = &pdevinfo->tx_rbuf;
+       struct mlb_ringbuf *rx_rbuf = &pdevinfo->rx_rbuf;
+       int head, tail;
+       unsigned long flags;
+
+
+       minor = MINOR(file_inode(filp)->i_rdev);
+
+       poll_wait(filp, &pdevinfo->rx_wq, wait);
+       poll_wait(filp, &pdevinfo->tx_wq, wait);
+
+       read_lock_irqsave(&tx_rbuf->rb_lock, flags);
+       head = tx_rbuf->head;
+       tail = tx_rbuf->tail;
+       read_unlock_irqrestore(&tx_rbuf->rb_lock, flags);
+
+       /* check the tx buffer is avaiable or not */
+       if (CIRC_SPACE(head, tail, TRANS_RING_NODES) >= 1)
+               ret |= POLLOUT | POLLWRNORM;
+
+       read_lock_irqsave(&rx_rbuf->rb_lock, flags);
+       head = rx_rbuf->head;
+       tail = rx_rbuf->tail;
+       read_unlock_irqrestore(&rx_rbuf->rb_lock, flags);
+
+       /* check the rx buffer filled or not */
+       if (CIRC_CNT(head, tail, TRANS_RING_NODES) >= 1)
+               ret |= POLLIN | POLLRDNORM;
+
+
+       /* check the exception event */
+       if (pdevinfo->ex_event)
+               ret |= POLLIN | POLLRDNORM;
+
+       return ret;
+}
+
+/*
+ * char dev file operations structure
+ */
+static const struct file_operations mxc_mlb150_fops = {
+
+       .owner = THIS_MODULE,
+       .open = mxc_mlb150_open,
+       .release = mxc_mlb150_release,
+       .unlocked_ioctl = mxc_mlb150_ioctl,
+       .poll = mxc_mlb150_poll,
+       .read = mxc_mlb150_read,
+       .write = mxc_mlb150_write,
+};
+
+static struct platform_device_id imx_mlb150_devtype[] = {
+       {
+               .name = "imx6q-mlb150",
+               .driver_data = MLB_QUIRK_MLB150,
+       }, {
+               .name = "imx6sx-mlb50",
+               .driver_data = 0,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(platform, imx_mlb150_devtype);
+
+static const struct of_device_id mlb150_imx_dt_ids[] = {
+       { .compatible = "fsl,imx6q-mlb150",
+               .data = &imx_mlb150_devtype[IMX6Q_MLB], },
+       { .compatible = "fsl,imx6sx-mlb50",
+               .data = &imx_mlb150_devtype[IMX6SX_MLB], },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mlb150_imx_dt_ids);
+
+/*
+ * This function is called whenever the MLB device is detected.
+ */
+static int mxc_mlb150_probe(struct platform_device *pdev)
+{
+       int ret, mlb_major, i;
+       struct mlb_data *drvdata;
+       struct resource *res;
+       struct device_node *np = pdev->dev.of_node;
+       const struct of_device_id *of_id;
+
+       drvdata = devm_kzalloc(&pdev->dev, sizeof(struct mlb_data),
+                               GFP_KERNEL);
+       if (!drvdata) {
+               dev_err(&pdev->dev, "can't allocate enough memory\n");
+               return -ENOMEM;
+       }
+
+       of_id = of_match_device(mlb150_imx_dt_ids, &pdev->dev);
+       if (of_id)
+               pdev->id_entry = of_id->data;
+       else
+               return -EINVAL;
+       /*
+        * Register MLB lld as four character devices
+        */
+       ret = alloc_chrdev_region(&drvdata->firstdev, 0,
+                       MLB_MINOR_DEVICES, "mxc_mlb150");
+       if (ret < 0) {
+               dev_err(&pdev->dev, "alloc region error\n");
+               goto err_reg;
+       }
+       mlb_major = MAJOR(drvdata->firstdev);
+       dev_dbg(&pdev->dev, "MLB device major: %d\n", mlb_major);
+
+       cdev_init(&drvdata->cdev, &mxc_mlb150_fops);
+       drvdata->cdev.owner = THIS_MODULE;
+
+       ret = cdev_add(&drvdata->cdev, drvdata->firstdev, MLB_MINOR_DEVICES);
+       if (ret) {
+               dev_err(&pdev->dev, "can't add cdev\n");
+               goto err_reg;
+       }
+
+       /* create class and device for udev information */
+       drvdata->class = class_create(THIS_MODULE, "mlb150");
+       if (IS_ERR(drvdata->class)) {
+               dev_err(&pdev->dev, "failed to create device class\n");
+               ret = -ENOMEM;
+               goto err_class;
+       }
+
+       for (i = 0; i < MLB_MINOR_DEVICES; i++) {
+               struct device *class_dev;
+
+               class_dev = device_create(drvdata->class, NULL,
+                               MKDEV(mlb_major, i),
+                               NULL, mlb_devinfo[i].dev_name);
+               if (IS_ERR(class_dev)) {
+                       dev_err(&pdev->dev, "failed to create mlb150 %s"
+                               " class device\n", mlb_devinfo[i].dev_name);
+                       ret = -ENOMEM;
+                       goto err_dev;
+               }
+       }
+
+       drvdata->quirk_flag = pdev->id_entry->driver_data;
+
+       /* ahb0 irq */
+       drvdata->irq_ahb0 = platform_get_irq(pdev,  1);
+       if (drvdata->irq_ahb0 < 0) {
+               dev_err(&pdev->dev, "No ahb0 irq line provided\n");
+               goto err_dev;
+       }
+       dev_dbg(&pdev->dev, "ahb0_irq: %d\n", drvdata->irq_ahb0);
+       if (devm_request_irq(&pdev->dev, drvdata->irq_ahb0, mlb_ahb_isr,
+                               0, "mlb_ahb0", NULL)) {
+               dev_err(&pdev->dev, "can't claim irq %d\n", drvdata->irq_ahb0);
+               goto err_dev;
+       }
+
+       /* ahb1 irq */
+       drvdata->irq_ahb1 = platform_get_irq(pdev,  2);
+       if (drvdata->irq_ahb1 < 0) {
+               dev_err(&pdev->dev, "No ahb1 irq line provided\n");
+               goto err_dev;
+       }
+       dev_dbg(&pdev->dev, "ahb1_irq: %d\n", drvdata->irq_ahb1);
+       if (devm_request_irq(&pdev->dev, drvdata->irq_ahb1, mlb_ahb_isr,
+                               0, "mlb_ahb1", NULL)) {
+               dev_err(&pdev->dev, "can't claim irq %d\n", drvdata->irq_ahb1);
+               goto err_dev;
+       }
+
+       /* mlb irq */
+       drvdata->irq_mlb  = platform_get_irq(pdev,  0);
+       if (drvdata->irq_mlb < 0) {
+               dev_err(&pdev->dev, "No mlb irq line provided\n");
+               goto err_dev;
+       }
+       dev_dbg(&pdev->dev, "mlb_irq: %d\n", drvdata->irq_mlb);
+       if (devm_request_irq(&pdev->dev, drvdata->irq_mlb, mlb_isr,
+                               0, "mlb", NULL)) {
+               dev_err(&pdev->dev, "can't claim irq %d\n", drvdata->irq_mlb);
+               goto err_dev;
+       }
+
+       /* ioremap from phy mlb to kernel space */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "can't get device resources\n");
+               ret = -ENOENT;
+               goto err_dev;
+       }
+       mlb_base = devm_ioremap_resource(&pdev->dev, res);
+       dev_dbg(&pdev->dev, "mapped base address: 0x%08x\n", (u32)mlb_base);
+       if (IS_ERR(mlb_base)) {
+               dev_err(&pdev->dev,
+                       "failed to get ioremap base\n");
+               ret = PTR_ERR(mlb_base);
+               goto err_dev;
+       }
+       drvdata->membase = mlb_base;
+
+#ifdef CONFIG_REGULATOR
+       drvdata->nvcc = devm_regulator_get(&pdev->dev, "reg_nvcc");
+       if (!IS_ERR(drvdata->nvcc)) {
+               regulator_set_voltage(drvdata->nvcc, 2500000, 2500000);
+               dev_err(&pdev->dev, "enalbe regulator\n");
+               ret = regulator_enable(drvdata->nvcc);
+               if (ret) {
+                       dev_err(&pdev->dev, "vdd set voltage error\n");
+                       goto err_dev;
+               }
+       }
+#endif
+
+       /* enable clock */
+       drvdata->clk_mlb3p = devm_clk_get(&pdev->dev, "mlb");
+       if (IS_ERR(drvdata->clk_mlb3p)) {
+               dev_err(&pdev->dev, "unable to get mlb clock\n");
+               ret = PTR_ERR(drvdata->clk_mlb3p);
+               goto err_dev;
+       }
+
+       if (drvdata->quirk_flag & MLB_QUIRK_MLB150) {
+               drvdata->clk_mlb6p = devm_clk_get(&pdev->dev, "pll8_mlb");
+               if (IS_ERR(drvdata->clk_mlb6p)) {
+                       dev_err(&pdev->dev, "unable to get mlb pll clock\n");
+                       ret = PTR_ERR(drvdata->clk_mlb6p);
+                       goto err_dev;
+               }
+       }
+
+       drvdata->iram_pool = of_gen_pool_get(np, "iram", 0);
+       if (!drvdata->iram_pool) {
+               dev_err(&pdev->dev, "iram pool not available\n");
+               ret = -ENOMEM;
+               goto err_dev;
+       }
+
+       drvdata->devinfo = NULL;
+       mxc_mlb150_irq_enable(drvdata, 0);
+       platform_set_drvdata(pdev, drvdata);
+       return 0;
+
+err_dev:
+       for (--i; i >= 0; i--)
+               device_destroy(drvdata->class, MKDEV(mlb_major, i));
+
+       class_destroy(drvdata->class);
+err_class:
+       cdev_del(&drvdata->cdev);
+err_reg:
+       unregister_chrdev_region(drvdata->firstdev, MLB_MINOR_DEVICES);
+
+       return ret;
+}
+
+static int mxc_mlb150_remove(struct platform_device *pdev)
+{
+       int i;
+       struct mlb_data *drvdata = platform_get_drvdata(pdev);
+       struct mlb_dev_info *pdevinfo = drvdata->devinfo;
+
+       if (pdevinfo && atomic_read(&pdevinfo->on)
+               && (pdevinfo->fps >= CLK_2048FS))
+               clk_disable_unprepare(drvdata->clk_mlb6p);
+
+       if (pdevinfo && atomic_read(&pdevinfo->opencnt))
+               clk_disable_unprepare(drvdata->clk_mlb3p);
+
+       /* disable mlb power */
+#ifdef CONFIG_REGULATOR
+       if (!IS_ERR(drvdata->nvcc))
+               regulator_disable(drvdata->nvcc);
+#endif
+
+       /* destroy mlb device class */
+       for (i = MLB_MINOR_DEVICES - 1; i >= 0; i--)
+               device_destroy(drvdata->class,
+                               MKDEV(MAJOR(drvdata->firstdev), i));
+       class_destroy(drvdata->class);
+
+       cdev_del(&drvdata->cdev);
+
+       /* Unregister the two MLB devices */
+       unregister_chrdev_region(drvdata->firstdev, MLB_MINOR_DEVICES);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int mxc_mlb150_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct mlb_data *drvdata = platform_get_drvdata(pdev);
+       struct mlb_dev_info *pdevinfo = drvdata->devinfo;
+
+       if (pdevinfo && atomic_read(&pdevinfo->on)
+               && (pdevinfo->fps >= CLK_2048FS))
+               clk_disable_unprepare(drvdata->clk_mlb6p);
+
+       if (pdevinfo && atomic_read(&pdevinfo->opencnt)) {
+               mlb150_dev_exit();
+               clk_disable_unprepare(drvdata->clk_mlb3p);
+       }
+
+       return 0;
+}
+
+static int mxc_mlb150_resume(struct platform_device *pdev)
+{
+       struct mlb_data *drvdata = platform_get_drvdata(pdev);
+       struct mlb_dev_info *pdevinfo = drvdata->devinfo;
+
+       if (pdevinfo && atomic_read(&pdevinfo->opencnt)) {
+               clk_prepare_enable(drvdata->clk_mlb3p);
+               mlb150_dev_init();
+       }
+
+       if (pdevinfo && atomic_read(&pdevinfo->on) &&
+               (pdevinfo->fps >= CLK_2048FS))
+               clk_prepare_enable(drvdata->clk_mlb6p);
+
+       return 0;
+}
+#else
+#define mxc_mlb150_suspend NULL
+#define mxc_mlb150_resume NULL
+#endif
+
+/*
+ * platform driver structure for MLB
+ */
+static struct platform_driver mxc_mlb150_driver = {
+       .driver = {
+               .name = DRIVER_NAME,
+               .owner  = THIS_MODULE,
+               .of_match_table = mlb150_imx_dt_ids,
+       },
+       .probe = mxc_mlb150_probe,
+       .remove = mxc_mlb150_remove,
+       .suspend = mxc_mlb150_suspend,
+       .resume = mxc_mlb150_resume,
+       .id_table = imx_mlb150_devtype,
+};
+
+static int __init mxc_mlb150_init(void)
+{
+       return platform_driver_register(&mxc_mlb150_driver);
+}
+
+static void __exit mxc_mlb150_exit(void)
+{
+       platform_driver_unregister(&mxc_mlb150_driver);
+}
+
+module_init(mxc_mlb150_init);
+module_exit(mxc_mlb150_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MLB150 low level driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mxc_mlb.h b/include/linux/mxc_mlb.h
new file mode 100644 (file)
index 0000000..d7c792a
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * mxc_mlb.h
+ *
+ * Copyright 2008-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef _MXC_MLB_H
+#define _MXC_MLB_H
+
+/* define IOCTL command */
+#define MLB_DBG_RUNTIME                _IO('S', 0x09)
+#define MLB_SET_FPS            _IOW('S', 0x10, unsigned int)
+#define MLB_GET_VER            _IOR('S', 0x11, unsigned long)
+#define MLB_SET_DEVADDR                _IOR('S', 0x12, unsigned char)
+
+/*!
+ * set channel address for each logical channel
+ * the MSB 16bits is for tx channel, the left LSB is for rx channel
+ */
+#define MLB_CHAN_SETADDR       _IOW('S', 0x13, unsigned int)
+#define MLB_CHAN_STARTUP       _IO('S', 0x14)
+#define MLB_CHAN_SHUTDOWN      _IO('S', 0x15)
+#define MLB_CHAN_GETEVENT      _IOR('S', 0x16, unsigned long)
+
+#define MLB_SET_ISOC_BLKSIZE_188 _IO('S', 0x17)
+#define MLB_SET_ISOC_BLKSIZE_196 _IO('S', 0x18)
+#define MLB_SET_SYNC_QUAD      _IOW('S', 0x19, unsigned int)
+#define MLB_IRQ_ENABLE         _IO('S', 0x20)
+#define MLB_IRQ_DISABLE                _IO('S', 0x21)
+
+/*!
+ * MLB event define
+ */
+enum {
+       MLB_EVT_TX_PROTO_ERR_CUR = 1 << 0,
+       MLB_EVT_TX_BRK_DETECT_CUR = 1 << 1,
+       MLB_EVT_TX_PROTO_ERR_PREV = 1 << 8,
+       MLB_EVT_TX_BRK_DETECT_PREV = 1 << 9,
+       MLB_EVT_RX_PROTO_ERR_CUR = 1 << 16,
+       MLB_EVT_RX_BRK_DETECT_CUR = 1 << 17,
+       MLB_EVT_RX_PROTO_ERR_PREV = 1 << 24,
+       MLB_EVT_RX_BRK_DETECT_PREV = 1 << 25,
+};
+
+
+#endif                         /* _MXC_MLB_H */
diff --git a/include/uapi/linux/mxc_mlb.h b/include/uapi/linux/mxc_mlb.h
new file mode 100644 (file)
index 0000000..20ba524
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * mxc_mlb.h
+ *
+ * Copyright 2008-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef _MXC_MLB_UAPI_H
+#define _MXC_MLB_UAPI_H
+
+/* define IOCTL command */
+#define MLB_DBG_RUNTIME                _IO('S', 0x09)
+#define MLB_SET_FPS            _IOW('S', 0x10, unsigned int)
+#define MLB_GET_VER            _IOR('S', 0x11, unsigned long)
+#define MLB_SET_DEVADDR                _IOR('S', 0x12, unsigned char)
+
+/*!
+ * set channel address for each logical channel
+ * the MSB 16bits is for tx channel, the left LSB is for rx channel
+ */
+#define MLB_CHAN_SETADDR       _IOW('S', 0x13, unsigned int)
+#define MLB_CHAN_STARTUP       _IO('S', 0x14)
+#define MLB_CHAN_SHUTDOWN      _IO('S', 0x15)
+#define MLB_CHAN_GETEVENT      _IOR('S', 0x16, unsigned long)
+
+#define MLB_SET_ISOC_BLKSIZE_188 _IO('S', 0x17)
+#define MLB_SET_ISOC_BLKSIZE_196 _IO('S', 0x18)
+#define MLB_SET_SYNC_QUAD      _IOW('S', 0x19, unsigned int)
+#define MLB_IRQ_ENABLE         _IO('S', 0x20)
+#define MLB_IRQ_DISABLE                _IO('S', 0x21)
+
+/*!
+ * MLB event define
+ */
+enum {
+       MLB_EVT_TX_PROTO_ERR_CUR = 1 << 0,
+       MLB_EVT_TX_BRK_DETECT_CUR = 1 << 1,
+       MLB_EVT_TX_PROTO_ERR_PREV = 1 << 8,
+       MLB_EVT_TX_BRK_DETECT_PREV = 1 << 9,
+       MLB_EVT_RX_PROTO_ERR_CUR = 1 << 16,
+       MLB_EVT_RX_BRK_DETECT_CUR = 1 << 17,
+       MLB_EVT_RX_PROTO_ERR_PREV = 1 << 24,
+       MLB_EVT_RX_BRK_DETECT_PREV = 1 << 25,
+};
+
+
+#endif                         /* _MXC_MLB_H */