From: Yoshihiro Shimoda <yoshihiro.shimoda.uh-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
To: ben-linux-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org
Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
SH-Linux <linux-sh-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>
Subject: [PATCH 2/2 v3] i2c: i2c-riic: add dmaengine supporting
Date: Mon, 26 Sep 2011 17:42:01 +0900 [thread overview]
Message-ID: <4E803AD9.8090301@renesas.com> (raw)
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-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
---
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(-)
diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c
index 1e6639c..93e114c 100644
--- a/drivers/i2c/busses/i2c-riic.c
+++ b/drivers/i2c/busses/i2c-riic.c
@@ -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);
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
WARNING: multiple messages have this Message-ID (diff)
From: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
To: ben-linux-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org
Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
SH-Linux <linux-sh-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>
Subject: [PATCH 2/2 v3] i2c: i2c-riic: add dmaengine supporting
Date: Mon, 26 Sep 2011 08:42:01 +0000 [thread overview]
Message-ID: <4E803AD9.8090301@renesas.com> (raw)
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>
---
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(-)
diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c
index 1e6639c..93e114c 100644
--- a/drivers/i2c/busses/i2c-riic.c
+++ b/drivers/i2c/busses/i2c-riic.c
@@ -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);
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
next reply other threads:[~2011-09-26 8:42 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-09-26 8:42 Yoshihiro Shimoda [this message]
2011-09-26 8:42 ` [PATCH 2/2 v3] i2c: i2c-riic: add dmaengine supporting Yoshihiro Shimoda
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4E803AD9.8090301@renesas.com \
--to=yoshihiro.shimoda.uh-zm6kxycvzfbbdgjk7y7tuq@public.gmane.org \
--cc=ben-linux-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org \
--cc=linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=linux-sh-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.