*/
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #define ICSAR  0x1C    /* slave address */
 #define ICMAR  0x20    /* master address */
 #define ICRXTX 0x24    /* data port */
+#define ICDMAER        0x3c    /* DMA enable */
+#define ICFBSCR        0x38    /* first bit setup cycle */
 
 /* ICSCR */
 #define SDBS   (1 << 3)        /* slave data buffer select */
 #define MDR    (1 << 1)
 #define MAT    (1 << 0)        /* slave addr xfer done */
 
+/* ICDMAER */
+#define RSDMAE (1 << 3)        /* DMA Slave Received Enable */
+#define TSDMAE (1 << 2)        /* DMA Slave Transmitted Enable */
+#define RMDMAE (1 << 1)        /* DMA Master Received Enable */
+#define TMDMAE (1 << 0)        /* DMA Master Transmitted Enable */
+
+/* ICFBSCR */
+#define TCYC06 0x04            /*  6*Tcyc delay 1st bit between SDA and SCL */
+#define TCYC17 0x0f            /* 17*Tcyc delay 1st bit between SDA and SCL */
+
 
 #define RCAR_BUS_PHASE_START   (MDBS | MIE | ESG)
 #define RCAR_BUS_PHASE_DATA    (MDBS | MIE)
        u32 flags;
        enum rcar_i2c_type devtype;
        struct i2c_client *slave;
+
+       struct resource *res;
+       struct dma_chan *dma_tx;
+       struct dma_chan *dma_rx;
+       struct scatterlist sg;
+       enum dma_data_direction dma_direction;
 };
 
 #define rcar_i2c_priv_to_dev(p)                ((p)->adap.dev.parent)
 /*
  *             interrupt functions
  */
+static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv)
+{
+       struct dma_chan *chan = priv->dma_direction == DMA_FROM_DEVICE
+               ? priv->dma_rx : priv->dma_tx;
+
+       /* Disable DMA Master Received/Transmitted */
+       rcar_i2c_write(priv, ICDMAER, 0);
+
+       /* Reset default delay */
+       rcar_i2c_write(priv, ICFBSCR, TCYC06);
+
+       dma_unmap_single(chan->device->dev, sg_dma_address(&priv->sg),
+                        priv->msg->len, priv->dma_direction);
+
+       priv->dma_direction = DMA_NONE;
+}
+
+static void rcar_i2c_cleanup_dma(struct rcar_i2c_priv *priv)
+{
+       if (priv->dma_direction == DMA_NONE)
+               return;
+       else if (priv->dma_direction == DMA_FROM_DEVICE)
+               dmaengine_terminate_all(priv->dma_rx);
+       else if (priv->dma_direction == DMA_TO_DEVICE)
+               dmaengine_terminate_all(priv->dma_tx);
+
+       rcar_i2c_dma_unmap(priv);
+}
+
+static void rcar_i2c_dma_callback(void *data)
+{
+       struct rcar_i2c_priv *priv = data;
+
+       priv->pos += sg_dma_len(&priv->sg);
+
+       rcar_i2c_dma_unmap(priv);
+}
+
+static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
+{
+       struct device *dev = rcar_i2c_priv_to_dev(priv);
+       struct i2c_msg *msg = priv->msg;
+       bool read = msg->flags & I2C_M_RD;
+       enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+       struct dma_chan *chan = read ? priv->dma_rx : priv->dma_tx;
+       struct dma_async_tx_descriptor *txdesc;
+       dma_addr_t dma_addr;
+       dma_cookie_t cookie;
+       unsigned char *buf;
+       int len;
+
+       /* Do not use DMA if it's not available or for messages < 8 bytes */
+       if (IS_ERR(chan) || msg->len < 8)
+               return;
+
+       if (read) {
+               /*
+                * The last two bytes needs to be fetched using PIO in
+                * order for the STOP phase to work.
+                */
+               buf = priv->msg->buf;
+               len = priv->msg->len - 2;
+       } else {
+               /*
+                * First byte in message was sent using PIO.
+                */
+               buf = priv->msg->buf + 1;
+               len = priv->msg->len - 1;
+       }
+
+       dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
+       if (dma_mapping_error(dev, dma_addr)) {
+               dev_dbg(dev, "dma map failed, using PIO\n");
+               return;
+       }
+
+       sg_dma_len(&priv->sg) = len;
+       sg_dma_address(&priv->sg) = dma_addr;
+
+       priv->dma_direction = dir;
+
+       txdesc = dmaengine_prep_slave_sg(chan, &priv->sg, 1,
+                                        read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV,
+                                        DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       if (!txdesc) {
+               dev_dbg(dev, "dma prep slave sg failed, using PIO\n");
+               rcar_i2c_cleanup_dma(priv);
+               return;
+       }
+
+       txdesc->callback = rcar_i2c_dma_callback;
+       txdesc->callback_param = priv;
+
+       cookie = dmaengine_submit(txdesc);
+       if (dma_submit_error(cookie)) {
+               dev_dbg(dev, "submitting dma failed, using PIO\n");
+               rcar_i2c_cleanup_dma(priv);
+               return;
+       }
+
+       /* Set delay for DMA operations */
+       rcar_i2c_write(priv, ICFBSCR, TCYC17);
+
+       /* Enable DMA Master Received/Transmitted */
+       if (read)
+               rcar_i2c_write(priv, ICDMAER, RMDMAE);
+       else
+               rcar_i2c_write(priv, ICDMAER, TMDMAE);
+
+       dma_async_issue_pending(chan);
+}
+
 static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
 {
        struct i2c_msg *msg = priv->msg;
                rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
                priv->pos++;
 
+               /*
+                * Try to use DMA to transmit the rest of the data if
+                * address transfer pashe just finished.
+                */
+               if (msr & MAT)
+                       rcar_i2c_dma(priv);
        } else {
                /*
                 * The last data was pushed to ICRXTX on _PREV_ empty irq.
                return;
 
        if (msr & MAT) {
-               /* Address transfer phase finished, but no data at this point. */
+               /*
+                * Address transfer phase finished, but no data at this point.
+                * Try to use DMA to receive data.
+                */
+               rcar_i2c_dma(priv);
        } else if (priv->pos < msg->len) {
                /* get received data */
                msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
        return IRQ_HANDLED;
 }
 
+static struct dma_chan *rcar_i2c_request_dma_chan(struct device *dev,
+                                       enum dma_transfer_direction dir,
+                                       dma_addr_t port_addr)
+{
+       struct dma_chan *chan;
+       struct dma_slave_config cfg;
+       char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx";
+       int ret;
+
+       chan = dma_request_slave_channel_reason(dev, chan_name);
+       if (IS_ERR(chan)) {
+               ret = PTR_ERR(chan);
+               dev_dbg(dev, "request_channel failed for %s (%d)\n",
+                       chan_name, ret);
+               return chan;
+       }
+
+       memset(&cfg, 0, sizeof(cfg));
+       cfg.direction = dir;
+       if (dir == DMA_MEM_TO_DEV) {
+               cfg.dst_addr = port_addr;
+               cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+       } else {
+               cfg.src_addr = port_addr;
+               cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+       }
+
+       ret = dmaengine_slave_config(chan, &cfg);
+       if (ret) {
+               dev_dbg(dev, "slave_config failed for %s (%d)\n",
+                       chan_name, ret);
+               dma_release_channel(chan);
+               return ERR_PTR(ret);
+       }
+
+       dev_dbg(dev, "got DMA channel for %s\n", chan_name);
+       return chan;
+}
+
+static void rcar_i2c_request_dma(struct rcar_i2c_priv *priv,
+                                struct i2c_msg *msg)
+{
+       struct device *dev = rcar_i2c_priv_to_dev(priv);
+       bool read;
+       struct dma_chan *chan;
+       enum dma_transfer_direction dir;
+
+       read = msg->flags & I2C_M_RD;
+
+       chan = read ? priv->dma_rx : priv->dma_tx;
+       if (PTR_ERR(chan) != -EPROBE_DEFER)
+               return;
+
+       dir = read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
+       chan = rcar_i2c_request_dma_chan(dev, dir, priv->res->start + ICRXTX);
+
+       if (read)
+               priv->dma_rx = chan;
+       else
+               priv->dma_tx = chan;
+}
+
+static void rcar_i2c_release_dma(struct rcar_i2c_priv *priv)
+{
+       if (!IS_ERR(priv->dma_tx)) {
+               dma_release_channel(priv->dma_tx);
+               priv->dma_tx = ERR_PTR(-EPROBE_DEFER);
+       }
+
+       if (!IS_ERR(priv->dma_rx)) {
+               dma_release_channel(priv->dma_rx);
+               priv->dma_rx = ERR_PTR(-EPROBE_DEFER);
+       }
+}
+
 static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
                                struct i2c_msg *msgs,
                                int num)
                        ret = -EOPNOTSUPP;
                        goto out;
                }
+               rcar_i2c_request_dma(priv, msgs + i);
        }
 
        /* init first message */
        time_left = wait_event_timeout(priv->wait, priv->flags & ID_DONE,
                                     num * adap->timeout);
        if (!time_left) {
+               rcar_i2c_cleanup_dma(priv);
                rcar_i2c_init(priv);
                ret = -ETIMEDOUT;
        } else if (priv->flags & ID_NACK) {
 {
        struct rcar_i2c_priv *priv;
        struct i2c_adapter *adap;
-       struct resource *res;
        struct device *dev = &pdev->dev;
        struct i2c_timings i2c_t;
        int irq, ret;
                return PTR_ERR(priv->clk);
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       priv->io = devm_ioremap_resource(dev, res);
+       priv->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       priv->io = devm_ioremap_resource(dev, priv->res);
        if (IS_ERR(priv->io))
                return PTR_ERR(priv->io);
 
 
        i2c_parse_fw_timings(dev, &i2c_t, false);
 
+       /* Init DMA */
+       sg_init_table(&priv->sg, 1);
+       priv->dma_direction = DMA_NONE;
+       priv->dma_rx = priv->dma_tx = ERR_PTR(-EPROBE_DEFER);
+
        pm_runtime_enable(dev);
        pm_runtime_get_sync(dev);
        ret = rcar_i2c_clock_calculate(priv, &i2c_t);
        struct device *dev = &pdev->dev;
 
        i2c_del_adapter(&priv->adap);
+       rcar_i2c_release_dma(priv);
        if (priv->flags & ID_P_PM_BLOCKED)
                pm_runtime_put(dev);
        pm_runtime_disable(dev);