diff options
-rwxr-xr-x | drivers/mxc/mlb/mxc_mlb150.c | 571 | ||||
-rw-r--r-- | include/linux/mxc_mlb.h | 7 |
2 files changed, 371 insertions, 207 deletions
diff --git a/drivers/mxc/mlb/mxc_mlb150.c b/drivers/mxc/mlb/mxc_mlb150.c index 4fa3ff873e1f..a74a72bb029b 100755 --- a/drivers/mxc/mlb/mxc_mlb150.c +++ b/drivers/mxc/mlb/mxc_mlb150.c @@ -37,6 +37,7 @@ #include <linux/delay.h> #include <linux/spinlock.h> #include <linux/sched.h> +#include <linux/circ_buf.h> #define DRIVER_NAME "mxc_mlb150" @@ -157,24 +158,30 @@ #define CAT_MODE_INBOUND_DMA (0x1 << 8) #define CAT_MODE_OUTBOUND_DMA (0x1 << 9) -#define CH_SYNC_BUF_DEP (128 * 4 * 4) -#define CH_CTRL_BUF_DEP (64) -#define CH_ASYNC_BUF_DEP (2048) -#define CH_ISOC_BLK_SIZE (196) -#define CH_ISOC_BLK_NUM (3) -#define CH_ISOC_BUF_DEP (CH_ISOC_BLK_SIZE * CH_ISOC_BLK_NUM) +#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_CDT_BUF_DEP (2048) +#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 (5) +#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_BUF_DEP) -#define CH_ASYNC_DBR_BUF_OFFSET (CH_CTRL_DBR_BUF_OFFSET + 2 * CH_CTRL_BUF_DEP) -#define CH_ISOC_DBR_BUF_OFFSET (CH_ASYNC_DBR_BUF_OFFSET + 2 * CH_ASYNC_BUF_DEP) - -static u32 mlb150_ch_packet_buf_size[4] = { - CH_SYNC_BUF_DEP, - CH_CTRL_BUF_DEP, - CH_ASYNC_BUF_DEP, - CH_ISOC_BUF_DEP -}; +#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 @@ -1273,8 +1280,22 @@ static s32 mlb150_dev_reset_all_regs(void) return 0; } -static inline s32 mlb150_dev_set_ch_amba_ahb(u32 ch, enum MLB_CTYPE ctype, - u32 dne_sts, u32 buf_addr) +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 }; @@ -1294,42 +1315,25 @@ static inline s32 mlb150_dev_set_ch_amba_ahb(u32 ch, enum MLB_CTYPE ctype, ctr_val[2] = buf_addr; } -#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))) + if (mlb150_dev_adt_write(ahb_ch, ctr_val)) return -ETIME; -#ifdef DEBUG_ADT_N - { - u32 ctr_rd[4] = { 0 }; - if (likely(!mlb150_dev_adt_read(ch, ctr_rd))) { - pr_debug("mxc_mlb150: ADT val of channel %d: " - "0x%08x 0x%08x 0x%08x 0x%08x\n", - ch, 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", - ch); - return -EBADE; - } - } -#endif + return 0; +} - 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, @@ -1505,26 +1509,43 @@ static s32 mlb150_dev_unmute_syn_ch(u32 rx_ch, u32 rx_cl, u32 tx_ch, u32 tx_cl) return 0; } -/*! - * MLB receive start function - * - * load phy_head to next buf register to start next rx - * here use single-packet buffer, set start=end - */ -static inline void mlb_start_rx(u32 ch, s32 ctype, u32 dne_sts, u32 buf_addr) +/* 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) { - /* Set ADT for RX */ - mlb150_dev_set_ch_amba_ahb(ch, ctype, dne_sts, buf_addr); -} + struct mlb_ringbuf *rx_rbuf = &pdevinfo->rx_rbuf; + struct mlb_ringbuf *tx_rbuf = &pdevinfo->tx_rbuf; + s32 timeout = 1024; -/*! - * MLB transmit start function - * make sure aquiring the rw buf_lock, when calling this - */ -static inline void mlb_start_tx(u32 ch, s32 ctype, u32 dne_sts, u32 buf_addr) -{ - /* Set ADT for TX */ - mlb150_dev_set_ch_amba_ahb(ch, ctype, dne_sts, buf_addr); + 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; + } + + return 0; } /*! @@ -1596,123 +1617,134 @@ static void mlb_channel_enable(struct mlb_data *drvdata, /*! * MLB interrupt handler */ -static void mlb_tx_isr(int minor) -{ - struct mlb_dev_info *pdevinfo = &mlb_devinfo[minor]; - - pdevinfo->tx_busy = 0; - - wake_up_interruptible(&pdevinfo->wt_wq); -} - -static void mlb_rx_isr(int minor) +static void mlb_rx_isr(s32 ctype, u32 ahb_ch, struct mlb_dev_info *pdevinfo) { - struct mlb_dev_info *pdevinfo = &mlb_devinfo[minor]; - struct mlb_channel_info *pchinfo = &_get_rxchan(minor); - struct mlb_ringbuf *rx_rbuf = &pdevinfo->rx_bufs; - s32 wpos, rpos, adt_sts; - u32 rx_ring_buf = 0; - s32 ctype = pdevinfo->channel_type; - u32 ch_addr = pchinfo->address; + struct mlb_ringbuf *rx_rbuf = &pdevinfo->rx_rbuf; + s32 head, tail, adt_sts; + unsigned long flags; + u32 rx_buf_ptr; #ifdef DEBUG_RX pr_debug("mxc_mlb150: mlb_rx_isr\n"); #endif - rpos = rx_rbuf->rpos; - wpos = rx_rbuf->wpos; + write_lock_irqsave(&rx_rbuf->rb_lock, flags); -#ifdef DEBUG_RX - pr_debug("adt_buf_ptr: 0x%08x\n", (u32)adt_buf_ptr); -#endif + head = (rx_rbuf->head + 1) & (TRANS_RING_NODES - 1); + tail = ACCESS_ONCE(rx_rbuf->tail); - /*! - * Copy packet from IRAM buf to ring buf. - * if the wpos++ == rpos, drop this packet - */ - if (((wpos + 1) % TRANS_RING_NODES) != rpos) { - rx_ring_buf = rx_rbuf->phy_addrs[(wpos + 1) % TRANS_RING_NODES]; -#ifdef DEBUG_RX - if (len > mlb150_ch_packet_buf_size[ctype]) - pr_debug("mxc_mlb150: packet overflow, " - "packet type: %d\n", ctype); -#endif + if (CIRC_SPACE(head, tail, TRANS_RING_NODES) >= 1) { + rx_buf_ptr = rx_rbuf->phy_addrs[head]; - /* update the ring wpos */ - rx_rbuf->wpos = (wpos + 1) % TRANS_RING_NODES; + /* commit the item before incrementing the head */ + smp_wmb(); - /* wake up the reader */ - wake_up_interruptible(&pdevinfo->rd_wq); + rx_rbuf->head = head; -#ifdef DEBUG_RX - pr_debug("recv package, len:%d, rx_rdpos: %d, rx_wtpos: %d\n", - len, rpos, pdevinfo->rx_bufs.wpos); -#endif - } else { - rx_ring_buf = pdevinfo->rx_bufs.phy_addrs[TRANS_RING_NODES]; + write_unlock_irqrestore(&rx_rbuf->rb_lock, flags); - pr_debug - ("drop package, due to no space, (%d,%d)\n", - rpos, pdevinfo->rx_bufs.wpos); + /* wake up the reader */ + wake_up_interruptible(&pdevinfo->rx_wq); + } else { + rx_buf_ptr = rx_rbuf->phy_addrs[TRANS_RING_NODES]; + write_unlock_irqrestore(&rx_rbuf->rb_lock, flags); + pr_debug("drop RX package, due to no space, (%d,%d)\n", + head, tail); } - adt_sts = mlb150_dev_get_adt_sts(ch_addr); - mlb_start_rx(ch_addr, ctype, adt_sts, rx_ring_buf); + 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; + unsigned long flags; + + write_lock_irqsave(&tx_rbuf->rb_lock, flags); + + head = ACCESS_ONCE(tx_rbuf->head); + tail = (tx_rbuf->tail + 1) & (TRANS_RING_NODES - 1); + smp_mb(); + tx_rbuf->tail = tail; + + /* 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]; + + write_unlock_irqrestore(&tx_rbuf->rb_lock, flags); + + 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); + } else + write_unlock_irqrestore(&tx_rbuf->rb_lock, flags); } 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; + 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); - acsr1 = __raw_readl(mlb_base + REG_ACSR1); hcer0 = __raw_readl(mlb_base + REG_HCER0); - hcer1 = __raw_readl(mlb_base + REG_HCER1); /* Step 6, If ACTL.SCE = 1, write the result of step 5 back to ACSR0 * and ACSR1 to clear the interrupt */ - if (ACTL_SCE & __raw_readl(mlb_base + REG_ACTL)) { + /* We'll not set ACTL_SCE */ + /* + if (ACTL_SCE & __raw_readl(mlb_base + REG_ACTL)) __raw_writel(acsr0, mlb_base + REG_ACSR0); - __raw_writel(acsr1, mlb_base + 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); - } - } + if (ch_mask & hcer0) + pr_err("CH encounters an AHB error: 0x%x\n", hcer0); - /* 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); - } - } - } + 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; } @@ -1997,6 +2029,37 @@ static long mxc_mlb150_ioctl(struct file *filp, } } 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: @@ -2117,35 +2180,59 @@ static long mxc_mlb150_ioctl(struct file *filp, 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_ringbuf *rx_rbuf = NULL; - struct mlb_dev_info *pdevinfo = NULL; - -#ifdef DEBUG_RX - pr_debug("mxc_mlb150: mxc_mlb150_read\n"); -#endif - - minor = MINOR(filp->f_dentry->d_inode->i_rdev); + 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; - pdevinfo = &mlb_devinfo[minor]; + read_lock_irqsave(&rx_rbuf->rb_lock, flags); - rdpos = pdevinfo->rx_bufs.rpos; - rx_rbuf = &pdevinfo->rx_bufs; + head = ACCESS_ONCE(rx_rbuf->head); + tail = rx_rbuf->tail; /* check the current rx buffer is available or not */ - if (rdpos == rx_rbuf->wpos) { + if (0 == CIRC_CNT(head, tail, TRANS_RING_NODES)) { + read_unlock_irqrestore(&rx_rbuf->rb_lock, flags); + if (filp->f_flags & O_NONBLOCK) return -EAGAIN; - /* if !O_NONBLOCK, we wait for recv packet */ - ret = wait_event_interruptible(pdevinfo->rd_wq, - (rx_rbuf->wpos != rdpos)); - if (ret < 0) - return ret; + + 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_lock_irqsave(&rx_rbuf->rb_lock, flags); } + read_unlock_irqrestore(&rx_rbuf->rb_lock, flags); + + /* read index before reading contents at that index */ + smp_read_barrier_depends(); - size = mlb150_ch_packet_buf_size[minor]; - if (unlikely(size > count)) { + size = pdevinfo->adt_buf_dep; + if (size > count) { /* the user buffer is too small */ pr_warning ("mxc_mlb150: received data size is bigger than " @@ -2153,14 +2240,18 @@ static ssize_t mxc_mlb150_read(struct file *filp, char __user *buf, return -EINVAL; } - /* copy rx buffer data to user buffer */ - if (likely(copy_to_user(buf, rx_rbuf->virt_bufs[rdpos], size))) { + /* 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; } - /* update the read ptr */ - rx_rbuf->rpos = (rdpos + 1) % TRANS_RING_NODES; + /* 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; @@ -2176,16 +2267,20 @@ static ssize_t mxc_mlb150_read(struct file *filp, char __user *buf, 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; + s32 ret = 0; struct mlb_channel_info *pchinfo = NULL; - struct mlb_dev_info *pdevinfo = NULL; - u32 adt_sts = 0; + 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 = &_get_txchan(minor); - pdevinfo = &mlb_devinfo[minor]; + */ + pchinfo = &pdevinfo->channels[TX_CHANNEL]; - if (unlikely(count > pchinfo->buf_size)) { + if (count > pdevinfo->buf_size) { /* too many data to write */ pr_warning("mxc_mlb150: overflow write data\n"); return -EFBIG; @@ -2193,31 +2288,77 @@ static ssize_t mxc_mlb150_write(struct file *filp, const char __user *buf, *f_pos = 0; - /* check the current tx buffer is used or not */ - if (1 == pdevinfo->tx_busy) { + read_lock_irqsave(&tx_rbuf->rb_lock, flags); + + head = tx_rbuf->head; + tail = ACCESS_ONCE(tx_rbuf->tail); + + if (0 == CIRC_SPACE(head, tail, TRANS_RING_NODES)) { + read_unlock_irqrestore(&tx_rbuf->rb_lock, flags); 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); - ret = wait_event_interruptible(pdevinfo->wt_wq, - 0 == pdevinfo->tx_busy); - - if (ret < 0) - goto out; + if (!signal_pending(current)) { + schedule(); + continue; + } + return -ERESTARTSYS; + } + finish_wait(&pdevinfo->tx_wq, &__wait); + } while (0); } - if (copy_from_user((void *)pchinfo->buf_ptr, buf, count)) { + 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; } - adt_sts = mlb150_dev_get_adt_sts(pchinfo->address); - pdevinfo->tx_busy = 1; - mlb_start_tx(pchinfo->address, pdevinfo->channel_type, - adt_sts, pchinfo->buf_phy_addr); + read_unlock_irqrestore(&tx_rbuf->rb_lock, flags); + 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); - ret = count; + read_lock_irqsave(&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]; + read_unlock_irqrestore(&tx_rbuf->rb_lock, flags); + 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); + } else { + read_unlock_irqrestore(&tx_rbuf->rb_lock, flags); + } + + ret = count; out: return ret; } @@ -2227,23 +2368,41 @@ static unsigned int mxc_mlb150_poll(struct file *filp, { int minor; unsigned int ret = 0; - struct mlb_dev_info *pdevinfo = NULL; + 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(filp->f_dentry->d_inode->i_rdev); - pdevinfo = &mlb_devinfo[minor]; + poll_wait(filp, &pdevinfo->rx_wq, wait); + poll_wait(filp, &pdevinfo->tx_wq, wait); - poll_wait(filp, &pdevinfo->rd_wq, wait); - poll_wait(filp, &pdevinfo->wt_wq, wait); + read_lock_irqsave(&tx_rbuf->rb_lock, flags); + + head = tx_rbuf->head; + tail = tx_rbuf->tail; /* check the tx buffer is avaiable or not */ - if (0 == pdevinfo->tx_busy) + if (CIRC_SPACE(head, tail, TRANS_RING_NODES) >= 1) ret |= POLLOUT | POLLWRNORM; + read_unlock_irqrestore(&tx_rbuf->rb_lock, flags); + + read_lock_irqsave(&rx_rbuf->rb_lock, flags); + + head = rx_rbuf->head; + tail = rx_rbuf->tail; + /* check the rx buffer filled or not */ - if (pdevinfo->rx_bufs.rpos != pdevinfo->rx_bufs.wpos) + if (CIRC_CNT(head, tail, TRANS_RING_NODES) >= 1) ret |= POLLIN | POLLRDNORM; + read_unlock_irqrestore(&rx_rbuf->rb_lock, flags); + /* check the exception event */ if (pdevinfo->ex_event) ret |= POLLIN | POLLRDNORM; diff --git a/include/linux/mxc_mlb.h b/include/linux/mxc_mlb.h index 7ac953c84dd3..3a63647b61b3 100644 --- a/include/linux/mxc_mlb.h +++ b/include/linux/mxc_mlb.h @@ -1,7 +1,7 @@ /* * mxc_mlb.h * - * Copyright 2008-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008-2013 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -21,6 +21,7 @@ #define MLB_SET_FPS _IOW('S', 0x10, unsigned int) #define MLB_GET_VER _IOR('S', 0x11, unsigned long) #define MLB_SET_DEVADDR _IOR('S', 0x12, unsigned char) + /*! * set channel address for each logical channel * the MSB 16bits is for tx channel, the left LSB is for rx channel @@ -30,6 +31,10 @@ #define MLB_CHAN_SHUTDOWN _IO('S', 0x15) #define MLB_CHAN_GETEVENT _IOR('S', 0x16, unsigned long) +#define MLB_SET_ISOC_BLKSIZE_188 _IO('S', 0x17) +#define MLB_SET_ISOC_BLKSIZE_196 _IO('S', 0x18) +#define MLB_SET_SYNC_QUAD _IOW('S', 0x19, unsigned int) + /*! * MLB event define */ |