i2c: i2c-riic: add dmaengine supporting
Subsystems (3)
PATCH v3 2/2 i2c: i2c-riic: add dmaengine supporting
We can use the dmaengine for the driver, if the dma_tx and/or dma_rx in riic_platform_data is set. If we use it, we have to set the irq number for EEI in the resource.
Signed-off-by Yoshihiro Shimoda <[email protected]>
---
about v3
- adjust for the latest kernel (fix compile error)
drivers/i2c/busses/i2c-riic.c | 290 +++++++++++++++++++++++++++++++++++------ include/linux/i2c/riic.h | 4 + 2 files changed, 251 insertions(+), 43 deletions(-)
drivers/i2c/busses/i2c-riic.c +247 -43
@@ -31,6 +31,9 @@#include <linux/delay.h> #include <linux/slab.h> #include <linux/clk.h> +#include <linux/dmaengine.h> +#include <linux/scatterlist.h> +#include <linux/dma-mapping.h> #define RIIC_ICCR1 0x00#define RIIC_ICCR2 0x01
@@ -166,6 +169,14 @@ struct riic_data {void __iomem *reg;struct i2c_adapter adap;struct i2c_msg *msg; +int index; +unsigned char icsr2; + +/* for DMAENGINE */ +struct dma_chan *chan_tx; +struct dma_chan *chan_rx; +int dma_callbacked; +wait_queue_head_t wait; };#define DRIVER_VERSION "2011-09-21"
@@ -250,7 +261,8 @@ static int riic_init_setting(struct riic_data *pd, int clock)return ret;riic_set_bit (pd,ICCR1_ICE ,RIIC_ICCR1 );/* Enable RIIC */ -riic_set_bit (pd,ICMR3_RDRFS |ICMR3_WAIT |ICMR3_ACKWP ,RIIC_ICMR3 ); +riic_set_bit (pd,ICMR3_WAIT |ICMR3_ACKWP ,RIIC_ICMR3 ); +riic_set_bit (pd,ICIER_TIE |ICIER_RIE ,RIIC_ICIER );return 0 ; }
@@ -312,10 +324,79 @@ static int riic_send_slave_address(struct riic_data *pd, int read)return 0 ; } +static void riic_dma_complete (void *arg) +{ +struct riic_data *pd = arg; + + pd->dma_callbacked =1 ; +wake_up (&pd->wait); +} + +static int riic_master_transmit_pio (struct riic_data *pd) +{ +int index; +int ret =0 ; + + index =0 ; +do { + ret =riic_wait_for_icsr2 (pd,ICSR2_TDRE ); +if (ret <0 ) +return ret; + +riic_write (pd, pd->msg->buf[index],RIIC_ICDRT ); + index++; + }while (pd->msg->len > index); + +return ret; +} + +static int riic_master_transmit_dma (struct riic_data *pd) +{ +struct scatterlist sg; +unsigned char *buf = pd->msg->buf; +struct dma_async_tx_descriptor *desc; +int ret; + +sg_init_table (&sg,1 ); +sg_set_buf (&sg, buf, pd->msg->len); +sg_dma_len (&sg) = pd->msg->len; +dma_map_sg (pd->chan_tx->device->dev, &sg,1 ,DMA_TO_DEVICE ); + + desc = pd->chan_tx->device->device_prep_slave_sg (pd->chan_tx, + &sg,1 ,DMA_TO_DEVICE , +DMA_PREP_INTERRUPT |DMA_CTRL_ACK ); +if (!desc) +return -EIO ; + + desc->callback = riic_dma_complete; + desc->callback_param = pd; + pd->dma_callbacked =0 ; + ret =riic_wait_for_icsr2 (pd,ICSR2_TDRE ); +if (ret <0 ) +return ret; +dmaengine_submit (desc); +dma_async_issue_pending (pd->chan_tx); + + pd->icsr2 =riic_read (pd,RIIC_ICSR2 ); +riic_set_bit (pd,ICIER_NAKIE ,RIIC_ICIER ); + ret =wait_event_timeout (pd->wait, pd->dma_callbacked || + pd->icsr2 &ICSR2_NACKF ,HZ ); +riic_clear_bit (pd,ICIER_NAKIE ,RIIC_ICIER ); +if (pd->icsr2 &ICSR2_NACKF ) { +dmaengine_terminate_all (pd->chan_tx); +return -EIO ; + } +if (!ret && !pd->dma_callbacked) { +dmaengine_terminate_all (pd->chan_tx); +return -ETIMEDOUT ; + } + +return 0 ; +} +static int riic_master_transmit (struct riic_data *pd,int stop) {int ret =0 ; -int index;riic_set_bit (pd,ICCR2_ST ,RIIC_ICCR2 ); ret =riic_wait_for_icsr2 (pd,ICSR2_START );
@@ -328,15 +409,12 @@ static int riic_master_transmit(struct riic_data *pd, int stop) goto force_exit;/* transmit data */ - index =0 ; -do { - ret =riic_wait_for_icsr2 (pd,ICSR2_TDRE ); -if (ret <0 ) - goto force_exit; - -riic_write (pd, pd->msg->buf[index],RIIC_ICDRT ); - index++; - }while (pd->msg->len > index); +if (pd->chan_tx && pd->msg->len >1 ) + ret =riic_master_transmit_dma (pd); +else + ret =riic_master_transmit_pio (pd); +if (ret <0 ) + goto force_exit; ret =riic_wait_for_icsr2 (pd,ICSR2_TEND );if (ret <0 )
@@ -361,12 +439,72 @@ static void riic_set_receive_ack(struct riic_data *pd, int ack)riic_set_bit (pd,ICMR3_ACKBT ,RIIC_ICMR3 ); } +static int riic_master_receive_pio (struct riic_data *pd) +{ +int ret; + + pd->index =0 ; + +while ((pd->msg->len -1 ) > pd->index) { + ret =riic_wait_for_icsr2 (pd,ICSR2_RDRF ); +if (ret <0 ) +return ret; + +if ((pd->index +1 ) >= (pd->msg->len -1 )) +break ; + + pd->msg->buf[pd->index] =riic_read (pd,RIIC_ICDRR ); + pd->index++; + } + +return 0 ; +} + +static int riic_master_receive_dma (struct riic_data *pd) +{ +struct scatterlist sg; +unsigned char *buf = pd->msg->buf; +struct dma_async_tx_descriptor *desc; +int ret; +int len = pd->msg->len -2 ; + + pd->index =0 ; + +sg_init_table (&sg,1 ); +sg_set_buf (&sg, buf, len); +sg_dma_len (&sg) = len; +dma_map_sg (pd->chan_rx->device->dev, &sg,1 ,DMA_FROM_DEVICE ); + + desc = pd->chan_rx->device->device_prep_slave_sg (pd->chan_rx, + &sg,1 ,DMA_FROM_DEVICE , +DMA_PREP_INTERRUPT |DMA_CTRL_ACK ); +if (!desc) +return -EIO ; + + desc->callback = riic_dma_complete; + desc->callback_param = pd; + pd->dma_callbacked =0 ; + ret =riic_wait_for_icsr2 (pd,ICSR2_RDRF ); +if (ret <0 ) +return ret; +dmaengine_submit (desc); +dma_async_issue_pending (pd->chan_rx); + + ret =wait_event_timeout (pd->wait, pd->dma_callbacked,HZ ); +if (!ret && !pd->dma_callbacked) { +dmaengine_terminate_all (pd->chan_rx); +return -ETIMEDOUT ; + } + + pd->index = len; +return 0 ; +} +static int riic_master_receive (struct riic_data *pd,int restart) { -int dummy_read =1 ;int ret =0 ; -int index; +riic_set_receive_ack (pd,1 );if (restart)riic_set_bit (pd,ICCR2_RS ,RIIC_ICCR2 );else
@@ -394,40 +532,26 @@ static int riic_master_receive(struct riic_data *pd, int restart) goto force_exit; } -/* receive data */ - index =0 ; -while ((pd->msg->len -1 ) > index) { - ret =riic_wait_for_icsr2 (pd,ICSR2_RDRF ); -if (ret <0 ) -return ret; - -if ((index +1 ) >= (pd->msg->len -1 )) -break ; +/* step 4 */ +riic_read (pd,RIIC_ICDRR );/* dummy read */ -if (dummy_read) { -riic_read (pd,RIIC_ICDRR ); - dummy_read =0 ; - }else { - pd->msg->buf[index] =riic_read (pd,RIIC_ICDRR ); - index++; -riic_set_receive_ack (pd,1 ); - } - } +/* receive data */ +if (pd->chan_rx && pd->msg->len >2 ) + ret =riic_master_receive_dma (pd); +else + ret =riic_master_receive_pio (pd); +if (ret <0 ) +return ret;/* step 6 */ ret =riic_wait_for_icsr2 (pd,ICSR2_RDRF );if (ret <0 )return ret; +riic_set_receive_ack (pd,0 );/* step 7 */ -if (dummy_read) { -riic_read (pd,RIIC_ICDRR ); - dummy_read =0 ; - }else { - pd->msg->buf[index] =riic_read (pd,RIIC_ICDRR ); - index++; - } -riic_set_receive_ack (pd,1 ); + pd->msg->buf[pd->index] =riic_read (pd,RIIC_ICDRR ); + pd->index++; ret =riic_wait_for_icsr2 (pd,ICSR2_RDRF );if (ret <0 )
@@ -436,11 +560,11 @@ static int riic_master_receive(struct riic_data *pd, int restart)riic_clear_bit (pd,ICSR2_STOP ,RIIC_ICSR2 );riic_set_bit (pd,ICCR2_SP ,RIIC_ICCR2 ); - pd->msg->buf[index] =riic_read (pd,RIIC_ICDRR ); - index++; -riic_set_receive_ack (pd,0 ); + pd->msg->buf[pd->index] =riic_read (pd,RIIC_ICDRR ); + pd->index++; force_exit: +/* step 8 */ ret =riic_wait_for_icsr2 (pd,ICSR2_STOP );if (ret <0 )return ret;
@@ -484,14 +608,74 @@ static struct i2c_algorithm riic_algorithm = { .master_xfer = riic_xfer, }; +static irqreturn_t riic_irq (int irq,void *data) +{ +struct riic_data *pd = data; +irqreturn_t ret =IRQ_NONE ; + + pd->icsr2 =riic_read (pd,RIIC_ICSR2 ); + +if (pd->icsr2 &ICSR2_NACKF ) { + ret =IRQ_HANDLED ; +riic_clear_bit (pd,ICIER_NAKIE ,RIIC_ICIER ); +wake_up (&pd->wait); + } + +return ret; +} + +static bool riic_filter (struct dma_chan *chan,void *filter_param) +{ + chan->private = filter_param; + +return true; +} + +static void riic_request_dma (struct riic_data *pd, +struct riic_platform_data *riic_data) +{ +dma_cap_mask_t mask; + +if (riic_data->dma_tx.slave_id) { +dma_cap_zero (mask); +dma_cap_set (DMA_SLAVE , mask); + pd->chan_tx =dma_request_channel (mask, riic_filter, + &riic_data->dma_tx); +if (!pd->chan_tx) +dev_warn (pd->dev,"dma_request_channel for tx failed." +" Then, use PIO.\n" ); + } +if (riic_data->dma_rx.slave_id) { +dma_cap_zero (mask); +dma_cap_set (DMA_SLAVE , mask); + pd->chan_rx =dma_request_channel (mask, riic_filter, + &riic_data->dma_rx); +if (!pd->chan_rx) +dev_warn (pd->dev,"dma_request_channel for rx failed." +" Then, use PIO.\n" ); + } +} + +static void riic_release_dma (struct riic_data *pd) +{ +if (pd->chan_tx) +dma_release_channel (pd->chan_tx); +if (pd->chan_rx) +dma_release_channel (pd->chan_rx); +} + static int __devexit riic_remove(struct platform_device *pdev) {struct riic_data *pd =platform_get_drvdata (pdev); +int irq =platform_get_irq (pdev,0 );if (!pd)return 0 ;i2c_del_adapter (&pd->adap); +if (irq >=0 ) +free_irq (irq, pd); +riic_release_dma (pd);iounmap (pd->reg);clk_disable (pd->clk);clk_put (pd->clk);
@@ -509,6 +693,7 @@ static int __devinit riic_probe(struct platform_device *pdev)void __iomem *reg =NULL ;int ret =0 ;char clk_name[16 ]; +int irq; res =platform_get_resource (pdev,IORESOURCE_MEM ,0 );if (!res) {
@@ -516,6 +701,7 @@ static int __devinit riic_probe(struct platform_device *pdev)dev_err (&pdev->dev,"platform_get_resource failed.\n" ); goto clean_up; } + irq =platform_get_irq (pdev,0 );if (!pdev->dev.platform_data) { ret = -ENOENT ;
@@ -562,27 +748,45 @@ static int __devinit riic_probe(struct platform_device *pdev) }clk_enable (pd->clk); +riic_request_dma (pd, riic_data); +init_waitqueue_head (&pd->wait); ret =riic_init_setting (pd, riic_data->clock);if (ret <0 ) {dev_err (&pdev->dev,"riic_init_setting failed.\n" ); goto clean_up; } +if (irq >=0 ) { +/* interruption of EEI for DMA */ + ret =request_irq (irq, riic_irq,0 ,dev_name (&pdev->dev), pd); +if (ret <0 ) { +dev_err (&pdev->dev,"request_irq error\n" ); + goto clean_up; + } + }else if (pd->chan_tx || pd->chan_rx) { +dev_err (&pdev->dev,"Interrupt resource needed.\n" ); + goto clean_up; + } + ret =i2c_add_numbered_adapter (adap);if (ret <0 ) {dev_err (&pdev->dev,"i2c_add_numbered_adapter failed.\n" ); - goto clean_up; + goto clean_up2; }dev_info (&pdev->dev,"version %s\n" ,DRIVER_VERSION );return ret; +clean_up2: +if (irq >=0 ) +free_irq (irq, pd); clean_up:if (pd) {if (!IS_ERR (pd->clk)) {clk_disable (pd->clk);clk_put (pd->clk); } +riic_release_dma (pd); }if (reg)iounmap (reg);
include/linux/i2c/riic.h +4 -0
@@ -21,8 +21,12 @@#ifndef _RIIC_H_#define _RIIC_H_ +#include <linux/sh_dma.h> +struct riic_platform_data {int clock;/* i2c clock (kHZ) */ +struct sh_dmae_slave dma_tx; +struct sh_dmae_slave dma_rx; };#endif
Signature · 1 line
1.7.1