Commit 86c92b99 authored by Sowjanya Komatineni's avatar Sowjanya Komatineni Committed by Wolfram Sang

i2c: tegra: Add DMA support

This patch adds DMA support for Tegra I2C.

Tegra I2C TX and RX FIFO depth is 8 words. PIO mode is used for
transfer size of the max FIFO depth and DMA mode is used for
transfer size higher than max FIFO depth to save CPU overhead.

PIO mode needs full intervention of CPU to fill or empty FIFO's
and also need to service multiple data requests interrupt for the
same transaction. This adds delay between data bytes of the same
transfer when CPU is fully loaded and some slave devices has
internal timeout for no bus activity and stops transaction to
avoid bus hang. DMA mode is helpful in such cases.

DMA mode is also helpful for Large transfers during downloading or
uploading FW over I2C to some external devices.

Tegra210 and prior Tegra chips use APBDMA driver which is replaced
with GPCDMA on Tegra186 and Tegra194.
This patch uses has_apb_dma flag in hw_feature to differentiate
DMA driver change between Tegra chipset.

APBDMA driver is registered from module-init level and this patch
also has a change to register I2C driver at module-init level
rather than subsys-init to avoid deferring I2C probe till APBDMA
driver is registered.
Acked-by: default avatarThierry Reding <treding@nvidia.com>
Reviewed-by: default avatarDmitry Osipenko <digetx@gmail.com>
Tested-by: default avatarDmitry Osipenko <digetx@gmail.com>
Signed-off-by: default avatarSowjanya Komatineni <skomatineni@nvidia.com>
Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
parent b03ff2a2
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -42,8 +44,8 @@ ...@@ -42,8 +44,8 @@
#define I2C_FIFO_CONTROL 0x05c #define I2C_FIFO_CONTROL 0x05c
#define I2C_FIFO_CONTROL_TX_FLUSH BIT(1) #define I2C_FIFO_CONTROL_TX_FLUSH BIT(1)
#define I2C_FIFO_CONTROL_RX_FLUSH BIT(0) #define I2C_FIFO_CONTROL_RX_FLUSH BIT(0)
#define I2C_FIFO_CONTROL_TX_TRIG_SHIFT 5 #define I2C_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 5)
#define I2C_FIFO_CONTROL_RX_TRIG_SHIFT 2 #define I2C_FIFO_CONTROL_RX_TRIG(x) (((x) - 1) << 2)
#define I2C_FIFO_STATUS 0x060 #define I2C_FIFO_STATUS 0x060
#define I2C_FIFO_STATUS_TX_MASK 0xF0 #define I2C_FIFO_STATUS_TX_MASK 0xF0
#define I2C_FIFO_STATUS_TX_SHIFT 4 #define I2C_FIFO_STATUS_TX_SHIFT 4
...@@ -128,6 +130,13 @@ ...@@ -128,6 +130,13 @@
/* Packet header size in bytes */ /* Packet header size in bytes */
#define I2C_PACKET_HEADER_SIZE 12 #define I2C_PACKET_HEADER_SIZE 12
/*
* Upto I2C_PIO_MODE_MAX_LEN bytes, controller will use PIO mode,
* above this, controller will use DMA to fill FIFO.
* MAX PIO len is 20 bytes excluding packet header.
*/
#define I2C_PIO_MODE_MAX_LEN 32
/* /*
* msg_end_type: The bus control which need to be send at end of transfer. * msg_end_type: The bus control which need to be send at end of transfer.
* @MSG_END_STOP: Send stop pulse at end of transfer. * @MSG_END_STOP: Send stop pulse at end of transfer.
...@@ -169,6 +178,7 @@ enum msg_end_type { ...@@ -169,6 +178,7 @@ enum msg_end_type {
* allowing 0 length transfers. * allowing 0 length transfers.
* @supports_bus_clear: Bus Clear support to recover from bus hang during * @supports_bus_clear: Bus Clear support to recover from bus hang during
* SDA stuck low from device for some unknown reasons. * SDA stuck low from device for some unknown reasons.
* @has_apb_dma: Support of APBDMA on corresponding Tegra chip.
*/ */
struct tegra_i2c_hw_feature { struct tegra_i2c_hw_feature {
bool has_continue_xfer_support; bool has_continue_xfer_support;
...@@ -183,6 +193,7 @@ struct tegra_i2c_hw_feature { ...@@ -183,6 +193,7 @@ struct tegra_i2c_hw_feature {
bool has_mst_fifo; bool has_mst_fifo;
const struct i2c_adapter_quirks *quirks; const struct i2c_adapter_quirks *quirks;
bool supports_bus_clear; bool supports_bus_clear;
bool has_apb_dma;
}; };
/** /**
...@@ -194,6 +205,7 @@ struct tegra_i2c_hw_feature { ...@@ -194,6 +205,7 @@ struct tegra_i2c_hw_feature {
* @fast_clk: clock reference for fast clock of I2C controller * @fast_clk: clock reference for fast clock of I2C controller
* @rst: reset control for the I2C controller * @rst: reset control for the I2C controller
* @base: ioremapped registers cookie * @base: ioremapped registers cookie
* @base_phys: physical base address of the I2C controller
* @cont_id: I2C controller ID, used for packet header * @cont_id: I2C controller ID, used for packet header
* @irq: IRQ number of transfer complete interrupt * @irq: IRQ number of transfer complete interrupt
* @irq_disabled: used to track whether or not the interrupt is enabled * @irq_disabled: used to track whether or not the interrupt is enabled
...@@ -207,6 +219,13 @@ struct tegra_i2c_hw_feature { ...@@ -207,6 +219,13 @@ struct tegra_i2c_hw_feature {
* @clk_divisor_non_hs_mode: clock divider for non-high-speed modes * @clk_divisor_non_hs_mode: clock divider for non-high-speed modes
* @is_multimaster_mode: track if I2C controller is in multi-master mode * @is_multimaster_mode: track if I2C controller is in multi-master mode
* @xfer_lock: lock to serialize transfer submission and processing * @xfer_lock: lock to serialize transfer submission and processing
* @tx_dma_chan: DMA transmit channel
* @rx_dma_chan: DMA receive channel
* @dma_phys: handle to DMA resources
* @dma_buf: pointer to allocated DMA buffer
* @dma_buf_size: DMA buffer size
* @is_curr_dma_xfer: indicates active DMA transfer
* @dma_complete: DMA completion notifier
*/ */
struct tegra_i2c_dev { struct tegra_i2c_dev {
struct device *dev; struct device *dev;
...@@ -216,6 +235,7 @@ struct tegra_i2c_dev { ...@@ -216,6 +235,7 @@ struct tegra_i2c_dev {
struct clk *fast_clk; struct clk *fast_clk;
struct reset_control *rst; struct reset_control *rst;
void __iomem *base; void __iomem *base;
phys_addr_t base_phys;
int cont_id; int cont_id;
int irq; int irq;
bool irq_disabled; bool irq_disabled;
...@@ -229,6 +249,13 @@ struct tegra_i2c_dev { ...@@ -229,6 +249,13 @@ struct tegra_i2c_dev {
u16 clk_divisor_non_hs_mode; u16 clk_divisor_non_hs_mode;
bool is_multimaster_mode; bool is_multimaster_mode;
spinlock_t xfer_lock; spinlock_t xfer_lock;
struct dma_chan *tx_dma_chan;
struct dma_chan *rx_dma_chan;
dma_addr_t dma_phys;
u32 *dma_buf;
unsigned int dma_buf_size;
bool is_curr_dma_xfer;
struct completion dma_complete;
}; };
static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
...@@ -297,6 +324,109 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) ...@@ -297,6 +324,109 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
i2c_writel(i2c_dev, int_mask, I2C_INT_MASK); i2c_writel(i2c_dev, int_mask, I2C_INT_MASK);
} }
static void tegra_i2c_dma_complete(void *args)
{
struct tegra_i2c_dev *i2c_dev = args;
complete(&i2c_dev->dma_complete);
}
static int tegra_i2c_dma_submit(struct tegra_i2c_dev *i2c_dev, size_t len)
{
struct dma_async_tx_descriptor *dma_desc;
enum dma_transfer_direction dir;
struct dma_chan *chan;
dev_dbg(i2c_dev->dev, "starting DMA for length: %zu\n", len);
reinit_completion(&i2c_dev->dma_complete);
dir = i2c_dev->msg_read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
chan = i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan;
dma_desc = dmaengine_prep_slave_single(chan, i2c_dev->dma_phys,
len, dir, DMA_PREP_INTERRUPT |
DMA_CTRL_ACK);
if (!dma_desc) {
dev_err(i2c_dev->dev, "failed to get DMA descriptor\n");
return -EINVAL;
}
dma_desc->callback = tegra_i2c_dma_complete;
dma_desc->callback_param = i2c_dev;
dmaengine_submit(dma_desc);
dma_async_issue_pending(chan);
return 0;
}
static void tegra_i2c_release_dma(struct tegra_i2c_dev *i2c_dev)
{
if (i2c_dev->dma_buf) {
dma_free_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
i2c_dev->dma_buf, i2c_dev->dma_phys);
i2c_dev->dma_buf = NULL;
}
if (i2c_dev->tx_dma_chan) {
dma_release_channel(i2c_dev->tx_dma_chan);
i2c_dev->tx_dma_chan = NULL;
}
if (i2c_dev->rx_dma_chan) {
dma_release_channel(i2c_dev->rx_dma_chan);
i2c_dev->rx_dma_chan = NULL;
}
}
static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev)
{
struct dma_chan *chan;
u32 *dma_buf;
dma_addr_t dma_phys;
int err;
if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA) ||
!i2c_dev->hw->has_apb_dma) {
err = -ENODEV;
goto err_out;
}
chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx");
if (IS_ERR(chan)) {
err = PTR_ERR(chan);
goto err_out;
}
i2c_dev->rx_dma_chan = chan;
chan = dma_request_slave_channel_reason(i2c_dev->dev, "tx");
if (IS_ERR(chan)) {
err = PTR_ERR(chan);
goto err_out;
}
i2c_dev->tx_dma_chan = chan;
dma_buf = dma_alloc_coherent(i2c_dev->dev, i2c_dev->dma_buf_size,
&dma_phys, GFP_KERNEL | __GFP_NOWARN);
if (!dma_buf) {
dev_err(i2c_dev->dev, "failed to allocate the DMA buffer\n");
err = -ENOMEM;
goto err_out;
}
i2c_dev->dma_buf = dma_buf;
i2c_dev->dma_phys = dma_phys;
return 0;
err_out:
tegra_i2c_release_dma(i2c_dev);
if (err != -EPROBE_DEFER) {
dev_err(i2c_dev->dev, "cannot use DMA: %d\n", err);
dev_err(i2c_dev->dev, "fallbacking to PIO\n");
return 0;
}
return err;
}
static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev) static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
{ {
unsigned long timeout = jiffies + HZ; unsigned long timeout = jiffies + HZ;
...@@ -574,16 +704,6 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) ...@@ -574,16 +704,6 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2); i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2);
} }
if (i2c_dev->hw->has_mst_fifo) {
val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) |
I2C_MST_FIFO_CONTROL_RX_TRIG(1);
i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL);
} else {
val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
}
err = tegra_i2c_flush_fifos(i2c_dev); err = tegra_i2c_flush_fifos(i2c_dev);
if (err) if (err)
goto err; goto err;
...@@ -663,25 +783,37 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) ...@@ -663,25 +783,37 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE)) if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE))
goto err; goto err;
if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) { if (!i2c_dev->is_curr_dma_xfer) {
if (i2c_dev->msg_buf_remaining) if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) {
tegra_i2c_empty_rx_fifo(i2c_dev); if (i2c_dev->msg_buf_remaining)
else tegra_i2c_empty_rx_fifo(i2c_dev);
BUG(); else
} BUG();
}
if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) { if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) {
if (i2c_dev->msg_buf_remaining) if (i2c_dev->msg_buf_remaining)
tegra_i2c_fill_tx_fifo(i2c_dev); tegra_i2c_fill_tx_fifo(i2c_dev);
else else
tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ); tegra_i2c_mask_irq(i2c_dev,
I2C_INT_TX_FIFO_DATA_REQ);
}
} }
i2c_writel(i2c_dev, status, I2C_INT_STATUS); i2c_writel(i2c_dev, status, I2C_INT_STATUS);
if (i2c_dev->is_dvc) if (i2c_dev->is_dvc)
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
/*
* During message read XFER_COMPLETE interrupt is triggered prior to
* DMA completion and during message write XFER_COMPLETE interrupt is
* triggered after DMA completion.
* PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer.
* so forcing msg_buf_remaining to 0 in DMA mode.
*/
if (status & I2C_INT_PACKET_XFER_COMPLETE) { if (status & I2C_INT_PACKET_XFER_COMPLETE) {
if (i2c_dev->is_curr_dma_xfer)
i2c_dev->msg_buf_remaining = 0;
BUG_ON(i2c_dev->msg_buf_remaining); BUG_ON(i2c_dev->msg_buf_remaining);
complete(&i2c_dev->msg_complete); complete(&i2c_dev->msg_complete);
} }
...@@ -697,12 +829,91 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) ...@@ -697,12 +829,91 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
if (i2c_dev->is_dvc) if (i2c_dev->is_dvc)
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
if (i2c_dev->is_curr_dma_xfer) {
if (i2c_dev->msg_read)
dmaengine_terminate_async(i2c_dev->rx_dma_chan);
else
dmaengine_terminate_async(i2c_dev->tx_dma_chan);
complete(&i2c_dev->dma_complete);
}
complete(&i2c_dev->msg_complete); complete(&i2c_dev->msg_complete);
done: done:
spin_unlock(&i2c_dev->xfer_lock); spin_unlock(&i2c_dev->xfer_lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev,
size_t len)
{
u32 val, reg;
u8 dma_burst;
struct dma_slave_config slv_config = {0};
struct dma_chan *chan;
int ret;
unsigned long reg_offset;
if (i2c_dev->hw->has_mst_fifo)
reg = I2C_MST_FIFO_CONTROL;
else
reg = I2C_FIFO_CONTROL;
if (i2c_dev->is_curr_dma_xfer) {
if (len & 0xF)
dma_burst = 1;
else if (len & 0x10)
dma_burst = 4;
else
dma_burst = 8;
if (i2c_dev->msg_read) {
chan = i2c_dev->rx_dma_chan;
reg_offset = tegra_i2c_reg_addr(i2c_dev, I2C_RX_FIFO);
slv_config.src_addr = i2c_dev->base_phys + reg_offset;
slv_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
slv_config.src_maxburst = dma_burst;
if (i2c_dev->hw->has_mst_fifo)
val = I2C_MST_FIFO_CONTROL_RX_TRIG(dma_burst);
else
val = I2C_FIFO_CONTROL_RX_TRIG(dma_burst);
} else {
chan = i2c_dev->tx_dma_chan;
reg_offset = tegra_i2c_reg_addr(i2c_dev, I2C_TX_FIFO);
slv_config.dst_addr = i2c_dev->base_phys + reg_offset;
slv_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
slv_config.dst_maxburst = dma_burst;
if (i2c_dev->hw->has_mst_fifo)
val = I2C_MST_FIFO_CONTROL_TX_TRIG(dma_burst);
else
val = I2C_FIFO_CONTROL_TX_TRIG(dma_burst);
}
slv_config.device_fc = true;
ret = dmaengine_slave_config(chan, &slv_config);
if (ret < 0) {
dev_err(i2c_dev->dev, "DMA slave config failed: %d\n",
ret);
dev_err(i2c_dev->dev, "fallbacking to PIO\n");
tegra_i2c_release_dma(i2c_dev);
i2c_dev->is_curr_dma_xfer = false;
} else {
goto out;
}
}
if (i2c_dev->hw->has_mst_fifo)
val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) |
I2C_MST_FIFO_CONTROL_RX_TRIG(1);
else
val = I2C_FIFO_CONTROL_TX_TRIG(8) |
I2C_FIFO_CONTROL_RX_TRIG(1);
out:
i2c_writel(i2c_dev, val, reg);
}
static int tegra_i2c_issue_bus_clear(struct i2c_adapter *adap) static int tegra_i2c_issue_bus_clear(struct i2c_adapter *adap)
{ {
struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap); struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
...@@ -748,6 +959,10 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, ...@@ -748,6 +959,10 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
u32 int_mask; u32 int_mask;
unsigned long time_left; unsigned long time_left;
unsigned long flags; unsigned long flags;
size_t xfer_size;
u32 *buffer = NULL;
int err = 0;
bool dma;
tegra_i2c_flush_fifos(i2c_dev); tegra_i2c_flush_fifos(i2c_dev);
...@@ -757,19 +972,57 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, ...@@ -757,19 +972,57 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
i2c_dev->msg_read = (msg->flags & I2C_M_RD); i2c_dev->msg_read = (msg->flags & I2C_M_RD);
reinit_completion(&i2c_dev->msg_complete); reinit_completion(&i2c_dev->msg_complete);
if (i2c_dev->msg_read)
xfer_size = msg->len;
else
xfer_size = msg->len + I2C_PACKET_HEADER_SIZE;
xfer_size = ALIGN(xfer_size, BYTES_PER_FIFO_WORD);
i2c_dev->is_curr_dma_xfer = (xfer_size > I2C_PIO_MODE_MAX_LEN) &&
i2c_dev->dma_buf;
tegra_i2c_config_fifo_trig(i2c_dev, xfer_size);
dma = i2c_dev->is_curr_dma_xfer;
spin_lock_irqsave(&i2c_dev->xfer_lock, flags); spin_lock_irqsave(&i2c_dev->xfer_lock, flags);
int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
tegra_i2c_unmask_irq(i2c_dev, int_mask); tegra_i2c_unmask_irq(i2c_dev, int_mask);
if (dma) {
if (i2c_dev->msg_read) {
dma_sync_single_for_device(i2c_dev->dev,
i2c_dev->dma_phys,
xfer_size,
DMA_FROM_DEVICE);
err = tegra_i2c_dma_submit(i2c_dev, xfer_size);
if (err < 0) {
dev_err(i2c_dev->dev,
"starting RX DMA failed, err %d\n",
err);
goto unlock;
}
} else {
dma_sync_single_for_cpu(i2c_dev->dev,
i2c_dev->dma_phys,
xfer_size,
DMA_TO_DEVICE);
buffer = i2c_dev->dma_buf;
}
}
packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) | packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
PACKET_HEADER0_PROTOCOL_I2C | PACKET_HEADER0_PROTOCOL_I2C |
(i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) | (i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) |
(1 << PACKET_HEADER0_PACKET_ID_SHIFT); (1 << PACKET_HEADER0_PACKET_ID_SHIFT);
i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); if (dma && !i2c_dev->msg_read)
*buffer++ = packet_header;
else
i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
packet_header = msg->len - 1; packet_header = msg->len - 1;
i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); if (dma && !i2c_dev->msg_read)
*buffer++ = packet_header;
else
i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
packet_header = I2C_HEADER_IE_ENABLE; packet_header = I2C_HEADER_IE_ENABLE;
if (end_state == MSG_END_CONTINUE) if (end_state == MSG_END_CONTINUE)
...@@ -786,23 +1039,77 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, ...@@ -786,23 +1039,77 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
packet_header |= I2C_HEADER_CONT_ON_NAK; packet_header |= I2C_HEADER_CONT_ON_NAK;
if (msg->flags & I2C_M_RD) if (msg->flags & I2C_M_RD)
packet_header |= I2C_HEADER_READ; packet_header |= I2C_HEADER_READ;
i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); if (dma && !i2c_dev->msg_read)
*buffer++ = packet_header;
if (!(msg->flags & I2C_M_RD)) else
tegra_i2c_fill_tx_fifo(i2c_dev); i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
if (!i2c_dev->msg_read) {
if (dma) {
memcpy(buffer, msg->buf, msg->len);
dma_sync_single_for_device(i2c_dev->dev,
i2c_dev->dma_phys,
xfer_size,
DMA_TO_DEVICE);
err = tegra_i2c_dma_submit(i2c_dev, xfer_size);
if (err < 0) {
dev_err(i2c_dev->dev,
"starting TX DMA failed, err %d\n",
err);
goto unlock;
}
} else {
tegra_i2c_fill_tx_fifo(i2c_dev);
}
}
if (i2c_dev->hw->has_per_pkt_xfer_complete_irq) if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
int_mask |= I2C_INT_PACKET_XFER_COMPLETE; int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
if (msg->flags & I2C_M_RD) if (!dma) {
int_mask |= I2C_INT_RX_FIFO_DATA_REQ; if (msg->flags & I2C_M_RD)
else if (i2c_dev->msg_buf_remaining) int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
int_mask |= I2C_INT_TX_FIFO_DATA_REQ; else if (i2c_dev->msg_buf_remaining)
int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
}
tegra_i2c_unmask_irq(i2c_dev, int_mask); tegra_i2c_unmask_irq(i2c_dev, int_mask);
spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n", dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
i2c_readl(i2c_dev, I2C_INT_MASK)); i2c_readl(i2c_dev, I2C_INT_MASK));
unlock:
spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags);
if (dma) {
if (err)
return err;
time_left = wait_for_completion_timeout(
&i2c_dev->dma_complete,
TEGRA_I2C_TIMEOUT);
if (time_left == 0) {
dev_err(i2c_dev->dev, "DMA transfer timeout\n");
dmaengine_terminate_sync(i2c_dev->msg_read ?
i2c_dev->rx_dma_chan :
i2c_dev->tx_dma_chan);
tegra_i2c_init(i2c_dev);
return -ETIMEDOUT;
}
if (i2c_dev->msg_read && i2c_dev->msg_err == I2C_ERR_NONE) {
dma_sync_single_for_cpu(i2c_dev->dev,
i2c_dev->dma_phys,
xfer_size,
DMA_FROM_DEVICE);
memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf,
msg->len);
}
if (i2c_dev->msg_err != I2C_ERR_NONE)
dmaengine_synchronize(i2c_dev->msg_read ?
i2c_dev->rx_dma_chan :
i2c_dev->tx_dma_chan);
}
time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
TEGRA_I2C_TIMEOUT); TEGRA_I2C_TIMEOUT);
tegra_i2c_mask_irq(i2c_dev, int_mask); tegra_i2c_mask_irq(i2c_dev, int_mask);
...@@ -818,6 +1125,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, ...@@ -818,6 +1125,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
time_left, completion_done(&i2c_dev->msg_complete), time_left, completion_done(&i2c_dev->msg_complete),
i2c_dev->msg_err); i2c_dev->msg_err);
i2c_dev->is_curr_dma_xfer = false;
if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) if (likely(i2c_dev->msg_err == I2C_ERR_NONE))
return 0; return 0;
...@@ -929,6 +1237,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { ...@@ -929,6 +1237,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
.has_mst_fifo = false, .has_mst_fifo = false,
.quirks = &tegra_i2c_quirks, .quirks = &tegra_i2c_quirks,
.supports_bus_clear = false, .supports_bus_clear = false,
.has_apb_dma = true,
}; };
static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
...@@ -944,6 +1253,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { ...@@ -944,6 +1253,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
.has_mst_fifo = false, .has_mst_fifo = false,
.quirks = &tegra_i2c_quirks, .quirks = &tegra_i2c_quirks,
.supports_bus_clear = false, .supports_bus_clear = false,
.has_apb_dma = true,
}; };
static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
...@@ -959,6 +1269,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { ...@@ -959,6 +1269,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
.has_mst_fifo = false, .has_mst_fifo = false,
.quirks = &tegra_i2c_quirks, .quirks = &tegra_i2c_quirks,
.supports_bus_clear = true, .supports_bus_clear = true,
.has_apb_dma = true,
}; };
static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
...@@ -974,6 +1285,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { ...@@ -974,6 +1285,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
.has_mst_fifo = false, .has_mst_fifo = false,
.quirks = &tegra_i2c_quirks, .quirks = &tegra_i2c_quirks,
.supports_bus_clear = true, .supports_bus_clear = true,
.has_apb_dma = true,
}; };
static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
...@@ -989,6 +1301,23 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { ...@@ -989,6 +1301,23 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
.has_mst_fifo = false, .has_mst_fifo = false,
.quirks = &tegra_i2c_quirks, .quirks = &tegra_i2c_quirks,
.supports_bus_clear = true, .supports_bus_clear = true,
.has_apb_dma = true,
};
static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
.has_continue_xfer_support = true,
.has_per_pkt_xfer_complete_irq = true,
.has_single_clk_source = true,
.clk_divisor_hs_mode = 1,
.clk_divisor_std_fast_mode = 0x19,
.clk_divisor_fast_plus_mode = 0x10,
.has_config_load_reg = true,
.has_multi_master_mode = true,
.has_slcg_override_reg = true,
.has_mst_fifo = true,
.quirks = &tegra_i2c_quirks,
.supports_bus_clear = true,
.has_apb_dma = false,
}; };
static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
...@@ -1004,11 +1333,13 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { ...@@ -1004,11 +1333,13 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
.has_mst_fifo = true, .has_mst_fifo = true,
.quirks = &tegra194_i2c_quirks, .quirks = &tegra194_i2c_quirks,
.supports_bus_clear = true, .supports_bus_clear = true,
.has_apb_dma = false,
}; };
/* Match table for of_platform binding */ /* Match table for of_platform binding */
static const struct of_device_id tegra_i2c_of_match[] = { static const struct of_device_id tegra_i2c_of_match[] = {
{ .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, }, { .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, },
{ .compatible = "nvidia,tegra186-i2c", .data = &tegra186_i2c_hw, },
{ .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, }, { .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, },
{ .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, }, { .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, },
{ .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, }, { .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
...@@ -1026,11 +1357,13 @@ static int tegra_i2c_probe(struct platform_device *pdev) ...@@ -1026,11 +1357,13 @@ static int tegra_i2c_probe(struct platform_device *pdev)
struct clk *div_clk; struct clk *div_clk;
struct clk *fast_clk; struct clk *fast_clk;
void __iomem *base; void __iomem *base;
phys_addr_t base_phys;
int irq; int irq;
int ret = 0; int ret = 0;
int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE; int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base_phys = res->start;
base = devm_ioremap_resource(&pdev->dev, res); base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base)) if (IS_ERR(base))
return PTR_ERR(base); return PTR_ERR(base);
...@@ -1053,6 +1386,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) ...@@ -1053,6 +1386,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
i2c_dev->base = base; i2c_dev->base = base;
i2c_dev->base_phys = base_phys;
i2c_dev->div_clk = div_clk; i2c_dev->div_clk = div_clk;
i2c_dev->adapter.algo = &tegra_i2c_algo; i2c_dev->adapter.algo = &tegra_i2c_algo;
i2c_dev->adapter.retries = 1; i2c_dev->adapter.retries = 1;
...@@ -1072,7 +1406,10 @@ static int tegra_i2c_probe(struct platform_device *pdev) ...@@ -1072,7 +1406,10 @@ static int tegra_i2c_probe(struct platform_device *pdev)
i2c_dev->is_dvc = of_device_is_compatible(pdev->dev.of_node, i2c_dev->is_dvc = of_device_is_compatible(pdev->dev.of_node,
"nvidia,tegra20-i2c-dvc"); "nvidia,tegra20-i2c-dvc");
i2c_dev->adapter.quirks = i2c_dev->hw->quirks; i2c_dev->adapter.quirks = i2c_dev->hw->quirks;
i2c_dev->dma_buf_size = i2c_dev->adapter.quirks->max_write_len +
I2C_PACKET_HEADER_SIZE;
init_completion(&i2c_dev->msg_complete); init_completion(&i2c_dev->msg_complete);
init_completion(&i2c_dev->dma_complete);
spin_lock_init(&i2c_dev->xfer_lock); spin_lock_init(&i2c_dev->xfer_lock);
if (!i2c_dev->hw->has_single_clk_source) { if (!i2c_dev->hw->has_single_clk_source) {
...@@ -1136,17 +1473,21 @@ static int tegra_i2c_probe(struct platform_device *pdev) ...@@ -1136,17 +1473,21 @@ static int tegra_i2c_probe(struct platform_device *pdev)
if (i2c_dev->hw->supports_bus_clear) if (i2c_dev->hw->supports_bus_clear)
i2c_dev->adapter.bus_recovery_info = &tegra_i2c_recovery_info; i2c_dev->adapter.bus_recovery_info = &tegra_i2c_recovery_info;
ret = tegra_i2c_init_dma(i2c_dev);
if (ret < 0)
goto disable_div_clk;
ret = tegra_i2c_init(i2c_dev); ret = tegra_i2c_init(i2c_dev);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Failed to initialize i2c controller\n"); dev_err(&pdev->dev, "Failed to initialize i2c controller\n");
goto disable_div_clk; goto release_dma;
} }
ret = devm_request_irq(&pdev->dev, i2c_dev->irq, ret = devm_request_irq(&pdev->dev, i2c_dev->irq,
tegra_i2c_isr, 0, dev_name(&pdev->dev), i2c_dev); tegra_i2c_isr, 0, dev_name(&pdev->dev), i2c_dev);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq); dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq);
goto disable_div_clk; goto release_dma;
} }
i2c_set_adapdata(&i2c_dev->adapter, i2c_dev); i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
...@@ -1160,10 +1501,13 @@ static int tegra_i2c_probe(struct platform_device *pdev) ...@@ -1160,10 +1501,13 @@ static int tegra_i2c_probe(struct platform_device *pdev)
ret = i2c_add_numbered_adapter(&i2c_dev->adapter); ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
if (ret) if (ret)
goto disable_div_clk; goto release_dma;
return 0; return 0;
release_dma:
tegra_i2c_release_dma(i2c_dev);
disable_div_clk: disable_div_clk:
if (i2c_dev->is_multimaster_mode) if (i2c_dev->is_multimaster_mode)
clk_disable(i2c_dev->div_clk); clk_disable(i2c_dev->div_clk);
...@@ -1200,6 +1544,7 @@ static int tegra_i2c_remove(struct platform_device *pdev) ...@@ -1200,6 +1544,7 @@ static int tegra_i2c_remove(struct platform_device *pdev)
if (!i2c_dev->hw->has_single_clk_source) if (!i2c_dev->hw->has_single_clk_source)
clk_unprepare(i2c_dev->fast_clk); clk_unprepare(i2c_dev->fast_clk);
tegra_i2c_release_dma(i2c_dev);
return 0; return 0;
} }
...@@ -1223,18 +1568,7 @@ static struct platform_driver tegra_i2c_driver = { ...@@ -1223,18 +1568,7 @@ static struct platform_driver tegra_i2c_driver = {
}, },
}; };
static int __init tegra_i2c_init_driver(void) module_platform_driver(tegra_i2c_driver);
{
return platform_driver_register(&tegra_i2c_driver);
}
static void __exit tegra_i2c_exit_driver(void)
{
platform_driver_unregister(&tegra_i2c_driver);
}
subsys_initcall(tegra_i2c_init_driver);
module_exit(tegra_i2c_exit_driver);
MODULE_DESCRIPTION("nVidia Tegra2 I2C Bus Controller driver"); MODULE_DESCRIPTION("nVidia Tegra2 I2C Bus Controller driver");
MODULE_AUTHOR("Colin Cross"); MODULE_AUTHOR("Colin Cross");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment