diff mbox

[2/2] i2c: i2c-riic: add dmaengine supporting

Message ID 4E0D1C42.2080804@renesas.com (mailing list archive)
State Superseded
Headers show

Commit Message

Yoshihiro Shimoda July 1, 2011, 1 a.m. UTC
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 <yoshihiro.shimoda.uh@renesas.com>
---
 drivers/i2c/busses/i2c-riic.c |  283 ++++++++++++++++++++++++++++++++++------
 include/linux/i2c/riic.h      |    4 +
 2 files changed, 244 insertions(+), 43 deletions(-)

Comments

Ben Dooks July 13, 2011, 8:10 p.m. UTC | #1
On Fri, Jul 01, 2011 at 10:00:50AM +0900, Yoshihiro Shimoda wrote:
> 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 <yoshihiro.shimoda.uh@renesas.com>
> ---
>  drivers/i2c/busses/i2c-riic.c |  283 ++++++++++++++++++++++++++++++++++------
>  include/linux/i2c/riic.h      |    4 +
>  2 files changed, 244 insertions(+), 43 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c
> index dcc183b..22dd779 100644
> --- a/drivers/i2c/busses/i2c-riic.c
> +++ b/drivers/i2c/busses/i2c-riic.c
> @@ -30,6 +30,7 @@
>  #include <linux/timer.h>
>  #include <linux/delay.h>
>  #include <linux/slab.h>
> +#include <linux/dmaengine.h>
> 
>  #define RIIC_ICCR1	0x00
>  #define RIIC_ICCR2	0x01
> @@ -164,6 +165,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;
>  };

What happens if the driver is compiled into a kernel
without dma engine support?
 
>  #define DRIVER_VERSION	"2011-07-01"
> @@ -199,6 +208,7 @@ static void riic_clear_bit(struct riic_data *pd, unsigned char val,
>  	riic_write(pd, tmp, offset);
>  }
> 
> +
>  static void riic_set_clock(struct riic_data *pd, int clock)
>  {
>  	switch (clock) {
> @@ -244,7 +254,8 @@ static void riic_init_setting(struct riic_data *pd, int clock)
>  	riic_set_clock(pd, clock);
> 
>  	riic_set_bit(pd, ICCR1_ICE, RIIC_ICCR1);
> -	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);
>  }
 
>  static int riic_check_busy(struct riic_data *pd)
> @@ -304,10 +315,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);

surely there's a better way of calling this?

> +	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 (!pd->dma_callbacked && !ret) {
> +		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);
> @@ -320,15 +400,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)
> @@ -353,12 +430,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);

again, no better way of calling this?

> +	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 (!pd->dma_callbacked && !ret) {

hmm, if you did not time out, then you should be able to
infer the 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
> @@ -386,40 +523,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;
> +	/* step 4 */
> +	riic_read(pd, RIIC_ICDRR);	/* dummy read */
> 
> -		if ((index + 1) >= (pd->msg->len - 1))
> -			break;
> -
> -		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)
> @@ -428,11 +551,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;
> @@ -476,14 +599,68 @@ 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 (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);
> +	}
> +}

should there be a warning if the slave_id is valid and
the dma channel cannot be requested?

> +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);
>  	kfree(pd);
> 
> @@ -498,6 +675,7 @@ static int __devinit riic_probe(struct platform_device *pdev)
>  	struct i2c_adapter *adap;
>  	void __iomem *reg = NULL;
>  	int ret = 0;
> +	int irq;
> 
>  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>  	if (!res) {
> @@ -505,6 +683,7 @@ static int __devinit riic_probe(struct platform_device *pdev)
>  		dev_err(&pdev->dev, "platform_get_resource error.\n");
>  		goto clean_up;
>  	}
> +	irq = platform_get_irq(pdev, 0);
> 
>  	if (!pdev->dev.platform_data) {
>  		dev_err(&pdev->dev, "no platform data\n");
> @@ -541,18 +720,36 @@ static int __devinit riic_probe(struct platform_device *pdev)
> 
>  	strlcpy(adap->name, pdev->name, sizeof(adap->name));
> 
> +	riic_request_dma(pd, riic_data);
> +	init_waitqueue_head(&pd->wait);
>  	riic_init_setting(pd, riic_data->clock);
> +	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 error.\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)
> +		riic_release_dma(pd);
>  	if (reg)
>  		iounmap(reg);
>  	kfree(pd);
> diff --git a/include/linux/i2c/riic.h b/include/linux/i2c/riic.h
> index 5839381..f97b65c 100644
> --- a/include/linux/i2c/riic.h
> +++ b/include/linux/i2c/riic.h
> @@ -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
> -- 
> 1.7.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Yoshihiro Shimoda July 14, 2011, 2:17 a.m. UTC | #2
Hi Ben,

2011/07/14 5:10, Ben Dooks wrote:
> On Fri, Jul 01, 2011 at 10:00:50AM +0900, Yoshihiro Shimoda wrote:
>> diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c
>> index dcc183b..22dd779 100644
>> --- a/drivers/i2c/busses/i2c-riic.c
>> +++ b/drivers/i2c/busses/i2c-riic.c
>> @@ -30,6 +30,7 @@
>>  #include <linux/timer.h>
>>  #include <linux/delay.h>
>>  #include <linux/slab.h>
>> +#include <linux/dmaengine.h>
>>
>>  #define RIIC_ICCR1	0x00
>>  #define RIIC_ICCR2	0x01
>> @@ -164,6 +165,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;
>>  };
> 
> What happens if the driver is compiled into a kernel
> without dma engine support?

I could compile the code without enabling "CONFIG_DMA_ENGINE".
This is because The "struct dma_chan" is always defined in
the "include/linux/dmaengine.h", I think.

>> +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);
> 
> surely there's a better way of calling this?

Umm, I checked other drivers in the "drivers/", but I didn't find
a better way. Also other drivers was similar code...

>> +	dmaengine_submit(desc);
>> +	dma_async_issue_pending(pd->chan_rx);
>> +
>> +	ret = wait_event_timeout(pd->wait, pd->dma_callbacked, HZ);
>> +	if (!pd->dma_callbacked && !ret) {
> 
> hmm, if you did not time out, then you should be able to
> infer the pd->dma_callbacked?

If this did not time out, the pd->dma_callbacked is always set.
So, I will fix the code to "if (!ret && !pd->dma_callbacked) {".

>> +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 (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);
>> +	}
>> +}
> 
> should there be a warning if the slave_id is valid and
> the dma channel cannot be requested?

I will add a warning message.
However, even if the driver doesn't use dma, it can use PIO mode.

Best regards,
Yoshihiro Shimoda
--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c
index dcc183b..22dd779 100644
--- a/drivers/i2c/busses/i2c-riic.c
+++ b/drivers/i2c/busses/i2c-riic.c
@@ -30,6 +30,7 @@ 
 #include <linux/timer.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
+#include <linux/dmaengine.h>

 #define RIIC_ICCR1	0x00
 #define RIIC_ICCR2	0x01
@@ -164,6 +165,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-07-01"
@@ -199,6 +208,7 @@  static void riic_clear_bit(struct riic_data *pd, unsigned char val,
 	riic_write(pd, tmp, offset);
 }

+
 static void riic_set_clock(struct riic_data *pd, int clock)
 {
 	switch (clock) {
@@ -244,7 +254,8 @@  static void riic_init_setting(struct riic_data *pd, int clock)
 	riic_set_clock(pd, clock);

 	riic_set_bit(pd, ICCR1_ICE, RIIC_ICCR1);
-	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);
 }

 static int riic_check_busy(struct riic_data *pd)
@@ -304,10 +315,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 (!pd->dma_callbacked && !ret) {
+		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);
@@ -320,15 +400,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)
@@ -353,12 +430,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 (!pd->dma_callbacked && !ret) {
+		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
@@ -386,40 +523,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;
+	/* step 4 */
+	riic_read(pd, RIIC_ICDRR);	/* dummy read */

-		if ((index + 1) >= (pd->msg->len - 1))
-			break;
-
-		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)
@@ -428,11 +551,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;
@@ -476,14 +599,68 @@  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 (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);
+	}
+}
+
+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);
 	kfree(pd);

@@ -498,6 +675,7 @@  static int __devinit riic_probe(struct platform_device *pdev)
 	struct i2c_adapter *adap;
 	void __iomem *reg = NULL;
 	int ret = 0;
+	int irq;

 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!res) {
@@ -505,6 +683,7 @@  static int __devinit riic_probe(struct platform_device *pdev)
 		dev_err(&pdev->dev, "platform_get_resource error.\n");
 		goto clean_up;
 	}
+	irq = platform_get_irq(pdev, 0);

 	if (!pdev->dev.platform_data) {
 		dev_err(&pdev->dev, "no platform data\n");
@@ -541,18 +720,36 @@  static int __devinit riic_probe(struct platform_device *pdev)

 	strlcpy(adap->name, pdev->name, sizeof(adap->name));

+	riic_request_dma(pd, riic_data);
+	init_waitqueue_head(&pd->wait);
 	riic_init_setting(pd, riic_data->clock);
+	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 error.\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)
+		riic_release_dma(pd);
 	if (reg)
 		iounmap(reg);
 	kfree(pd);
diff --git a/include/linux/i2c/riic.h b/include/linux/i2c/riic.h
index 5839381..f97b65c 100644
--- a/include/linux/i2c/riic.h
+++ b/include/linux/i2c/riic.h
@@ -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