summaryrefslogtreecommitdiff
path: root/drivers/mxc/mlb/mxc_mlb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mxc/mlb/mxc_mlb.c')
-rwxr-xr-xdrivers/mxc/mlb/mxc_mlb.c2812
1 files changed, 2812 insertions, 0 deletions
diff --git a/drivers/mxc/mlb/mxc_mlb.c b/drivers/mxc/mlb/mxc_mlb.c
new file mode 100755
index 000000000000..a664118f6aad
--- /dev/null
+++ b/drivers/mxc/mlb/mxc_mlb.c
@@ -0,0 +1,2812 @@
+/*
+ * 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/dma-mapping.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 device *dev;
+ struct mlb_dev_info *devinfo;
+#ifdef CONFIG_ARCH_MXC_ARM64
+ struct clk *ipg;
+ struct clk *hclk;
+#endif
+ struct clk *mlb;
+ 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;
+ int irq_ahb0;
+ int irq_ahb1;
+ int irq_mlb;
+ u32 quirk_flag;
+ bool use_iram;
+};
+
+/*
+ * 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);
+ }
+
+ 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;
+
+ 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_mlb);
+ if (drvdata->irq_ahb1 > 0)
+ enable_irq(drvdata->irq_ahb1);
+ } else {
+ disable_irq(drvdata->irq_ahb0);
+ disable_irq(drvdata->irq_mlb);
+ if (drvdata->irq_ahb1 > 0)
+ disable_irq(drvdata->irq_ahb1);
+ }
+}
+
+/*
+ * 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:
+ rx_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:
+ rx_cis =
+ (cdt_val[2] & CDT_CTRL_ASYNC_RSTS_MASK)
+ >> CDT_CTRL_ASYNC_RSTS_SHIFT;
+ rx_cis = (cdt_val[3] & CDT_CTRL_ASYNC_RSTS_1) ?
+ (rx_cis | (0x1 << 4)) : rx_cis;
+ cdt_val[3] &= ~CDT_CTRL_ASYNC_RSTS_1;
+ cdt_val[2] &=
+ ~(0x4 << CDT_CTRL_ASYNC_RSTS_SHIFT);
+ break;
+ case MLB_CTYPE_ISOC:
+ rx_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 *buf_addr;
+ dma_addr_t 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);
+ drvdata->use_iram = true;
+
+ 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;
+ }
+
+#ifdef CONFIG_ARCH_MXC_ARM64
+ clk_prepare_enable(drvdata->ipg);
+ clk_prepare_enable(drvdata->hclk);
+#endif
+ clk_prepare_enable(drvdata->mlb);
+
+ /* 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 = gen_pool_dma_alloc(drvdata->iram_pool, buf_size, &phy_addr);
+ if (!buf_addr) {
+ drvdata->use_iram = false;
+ buf_addr = dma_alloc_coherent(drvdata->dev, buf_size, &phy_addr, GFP_KERNEL);
+ if (!buf_addr) {
+ ret = -ENOMEM;
+ pr_err("can not alloc rx/tx buffers: %d\n", buf_size);
+ return ret;
+ }
+ }
+
+ 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 = drvdata->use_iram ? buf_size : 0;
+
+ 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
+
+ if (drvdata->use_iram)
+ gen_pool_free(drvdata->iram_pool,
+ (ulong)pdevinfo->rbuf_base_virt, drvdata->iram_size);
+
+ mlb150_dev_exit();
+
+ atomic_set(&pdevinfo->on, 0);
+
+ clk_disable_unprepare(drvdata->mlb);
+#ifdef CONFIG_ARCH_MXC_ARM64
+ clk_disable_unprepare(drvdata->hclk);
+ clk_disable_unprepare(drvdata->ipg);
+#endif
+ /* 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_warn("mxc_mlb150: received data size is biggerthan size:"
+ "%d, count: %d\n", size, (int)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;
+ }
+
+ drvdata->dev = &pdev->dev;
+ 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);
+ dev_dbg(&pdev->dev, "ahb1_irq: %d\n", drvdata->irq_ahb1);
+ if (drvdata->irq_ahb1 > 0) {
+ 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);
+ 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
+
+#ifdef CONFIG_ARCH_MXC_ARM64
+ drvdata->ipg = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(drvdata->ipg)) {
+ dev_err(&pdev->dev, "unable to get mlb ipg clock\n");
+ ret = PTR_ERR(drvdata->ipg);
+ goto err_dev;
+ };
+
+ drvdata->hclk = devm_clk_get(&pdev->dev, "hclk");
+ if (IS_ERR(drvdata->hclk)) {
+ dev_err(&pdev->dev, "unable to get mlb hclk clock\n");
+ ret = PTR_ERR(drvdata->hclk);
+ goto err_dev;
+ };
+#endif
+
+ drvdata->mlb = devm_clk_get(&pdev->dev, "mlb");
+ if (IS_ERR(drvdata->mlb)) {
+ dev_err(&pdev->dev, "unable to get mlb clock\n");
+ ret = PTR_ERR(drvdata->mlb);
+ goto err_dev;
+ }
+
+ drvdata->iram_pool = of_gen_pool_get(np, "iram", 0);
+ if (!drvdata->iram_pool)
+ dev_warn(&pdev->dev, "no iram assigned, using external mem\n");
+
+ 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->opencnt))
+ clk_disable_unprepare(drvdata->mlb);
+
+ /* 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->opencnt)) {
+ mlb150_dev_exit();
+ clk_disable_unprepare(drvdata->mlb);
+ }
+
+ 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->mlb);
+ mlb150_dev_init();
+ }
+
+ 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");