From: Terry Lv Date: Mon, 26 Dec 2011 08:21:26 +0000 (+0800) Subject: ENGR00139278-3: Add MLB main driver for mx6q X-Git-Tag: v3.0.35-fsl~1705 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=d1f0f6cb6340ebf1c28fa79c2ba5cbbf90fae274;p=karo-tx-linux.git ENGR00139278-3: Add MLB main driver for mx6q Add MLB main driver for mx6q. Signed-off-by: Terry Lv --- diff --git a/drivers/mxc/mlb/Kconfig b/drivers/mxc/mlb/Kconfig index 7e3b16c2ddae..727451c1720c 100644 --- a/drivers/mxc/mlb/Kconfig +++ b/drivers/mxc/mlb/Kconfig @@ -5,9 +5,20 @@ menu "MXC Media Local Bus Driver" config MXC_MLB + boolean + +config MXC_MLB50 tristate "MLB support" depends on ARCH_MX35 || ARCH_MX53 + select MXC_MLB ---help--- Say Y to get the MLB support. +config MXC_MLB150 + tristate "MLB150 support" + depends on ARCH_MX6Q + select MXC_MLB + ---help--- + Say Y to get the MLB150 support. + endmenu diff --git a/drivers/mxc/mlb/Makefile b/drivers/mxc/mlb/Makefile index 60662eb1c031..d519978ffece 100644 --- a/drivers/mxc/mlb/Makefile +++ b/drivers/mxc/mlb/Makefile @@ -2,4 +2,5 @@ # Makefile for the kernel MLB driver # -obj-$(CONFIG_MXC_MLB) += mxc_mlb.o +obj-$(CONFIG_MXC_MLB50) += mxc_mlb.o +obj-$(CONFIG_MXC_MLB150) += mxc_mlb150.o diff --git a/drivers/mxc/mlb/mxc_mlb150.c b/drivers/mxc/mlb/mxc_mlb150.c new file mode 100755 index 000000000000..55bb99e94c1f --- /dev/null +++ b/drivers/mxc/mlb/mxc_mlb150.c @@ -0,0 +1,2338 @@ +/* + * Copyright (C) 2011 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "mxc_mlb150" + +/*! + * MLB module memory map registers define + */ +#define MLB150_REG_MLBC0 0x0 +#define MLB150_MLBC0_MLBEN (0x1) +#define MLB150_MLBC0_MLBCLK_MASK (0x7 << 2) +#define MLB150_MLBC0_MLBCLK_SHIFT (2) +#define MLB150_MLBC0_MLBPEN (0x1 << 5) +#define MLB150_MLBC0_MLBLK (0x1 << 7) +#define MLB150_MLBC0_ASYRETRY (0x1 << 12) +#define MLB150_MLBC0_CTLRETRY (0x1 << 12) +#define MLB150_MLBC0_FCNT_MASK (0x7 << 15) +#define MLB150_MLBC0_FCNT_SHIFT (15) + +#define MLB150_REG_MLBPC0 0x8 +#define MLB150_MLBPC0_MCLKHYS (0x1 << 11) + +#define MLB150_REG_MS0 0xC +#define MLB150_REG_MS1 0x14 + +#define MLB150_REG_MSS 0x20 +#define MLB150_MSS_RSTSYSCMD (0x1) +#define MLB150_MSS_LKSYSCMD (0x1 << 1) +#define MLB150_MSS_ULKSYSCMD (0x1 << 2) +#define MLB150_MSS_CSSYSCMD (0x1 << 3) +#define MLB150_MSS_SWSYSCMD (0x1 << 4) +#define MLB150_MSS_SERVREQ (0x1 << 5) + +#define MLB150_REG_MSD 0x24 + +#define MLB150_REG_MIEN 0x2C +#define MLB150_MIEN_ISOC_PE (0x1) +#define MLB150_MIEN_ISOC_BUFO (0x1 << 1) +#define MLB150_MIEN_SYNC_PE (0x1 << 16) +#define MLB150_MIEN_ARX_DONE (0x1 << 17) +#define MLB150_MIEN_ARX_PE (0x1 << 18) +#define MLB150_MIEN_ARX_BREAK (0x1 << 19) +#define MLB150_MIEN_ATX_DONE (0x1 << 20) +#define MLB150_MIEN_ATX_PE (0x1 << 21) +#define MLB150_MIEN_ATX_BREAK (0x1 << 22) +#define MLB150_MIEN_CRX_DONE (0x1 << 24) +#define MLB150_MIEN_CRX_PE (0x1 << 25) +#define MLB150_MIEN_CRX_BREAK (0x1 << 26) +#define MLB150_MIEN_CTX_DONE (0x1 << 27) +#define MLB150_MIEN_CTX_PE (0x1 << 28) +#define MLB150_MIEN_CTX_BREAK (0x1 << 29) + +#define MLB150_REG_MLBPC2 0x34 +#define MLB150_REG_MLBPC1 0x38 +#define MLB150_MLBPC1_VAL (0x00000888) + +#define MLB150_REG_MLBC1 0x3C +#define MLB150_MLBC1_LOCK (0x1 << 6) +#define MLB150_MLBC1_CLKM (0x1 << 7) +#define MLB150_MLBC1_NDA_MASK (0xFF << 8) +#define MLB150_MLBC1_NDA_SHIFT (8) + +#define MLB150_REG_HCTL 0x80 +#define MLB150_HCTL_RST0 (0x1) +#define MLB150_HCTL_RST1 (0x1 << 1) +#define MLB150_HCTL_EN (0x1 << 15) + +#define MLB150_REG_HCMR0 0x88 +#define MLB150_REG_HCMR1 0x8C +#define MLB150_REG_HCER0 0x90 +#define MLB150_REG_HCER1 0x94 +#define MLB150_REG_HCBR0 0x98 +#define MLB150_REG_HCBR1 0x9C + +#define MLB150_REG_MDAT0 0xC0 +#define MLB150_REG_MDAT1 0xC4 +#define MLB150_REG_MDAT2 0xC8 +#define MLB150_REG_MDAT3 0xCC + +#define MLB150_REG_MDWE0 0xD0 +#define MLB150_REG_MDWE1 0xD4 +#define MLB150_REG_MDWE2 0xD8 +#define MLB150_REG_MDWE3 0xDC + +#define MLB150_REG_MCTL 0xE0 +#define MLB150_MCTL_XCMP (0x1) + +#define MLB150_REG_MADR 0xE4 +#define MLB150_MADR_WNR (0x1 << 31) +#define MLB150_MADR_TB (0x1 << 30) +#define MLB150_MADR_ADDR_MASK (0x7f << 8) +#define MLB150_MADR_ADDR_SHIFT (0) + +#define MLB150_REG_ACTL 0x3C0 +#define MLB150_ACTL_MPB (0x1 << 4) +#define MLB150_ACTL_DMAMODE (0x1 << 2) +#define MLB150_ACTL_SMX (0x1 << 1) +#define MLB150_ACTL_SCE (0x1) + +#define MLB150_REG_ACSR0 0x3D0 +#define MLB150_REG_ACSR1 0x3D4 +#define MLB150_REG_ACMR0 0x3D8 +#define MLB150_REG_ACMR1 0x3DC + +#define MLB150_REG_CAT_MDATn(ch) (MLB150_REG_MDAT0 + ((ch % 8) >> 1) * 4) +#define MLB150_REG_CAT_MDWEn(ch) (MLB150_REG_MDWE0 + ((ch % 8) >> 1) * 4) + +#define MLB150_LOGIC_CH_NUM (64) +#define MLB150_BUF_CDT_OFFSET (0x0) +#define MLB150_BUF_ADT_OFFSET (0x40) +#define MLB150_BUF_CAT_MLB_OFFSET (0x80) +#define MLB150_BUF_CAT_HBI_OFFSET (0x88) +#define MLB150_BUF_CTR_END_OFFSET (0x8F) + +#define MLB150_CAT_MODE_RX (0x1 << 0) +#define MLB150_CAT_MODE_TX (0x1 << 1) +#define MLB150_CAT_MODE_INBOUND_DMA (0x1 << 8) +#define MLB150_CAT_MODE_OUTBOUND_DMA (0x1 << 9) + + +static u32 mlb150_fs_phy_ch_num_per_frm[8] = { + 8, 16, 32, 58, 87, 117, 161, 215 +}; + +#define MLB150_CH_SYNC_BUF_DEP (215 * 4 * 4) +#define MLB150_CH_CTRL_BUF_DEP (64) +#define MLB150_CH_ASYNC_BUF_DEP (1536) +#define MLB150_CH_ISOC_BLK_SIZE (196) +#define MLB150_CH_ISOC_BUF_DEP (MLB150_CH_ISOC_BLK_SIZE * 3) + +#define MLB150_CH_SYNC_DBR_BUF_OFFSET (0x0) +#define MLB150_CH_CTRL_DBR_BUF_OFFSET (MLB150_CH_SYNC_DBR_BUF_OFFSET + 2 * MLB150_CH_SYNC_BUF_DEP) +#define MLB150_CH_ASYNC_DBR_BUF_OFFSET (MLB150_CH_CTRL_DBR_BUF_OFFSET + 2 * MLB150_CH_CTRL_BUF_DEP) +#define MLB150_CH_ISOC_DBR_BUF_OFFSET (MLB150_CH_ASYNC_DBR_BUF_OFFSET + 2 * MLB150_CH_ASYNC_BUF_DEP) + +static u32 mlb150_ch_packet_buf_size[4] = { + MLB150_CH_SYNC_BUF_DEP, + MLB150_CH_CTRL_BUF_DEP, + MLB150_CH_ASYNC_BUF_DEP, + MLB150_CH_ISOC_BUF_DEP +}; + +#define MLB150_DBR_BUF_START 0x00000 + +#define MLB150_CDT_LEN (16) +#define MLB150_ADT_LEN (16) +#define MLB150_CAT_LEN (2) + +#define MLB150_CDT_SZ (MLB150_CDT_LEN * MLB150_LOGIC_CH_NUM) +#define MLB150_ADT_SZ (MLB150_ADT_LEN * MLB150_LOGIC_CH_NUM) +#define MLB150_CAT_SZ (MLB150_CAT_LEN * MLB150_LOGIC_CH_NUM * 2) + +#define MLB150_CDT_BASE(base) (base + MLB150_BUF_CDT_OFFSET) +#define MLB150_ADT_BASE(base) (base + MLB150_BUF_ADT_OFFSET) +#define MLB150_CAT_MLB_BASE(base) (base + MLB150_BUF_CAT_MLB_OFFSET) +#define MLB150_CAT_HBI_BASE(base) (base + MLB150_BUF_CAT_HBI_OFFSET) + +#define MLB150_CDTn_ADDR(base, n) (base + MLB150_BUF_CDT_OFFSET + n * MLB150_CDT_LEN) +#define MLB150_ADTn_ADDR(base, n) (base + MLB150_BUF_ADT_OFFSET + n * MLB150_ADT_LEN) +#define MLB150_CATn_MLB_ADDR(base, n) (base + MLB150_BUF_CAT_MLB_OFFSET + n * MLB150_CAT_LEN) +#define MLB150_CATn_HBI_ADDR(base, n) (base + MLB150_BUF_CAT_HBI_OFFSET + n * MLB150_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_CONTROL_TX_CHANN (0 << 4) +#define MLB_CONTROL_RX_CHANN (1 << 4) +#define MLB_ASYNC_TX_CHANN (2 << 4) +#define MLB_ASYNC_RX_CHANN (3 << 4) + +#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 PING_BUF_MAX_SIZE (2 * 1024) +#define PONG_BUF_MAX_SIZE (2 * 1024) +/* max package data size */ +#define ASYNC_PACKET_SIZE 1024 +#define CTRL_PACKET_SIZE 64 +#define TRANS_RING_NODES 10 + +#define MLB_IRAM_SIZE (MLB_MINOR_DEVICES * (PING_BUF_MAX_SIZE + PONG_BUF_MAX_SIZE) * 2) +#define _get_txchan(dev) mlb_devinfo[dev].channels[TX_CHANNEL] +#define _get_rxchan(dev) mlb_devinfo[dev].channels[RX_CHANNEL] + +enum MLB_CTYPE { + MLB_CTYPE_SYNC, + MLB_CTYPE_CTRL, + MLB_CTYPE_ASYNC, + MLB_CTYPE_ISOC, +}; + +enum MLB150_CLK_SPEED { + MLB150_CLK_256FS, + MLB150_CLK_512FS, + MLB150_CLK_1024FS, + MLB150_CLK_2048FS, + MLB150_CLK_3072FS, + MLB150_CLK_4096FS, + MLB150_CLK_6144FS, + MLB150_CLK_8192FS, +}; + +/*! + * Ring buffer + */ +#define MLB_RING_BUF_INIT(r) { \ + r->wpos = 0; \ + r->rpos = 0; \ +} + +#define MLB_RING_BUF_IS_FULL(r) (((r->wpos + 1) % TRANS_RING_NODES) == r->rpos) +#define MLB_RING_BUF_IS_EMPTY(r) (r->rpos == r->wpos) +#define MLB_RING_BUF_ENQUE(r, buf) { \ + memcpy(r->node[r->wpos].data, buf, r->node.size); \ + r->wpos = (r->wpos + 1) % TRANS_RING_NODES; \ +} +#define MLB_RING_BUF_DEQUE(r, buf) { \ + memcpy(buf, r->node[r->rpos].data, r->node.size); \ + r->rpos = (r->rpos + 1) % TRANS_RING_NODES; \ +} + + +struct mlb_ringnode { + u32 size; + u8 *data; +}; + +struct mlb_ringbuffer { + u32 wpos; + u32 rpos; + struct mlb_ringnode node[TRANS_RING_NODES]; +}; + +struct mlb_channel_info { + + /* channel address */ + s32 address; + /* DBR buf head */ + u32 dbr_buf_head; + /* ping buffer head */ + u32 ping_buf_head; + /* pong buffer head */ + u32 pong_buf_head; + /* ping buffer physical head */ + u32 ping_phy_head; + /* pong buffer physical head */ + u32 pong_phy_head; + /* channel buffer size */ + u32 buf_size; + /* channel buffer current ptr */ + u32 buf_ptr; + /* packet start indicator */ + u32 ps_ind; + /* packet remain size */ + u32 pkt_remain_size; + /* buffer spin lock */ + rwlock_t buf_lock; +}; + +struct mlb_dev_info { + + /* device node name */ + const char dev_name[20]; + /* channel type */ + const unsigned int channel_type; + /* ch fps */ + enum MLB150_CLK_SPEED fps; + /* channel info for tx/rx */ + struct mlb_channel_info channels[2]; + /* rx ring buffer */ + struct mlb_ringnode rx_bufs[TRANS_RING_NODES]; + /* rx ring buffer read/write ptr */ + int rx_rdpos, rx_wtpos; + /* tx ring buffer */ + struct mlb_ringnode tx_bufs[TRANS_RING_NODES]; + /* tx ring buffer read/write ptr */ + int tx_rdpos, tx_wtpos; + /* exception event */ + unsigned long ex_event; + /* channel started up or not */ + atomic_t on; + /* device open count */ + atomic_t opencnt; + /* wait queue head for channel */ + wait_queue_head_t rd_wq; + wait_queue_head_t wt_wq; + /* spinlock for event access */ + spinlock_t event_lock; +}; + +static struct mlb_dev_info mlb_devinfo[MLB_MINOR_DEVICES] = { + { + .dev_name = MLB_SYNC_DEV_NAME, + .channel_type = MLB_CTYPE_SYNC, + .channels = { + [0] = { + .buf_size = MLB150_CH_SYNC_BUF_DEP, + .dbr_buf_head = MLB150_CH_SYNC_DBR_BUF_OFFSET, + .buf_lock = + __RW_LOCK_UNLOCKED(mlb_devinfo[0].channels[0]. + buf_lock), + }, + [1] = { + .buf_size = MLB150_CH_SYNC_BUF_DEP, + .dbr_buf_head = MLB150_CH_SYNC_DBR_BUF_OFFSET + + MLB150_CH_SYNC_BUF_DEP, + .buf_lock = + __RW_LOCK_UNLOCKED(mlb_devinfo[0].channels[1]. + buf_lock), + }, + }, + .on = ATOMIC_INIT(0), + .opencnt = ATOMIC_INIT(0), + .rd_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[0].rd_wq), + .wt_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[0].wt_wq), + .event_lock = __SPIN_LOCK_UNLOCKED(mlb_devinfo[0].event_lock), + }, + { + .dev_name = MLB_CONTROL_DEV_NAME, + .channel_type = MLB_CTYPE_CTRL, + .channels = { + [0] = { + .buf_size = MLB150_CH_CTRL_BUF_DEP, + .dbr_buf_head = MLB150_CH_CTRL_DBR_BUF_OFFSET, + .buf_lock = + __RW_LOCK_UNLOCKED(mlb_devinfo[1].channels[0]. + buf_lock), + }, + [1] = { + .buf_size = MLB150_CH_CTRL_BUF_DEP, + .dbr_buf_head = MLB150_CH_CTRL_DBR_BUF_OFFSET + + MLB150_CH_CTRL_BUF_DEP, + .buf_lock = + __RW_LOCK_UNLOCKED(mlb_devinfo[1].channels[1]. + buf_lock), + }, + }, + .on = ATOMIC_INIT(0), + .opencnt = ATOMIC_INIT(0), + .rd_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[1].rd_wq), + .wt_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[1].wt_wq), + .event_lock = __SPIN_LOCK_UNLOCKED(mlb_devinfo[1].event_lock), + }, + { + .dev_name = MLB_ASYNC_DEV_NAME, + .channel_type = MLB_CTYPE_ASYNC, + .channels = { + [0] = { + .buf_size = MLB150_CH_ASYNC_BUF_DEP, + .dbr_buf_head = MLB150_CH_ASYNC_DBR_BUF_OFFSET, + .buf_lock = + __RW_LOCK_UNLOCKED(mlb_devinfo[2].channels[0]. + buf_lock), + }, + [1] = { + .buf_size = MLB150_CH_ASYNC_BUF_DEP, + .dbr_buf_head = MLB150_CH_ASYNC_DBR_BUF_OFFSET + + MLB150_CH_ASYNC_BUF_DEP, + .buf_lock = + __RW_LOCK_UNLOCKED(mlb_devinfo[2].channels[1]. + buf_lock), + }, + }, + .on = ATOMIC_INIT(0), + .opencnt = ATOMIC_INIT(0), + .rd_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[2].rd_wq), + .wt_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[2].wt_wq), + .event_lock = __SPIN_LOCK_UNLOCKED(mlb_devinfo[2].event_lock), + }, + { + .dev_name = MLB_ISOC_DEV_NAME, + .channel_type = MLB_CTYPE_ISOC, + .channels = { + [0] = { + .buf_size = MLB150_CH_ISOC_BUF_DEP, + .dbr_buf_head = MLB150_CH_ISOC_DBR_BUF_OFFSET, + .buf_lock = + __RW_LOCK_UNLOCKED(mlb_devinfo[3].channels[0]. + buf_lock), + }, + [1] = { + .buf_size = MLB150_CH_ISOC_BUF_DEP, + .dbr_buf_head = MLB150_CH_ISOC_DBR_BUF_OFFSET + + MLB150_CH_ISOC_BUF_DEP, + .buf_lock = + __RW_LOCK_UNLOCKED(mlb_devinfo[3].channels[1]. + buf_lock), + }, + }, + .on = ATOMIC_INIT(0), + .opencnt = ATOMIC_INIT(0), + .rd_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[3].rd_wq), + .wt_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[3].wt_wq), + .event_lock = __SPIN_LOCK_UNLOCKED(mlb_devinfo[3].event_lock), + }, +}; + +static struct regulator *reg_nvcc; /* NVCC_MLB regulator */ +static struct clk *mlb_clk; +static struct clk *mlb_pll_clk; +static dev_t dev; +static struct class *mlb_class; /* device class */ +static struct device *class_dev; +static u32 mlb_base; /* mlb module base address */ +static u32 ahb0_irq, ahb1_irq, mlb_irq; +static unsigned long iram_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(MLB150_REG_MLBC0); + DUMP_REG(MLB150_REG_MLBPC0); + DUMP_REG(MLB150_REG_MS0); + DUMP_REG(MLB150_REG_MS1); + DUMP_REG(MLB150_REG_MSS); + DUMP_REG(MLB150_REG_MSD); + DUMP_REG(MLB150_REG_MIEN); + DUMP_REG(MLB150_REG_MLBPC2); + DUMP_REG(MLB150_REG_MLBPC1); + DUMP_REG(MLB150_REG_MLBC1); + DUMP_REG(MLB150_REG_HCTL); + DUMP_REG(MLB150_REG_HCMR0); + DUMP_REG(MLB150_REG_HCMR1); + DUMP_REG(MLB150_REG_HCER0); + DUMP_REG(MLB150_REG_HCER1); + DUMP_REG(MLB150_REG_HCBR0); + DUMP_REG(MLB150_REG_HCBR1); + DUMP_REG(MLB150_REG_MDAT0); + DUMP_REG(MLB150_REG_MDAT1); + DUMP_REG(MLB150_REG_MDAT2); + DUMP_REG(MLB150_REG_MDAT3); + DUMP_REG(MLB150_REG_MDWE0); + DUMP_REG(MLB150_REG_MDWE1); + DUMP_REG(MLB150_REG_MDWE2); + DUMP_REG(MLB150_REG_MDWE3); + DUMP_REG(MLB150_REG_MCTL); + DUMP_REG(MLB150_REG_MADR); + DUMP_REG(MLB150_REG_ACTL); + DUMP_REG(MLB150_REG_ACSR0); + DUMP_REG(MLB150_REG_ACSR1); + DUMP_REG(MLB150_REG_ACMR0); + DUMP_REG(MLB150_REG_ACMR1); +} +#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 + MLB150_REG_MDWE0); + __raw_writel(mdat1_bits_en, mlb_base + MLB150_REG_MDWE1); + __raw_writel(mdat2_bits_en, mlb_base + MLB150_REG_MDWE2); + __raw_writel(mdat3_bits_en, mlb_base + MLB150_REG_MDWE3); +} + +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(MLB150_MADR_TB | dbr_addr, + mlb_base + MLB150_REG_MADR); + + while ((!(__raw_readl(mlb_base + MLB150_REG_MCTL) + & MLB150_MCTL_XCMP)) && + timeout--) + ; + + if (unlikely(0 == timeout)) + return -ETIME; + + dbr_val = __raw_readl(mlb_base + MLB150_REG_MDAT0) & 0x000000ff; + + __raw_writel(0, mlb_base + MLB150_REG_MCTL); + spin_unlock_irqrestore(&ctr_lock, flags); + + return dbr_val; +} + +static inline s32 mlb150_dev_dbr_write(u32 dbr_addr, u32 dbr_val) +{ + s32 timeout = 1000; + unsigned long flags; + + spin_lock_irqsave(&ctr_lock, flags); + __raw_writel(dbr_val, mlb_base + MLB150_REG_MDAT0); + + __raw_writel(MLB150_MADR_WNR | MLB150_MADR_TB | dbr_addr, + mlb_base + MLB150_REG_MADR); + + while ((!(__raw_readl(mlb_base + MLB150_REG_MCTL) + & MLB150_MCTL_XCMP)) && + timeout--) + ; + + if (unlikely(timeout <= 0)) + return -ETIME; + + __raw_writel(0, mlb_base + MLB150_REG_MCTL); + spin_unlock_irqrestore(&ctr_lock, flags); + + return 0; +} + +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 + MLB150_REG_MADR); + + while ((!(__raw_readl(mlb_base + MLB150_REG_MCTL) + & MLB150_MCTL_XCMP)) && + timeout--) + ; + + if (unlikely(timeout <= 0)) { + pr_debug("mxc_mlb150: Read CTR timeout\n"); + return -ETIME; + } + + ctr_val[0] = __raw_readl(mlb_base + MLB150_REG_MDAT0); + ctr_val[1] = __raw_readl(mlb_base + MLB150_REG_MDAT1); + ctr_val[2] = __raw_readl(mlb_base + MLB150_REG_MDAT2); + ctr_val[3] = __raw_readl(mlb_base + MLB150_REG_MDAT3); + + __raw_writel(0, mlb_base + MLB150_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 + MLB150_REG_MDAT0); + __raw_writel(ctr_val[1], mlb_base + MLB150_REG_MDAT1); + __raw_writel(ctr_val[2], mlb_base + MLB150_REG_MDAT2); + __raw_writel(ctr_val[3], mlb_base + MLB150_REG_MDAT3); + + __raw_writel(MLB150_MADR_WNR | ctr_offset, + mlb_base + MLB150_REG_MADR); + + while ((!(__raw_readl(mlb_base + MLB150_REG_MCTL) + & MLB150_MCTL_XCMP)) && + timeout--) + ; + + if (unlikely(timeout <= 0)) { + pr_debug("mxc_mlb150: Write CTR timeout\n"); + return -ETIME; + } + + __raw_writel(0, mlb_base + MLB150_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"); + return -EBADE; + } + } else { + pr_debug("mxc_mlb150: ctr read failed\n"); + return -EBADE; + } + } +#endif + + return 0; +} + +static s32 mlb150_dev_cat_read(u32 ctr_offset, u32 ch, u16 *cat_val) +{ + u16 ctr_val[8] = { 0 }; + + if (unlikely(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; +} + +static s32 mlb150_dev_cat_write(u32 ctr_offset, u32 ch, const u16 cat_val) +{ + u16 ctr_val[8] = { 0 }; + + if (unlikely(mlb150_dev_ctr_read(ctr_offset, (u32 *)ctr_val))) + return -ETIME; + + /* Use u16 array to write u32 array value, + * need to convert */ + ctr_val[ch % 8] = cat_val; + if (unlikely(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(MLB150_BUF_CAT_MLB_OFFSET + (ch >> 3), ch, cat_val) +#define mlb150_dev_cat_mlb_write(ch, cat_val) \ + mlb150_dev_cat_write(MLB150_BUF_CAT_MLB_OFFSET + (ch >> 3), ch, cat_val) +#define mlb150_dev_cat_hbi_read(ch, cat_val) \ + mlb150_dev_cat_read(MLB150_BUF_CAT_HBI_OFFSET + (ch >> 3), ch, cat_val) +#define mlb150_dev_cat_hbi_write(ch, cat_val) \ + mlb150_dev_cat_write(MLB150_BUF_CAT_HBI_OFFSET + (ch >> 3), ch, cat_val) + +#define mlb150_dev_cdt_read(ch, cdt_val) \ + mlb150_dev_ctr_read(MLB150_BUF_CDT_OFFSET + ch, cdt_val) +#define mlb150_dev_cdt_write(ch, cdt_val) \ + mlb150_dev_ctr_write(MLB150_BUF_CDT_OFFSET + ch, cdt_val) +#define mlb150_dev_adt_read(ch, adt_val) \ + mlb150_dev_ctr_read(MLB150_BUF_ADT_OFFSET + ch, adt_val) +#define mlb150_dev_adt_write(ch, adt_val) \ + mlb150_dev_ctr_write(MLB150_BUF_ADT_OFFSET + ch, adt_val) + +#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 = MLB150_BUF_CDT_OFFSET + ch_start; + i < MLB150_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 = MLB150_BUF_ADT_OFFSET + ch_start; + i < MLB150_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 = MLB150_BUF_CAT_MLB_OFFSET + (ch_start >> 3); + i < MLB150_BUF_CAT_MLB_OFFSET + (ch_end >> 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 = MLB150_BUF_CAT_HBI_OFFSET + (ch_start >> 3); + i < MLB150_BUF_CAT_HBI_OFFSET + (ch_end >> 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 s32 mlb150_dev_enable_dma_irq(u32 enable) +{ + if (enable) { + __raw_writel(0xffffffff, mlb_base + MLB150_REG_ACMR0); + __raw_writel(0xffffffff, mlb_base + MLB150_REG_ACMR1); + } else { + __raw_writel(0x0, mlb_base + MLB150_REG_ACMR0); + __raw_writel(0x0, mlb_base + MLB150_REG_ACMR1); + } + + return 0; +} + + +static s32 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 + MLB150_REG_ACTL); + reg |= MLB150_ACTL_DMAMODE; +#ifdef MLB150_MULTIPLE_PACKAGE_MODE + reg |= MLB150_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 |= MLB150_ACTL_SMX; + */ + + __raw_writel(reg, mlb_base + MLB150_REG_ACTL); + + return 0; +} + +static inline s32 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 + MLB150_REG_MS0); + __raw_writel(0, mlb_base + MLB150_REG_MS1); + + /* Step 1, Program MIEN to enable protocol error + * interrupts for all active MLB channels */ + if (enable) + __raw_writel(MLB150_MIEN_CTX_PE | + MLB150_MIEN_CRX_PE | MLB150_MIEN_ATX_PE | + MLB150_MIEN_ARX_PE | MLB150_MIEN_SYNC_PE | + MLB150_MIEN_ISOC_PE, + mlb_base + MLB150_REG_MIEN); + else + __raw_writel(0, mlb_base + MLB150_REG_MIEN); + + return 0; +} + +static void mlb150_dev_init(void) +{ + u32 c0_val, hctl_val; + + /* Disable EN bits */ + c0_val = __raw_readl(mlb_base + MLB150_REG_MLBC0); + c0_val &= ~MLB150_MLBC0_MLBEN; + __raw_writel(c0_val, mlb_base + MLB150_REG_MLBC0); + + hctl_val = __raw_readl(mlb_base + MLB150_REG_HCTL); + hctl_val &= ~MLB150_HCTL_EN; + __raw_writel(hctl_val, mlb_base + MLB150_REG_HCTL); + + /* Step 1, Configure the MediaLB interface */ + /* Select pin mode and clock, 3-pin and 256fs */ + c0_val = __raw_readl(mlb_base + MLB150_REG_MLBC0); + c0_val &= ~(MLB150_MLBC0_MLBPEN | MLB150_MLBC0_MLBCLK_MASK); + __raw_writel(c0_val, mlb_base + MLB150_REG_MLBC0); + + c0_val |= MLB150_MLBC0_MLBEN; + __raw_writel(c0_val, mlb_base + MLB150_REG_MLBC0); + + /* Step 2, Configure the HBI interface */ + __raw_writel(0xffffffff, mlb_base + MLB150_REG_HCMR0); + __raw_writel(0xffffffff, mlb_base + MLB150_REG_HCMR1); + __raw_writel(MLB150_HCTL_EN, mlb_base + MLB150_REG_HCTL); + + mlb150_dev_init_ir_amba_ahb(); + + mlb150_dev_enable_ir_mlb(1); +} + +static s32 mlb150_dev_reset_cdt(void) +{ + int i = 0; + u32 ctr_val[4] = { 0 }; + + for (i = 0; i < (MLB150_LOGIC_CH_NUM); ++i) + mlb150_dev_ctr_write(MLB150_BUF_CDT_OFFSET + i, ctr_val); + + return 0; +} + +static s32 mlb150_dev_init_ch_cdt(u32 ch, enum MLB_CTYPE ctype, u32 ch_func) +{ + u32 cdt_val[4] = { 0 }; + + /* a. Set the 14-bit base address (BA) */ + cdt_val[3] = (mlb_devinfo[ctype].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 */ + switch (ctype) { + case MLB_CTYPE_SYNC: + /* For synchronous channels: (BD + 1) = 4 * m * bpf */ + cdt_val[3] |= ((4 * mlb150_fs_phy_ch_num_per_frm[mlb_devinfo[0].fps] * 4) - 1) + << CDT_BD_SHIFT; + break; + case MLB_CTYPE_CTRL: + /* For control channels: (BD + 1) >= max packet length (64) */ + /* BD */ + cdt_val[3] |= ((MLB150_CH_CTRL_BUF_DEP - 1) << CDT_BD_SHIFT); + break; + case MLB_CTYPE_ASYNC: + /* For asynchronous channels: (BD + 1) >= max packet length + * 1024 for a MOST Data packet (MDP); + * 1536 for a MOST Ethernet Packet (MEP) */ + cdt_val[3] |= ((MLB150_CH_ASYNC_BUF_DEP - 1) << CDT_BD_SHIFT); + break; + case MLB_CTYPE_ISOC: + /* For isochronous channels: (BD + 1) mod (BS + 1) = 0 */ + /* BS */ + cdt_val[1] |= (MLB150_CH_ISOC_BLK_SIZE - 1); + /* BD */ + cdt_val[3] |= (MLB150_CH_ISOC_BUF_DEP - 1) + << CDT_BD_SHIFT; + break; + default: + break; + } + + 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 (unlikely(mlb150_dev_cdt_write(ch, cdt_val))) + return -ETIME; + +#ifdef DEBUG_CTR + { + u32 cdt_rd[4] = { 0 }; + if (likely(!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 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) | ch; + + if (cat_mode & MLB150_CAT_MODE_OUTBOUND_DMA) + cat_val |= CAT_RNW; + + if (MLB_CTYPE_SYNC == ctype) + cat_val |= CAT_MT; + + switch (cat_mode) { + case MLB150_CAT_MODE_RX | MLB150_CAT_MODE_INBOUND_DMA: + case MLB150_CAT_MODE_TX | MLB150_CAT_MODE_OUTBOUND_DMA: + if (unlikely(mlb150_dev_cat_mlb_write(ch, cat_val))) + return -ETIME; +#ifdef DEBUG_CTR + if (likely(!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 MLB150_CAT_MODE_TX | MLB150_CAT_MODE_INBOUND_DMA: + case MLB150_CAT_MODE_RX | MLB150_CAT_MODE_OUTBOUND_DMA: + if (unlikely(mlb150_dev_cat_hbi_write(ch, cat_val))) + return -ETIME; +#ifdef DEBUG_CTR + if (likely(!mlb150_dev_cat_hbi_read(ch, &cat_rd))) + pr_debug("mxc_mlb150: CAT val of hbi channel %d: 0x%04x", + ch, cat_rd); + else { + pr_debug("mxc_mlb150: Read CAT of hbi channel %d failed\n", + ch); + 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 s32 mlb150_dev_reset_cat(void) +{ + int i = 0; + u32 ctr_val[4] = { 0 }; + + for (i = 0; i < (MLB150_LOGIC_CH_NUM >> 3); ++i) { + mlb150_dev_ctr_write(MLB150_BUF_CAT_MLB_OFFSET + i, ctr_val); + mlb150_dev_ctr_write(MLB150_BUF_CAT_HBI_OFFSET + i, ctr_val); + } + + return 0; +} + +static s32 mlb150_dev_init_rfb(u32 rx_ch, u32 tx_ch, enum MLB_CTYPE ctype) +{ + mlb150_dev_enable_ctr_write(0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff); + + /* 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(rx_ch, ctype, RX_CHANNEL); + mlb150_dev_init_ch_cdt(tx_ch, ctype, TX_CHANNEL); + + /* Step 4&5, Program the CAT for the inbound and outbound DMA */ + mlb150_dev_init_ch_cat(rx_ch, + MLB150_CAT_MODE_RX | MLB150_CAT_MODE_INBOUND_DMA, + ctype); + mlb150_dev_init_ch_cat(rx_ch, + MLB150_CAT_MODE_RX | MLB150_CAT_MODE_OUTBOUND_DMA, + ctype); + mlb150_dev_init_ch_cat(tx_ch, + MLB150_CAT_MODE_TX | MLB150_CAT_MODE_INBOUND_DMA, + ctype); + mlb150_dev_init_ch_cat(tx_ch, + MLB150_CAT_MODE_TX | MLB150_CAT_MODE_OUTBOUND_DMA, + ctype); + + return 0; +} + +static s32 mlb150_dev_reset_adt(void) +{ + int i = 0; + u32 ctr_val[4] = { 0 }; + + for (i = 0; i < (MLB150_LOGIC_CH_NUM); ++i) + mlb150_dev_ctr_write(MLB150_BUF_ADT_OFFSET + i, ctr_val); + + return 0; +} + +static s32 mlb150_dev_set_ch_amba_ahb(u32 ch, enum MLB_CTYPE ctype, u32 dne_sts) +{ + /* Only set MDAT1 in this function */ + /* In MDWE, only MDWE1 should be enabled */ + 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; + else + ctr_val[1] |= ADT_RDY1; + +#ifdef DEBUG_ADT + pr_debug("mxc_mlb150: Set ADT val of channel %d, ctype: %d: " + "0x%08x 0x%08x 0x%08x 0x%08x\n", + ch, ctype, ctr_val[3], ctr_val[2], ctr_val[1], ctr_val[0]); +#endif + + if (unlikely(mlb150_dev_adt_write(ch, ctr_val))) + return -ETIME; + +#ifdef DEBUG_ADT + { + u32 ctr_rd[4] = { 0 }; + if (likely(!mlb150_dev_adt_read(ch, ctr_rd))) { + 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", + ch); + return -EBADE; + } + } +#endif + + return 0; +} + +static s32 mlb150_dev_init_ch_amba_ahb(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] = chinfo->pong_phy_head; + ctr_val[2] = chinfo->ping_phy_head; + ctr_val[1] = (chinfo->buf_size - 1) << ADT_BD1_SHIFT; + ctr_val[1] |= (chinfo->buf_size - 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); + + if (unlikely(mlb150_dev_adt_write(chinfo->address, ctr_val))) + return -ETIME; + +#ifdef DEBUG_CTR + { + u32 ctr_rd[4] = { 0 }; + if (likely(!mlb150_dev_adt_read(chinfo->address, ctr_rd))) { + pr_debug("mxc_mlb150: ADT val of channel %d: " + "0x%08x 0x%08x 0x%08x 0x%08x\n", + chinfo->address, 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->address); + return -EBADE; + } + } +#endif + + return 0; +} + +static s32 mlb150_dev_init_amba_ahb(struct mlb_channel_info *rx_chinfo, + struct mlb_channel_info *tx_chinfo, enum MLB_CTYPE ctype) +{ + mlb150_dev_enable_ctr_write(0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff); + + /* 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(rx_chinfo, ctype); + mlb150_dev_init_ch_amba_ahb(tx_chinfo, ctype); + + return 0; +} + +static s32 mlb150_dev_unmute_syn_ch(u32 rx_ch, u32 tx_ch) +{ + 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 + MLB150_REG_MLBC1) & MLB150_MLBC1_CLKM) + || timeout--) + __raw_writel(~MLB150_MLBC1_CLKM, mlb_base + MLB150_REG_MLBC1); + + if (unlikely(0 == timeout)) + return -ETIME; + + timeout = 10000; + /* Poll for MLB lock (MLBC0.MLBLK = 1) */ + while (!(__raw_readl(mlb_base + MLB150_REG_MLBC0) & MLB150_MLBC0_MLBLK) + || timeout--) + ; + + if (unlikely(0 == timeout)) + return -ETIME; + + /* Unmute synchronous channel(s) */ + mlb150_dev_cat_mlb_write(rx_ch, CAT_CE | rx_ch); + mlb150_dev_cat_mlb_write(tx_ch, + CAT_CE | tx_ch | CAT_RNW); + mlb150_dev_cat_hbi_write(rx_ch, + CAT_CE | rx_ch | CAT_RNW); + mlb150_dev_cat_hbi_write(tx_ch, CAT_CE | tx_ch); + + return 0; +} + +static void mlb150_dev_exit(void) +{ + mlb150_dev_enable_dma_irq(0); + mlb150_dev_enable_ir_mlb(0); + + __raw_writel(0, mlb_base + MLB150_REG_HCTL); + __raw_writel(0, mlb_base + MLB150_REG_MLBC0); +} + +/*! + * MLB receive start function + * + * load phy_head to next buf register to start next rx + * here use single-packet buffer, set start=end + */ +static void mlb_start_rx(int cdev_id, u32 dne_sts) +{ + struct mlb_channel_info *chinfo = &_get_rxchan(cdev_id); + unsigned int ctype = mlb_devinfo[cdev_id].channel_type; + + /* Set ADT for RX */ + mlb150_dev_set_ch_amba_ahb(chinfo->address, ctype, dne_sts); +} + +/*! + * MLB transmit start function + * make sure aquiring the rw buf_lock, when calling this + */ +static void mlb_start_tx(int cdev_id, u32 dne_sts) +{ + struct mlb_channel_info *chinfo = &_get_txchan(cdev_id); + unsigned int ctype = mlb_devinfo[cdev_id].channel_type; + + /* Set ADT for TX */ + mlb150_dev_set_ch_amba_ahb(chinfo->address, ctype, dne_sts); +} + +/*! + * Enable the MLB channel + */ +static void mlb_channel_enable(int chan_dev_id, int on) +{ + u32 c0_val = 0; + /*! + * setup the direction, enable, channel type, + * mode select, channel address and mask buf start + */ + if (on) { + u32 ctype = mlb_devinfo[chan_dev_id].channel_type; + struct mlb_channel_info *tx_chinfo = &_get_txchan(chan_dev_id); + struct mlb_channel_info *rx_chinfo = &_get_rxchan(chan_dev_id); + u32 tx_ch = tx_chinfo->address; + u32 rx_ch = rx_chinfo->address; + + mlb150_dev_enable_ctr_write(0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff); + mlb150_dev_init_rfb(rx_ch, tx_ch, ctype); + + mlb150_dev_init_amba_ahb(rx_chinfo, tx_chinfo, ctype); + + /* Synchronize and unmute synchrouous channel */ + if (MLB_CTYPE_SYNC == ctype) + mlb150_dev_unmute_syn_ch(rx_ch, tx_ch); + + mlb150_dev_enable_ctr_write(0x0, ADT_RDY1 | ADT_DNE1 | + ADT_ERR1 | ADT_PS1 | + ADT_MEP1 | ADT_RDY2 | ADT_DNE2 | ADT_ERR2 | + ADT_PS2 | ADT_MEP2, + 0x0, 0x0); + + if (mlb_devinfo[chan_dev_id].fps >= MLB150_CLK_2048FS) { + c0_val = __raw_readl(mlb_base + MLB150_REG_MLBC0); + + __raw_writel(MLB150_MLBPC1_VAL, + mlb_base + MLB150_REG_MLBPC1); + + if (c0_val & MLB150_MLBC0_MLBPEN) { + c0_val &= ~MLB150_MLBC0_MLBPEN; + __raw_writel(c0_val, + mlb_base + MLB150_REG_MLBC0); + } + + c0_val |= MLB150_MLBC0_MLBPEN; + __raw_writel(c0_val, mlb_base + MLB150_REG_MLBC0); + + clk_enable(mlb_pll_clk); + } + + atomic_set(&mlb_devinfo[chan_dev_id].on, 1); + + mlb_start_rx(chan_dev_id, ADT_DNE2); + } else { + mlb150_dev_enable_dma_irq(0); + mlb150_dev_enable_ir_mlb(0); + + mlb150_dev_reset_cat(); + + atomic_set(&mlb_devinfo[chan_dev_id].on, 0); + + if (mlb_devinfo[chan_dev_id].fps >= MLB150_CLK_2048FS) { + c0_val = __raw_readl(mlb_base + MLB150_REG_MLBC0); + + __raw_writel(0x0, mlb_base + MLB150_REG_MLBPC1); + + c0_val &= ~MLB150_MLBC0_MLBPEN; + __raw_writel(c0_val, mlb_base + MLB150_REG_MLBC0); + + clk_disable(mlb_pll_clk); + } + } +} + +/*! + * MLB interrupt handler + */ +static void mlb_tx_isr(int minor) +{ + struct mlb_channel_info *pchinfo = &_get_txchan(minor); + u32 adt_val[4] = { 0 }; + + mlb150_dev_adt_read(pchinfo->address, adt_val); + + /* Select another buffer to tx */ + if (adt_val[1] & ADT_DNE1) + pchinfo->buf_ptr = pchinfo->pong_buf_head; + else if (adt_val[1] & ADT_DNE2) + pchinfo->buf_ptr = pchinfo->ping_buf_head; + else { + panic("No DNE bit detected!\n"); + return; + } + + wake_up_interruptible(&mlb_devinfo[minor].wt_wq); +} + +static void mlb_rx_isr(int minor) +{ + struct mlb_channel_info *pchinfo = &_get_rxchan(minor); + struct mlb_dev_info *pdevinfo = &mlb_devinfo[minor]; + u32 len; + u32 adt_val[4] = { 0 }; + s32 wpos, rpos; + u8 *adt_buf_ptr = (u8 *)pchinfo->buf_ptr; + + mlb150_dev_adt_read(pchinfo->address, adt_val); + /* Decide which buffer to copy data from. + * Not setting ahb */ + if (adt_val[1] & ADT_DNE1) + adt_buf_ptr = (u8 *)pchinfo->ping_buf_head; + else if (adt_val[1] & ADT_DNE2) + adt_buf_ptr = (u8 *)pchinfo->pong_buf_head; + else { + panic("No DNE bit detected!\n"); + return; + } + + rpos = pdevinfo->rx_rdpos; + wpos = pdevinfo->rx_wtpos; + + len = pchinfo->buf_size; + + /*! + * Copy packet from IRAM buf to ring buf. + * if the wpos++ == rpos, drop this packet + */ + if (((wpos + 1) % TRANS_RING_NODES) != rpos) { + u8 *rx_ring_buf = pdevinfo->rx_bufs[wpos].data; +#ifdef DEBUG_RX + if (len > mlb150_ch_packet_buf_size[pdevinfo->channel_type]) + pr_debug("mxc_mlb150: packet overflow, " + "packet type: %d\n", pdevinfo->channel_type); +#endif + + memcpy(rx_ring_buf, (const void *)adt_buf_ptr, len); + + pdevinfo->rx_bufs[wpos].size = len; + + /* update the ring wpos */ + pdevinfo->rx_wtpos = (wpos + 1) % TRANS_RING_NODES; + + /* wake up the reader */ + wake_up_interruptible(&pdevinfo->rd_wq); + +#ifdef DEBUG_RX + pr_debug("recv package, len:%d, rx_rdpos: %d, rx_wtpos: %d\n", + len, rpos, pdevinfo->rx_wtpos); +#endif + } else { + pr_debug + ("drop package, due to no space, (%d,%d)\n", + rpos, pdevinfo->rx_wtpos); + } + + /* start next rx */ + mlb_start_rx(minor, adt_val[1]); +} + +static irqreturn_t mlb_ahb_isr(int irq, void *dev_id) +{ + u32 rx_int_sts, tx_int_sts, acsr0, + acsr1, rx_err, tx_err, hcer0, hcer1; + struct mlb_dev_info *pdev = NULL; + struct mlb_channel_info *ptxchinfo = NULL, *prxchinfo = NULL; + int minor; + + /* Step 5, Read the ACSRn registers to determine which channel or + * channels are causing the interrupt */ + acsr0 = __raw_readl(mlb_base + MLB150_REG_ACSR0); + acsr1 = __raw_readl(mlb_base + MLB150_REG_ACSR1); + + hcer0 = __raw_readl(mlb_base + MLB150_REG_HCER0); + hcer1 = __raw_readl(mlb_base + MLB150_REG_HCER1); + + pr_debug("mlb_ahb_isr: acsr0: 0x%08x, acsr1: 0x%08x\n", acsr0, acsr1); + + /* Step 6, If ACTL.SCE = 1, write the result of step 5 back to ACSR0 + * and ACSR1 to clear the interrupt */ + if (MLB150_ACTL_SCE & __raw_readl(mlb_base + MLB150_REG_ACTL)) { + __raw_writel(acsr0, mlb_base + MLB150_REG_ACSR0); + __raw_writel(acsr1, mlb_base + MLB150_REG_ACSR1); + } + + for (minor = 0; minor < MLB_MINOR_DEVICES; minor++) { + pdev = &mlb_devinfo[minor]; + prxchinfo = &_get_rxchan(minor); + ptxchinfo = &_get_txchan(minor); + + rx_int_sts = (prxchinfo->address < 31) ? acsr0 : acsr1; + tx_int_sts = (ptxchinfo->address < 31) ? acsr0 : acsr1; + rx_err = (prxchinfo->address < 31) ? hcer0 : hcer1; + tx_err = (ptxchinfo->address < 31) ? hcer0 : hcer1; + + /* get tx channel interrupt status */ + if (tx_int_sts & (1 << (ptxchinfo->address % 32))) { + if (!(tx_err & (1 << (ptxchinfo->address % 32)))) + mlb_tx_isr(minor); + else { + pr_debug("tx channel %d encountered an AHB error!\n", + ptxchinfo->address); + } + } + + /* get rx channel interrupt status */ + if (rx_int_sts & (1 << (prxchinfo->address % 32))) { + if (!(rx_err & (1 << (prxchinfo->address % 32)))) + mlb_rx_isr(minor); + else { + pr_debug("rx channel %d encountered an AHB error!\n", + prxchinfo->address); + } + } + } + + 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; + struct mlb_dev_info *pdev; + 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 + MLB150_REG_MS0); + ms1 = __raw_readl(mlb_base + MLB150_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++) { + pdev = &mlb_devinfo[minor]; + tx_cis = rx_cis = 0; + + ctype = pdev->channel_type; + rx_int_sts = (_get_rxchan(minor).address < 31) ? ms0 : ms1; + tx_int_sts = (_get_txchan(minor).address < 31) ? ms0 : ms1; + + /* Get tx channel interrupt status */ + if (tx_int_sts & (1 << (_get_txchan(minor).address % 32))) { + mlb150_dev_cdt_read(_get_txchan(minor).address, + cdt_val); + pr_debug("mxc_mlb150: cdt_val[3]: 0x%08x, " + "cdt_val[2]: 0x%08x, " + "cdt_val[1]: 0x%08x, " + "cdt_val[0]: 0x%08x\n", + 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(_get_txchan(minor).address, + cdt_val); + } + + /* Get rx channel interrupt status */ + if (rx_int_sts & (1 << (_get_rxchan(minor).address % 32))) { + mlb150_dev_cdt_read(_get_rxchan(minor).address, + cdt_val); + 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(_get_rxchan(minor).address, + cdt_val); + } + + if (!tx_cis && !rx_cis) + continue; + + /* fill exception event */ + spin_lock(&pdev->event_lock); + pdev->ex_event |= (rx_cis << 16) | tx_cis; + spin_unlock(&pdev->event_lock); + } + + return IRQ_HANDLED; +} + +static int mxc_mlb150_open(struct inode *inode, struct file *filp) +{ + int minor; + struct mxc_mlb_platform_data *plat_data; + + plat_data = container_of(inode->i_cdev, struct mxc_mlb_platform_data, + cdev); + filp->private_data = plat_data; + + minor = MINOR(inode->i_rdev); + + if (unlikely(minor < 0 || minor >= MLB_MINOR_DEVICES)) + return -ENODEV; + + /* open for each channel device */ + if (unlikely(atomic_cmpxchg(&mlb_devinfo[minor].opencnt, 0, 1) != 0)) + return -EBUSY; + + /* reset the buffer read/write ptr */ + _get_txchan(minor).buf_ptr = _get_txchan(minor).ping_buf_head; + _get_rxchan(minor).buf_ptr = _get_rxchan(minor).ping_buf_head; + mlb_devinfo[minor].rx_rdpos = mlb_devinfo[minor].rx_wtpos = 0; + mlb_devinfo[minor].tx_rdpos = mlb_devinfo[minor].tx_wtpos = 0; + mlb_devinfo[minor].ex_event = 0; + + return 0; +} + +static int mxc_mlb150_release(struct inode *inode, struct file *filp) +{ + int minor; + + minor = MINOR(inode->i_rdev); + + /* clear channel settings and info */ + mlb_channel_enable(minor, 0); + + /* decrease the open count */ + atomic_set(&mlb_devinfo[minor].opencnt, 0); + + return 0; +} + +static long mxc_mlb150_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct inode *inode = filp->f_dentry->d_inode; + void __user *argp = (void __user *)arg; + unsigned long flags, event; + int minor; + struct mxc_mlb_platform_data *plat_data = filp->private_data; + + minor = MINOR(inode->i_rdev); + + pr_debug("mxc_mlb150: minor: %d\n", minor); + + 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; + } + _get_txchan(minor).address = (caddr >> 16) & 0xFFFF; + _get_rxchan(minor).address = caddr & 0xFFFF; + pr_debug("mxc_mlb150: set ch addr, tx: %d, rx: %d\n", + _get_txchan(minor).address, + _get_rxchan(minor).address); + break; + } + + case MLB_CHAN_STARTUP: + if (unlikely(atomic_read(&mlb_devinfo[minor].on))) { + pr_debug("mxc_mlb150: channel areadly startup\n"); + break; + } + pr_debug("mxc_mlb150: start channel\n"); + mlb_channel_enable(minor, 1); + break; + case MLB_CHAN_SHUTDOWN: + if (unlikely(atomic_read(&mlb_devinfo[minor].on) == 0)) { + pr_debug("mxc_mlb150: channel areadly shutdown\n"); + break; + } + pr_debug("mxc_mlb150: shutdown channel\n"); + mlb_channel_enable(minor, 0); + break; + case MLB_CHAN_GETEVENT: + /* get and clear the ex_event */ + spin_lock_irqsave(&mlb_devinfo[minor].event_lock, flags); + event = mlb_devinfo[minor].ex_event; + mlb_devinfo[minor].ex_event = 0; + spin_unlock_irqrestore(&mlb_devinfo[minor].event_lock, flags); + + pr_debug("mxc_mlb150: get event\n"); + if (event) { + if (copy_to_user(argp, &event, sizeof(event))) { + pr_err("mxc_mlb150: copy to user failed\n"); + return -EFAULT; + } + } else { + pr_debug("mxc_mlb150: no exception event now\n"); + return -EAGAIN; + } + break; + case MLB_SET_FPS: + { + u32 fps, c0_val; + + /* get fps from user space */ + if (unlikely(copy_from_user(&fps, argp, sizeof(fps)))) { + pr_err("mxc_mlb150: copy from user failed\n"); + return -EFAULT; + } + + if (plat_data->fps_sel) + plat_data->fps_sel(fps); + + c0_val = __raw_readl(mlb_base + MLB150_REG_MLBC0); + c0_val &= ~MLB150_MLBC0_MLBCLK_MASK; + + /* check fps value */ + switch (fps) { + case 256: + case 512: + case 1024: + mlb_devinfo[minor].fps = fps >> 9; + c0_val &= ~MLB150_MLBC0_MLBPEN; + c0_val |= (fps >> 9) + << MLB150_MLBC0_MLBCLK_SHIFT; + break; + case 2048: + case 3072: + case 4096: + mlb_devinfo[minor].fps = (fps >> 10) + 1; + c0_val |= ((fps >> 10) + 1) + << MLB150_MLBC0_MLBCLK_SHIFT; + break; + case 6144: + mlb_devinfo[minor].fps = fps >> 10; + c0_val |= ((fps >> 10) + 1) + << MLB150_MLBC0_MLBCLK_SHIFT; + break; + case 8192: + mlb_devinfo[minor].fps = (fps >> 10) - 1; + c0_val |= ((fps >> 10) - 1) + << MLB150_MLBC0_MLBCLK_SHIFT; + break; + default: + pr_debug("mxc_mlb150: invalid fps argument: %d\n", + fps); + return -EINVAL; + } + + __raw_writel(c0_val, mlb_base + MLB150_REG_MLBC0); + + pr_debug("mxc_mlb150: set fps to %d, MLBC0: 0x%08x\n", + fps, + (u32)__raw_readl(mlb_base + MLB150_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 (unlikely(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 + MLB150_REG_MLBC1); + c1_val &= ~MLB150_MLBC1_NDA_MASK; + c1_val |= devaddr << MLB150_MLBC1_NDA_SHIFT; + __raw_writel(c1_val, mlb_base + MLB150_REG_MLBC1); + pr_debug("mxc_mlb150: set dev addr, dev addr: %d, " + "MLBC1: 0x%08x\n", devaddr, + (u32)__raw_readl(mlb_base + MLB150_REG_MLBC1)); + + 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 minor, ret; + int size, rdpos; + struct mlb_ringnode *rxbuf = NULL; + +#ifdef DEBUG_RX + pr_debug("mxc_mlb150: mxc_mlb150_read\n"); +#endif + + minor = MINOR(filp->f_dentry->d_inode->i_rdev); + + rdpos = mlb_devinfo[minor].rx_rdpos; + rxbuf = mlb_devinfo[minor].rx_bufs; + + /* check the current rx buffer is available or not */ + if (rdpos == mlb_devinfo[minor].rx_wtpos) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + /* if !O_NONBLOCK, we wait for recv packet */ + ret = wait_event_interruptible(mlb_devinfo[minor].rd_wq, + (mlb_devinfo[minor].rx_wtpos != + rdpos)); + if (ret < 0) + return ret; + } + + size = rxbuf[rdpos].size; + if (unlikely(size > count)) { + /* the user buffer is too small */ + pr_warning + ("mxc_mlb150: received data size is bigger than count\n"); + return -EINVAL; + } + + /* copy rx buffer data to user buffer */ + if (unlikely(copy_to_user(buf, rxbuf[rdpos].data, size))) { + pr_err("mxc_mlb150: copy from user failed\n"); + return -EFAULT; + } + + /* update the read ptr */ + mlb_devinfo[minor].rx_rdpos = (rdpos + 1) % TRANS_RING_NODES; + + *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 minor = 0, ret = 0; + struct mlb_channel_info *pchinfo = NULL; + struct mlb_dev_info *pdevinfo = NULL; + u32 adt_val[4] = { 0 }; + + minor = MINOR(filp->f_dentry->d_inode->i_rdev); + pchinfo = &_get_txchan(minor); + pdevinfo = &mlb_devinfo[minor]; + + if (unlikely(count > pchinfo->buf_size)) { + /* too many data to write */ + pr_warning("mxc_mlb150: overflow write data\n"); + return -EFBIG; + } + + *f_pos = 0; + + memcpy((void *)pchinfo->buf_ptr, buf, count); + + mlb150_dev_adt_read(pchinfo->address, adt_val); + mlb_start_tx(minor, adt_val[1]); + + ret = count; + + return ret; +} + +static unsigned int mxc_mlb150_poll(struct file *filp, + struct poll_table_struct *wait) +{ + int minor; + unsigned int ret = 0; + + minor = MINOR(filp->f_dentry->d_inode->i_rdev); + + poll_wait(filp, &mlb_devinfo[minor].rd_wq, wait); + poll_wait(filp, &mlb_devinfo[minor].wt_wq, wait); + + /* check the tx buffer is avaiable or not */ + if (mlb_devinfo[minor].tx_rdpos != mlb_devinfo[minor].tx_wtpos) + ret |= POLLOUT | POLLWRNORM; + + /* check the rx buffer filled or not */ + if (mlb_devinfo[minor].rx_rdpos != mlb_devinfo[minor].rx_wtpos) + ret |= POLLIN | POLLRDNORM; + + /* check the exception event */ + if (mlb_devinfo[minor].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, +}; + +/*! + * This function is called whenever the MLB device is detected. + */ +static int __devinit mxc_mlb150_probe(struct platform_device *pdev) +{ + int ret, mlb_major, i, j; + struct mxc_mlb_platform_data *plat_data; + struct resource *res; + void __iomem *base, *bufaddr; + unsigned long phyaddr; + + plat_data = (struct mxc_mlb_platform_data *)pdev->dev.platform_data; + plat_data->dev = &pdev->dev; + + /* malloc the Rx ring buffer firstly */ + for (i = 0; i < MLB_MINOR_DEVICES; ++i) { + char *buf; + int bufsize; + + bufsize = + mlb150_ch_packet_buf_size[mlb_devinfo[i].channel_type]; + buf = kmalloc(bufsize * TRANS_RING_NODES * 2, GFP_KERNEL); + if (unlikely(buf == NULL)) { + ret = -ENOMEM; + dev_err(plat_data->dev, "can not alloc rx buffers\n"); + goto err3; + } + + dev_dbg(plat_data->dev, "ch_type: %d, ring buf base: 0x%08x\n", + mlb_devinfo[i].channel_type, (u32)buf); + + for (j = 0; j < TRANS_RING_NODES; ++j) { + mlb_devinfo[i].rx_bufs[j].data = buf; + buf += bufsize; + } + + for (j = 0; j < TRANS_RING_NODES; ++j) { + mlb_devinfo[i].tx_bufs[j].data = buf; + buf += bufsize; + } + } + + /** + * Register MLB lld as four character devices + */ + ret = alloc_chrdev_region(&dev, 0, MLB_MINOR_DEVICES, "mxc_mlb150"); + mlb_major = MAJOR(dev); + dev_dbg(plat_data->dev, "MLB device major: %d\n", mlb_major); + + if (unlikely(ret < 0)) { + dev_err(plat_data->dev, "can't get major %d\n", mlb_major); + goto err2; + } + + cdev_init(&plat_data->cdev, &mxc_mlb150_fops); + plat_data->cdev.owner = THIS_MODULE; + + ret = cdev_add(&plat_data->cdev, dev, MLB_MINOR_DEVICES); + if (unlikely(ret)) { + dev_err(plat_data->dev, "can't add cdev\n"); + goto err2; + } + + /* create class and device for udev information */ + mlb_class = class_create(THIS_MODULE, "mlb150"); + if (unlikely(IS_ERR(mlb_class))) { + dev_err(plat_data->dev, "failed to create mlb150 class\n"); + ret = -ENOMEM; + goto err2; + } + + for (i = 0; i < MLB_MINOR_DEVICES; i++) { + class_dev = device_create(mlb_class, NULL, MKDEV(mlb_major, i), + NULL, mlb_devinfo[i].dev_name); + if (unlikely(IS_ERR(class_dev))) { + dev_err(plat_data->dev, "failed to create mlb150 %s" + " class device\n", mlb_devinfo[i].dev_name); + ret = -ENOMEM; + goto err1; + } + } + + /* get irq line */ + /* AHB0 IRQ */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (unlikely(res == NULL)) { + dev_err(plat_data->dev, "No mlb150 ahb0 irq line provided\n"); + goto err0; + } + + ahb0_irq = res->start; + dev_dbg(plat_data->dev, "ahb0_irq: %d\n", ahb0_irq); + if (request_irq(ahb0_irq, mlb_ahb_isr, 0, "mlb_ahb0", NULL)) { + dev_err(plat_data->dev, "failed to request irq\n"); + ret = -EBUSY; + goto err0; + } + + /* AHB1 IRQ */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 2); + if (unlikely(res == NULL)) { + dev_err(plat_data->dev, "No mlb150 ahb0 irq line provided\n"); + goto err0; + } + + ahb1_irq = res->start; + dev_dbg(plat_data->dev, "ahb1_irq: %d\n", ahb1_irq); + if (request_irq(ahb1_irq, mlb_ahb_isr, 0, "mlb_ahb1", NULL)) { + dev_err(plat_data->dev, "failed to request irq\n"); + ret = -EBUSY; + goto err0; + } + + /* MLB IRQ */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res == NULL) { + dev_err(plat_data->dev, "No mlb150 irq line provided\n"); + goto err0; + } + + mlb_irq = res->start; + dev_dbg(plat_data->dev, "mlb_irq: %d\n", mlb_irq); + if (request_irq(mlb_irq, mlb_isr, 0, "mlb", NULL)) { + dev_err(plat_data->dev, "failed to request irq\n"); + ret = -EBUSY; + goto err0; + } + + /* ioremap from phy mlb to kernel space */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(res == NULL)) { + dev_err(plat_data->dev, "No mlb150 base address provided\n"); + goto err0; + } + + base = ioremap(res->start, res->end - res->start); + dev_dbg(plat_data->dev, "mapped mlb150 base address: 0x%08x\n", + (u32)base); + + if (unlikely(base == NULL)) { + dev_err(plat_data->dev, + "failed to do ioremap with mlb150 base\n"); + goto err0; + } + mlb_base = (u32)base; + + dev_dbg(plat_data->dev, "mlb reg base: 0x%08x\n", mlb_base); + + /*! + * get rx/tx buffer address from platform data + * make sure the buf_address is 4bytes aligned + * + * ------------------- <-- plat_data->buf_address + * | minor 0 tx buf | + * ----------------- + * | minor 0 rx buf | + * ----------------- + * | .... | + * ----------------- + * | minor n tx buf | + * ----------------- + * | minor n rx buf | + * ------------------- + */ + bufaddr = iram_alloc(MLB_IRAM_SIZE, &iram_base); + plat_data->buf_addr = (u32)bufaddr; + plat_data->phy_addr = phyaddr = iram_base; + + dev_dbg(plat_data->dev, "iram buf base: 0x%08x, phy base: 0x%08x\n", + plat_data->buf_addr, plat_data->phy_addr); + + for (i = 0; i < MLB_MINOR_DEVICES; i++) { + /* set the virtual and physical buf head address */ + _get_txchan(i).ping_buf_head = (u32)bufaddr; + _get_txchan(i).ping_phy_head = phyaddr; + + bufaddr += PING_BUF_MAX_SIZE; + phyaddr += PING_BUF_MAX_SIZE; + + _get_rxchan(i).ping_buf_head = (u32)bufaddr; + _get_rxchan(i).ping_phy_head = phyaddr; + + bufaddr += PING_BUF_MAX_SIZE; + phyaddr += PING_BUF_MAX_SIZE; + + _get_txchan(i).pong_buf_head = (u32)bufaddr; + _get_txchan(i).pong_phy_head = phyaddr; + + bufaddr += PONG_BUF_MAX_SIZE; + phyaddr += PONG_BUF_MAX_SIZE; + + _get_rxchan(i).pong_buf_head = (u32)bufaddr; + _get_rxchan(i).pong_phy_head = phyaddr; + + bufaddr += PONG_BUF_MAX_SIZE; + phyaddr += PONG_BUF_MAX_SIZE; + + dev_dbg(plat_data->dev, "ctype: %d, tx phy_head: ping(0x%08x), " + "pong(0x%08x)\n", + i, + (u32)_get_txchan(i).ping_phy_head, + (u32)_get_txchan(i).pong_phy_head); + dev_dbg(plat_data->dev, "ctype: %d, tx buf_head: ping(0x%08x), " + "pong(0x%08x)\n", + i, + (u32)_get_txchan(i).ping_buf_head, + (u32)_get_txchan(i).pong_buf_head); + dev_dbg(plat_data->dev, "ctype: %d, rx phy_head: ping(0x%08x), " + "pong(0x%08x)\n", + i, + (u32)_get_rxchan(i).ping_phy_head, + (u32)_get_rxchan(i).pong_phy_head); + dev_dbg(plat_data->dev, "ctype: %d, rx buf_head: ping(0x%08x), " + "pong(0x%08x)\n", + i, + (u32)_get_rxchan(i).ping_buf_head, + (u32)_get_rxchan(i).pong_buf_head); + } + + if (plat_data->reg_nvcc) { + /* power on MLB */ + reg_nvcc = regulator_get(plat_data->dev, plat_data->reg_nvcc); + if (unlikely(!IS_ERR(reg_nvcc))) { + /* set MAX LDO6 for NVCC to 2.5V */ + regulator_set_voltage(reg_nvcc, 2500000, 2500000); + regulator_enable(reg_nvcc); + } + } + + /* enable clock */ + if (likely(plat_data->mlb_clk)) { + mlb_clk = clk_get(plat_data->dev, plat_data->mlb_clk); + if (unlikely(IS_ERR(mlb_clk))) { + dev_err(&pdev->dev, "unable to get mlb clock\n"); + ret = PTR_ERR(mlb_clk); + goto err0; + } + clk_enable(mlb_clk); + } + + if (likely(plat_data->mlb_pll_clk)) { + mlb_pll_clk = clk_get(plat_data->dev, plat_data->mlb_pll_clk); + if (unlikely(IS_ERR(mlb_pll_clk))) { + dev_err(&pdev->dev, "unable to get mlb pll clock\n"); + ret = PTR_ERR(mlb_pll_clk); + goto err0; + } + } + + /* initial MLB module */ + mlb150_dev_init(); + + return 0; + +err0: + if (likely(ahb0_irq)) { + free_irq(ahb0_irq, NULL); + ahb0_irq = 0; + } + if (likely(ahb1_irq)) { + free_irq(ahb1_irq, NULL); + ahb1_irq = 0; + } + if (likely(mlb_irq)) { + free_irq(mlb_irq, NULL); + mlb_irq = 0; + } +err1: + for (--i; i >= 0; i--) + device_destroy(mlb_class, MKDEV(mlb_major, i)); + + class_destroy(mlb_class); +err2: + unregister_chrdev_region(dev, MLB_MINOR_DEVICES); +err3: + for (i = 0; i < MLB_MINOR_DEVICES; i++) + kfree(mlb_devinfo[i].rx_bufs[0].data); + + return ret; +} + +static int __devexit mxc_mlb150_remove(struct platform_device *pdev) +{ + int i; + struct mxc_mlb_platform_data *plat_data; + + plat_data = (struct mxc_mlb_platform_data *)pdev->dev.platform_data; + + mlb150_dev_exit(); + + /* disable mlb clock */ + if (plat_data->mlb_clk) { + clk_disable(mlb_clk); + clk_put(mlb_clk); + } + + if (plat_data->mlb_pll_clk) + clk_put(mlb_pll_clk); + + /* disable mlb power */ + if (plat_data->reg_nvcc) { + regulator_disable(reg_nvcc); + regulator_put(reg_nvcc); + } + + /* inactive GPIO */ + gpio_mlb_inactive(); + + if (iram_base) { + iram_free(iram_base, MLB_IRAM_SIZE); + iram_base = 0; + } + + /* iounmap */ + if (mlb_base) { + iounmap((void *)mlb_base); + mlb_base = 0; + } + + if (ahb0_irq) + free_irq(ahb0_irq, NULL); + if (ahb1_irq) + free_irq(ahb1_irq, NULL); + if (mlb_irq) + free_irq(mlb_irq, NULL); + ahb0_irq = ahb1_irq = mlb_irq = 0; + + /* destroy mlb device class */ + for (i = MLB_MINOR_DEVICES - 1; i >= 0; i--) + device_destroy(mlb_class, MKDEV(MAJOR(dev), i)); + class_destroy(mlb_class); + + /* Unregister the two MLB devices */ + unregister_chrdev_region(dev, MLB_MINOR_DEVICES); + + for (i = 0; i < MLB_MINOR_DEVICES; i++) + kfree(mlb_devinfo[i].rx_bufs[0].data); + + return 0; +} + +#ifdef CONFIG_PM +static int mxc_mlb150_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int mxc_mlb150_resume(struct platform_device *pdev) +{ + 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, + }, + .probe = mxc_mlb150_probe, + .remove = __devexit_p(mxc_mlb150_remove), + .suspend = mxc_mlb150_suspend, + .resume = mxc_mlb150_resume, +}; + +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 index 7c8afea2f64d..7ac953c84dd3 100644 --- a/include/linux/mxc_mlb.h +++ b/include/linux/mxc_mlb.h @@ -17,6 +17,7 @@ #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)