summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-tegra.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-tegra.c')
-rw-r--r--drivers/i2c/busses/i2c-tegra.c1082
1 files changed, 944 insertions, 138 deletions
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 9aa1b60f7fdd..0a444595dac7 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -4,6 +4,8 @@
* Copyright (C) 2010 Google, Inc.
* Author: Colin Cross <ccross@android.com>
*
+ * Copyright (C) 2010-2013 NVIDIA Corporation. All rights reserved.
+ *
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
@@ -21,25 +23,36 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/i2c-gpio.h>
#include <linux/io.h>
#include <linux/interrupt.h>
+#include <linux/gpio.h>
#include <linux/delay.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
-#include <linux/of_i2c.h>
+#include <linux/i2c-tegra.h>
#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_i2c.h>
#include <linux/module.h>
#include <linux/clk/tegra.h>
+#include <linux/spinlock.h>
+#include <linux/clk/tegra.h>
+#include <linux/tegra-pm.h>
#include <asm/unaligned.h>
-#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
-#define BYTES_PER_FIFO_WORD 4
+#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
+#define TEGRA_I2C_RETRIES 3
+#define BYTES_PER_FIFO_WORD 4
#define I2C_CNFG 0x000
#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12
#define I2C_CNFG_PACKET_MODE_EN (1<<10)
#define I2C_CNFG_NEW_MASTER_FSM (1<<11)
#define I2C_STATUS 0x01C
+#define I2C_STATUS_BUSY (1<<8)
#define I2C_SL_CNFG 0x020
#define I2C_SL_CNFG_NACK (1<<1)
#define I2C_SL_CNFG_NEWSL (1<<2)
@@ -60,6 +73,7 @@
#define I2C_FIFO_STATUS_RX_SHIFT 0
#define I2C_INT_MASK 0x064
#define I2C_INT_STATUS 0x068
+#define I2C_INT_BUS_CLEAR_DONE (1<<11)
#define I2C_INT_PACKET_XFER_COMPLETE (1<<7)
#define I2C_INT_ALL_PACKETS_XFER_COMPLETE (1<<6)
#define I2C_INT_TX_FIFO_OVERFLOW (1<<5)
@@ -68,6 +82,7 @@
#define I2C_INT_ARBITRATION_LOST (1<<2)
#define I2C_INT_TX_FIFO_DATA_REQ (1<<1)
#define I2C_INT_RX_FIFO_DATA_REQ (1<<0)
+
#define I2C_CLK_DIVISOR 0x06c
#define I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT 16
#define I2C_CLK_MULTIPLIER_STD_FAST_MODE 8
@@ -85,6 +100,7 @@
#define I2C_ERR_NO_ACK 0x01
#define I2C_ERR_ARBITRATION_LOST 0x02
#define I2C_ERR_UNKNOWN_INTERRUPT 0x04
+#define I2C_ERR_UNEXPECTED_STATUS 0x08
#define PACKET_HEADER0_HEADER_SIZE_SHIFT 28
#define PACKET_HEADER0_PACKET_ID_SHIFT 16
@@ -101,6 +117,25 @@
#define I2C_HEADER_CONTINUE_XFER (1<<15)
#define I2C_HEADER_MASTER_ADDR_SHIFT 12
#define I2C_HEADER_SLAVE_ADDR_SHIFT 1
+
+#define I2C_BUS_CLEAR_CNFG 0x084
+#define I2C_BC_SCLK_THRESHOLD (9<<16)
+#define I2C_BC_STOP_COND (1<<2)
+#define I2C_BC_TERMINATE (1<<1)
+#define I2C_BC_ENABLE (1<<0)
+
+#define I2C_BUS_CLEAR_STATUS 0x088
+#define I2C_BC_STATUS (1<<0)
+
+#define I2C_CONFIG_LOAD 0x08C
+#define I2C_MSTR_CONFIG_LOAD (1 << 0)
+#define I2C_SLV_CONFIG_LOAD (1 << 1)
+#define I2C_TIMEOUT_CONFIG_LOAD (1 << 2)
+
+#define SL_ADDR1(addr) (addr & 0xff)
+#define SL_ADDR2(addr) ((addr >> 8) & 0xff)
+
+#define MAX_BUSCLEAR_CLOCK (9 * 8 + 1)
/*
* 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.
@@ -114,32 +149,23 @@ enum msg_end_type {
MSG_END_CONTINUE,
};
-/**
- * struct tegra_i2c_hw_feature : Different HW support on Tegra
- * @has_continue_xfer_support: Continue transfer supports.
- * @has_per_pkt_xfer_complete_irq: Has enable/disable capability for transfer
- * complete interrupt per packet basis.
- * @has_single_clk_source: The i2c controller has single clock source. Tegra30
- * and earlier Socs has two clock sources i.e. div-clk and
- * fast-clk.
- * @clk_divisor_hs_mode: Clock divisor in HS mode.
- * @clk_divisor_std_fast_mode: Clock divisor in standard/fast mode. It is
- * applicable if there is no fast clock source i.e. single clock
- * source.
- */
-
-struct tegra_i2c_hw_feature {
+struct tegra_i2c_chipdata {
+ bool timeout_irq_occurs_before_bus_inactive;
+ bool has_xfer_complete_interrupt;
+ bool has_hw_arb_support;
+ bool has_fast_clock;
+ bool has_clk_divisor_std_fast_mode;
bool has_continue_xfer_support;
- bool has_per_pkt_xfer_complete_irq;
- bool has_single_clk_source;
- int clk_divisor_hs_mode;
- int clk_divisor_std_fast_mode;
+ u16 clk_divisor_std_fast_mode;
+ u16 clk_divisor_fast_plus_mode;
+ u16 clk_divisor_hs_mode;
+ int clk_multiplier_hs_mode;
+ bool has_config_load_reg;
};
/**
* struct tegra_i2c_dev - per device i2c context
* @dev: device reference for power management
- * @hw: Tegra i2c hw feature.
* @adapter: core i2c layer adapter information
* @div_clk: clock reference for div clock of i2c controller.
* @fast_clk: clock reference for fast clock of i2c controller.
@@ -157,10 +183,13 @@ struct tegra_i2c_hw_feature {
*/
struct tegra_i2c_dev {
struct device *dev;
- const struct tegra_i2c_hw_feature *hw;
struct i2c_adapter adapter;
struct clk *div_clk;
struct clk *fast_clk;
+ bool needs_cl_dvfs_clock;
+ struct clk *dvfs_ref_clk;
+ struct clk *dvfs_soc_clk;
+ spinlock_t fifo_lock;
void __iomem *base;
int cont_id;
int irq;
@@ -168,11 +197,39 @@ struct tegra_i2c_dev {
int is_dvc;
struct completion msg_complete;
int msg_err;
+ int next_msg_err;
u8 *msg_buf;
+ u8 *next_msg_buf;
+ u32 packet_header;
+ u32 next_packet_header;
+ u32 payload_size;
+ u32 next_payload_size;
+ u32 io_header;
+ u32 next_io_header;
size_t msg_buf_remaining;
+ size_t next_msg_buf_remaining;
int msg_read;
- u32 bus_clk_rate;
+ int next_msg_read;
+ struct i2c_msg *msgs;
+ int msg_add;
+ int next_msg_add;
+ int msgs_num;
+ unsigned long bus_clk_rate;
bool is_suspended;
+ u16 slave_addr;
+ bool is_clkon_always;
+ bool is_high_speed_enable;
+ u16 hs_master_code;
+ u16 clk_divisor_non_hs_mode;
+ bool use_single_xfer_complete;
+ const struct tegra_i2c_chipdata *chipdata;
+ int scl_gpio;
+ int sda_gpio;
+ struct i2c_algo_bit_data bit_data;
+ const struct i2c_algorithm *bit_algo;
+ bool bit_banging_xfer_after_shutdown;
+ bool is_shutdown;
+ struct notifier_block pm_nb;
};
static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned long reg)
@@ -185,6 +242,20 @@ static u32 dvc_readl(struct tegra_i2c_dev *i2c_dev, unsigned long reg)
return readl(i2c_dev->base + reg);
}
+static void dvc_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
+{
+ u32 int_mask = dvc_readl(i2c_dev, DVC_CTRL_REG3);
+ int_mask &= ~mask;
+ dvc_writel(i2c_dev, int_mask, DVC_CTRL_REG3);
+}
+
+static void dvc_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
+{
+ u32 int_mask = dvc_readl(i2c_dev, DVC_CTRL_REG3);
+ int_mask |= mask;
+ dvc_writel(i2c_dev, int_mask, DVC_CTRL_REG3);
+}
+
/*
* i2c_writel and i2c_readl will offset the register if necessary to talk
* to the I2C block inside the DVC block
@@ -224,6 +295,99 @@ static void i2c_readsl(struct tegra_i2c_dev *i2c_dev, void *data,
readsl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len);
}
+static inline void tegra_i2c_gpio_setscl(void *data, int state)
+{
+ struct tegra_i2c_dev *i2c_dev = data;
+
+ gpio_set_value(i2c_dev->scl_gpio, state);
+}
+
+static inline int tegra_i2c_gpio_getscl(void *data)
+{
+ struct tegra_i2c_dev *i2c_dev = data;
+
+ return gpio_get_value(i2c_dev->scl_gpio);
+}
+
+static inline void tegra_i2c_gpio_setsda(void *data, int state)
+{
+ struct tegra_i2c_dev *i2c_dev = data;
+
+ gpio_set_value(i2c_dev->sda_gpio, state);
+}
+
+static inline int tegra_i2c_gpio_getsda(void *data)
+{
+ struct tegra_i2c_dev *i2c_dev = data;
+
+ return gpio_get_value(i2c_dev->sda_gpio);
+}
+
+static int tegra_i2c_gpio_request(struct tegra_i2c_dev *i2c_dev)
+{
+ int ret;
+
+ ret = gpio_request_one(i2c_dev->scl_gpio,
+ GPIOF_OUT_INIT_HIGH | GPIOF_OPEN_DRAIN,
+ "i2c-gpio-scl");
+ if (ret < 0) {
+ dev_err(i2c_dev->dev, "GPIO request for gpio %d failed %d\n",
+ i2c_dev->scl_gpio, ret);
+ return ret;
+ }
+
+ ret = gpio_request_one(i2c_dev->sda_gpio,
+ GPIOF_OUT_INIT_HIGH | GPIOF_OPEN_DRAIN,
+ "i2c-gpio-sda");
+ if (ret < 0) {
+ dev_err(i2c_dev->dev, "GPIO request for gpio %d failed %d\n",
+ i2c_dev->sda_gpio, ret);
+ gpio_free(i2c_dev->scl_gpio);
+ return ret;
+ }
+ return ret;
+}
+
+static void tegra_i2c_gpio_free(struct tegra_i2c_dev *i2c_dev)
+{
+ gpio_free(i2c_dev->scl_gpio);
+ gpio_free(i2c_dev->sda_gpio);
+}
+
+static int tegra_i2c_gpio_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+ int ret;
+
+ ret = tegra_i2c_gpio_request(i2c_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_dev->bit_algo->master_xfer(adap, msgs, num);
+ if (ret < 0)
+ dev_err(i2c_dev->dev, "i2c-bit-algo xfer failed %d\n", ret);
+
+ tegra_i2c_gpio_free(i2c_dev);
+ return ret;
+}
+
+static int tegra_i2c_gpio_init(struct tegra_i2c_dev *i2c_dev)
+{
+ struct i2c_algo_bit_data *bit_data = &i2c_dev->bit_data;
+
+ bit_data->setsda = tegra_i2c_gpio_setsda;
+ bit_data->getsda = tegra_i2c_gpio_getsda;
+ bit_data->setscl = tegra_i2c_gpio_setscl;
+ bit_data->getscl = tegra_i2c_gpio_getscl;
+ bit_data->data = i2c_dev;
+ bit_data->udelay = 5; /* 100KHz */
+ bit_data->timeout = HZ; /* 10 ms*/
+ i2c_dev->bit_algo = &i2c_bit_algo;
+ i2c_dev->adapter.algo_data = bit_data;
+ return 0;
+}
+
static void tegra_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
{
u32 int_mask = i2c_readl(i2c_dev, I2C_INT_MASK);
@@ -301,10 +465,15 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
{
u32 val;
int tx_fifo_avail;
- u8 *buf = i2c_dev->msg_buf;
- size_t buf_remaining = i2c_dev->msg_buf_remaining;
+ u8 *buf;
+ size_t buf_remaining;
int words_to_transfer;
+ if (!i2c_dev->msg_buf_remaining)
+ return 0;
+ buf = i2c_dev->msg_buf;
+ buf_remaining = i2c_dev->msg_buf_remaining;
+
val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
tx_fifo_avail = (val & I2C_FIFO_STATUS_TX_MASK) >>
I2C_FIFO_STATUS_TX_SHIFT;
@@ -342,7 +511,12 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
* boundary and fault.
*/
if (tx_fifo_avail > 0 && buf_remaining > 0) {
- BUG_ON(buf_remaining > 3);
+ if (buf_remaining > 3) {
+ dev_err(i2c_dev->dev,
+ "Remaining buffer more than 3 %d\n",
+ buf_remaining);
+ BUG();
+ }
memcpy(&val, buf, buf_remaining);
/* Again update before writing to FIFO to make sure isr sees. */
@@ -368,7 +542,6 @@ static void tegra_dvc_init(struct tegra_i2c_dev *i2c_dev)
u32 val = 0;
val = dvc_readl(i2c_dev, DVC_CTRL_REG3);
val |= DVC_CTRL_REG3_SW_PROG;
- val |= DVC_CTRL_REG3_I2C_DONE_INTR_EN;
dvc_writel(i2c_dev, val, DVC_CTRL_REG3);
val = dvc_readl(i2c_dev, DVC_CTRL_REG1);
@@ -376,39 +549,97 @@ static void tegra_dvc_init(struct tegra_i2c_dev *i2c_dev)
dvc_writel(i2c_dev, val, DVC_CTRL_REG1);
}
+static void tegra_i2c_slave_init(struct tegra_i2c_dev *i2c_dev)
+{
+ u32 val = I2C_SL_CNFG_NEWSL | I2C_SL_CNFG_NACK;
+
+ i2c_writel(i2c_dev, val, I2C_SL_CNFG);
+
+ if (i2c_dev->slave_addr) {
+ u16 addr = i2c_dev->slave_addr;
+
+ i2c_writel(i2c_dev, SL_ADDR1(addr), I2C_SL_ADDR1);
+ i2c_writel(i2c_dev, SL_ADDR2(addr), I2C_SL_ADDR2);
+ }
+}
+
static inline int tegra_i2c_clock_enable(struct tegra_i2c_dev *i2c_dev)
{
int ret;
- if (!i2c_dev->hw->has_single_clk_source) {
+
+ if (i2c_dev->needs_cl_dvfs_clock) {
+ ret = clk_prepare_enable(i2c_dev->dvfs_soc_clk);
+ if (ret < 0) {
+ dev_err(i2c_dev->dev,
+ "Error in enabling dvfs soc clock %d\n", ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(i2c_dev->dvfs_ref_clk);
+ if (ret < 0) {
+ dev_err(i2c_dev->dev,
+ "Error in enabling dvfs ref clock %d\n", ret);
+ goto ref_clk_err;
+ }
+ }
+ if (i2c_dev->chipdata->has_fast_clock) {
ret = clk_prepare_enable(i2c_dev->fast_clk);
if (ret < 0) {
dev_err(i2c_dev->dev,
"Enabling fast clk failed, err %d\n", ret);
- return ret;
+ goto fast_clk_err;
}
}
ret = clk_prepare_enable(i2c_dev->div_clk);
if (ret < 0) {
dev_err(i2c_dev->dev,
"Enabling div clk failed, err %d\n", ret);
- clk_disable_unprepare(i2c_dev->fast_clk);
+ goto div_clk_err;
}
+ return 0;
+
+div_clk_err:
+ if (i2c_dev->chipdata->has_fast_clock)
+ clk_disable_unprepare(i2c_dev->fast_clk);
+fast_clk_err:
+ if (i2c_dev->needs_cl_dvfs_clock)
+ clk_disable_unprepare(i2c_dev->dvfs_ref_clk);
+ref_clk_err:
+ if (i2c_dev->needs_cl_dvfs_clock)
+ clk_disable_unprepare(i2c_dev->dvfs_soc_clk);
return ret;
}
static inline void tegra_i2c_clock_disable(struct tegra_i2c_dev *i2c_dev)
{
clk_disable_unprepare(i2c_dev->div_clk);
- if (!i2c_dev->hw->has_single_clk_source)
+ if (i2c_dev->chipdata->has_fast_clock)
clk_disable_unprepare(i2c_dev->fast_clk);
+ if (i2c_dev->needs_cl_dvfs_clock) {
+ clk_disable_unprepare(i2c_dev->dvfs_soc_clk);
+ clk_disable_unprepare(i2c_dev->dvfs_ref_clk);
+ }
+}
+
+static void tegra_i2c_set_clk_rate(struct tegra_i2c_dev *i2c_dev)
+{
+ u32 clk_multiplier;
+ if (i2c_dev->is_high_speed_enable)
+ clk_multiplier = i2c_dev->chipdata->clk_multiplier_hs_mode
+ * (i2c_dev->chipdata->clk_divisor_hs_mode + 1);
+ else
+ clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE
+ * (i2c_dev->clk_divisor_non_hs_mode + 1);
+
+ clk_set_rate(i2c_dev->div_clk, i2c_dev->bus_clk_rate
+ * clk_multiplier);
}
static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
{
u32 val;
int err = 0;
- int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE;
- u32 clk_divisor;
+ u32 clk_divisor = 0;
+ unsigned long timeout = jiffies + HZ;
err = tegra_i2c_clock_enable(i2c_dev);
if (err < 0) {
@@ -428,13 +659,12 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
i2c_writel(i2c_dev, val, I2C_CNFG);
i2c_writel(i2c_dev, 0, I2C_INT_MASK);
- clk_multiplier *= (i2c_dev->hw->clk_divisor_std_fast_mode + 1);
- clk_set_rate(i2c_dev->div_clk, i2c_dev->bus_clk_rate * clk_multiplier);
+ tegra_i2c_set_clk_rate(i2c_dev);
- /* Make sure clock divisor programmed correctly */
- clk_divisor = i2c_dev->hw->clk_divisor_hs_mode;
- clk_divisor |= i2c_dev->hw->clk_divisor_std_fast_mode <<
- I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT;
+ clk_divisor |= i2c_dev->chipdata->clk_divisor_hs_mode;
+ if (i2c_dev->chipdata->has_clk_divisor_std_fast_mode)
+ clk_divisor |= i2c_dev->clk_divisor_non_hs_mode
+ << I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT;
i2c_writel(i2c_dev, clk_divisor, I2C_CLK_DIVISOR);
if (!i2c_dev->is_dvc) {
@@ -450,9 +680,23 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
+ if (!i2c_dev->is_dvc)
+ tegra_i2c_slave_init(i2c_dev);
+
if (tegra_i2c_flush_fifos(i2c_dev))
err = -ETIMEDOUT;
+ if (i2c_dev->chipdata->has_config_load_reg) {
+ i2c_writel(i2c_dev, I2C_MSTR_CONFIG_LOAD, I2C_CONFIG_LOAD);
+ while (i2c_readl(i2c_dev, I2C_CONFIG_LOAD) != 0) {
+ if (time_after(jiffies, timeout)) {
+ dev_warn(i2c_dev->dev, "timeout waiting for config load\n");
+ return -ETIMEDOUT;
+ }
+ msleep(1);
+ }
+ }
+
tegra_i2c_clock_disable(i2c_dev);
if (i2c_dev->irq_disabled) {
@@ -462,20 +706,35 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
return err;
}
+static int tegra_i2c_copy_next_to_current(struct tegra_i2c_dev *i2c_dev)
+{
+ i2c_dev->msg_buf = i2c_dev->next_msg_buf;
+ i2c_dev->msg_buf_remaining = i2c_dev->next_msg_buf_remaining;
+ i2c_dev->msg_err = i2c_dev->next_msg_err;
+ i2c_dev->msg_read = i2c_dev->next_msg_read;
+ i2c_dev->msg_add = i2c_dev->next_msg_add;
+ i2c_dev->packet_header = i2c_dev->next_packet_header;
+ i2c_dev->io_header = i2c_dev->next_io_header;
+ i2c_dev->payload_size = i2c_dev->next_payload_size;
+
+ return 0;
+}
static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
{
u32 status;
- const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
+ unsigned long flags = 0;
+
+ const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST
+ | I2C_INT_TX_FIFO_OVERFLOW;
struct tegra_i2c_dev *i2c_dev = dev_id;
+ u32 mask;
status = i2c_readl(i2c_dev, I2C_INT_STATUS);
if (status == 0) {
- dev_warn(i2c_dev->dev, "irq status 0 %08x %08x %08x\n",
- i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS),
- i2c_readl(i2c_dev, I2C_STATUS),
- i2c_readl(i2c_dev, I2C_CNFG));
+ dev_dbg(i2c_dev->dev, "unknown interrupt Add 0x%02x\n",
+ i2c_dev->msg_add);
i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT;
if (!i2c_dev->irq_disabled) {
@@ -486,10 +745,49 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
}
if (unlikely(status & status_err)) {
- if (status & I2C_INT_NO_ACK)
+ dev_dbg(i2c_dev->dev, "I2c error status 0x%08x\n", status);
+ if (status & I2C_INT_NO_ACK) {
i2c_dev->msg_err |= I2C_ERR_NO_ACK;
- if (status & I2C_INT_ARBITRATION_LOST)
+ dev_dbg(i2c_dev->dev, "no acknowledge from address"
+ " 0x%x\n", i2c_dev->msg_add);
+ dev_dbg(i2c_dev->dev, "Packet status 0x%08x\n",
+ i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS));
+ }
+
+ if (status & I2C_INT_ARBITRATION_LOST) {
i2c_dev->msg_err |= I2C_ERR_ARBITRATION_LOST;
+ dev_dbg(i2c_dev->dev, "arbitration lost during "
+ " communicate to add 0x%x\n", i2c_dev->msg_add);
+ dev_dbg(i2c_dev->dev, "Packet status 0x%08x\n",
+ i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS));
+ }
+
+ if (status & I2C_INT_TX_FIFO_OVERFLOW) {
+ i2c_dev->msg_err |= I2C_INT_TX_FIFO_OVERFLOW;
+ dev_dbg(i2c_dev->dev, "Tx fifo overflow during "
+ " communicate to add 0x%x\n", i2c_dev->msg_add);
+ dev_dbg(i2c_dev->dev, "Packet status 0x%08x\n",
+ i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS));
+ }
+ goto err;
+ }
+
+ if (i2c_dev->chipdata->has_hw_arb_support &&
+ (status & I2C_INT_BUS_CLEAR_DONE))
+ goto err;
+
+ if (unlikely((i2c_readl(i2c_dev, I2C_STATUS) & I2C_STATUS_BUSY)
+ && (status == I2C_INT_TX_FIFO_DATA_REQ)
+ && i2c_dev->msg_read
+ && i2c_dev->msg_buf_remaining)) {
+ dev_dbg(i2c_dev->dev, "unexpected status\n");
+ i2c_dev->msg_err |= I2C_ERR_UNEXPECTED_STATUS;
+
+ if (!i2c_dev->irq_disabled) {
+ disable_irq_nosync(i2c_dev->irq);
+ i2c_dev->irq_disabled = 1;
+ }
+
goto err;
}
@@ -501,97 +799,274 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
}
if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) {
- if (i2c_dev->msg_buf_remaining)
+ if (i2c_dev->msg_buf_remaining) {
+
+ if (!i2c_dev->chipdata->has_xfer_complete_interrupt)
+ spin_lock_irqsave(&i2c_dev->fifo_lock, flags);
+
tegra_i2c_fill_tx_fifo(i2c_dev);
+
+ if (!i2c_dev->chipdata->has_xfer_complete_interrupt)
+ spin_unlock_irqrestore(&i2c_dev->fifo_lock, flags);
+
+ }
else
tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ);
}
i2c_writel(i2c_dev, status, I2C_INT_STATUS);
+
if (i2c_dev->is_dvc)
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
- if (status & I2C_INT_PACKET_XFER_COMPLETE) {
+ if (status & I2C_INT_ALL_PACKETS_XFER_COMPLETE) {
+ BUG_ON(i2c_dev->msg_buf_remaining);
+ complete(&i2c_dev->msg_complete);
+ } else if ((status & I2C_INT_PACKET_XFER_COMPLETE)
+ && i2c_dev->use_single_xfer_complete) {
BUG_ON(i2c_dev->msg_buf_remaining);
complete(&i2c_dev->msg_complete);
}
+
return IRQ_HANDLED;
+
err:
- /* An error occurred, mask all interrupts */
- tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST |
+ dev_dbg(i2c_dev->dev, "reg: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ i2c_readl(i2c_dev, I2C_CNFG), i2c_readl(i2c_dev, I2C_STATUS),
+ i2c_readl(i2c_dev, I2C_INT_STATUS),
+ i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS));
+
+ dev_dbg(i2c_dev->dev, "packet: 0x%08x %u 0x%08x\n",
+ i2c_dev->packet_header, i2c_dev->payload_size,
+ i2c_dev->io_header);
+
+ if (i2c_dev->msgs) {
+ struct i2c_msg *msgs = i2c_dev->msgs;
+ int i;
+
+ for (i = 0; i < i2c_dev->msgs_num; i++)
+ dev_dbg(i2c_dev->dev,
+ "msgs[%d] %c, addr=0x%04x, len=%d\n",
+ i, (msgs[i].flags & I2C_M_RD) ? 'R' : 'W',
+ msgs[i].addr, msgs[i].len);
+ }
+
+ mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST |
I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ |
- I2C_INT_RX_FIFO_DATA_REQ);
+ I2C_INT_RX_FIFO_DATA_REQ | I2C_INT_TX_FIFO_OVERFLOW;
+
i2c_writel(i2c_dev, status, I2C_INT_STATUS);
+
+ if (i2c_dev->chipdata->has_xfer_complete_interrupt)
+ mask |= I2C_INT_ALL_PACKETS_XFER_COMPLETE;
+
+ if (!(i2c_dev->use_single_xfer_complete &&
+ i2c_dev->chipdata->has_xfer_complete_interrupt))
+ mask |= I2C_INT_ALL_PACKETS_XFER_COMPLETE;
+
+ if (i2c_dev->chipdata->has_hw_arb_support)
+ mask |= I2C_INT_BUS_CLEAR_DONE;
+
+ /* An error occurred, mask all interrupts */
+ tegra_i2c_mask_irq(i2c_dev, mask);
+
+ /* An error occured, mask dvc interrupt */
+ if (i2c_dev->is_dvc)
+ dvc_i2c_mask_irq(i2c_dev, DVC_CTRL_REG3_I2C_DONE_INTR_EN);
+
if (i2c_dev->is_dvc)
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
complete(&i2c_dev->msg_complete);
+
return IRQ_HANDLED;
}
+static int tegra_i2c_send_next_read_msg_pkt_header(struct tegra_i2c_dev *i2c_dev, struct i2c_msg *next_msg, enum msg_end_type end_state)
+{
+ i2c_dev->next_msg_buf = next_msg->buf;
+ i2c_dev->next_msg_buf_remaining = next_msg->len;
+ i2c_dev->next_msg_err = I2C_ERR_NONE;
+ i2c_dev->next_msg_read = 1;
+ i2c_dev->next_msg_add = next_msg->addr;
+ i2c_dev->next_packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
+ PACKET_HEADER0_PROTOCOL_I2C |
+ (i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) |
+ (1 << PACKET_HEADER0_PACKET_ID_SHIFT);
+
+ i2c_writel(i2c_dev, i2c_dev->next_packet_header, I2C_TX_FIFO);
+
+ i2c_dev->next_payload_size = next_msg->len - 1;
+ i2c_writel(i2c_dev, i2c_dev->next_payload_size, I2C_TX_FIFO);
+
+ i2c_dev->next_io_header = I2C_HEADER_IE_ENABLE;
+
+ if (end_state == MSG_END_CONTINUE)
+ i2c_dev->next_io_header |= I2C_HEADER_CONTINUE_XFER;
+ else if (end_state == MSG_END_REPEAT_START)
+ i2c_dev->next_io_header |= I2C_HEADER_REPEAT_START;
+
+ if (next_msg->flags & I2C_M_TEN) {
+ i2c_dev->next_io_header |= next_msg->addr;
+ i2c_dev->next_io_header |= I2C_HEADER_10BIT_ADDR;
+ } else {
+ i2c_dev->next_io_header |= (next_msg->addr << I2C_HEADER_SLAVE_ADDR_SHIFT);
+ }
+ if (next_msg->flags & I2C_M_IGNORE_NAK)
+ i2c_dev->next_io_header |= I2C_HEADER_CONT_ON_NAK;
+
+ i2c_dev->next_io_header |= I2C_HEADER_READ;
+
+ if (i2c_dev->is_high_speed_enable) {
+ i2c_dev->next_io_header |= I2C_HEADER_HIGHSPEED_MODE;
+ i2c_dev->next_io_header |= ((i2c_dev->hs_master_code & 0x7)
+ << I2C_HEADER_MASTER_ADDR_SHIFT);
+ }
+ i2c_writel(i2c_dev, i2c_dev->next_io_header, I2C_TX_FIFO);
+
+ return 0;
+}
+
static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
- struct i2c_msg *msg, enum msg_end_type end_state)
+ struct i2c_msg *msg, enum msg_end_type end_state,
+ struct i2c_msg *next_msg, enum msg_end_type next_msg_end_state)
{
- u32 packet_header;
u32 int_mask;
int ret;
-
- tegra_i2c_flush_fifos(i2c_dev);
+ unsigned long flags = 0;
+ unsigned long timeout = jiffies + HZ;
if (msg->len == 0)
return -EINVAL;
+ tegra_i2c_flush_fifos(i2c_dev);
+
+
i2c_dev->msg_buf = msg->buf;
i2c_dev->msg_buf_remaining = msg->len;
i2c_dev->msg_err = I2C_ERR_NONE;
i2c_dev->msg_read = (msg->flags & I2C_M_RD);
INIT_COMPLETION(i2c_dev->msg_complete);
- packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
+ if (!i2c_dev->chipdata->has_xfer_complete_interrupt)
+ spin_lock_irqsave(&i2c_dev->fifo_lock, flags);
+
+ i2c_dev->msg_add = msg->addr;
+
+ i2c_dev->packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
PACKET_HEADER0_PROTOCOL_I2C |
(i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) |
(1 << PACKET_HEADER0_PACKET_ID_SHIFT);
- i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+ i2c_writel(i2c_dev, i2c_dev->packet_header, I2C_TX_FIFO);
+
+ i2c_dev->payload_size = msg->len - 1;
+ i2c_writel(i2c_dev, i2c_dev->payload_size, I2C_TX_FIFO);
- packet_header = msg->len - 1;
- i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+ i2c_dev->use_single_xfer_complete = true;
+ i2c_dev->io_header = 0;
+ if (next_msg == NULL)
+ i2c_dev->io_header = I2C_HEADER_IE_ENABLE;
- packet_header = I2C_HEADER_IE_ENABLE;
if (end_state == MSG_END_CONTINUE)
- packet_header |= I2C_HEADER_CONTINUE_XFER;
+ i2c_dev->io_header |= I2C_HEADER_CONTINUE_XFER;
else if (end_state == MSG_END_REPEAT_START)
- packet_header |= I2C_HEADER_REPEAT_START;
+ i2c_dev->io_header |= I2C_HEADER_REPEAT_START;
if (msg->flags & I2C_M_TEN) {
- packet_header |= msg->addr;
- packet_header |= I2C_HEADER_10BIT_ADDR;
+ i2c_dev->io_header |= msg->addr;
+ i2c_dev->io_header |= I2C_HEADER_10BIT_ADDR;
} else {
- packet_header |= msg->addr << I2C_HEADER_SLAVE_ADDR_SHIFT;
+ i2c_dev->io_header |= msg->addr << I2C_HEADER_SLAVE_ADDR_SHIFT;
}
if (msg->flags & I2C_M_IGNORE_NAK)
- packet_header |= I2C_HEADER_CONT_ON_NAK;
+ i2c_dev->io_header |= I2C_HEADER_CONT_ON_NAK;
if (msg->flags & I2C_M_RD)
- packet_header |= I2C_HEADER_READ;
- i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+ i2c_dev->io_header |= I2C_HEADER_READ;
+ if (i2c_dev->is_high_speed_enable) {
+ i2c_dev->io_header |= I2C_HEADER_HIGHSPEED_MODE;
+ i2c_dev->io_header |= ((i2c_dev->hs_master_code & 0x7)
+ << I2C_HEADER_MASTER_ADDR_SHIFT);
+ }
+ i2c_writel(i2c_dev, i2c_dev->io_header, I2C_TX_FIFO);
if (!(msg->flags & I2C_M_RD))
tegra_i2c_fill_tx_fifo(i2c_dev);
- int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
- if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
+ if (i2c_dev->is_dvc)
+ dvc_i2c_unmask_irq(i2c_dev, DVC_CTRL_REG3_I2C_DONE_INTR_EN);
+
+ int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST
+ | I2C_INT_TX_FIFO_OVERFLOW;
+ if (i2c_dev->chipdata->has_xfer_complete_interrupt)
int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
+
+ if (i2c_dev->chipdata->has_xfer_complete_interrupt)
+ int_mask |= I2C_INT_ALL_PACKETS_XFER_COMPLETE;
+
if (msg->flags & I2C_M_RD)
int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
else if (i2c_dev->msg_buf_remaining)
int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
+
+ if (next_msg != NULL) {
+ tegra_i2c_send_next_read_msg_pkt_header(i2c_dev, next_msg,
+ next_msg_end_state);
+ tegra_i2c_copy_next_to_current(i2c_dev);
+ int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
+ i2c_dev->use_single_xfer_complete = false;
+ }
+
+ if (!(i2c_dev->use_single_xfer_complete &&
+ i2c_dev->chipdata->has_xfer_complete_interrupt))
+ int_mask |= I2C_INT_ALL_PACKETS_XFER_COMPLETE;
+
+ if (!i2c_dev->chipdata->has_xfer_complete_interrupt)
+ spin_unlock_irqrestore(&i2c_dev->fifo_lock, flags);
+
tegra_i2c_unmask_irq(i2c_dev, int_mask);
+
dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
i2c_readl(i2c_dev, I2C_INT_MASK));
- ret = wait_for_completion_timeout(&i2c_dev->msg_complete, TEGRA_I2C_TIMEOUT);
+ ret = wait_for_completion_timeout(&i2c_dev->msg_complete,
+ TEGRA_I2C_TIMEOUT);
+ if (ret == 0) {
+ dev_err(i2c_dev->dev, "--- register dump for debugging ----\n");
+ dev_err(i2c_dev->dev, "I2C_CNFG - 0x%x\n",
+ i2c_readl(i2c_dev, I2C_CNFG));
+ dev_err(i2c_dev->dev, "I2C_PACKET_TRANSFER_STATUS - 0x%x\n",
+ i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS));
+ dev_err(i2c_dev->dev, "I2C_FIFO_CONTROL - 0x%x\n",
+ i2c_readl(i2c_dev, I2C_FIFO_CONTROL));
+ dev_err(i2c_dev->dev, "I2C_FIFO_STATUS - 0x%x\n",
+ i2c_readl(i2c_dev, I2C_FIFO_STATUS));
+ dev_err(i2c_dev->dev, "I2C_INT_MASK - 0x%x\n",
+ i2c_readl(i2c_dev, I2C_INT_MASK));
+ dev_err(i2c_dev->dev, "I2C_INT_STATUS - 0x%x\n",
+ i2c_readl(i2c_dev, I2C_INT_STATUS));
+
+ dev_err(i2c_dev->dev, "msg->len - %d\n", msg->len);
+ dev_err(i2c_dev->dev, "is_msg_write - %d\n",
+ !(msg->flags & I2C_M_RD));
+ if (next_msg != NULL) {
+ dev_err(i2c_dev->dev, "next_msg->len - %d\n",
+ next_msg->len);
+ dev_err(i2c_dev->dev, "is_next_msg_write - %d\n",
+ !(next_msg->flags & I2C_M_RD));
+ }
+
+ dev_err(i2c_dev->dev, "buf_remaining - %d\n",
+ i2c_dev->msg_buf_remaining);
+ }
+
tegra_i2c_mask_irq(i2c_dev, int_mask);
+ if (i2c_dev->is_dvc)
+ dvc_i2c_mask_irq(i2c_dev, DVC_CTRL_REG3_I2C_DONE_INTR_EN);
+
if (ret == 0) {
- dev_err(i2c_dev->dev, "i2c transfer timed out\n");
+ dev_err(i2c_dev->dev,
+ "i2c transfer timed out, addr 0x%04x, data 0x%02x\n",
+ msg->addr, msg->buf[0]);
tegra_i2c_init(i2c_dev);
return -ETIMEDOUT;
@@ -603,6 +1078,33 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
if (likely(i2c_dev->msg_err == I2C_ERR_NONE))
return 0;
+ /* Prints errors */
+ if (i2c_dev->msg_err & I2C_ERR_UNKNOWN_INTERRUPT)
+ dev_warn(i2c_dev->dev, "unknown interrupt Add 0x%02x\n",
+ i2c_dev->msg_add);
+ if (i2c_dev->msg_err & I2C_ERR_NO_ACK)
+ dev_warn(i2c_dev->dev, "no acknowledge from address 0x%x\n",
+ i2c_dev->msg_add);
+ if (i2c_dev->msg_err & I2C_ERR_ARBITRATION_LOST)
+ dev_warn(i2c_dev->dev, "arb lost in communicate to add 0x%x\n",
+ i2c_dev->msg_add);
+ if (i2c_dev->msg_err & I2C_INT_TX_FIFO_OVERFLOW)
+ dev_warn(i2c_dev->dev, "Tx fifo overflow to add 0x%x\n",
+ i2c_dev->msg_add);
+ if (i2c_dev->msg_err & I2C_ERR_UNEXPECTED_STATUS)
+ dev_warn(i2c_dev->dev, "unexpected status to add 0x%x\n",
+ i2c_dev->msg_add);
+
+ if ((i2c_dev->chipdata->timeout_irq_occurs_before_bus_inactive) &&
+ (i2c_dev->msg_err == I2C_ERR_NO_ACK)) {
+ /*
+ * In NACK error condition resetting of I2C controller happens
+ * before STOP condition is properly completed by I2C controller,
+ * so wait for 2 clock cycle to complete STOP condition.
+ */
+ udelay(DIV_ROUND_UP(2 * 1000000, i2c_dev->bus_clk_rate));
+ }
+
/*
* NACK interrupt is generated before the I2C controller generates the
* STOP condition on the bus. So wait for 2 clock periods before resetting
@@ -612,12 +1114,55 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
udelay(DIV_ROUND_UP(2 * 1000000, i2c_dev->bus_clk_rate));
tegra_i2c_init(i2c_dev);
+
+ /* Arbitration Lost occurs, Start recovery */
+ if (i2c_dev->msg_err == I2C_ERR_ARBITRATION_LOST) {
+ if (i2c_dev->chipdata->has_hw_arb_support) {
+ INIT_COMPLETION(i2c_dev->msg_complete);
+ i2c_writel(i2c_dev, I2C_BC_ENABLE
+ | I2C_BC_SCLK_THRESHOLD
+ | I2C_BC_STOP_COND
+ | I2C_BC_TERMINATE
+ , I2C_BUS_CLEAR_CNFG);
+
+ if (i2c_dev->chipdata->has_config_load_reg) {
+ i2c_writel(i2c_dev, I2C_MSTR_CONFIG_LOAD,
+ I2C_CONFIG_LOAD);
+ while (i2c_readl(i2c_dev, I2C_CONFIG_LOAD) != 0) {
+ if (time_after(jiffies, timeout)) {
+ dev_warn(i2c_dev->dev,
+ "timeout config_load");
+ return -ETIMEDOUT;
+ }
+ msleep(1);
+ }
+ }
+
+ tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLEAR_DONE);
+
+ wait_for_completion_timeout(&i2c_dev->msg_complete,
+ TEGRA_I2C_TIMEOUT);
+
+ if (!(i2c_readl(i2c_dev, I2C_BUS_CLEAR_STATUS) & I2C_BC_STATUS))
+ dev_warn(i2c_dev->dev, "Un-recovered Arbitration lost\n");
+ } else {
+ i2c_algo_busclear_gpio(i2c_dev->dev,
+ i2c_dev->scl_gpio, GPIOF_OPEN_DRAIN,
+ i2c_dev->sda_gpio,GPIOF_OPEN_DRAIN,
+ MAX_BUSCLEAR_CLOCK, 100000);
+ }
+ return -EAGAIN;
+ }
+
if (i2c_dev->msg_err == I2C_ERR_NO_ACK) {
if (msg->flags & I2C_M_IGNORE_NAK)
return 0;
return -EREMOTEIO;
}
+ if (i2c_dev->msg_err & I2C_ERR_UNEXPECTED_STATUS)
+ return -EAGAIN;
+
return -EIO;
}
@@ -631,25 +1176,59 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
if (i2c_dev->is_suspended)
return -EBUSY;
+ if (i2c_dev->is_shutdown && i2c_dev->bit_banging_xfer_after_shutdown)
+ return tegra_i2c_gpio_xfer(adap, msgs, num);
+
+ i2c_dev->msgs = msgs;
+ i2c_dev->msgs_num = num;
+
+ pm_runtime_get_sync(&adap->dev);
ret = tegra_i2c_clock_enable(i2c_dev);
if (ret < 0) {
dev_err(i2c_dev->dev, "Clock enable failed %d\n", ret);
+ pm_runtime_put(&adap->dev);
return ret;
}
for (i = 0; i < num; i++) {
enum msg_end_type end_type = MSG_END_STOP;
+ enum msg_end_type next_msg_end_type = MSG_END_STOP;
+
if (i < (num - 1)) {
if (msgs[i + 1].flags & I2C_M_NOSTART)
end_type = MSG_END_CONTINUE;
else
end_type = MSG_END_REPEAT_START;
+ if (i < num - 2) {
+ if (msgs[i + 2].flags & I2C_M_NOSTART)
+ next_msg_end_type = MSG_END_CONTINUE;
+ else
+ next_msg_end_type = MSG_END_REPEAT_START;
+ }
+ if ((!(msgs[i].flags & I2C_M_RD)) && (msgs[i].len <= 8) && (msgs[i+1].flags & I2C_M_RD)
+ && (next_msg_end_type != MSG_END_CONTINUE) && (end_type == MSG_END_REPEAT_START)) {
+ ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type, &msgs[i+1], next_msg_end_type);
+ if (ret)
+ break;
+ i++;
+ } else {
+ ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type, NULL, next_msg_end_type);
+ if (ret)
+ break;
+ }
+ } else {
+ ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type, NULL, next_msg_end_type);
+ if (ret)
+ break;
}
- ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type);
- if (ret)
- break;
}
+
tegra_i2c_clock_disable(i2c_dev);
+ pm_runtime_put(&adap->dev);
+
+ i2c_dev->msgs = NULL;
+ i2c_dev->msgs_num = 0;
+
return ret ?: i;
}
@@ -659,7 +1238,7 @@ static u32 tegra_i2c_func(struct i2c_adapter *adap)
u32 ret = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
I2C_FUNC_PROTOCOL_MANGLING;
- if (i2c_dev->hw->has_continue_xfer_support)
+ if (i2c_dev->chipdata->has_continue_xfer_support)
ret |= I2C_FUNC_NOSTART;
return ret;
}
@@ -669,51 +1248,195 @@ static const struct i2c_algorithm tegra_i2c_algo = {
.functionality = tegra_i2c_func,
};
-static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
+static int __tegra_i2c_suspend_noirq(struct tegra_i2c_dev *i2c_dev);
+static int __tegra_i2c_resume_noirq(struct tegra_i2c_dev *i2c_dev);
+
+static int tegra_i2c_pm_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct tegra_i2c_dev *i2c_dev = container_of(nb, struct tegra_i2c_dev, pm_nb);
+
+ if (event == TEGRA_PM_SUSPEND)
+ __tegra_i2c_suspend_noirq(i2c_dev);
+ else if (event == TEGRA_PM_RESUME)
+ __tegra_i2c_resume_noirq(i2c_dev);
+
+ return NOTIFY_OK;
+}
+
+static struct tegra_i2c_platform_data *parse_i2c_tegra_dt(
+ struct platform_device *pdev)
+{
+ struct tegra_i2c_platform_data *pdata;
+ struct device_node *np = pdev->dev.of_node;
+ u32 prop;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ if (!of_property_read_u32(np, "clock-frequency", &prop))
+ pdata->bus_clk_rate = prop;
+
+ if (of_find_property(np, "nvidia,clock-always-on", NULL))
+ pdata->is_clkon_always = true;
+
+ if (!of_property_read_u32(np, "nvidia,hs-master-code", &prop)) {
+ pdata->hs_master_code = prop;
+ pdata->is_high_speed_enable = true;
+ }
+
+ if (of_find_property(np, "nvidia,bit-banging-xfer-after-shutdown", NULL))
+ pdata->bit_banging_xfer_after_shutdown = true;
+
+ pdata->scl_gpio = of_get_named_gpio(np, "scl-gpio", 0);
+ pdata->sda_gpio = of_get_named_gpio(np, "sda-gpio", 0);
+ pdata->is_dvc = of_device_is_compatible(np, "nvidia,tegra20-i2c-dvc");
+
+ /* Default configuration for device tree initiated driver */
+ pdata->slave_addr = 0xFC;
+ return pdata;
+}
+
+static struct tegra_i2c_chipdata tegra20_i2c_chipdata = {
+ .timeout_irq_occurs_before_bus_inactive = true,
+ .has_xfer_complete_interrupt = false,
.has_continue_xfer_support = false,
- .has_per_pkt_xfer_complete_irq = false,
- .has_single_clk_source = false,
- .clk_divisor_hs_mode = 3,
+ .has_hw_arb_support = false,
+ .has_fast_clock = true,
+ .has_clk_divisor_std_fast_mode = false,
.clk_divisor_std_fast_mode = 0,
+ .clk_divisor_hs_mode = 3,
+ .clk_multiplier_hs_mode = 12,
+ .has_config_load_reg = false,
};
-static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
+static struct tegra_i2c_chipdata tegra30_i2c_chipdata = {
+ .timeout_irq_occurs_before_bus_inactive = true,
+ .has_xfer_complete_interrupt = false,
.has_continue_xfer_support = true,
- .has_per_pkt_xfer_complete_irq = false,
- .has_single_clk_source = false,
- .clk_divisor_hs_mode = 3,
+ .has_hw_arb_support = false,
+ .has_fast_clock = true,
+ .has_clk_divisor_std_fast_mode = false,
.clk_divisor_std_fast_mode = 0,
+ .clk_divisor_hs_mode = 3,
+ .clk_multiplier_hs_mode = 12,
+ .has_config_load_reg = false,
};
-static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
+static struct tegra_i2c_chipdata tegra114_i2c_chipdata = {
+ .timeout_irq_occurs_before_bus_inactive = false,
+ .has_xfer_complete_interrupt = true,
.has_continue_xfer_support = true,
- .has_per_pkt_xfer_complete_irq = true,
- .has_single_clk_source = true,
+ .has_hw_arb_support = true,
+ .has_fast_clock = false,
+ .has_clk_divisor_std_fast_mode = true,
+ .clk_divisor_std_fast_mode = 0x19,
+ .clk_divisor_fast_plus_mode = 0x10,
.clk_divisor_hs_mode = 1,
+ .clk_multiplier_hs_mode = 3,
+ .has_config_load_reg = false,
+};
+
+static struct tegra_i2c_chipdata tegra148_i2c_chipdata = {
+ .timeout_irq_occurs_before_bus_inactive = false,
+ .has_xfer_complete_interrupt = true,
+ .has_continue_xfer_support = true,
+ .has_hw_arb_support = true,
+ .has_fast_clock = false,
+ .has_clk_divisor_std_fast_mode = true,
.clk_divisor_std_fast_mode = 0x19,
+ .clk_divisor_fast_plus_mode = 0x19,
+ .clk_divisor_hs_mode = 2,
+ .clk_multiplier_hs_mode = 13,
+ .has_config_load_reg = true,
};
+#define tegra124_i2c_chipdata tegra148_i2c_chipdata
+
/* Match table for of_platform binding */
static const struct of_device_id tegra_i2c_of_match[] = {
- { .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
- { .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, },
- { .compatible = "nvidia,tegra20-i2c", .data = &tegra20_i2c_hw, },
- { .compatible = "nvidia,tegra20-i2c-dvc", .data = &tegra20_i2c_hw, },
+ { .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_chipdata, },
+ { .compatible = "nvidia,tegra148-i2c", .data = &tegra148_i2c_chipdata, },
+ { .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_chipdata, },
+ { .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_chipdata, },
+ { .compatible = "nvidia,tegra20-i2c", .data = &tegra20_i2c_chipdata, },
+ { .compatible = "nvidia,tegra20-i2c-dvc", .data = &tegra20_i2c_chipdata, },
{},
};
MODULE_DEVICE_TABLE(of, tegra_i2c_of_match);
+static struct platform_device_id tegra_i2c_devtype[] = {
+ {
+ .name = "tegra-i2c",
+ .driver_data = (unsigned long)&tegra30_i2c_chipdata,
+ },
+ {
+ .name = "tegra20-i2c",
+ .driver_data = (unsigned long)&tegra20_i2c_chipdata,
+ },
+ {
+ .name = "tegra30-i2c",
+ .driver_data = (unsigned long)&tegra30_i2c_chipdata,
+ },
+ {
+ .name = "tegra11-i2c",
+ .driver_data = (unsigned long)&tegra114_i2c_chipdata,
+ },
+ {
+ .name = "tegra14-i2c",
+ .driver_data = (unsigned long)&tegra148_i2c_chipdata,
+ },
+ {
+ .name = "tegra12-i2c",
+ .driver_data = (unsigned long)&tegra124_i2c_chipdata,
+ },
+};
+
static int tegra_i2c_probe(struct platform_device *pdev)
{
struct tegra_i2c_dev *i2c_dev;
+ struct tegra_i2c_platform_data *pdata = pdev->dev.platform_data;
struct resource *res;
struct clk *div_clk;
- struct clk *fast_clk;
+ struct clk *fast_clk = NULL;
+ struct clk *dvfs_ref_clk = NULL;
+ struct clk *dvfs_soc_clk = NULL;
void __iomem *base;
int irq;
int ret = 0;
+ const struct tegra_i2c_chipdata *chip_data = NULL;
+ const struct of_device_id *match;
+ int bus_num;
+
+ if (pdev->dev.of_node) {
+ match = of_match_device(of_match_ptr(tegra_i2c_of_match), &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Device Not matching\n");
+ return -ENODEV;
+ }
+ chip_data = match->data;
+ if (!pdata)
+ pdata = parse_i2c_tegra_dt(pdev);
+ bus_num = of_alias_get_id(pdev->dev.of_node, "i2c");
+ if (bus_num < 0)
+ dev_warn(&pdev->dev, "No bus number specified from device-tree\n");
+ } else {
+ chip_data = (struct tegra_i2c_chipdata *)pdev->id_entry->driver_data;
+ bus_num = pdev->id;
+ }
+
+ if (IS_ERR(pdata) || !pdata || !chip_data) {
+ dev_err(&pdev->dev, "no platform/chip data?\n");
+ return IS_ERR(pdata) ? PTR_ERR(pdata) : -ENODEV;
+ }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no mem resource\n");
+ return -EINVAL;
+ }
+
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -725,54 +1448,76 @@ static int tegra_i2c_probe(struct platform_device *pdev)
}
irq = res->start;
+ i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
+ if (!i2c_dev) {
+ dev_err(&pdev->dev, "Could not allocate struct tegra_i2c_dev");
+ return -ENOMEM;
+ }
+
+ i2c_dev->chipdata = chip_data;
+ i2c_dev->needs_cl_dvfs_clock = pdata->needs_cl_dvfs_clock;
+
div_clk = devm_clk_get(&pdev->dev, "div-clk");
if (IS_ERR(div_clk)) {
dev_err(&pdev->dev, "missing controller clock");
return PTR_ERR(div_clk);
}
- i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
- if (!i2c_dev) {
- dev_err(&pdev->dev, "Could not allocate struct tegra_i2c_dev");
- return -ENOMEM;
+ if (i2c_dev->chipdata->has_fast_clock) {
+ fast_clk = devm_clk_get(&pdev->dev, "fast-clk");
+ if (IS_ERR(fast_clk)) {
+ dev_err(&pdev->dev, "missing bus clock");
+ return PTR_ERR(fast_clk);
+ }
+ }
+
+ if (i2c_dev->needs_cl_dvfs_clock) {
+ dvfs_ref_clk = devm_clk_get(&pdev->dev, "cl_dvfs_ref");
+ if (IS_ERR(dvfs_ref_clk)) {
+ dev_err(&pdev->dev, "missing cl_dvfs_ref clock");
+ return PTR_ERR(dvfs_ref_clk);
+ }
+ i2c_dev->dvfs_ref_clk = dvfs_ref_clk;
+ dvfs_soc_clk = devm_clk_get(&pdev->dev, "cl_dvfs_soc");
+ if (IS_ERR(dvfs_soc_clk)) {
+ dev_err(&pdev->dev, "missing cl_dvfs_soc clock");
+ return PTR_ERR(dvfs_soc_clk);
+ }
+ i2c_dev->dvfs_soc_clk = dvfs_soc_clk;
}
i2c_dev->base = base;
i2c_dev->div_clk = div_clk;
- i2c_dev->adapter.algo = &tegra_i2c_algo;
+ if (i2c_dev->chipdata->has_fast_clock)
+ i2c_dev->fast_clk = fast_clk;
i2c_dev->irq = irq;
i2c_dev->cont_id = pdev->id;
i2c_dev->dev = &pdev->dev;
-
- ret = of_property_read_u32(i2c_dev->dev->of_node, "clock-frequency",
- &i2c_dev->bus_clk_rate);
- if (ret)
- i2c_dev->bus_clk_rate = 100000; /* default clock rate */
-
- i2c_dev->hw = &tegra20_i2c_hw;
-
- if (pdev->dev.of_node) {
- const struct of_device_id *match;
- match = of_match_device(tegra_i2c_of_match, &pdev->dev);
- i2c_dev->hw = match->data;
- i2c_dev->is_dvc = of_device_is_compatible(pdev->dev.of_node,
- "nvidia,tegra20-i2c-dvc");
- } else if (pdev->id == 3) {
- i2c_dev->is_dvc = 1;
- }
+ i2c_dev->is_clkon_always = pdata->is_clkon_always;
+ i2c_dev->bus_clk_rate = pdata->bus_clk_rate ? pdata->bus_clk_rate: 100000;
+ i2c_dev->is_high_speed_enable = pdata->is_high_speed_enable;
+ i2c_dev->clk_divisor_non_hs_mode =
+ i2c_dev->chipdata->clk_divisor_std_fast_mode;
+ if (i2c_dev->bus_clk_rate == 1000000)
+ i2c_dev->clk_divisor_non_hs_mode =
+ i2c_dev->chipdata->clk_divisor_fast_plus_mode;
+ i2c_dev->msgs = NULL;
+ i2c_dev->msgs_num = 0;
+ i2c_dev->is_dvc = pdata->is_dvc;
+ i2c_dev->slave_addr = pdata->slave_addr;
+ i2c_dev->hs_master_code = pdata->hs_master_code;
+ i2c_dev->bit_banging_xfer_after_shutdown =
+ pdata->bit_banging_xfer_after_shutdown;
init_completion(&i2c_dev->msg_complete);
- if (!i2c_dev->hw->has_single_clk_source) {
- fast_clk = devm_clk_get(&pdev->dev, "fast-clk");
- if (IS_ERR(fast_clk)) {
- dev_err(&pdev->dev, "missing fast clock");
- return PTR_ERR(fast_clk);
- }
- i2c_dev->fast_clk = fast_clk;
- }
+ if (!i2c_dev->chipdata->has_xfer_complete_interrupt)
+ spin_lock_init(&i2c_dev->fifo_lock);
platform_set_drvdata(pdev, i2c_dev);
+ if (i2c_dev->is_clkon_always)
+ tegra_i2c_clock_enable(i2c_dev);
+
ret = tegra_i2c_init(i2c_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize i2c controller");
@@ -780,12 +1525,17 @@ static int tegra_i2c_probe(struct platform_device *pdev)
}
ret = devm_request_irq(&pdev->dev, i2c_dev->irq,
- tegra_i2c_isr, 0, dev_name(&pdev->dev), i2c_dev);
+ tegra_i2c_isr, IRQF_NO_SUSPEND,
+ dev_name(&pdev->dev), i2c_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq);
return ret;
}
+ pm_runtime_enable(&pdev->dev);
+
+ i2c_dev->scl_gpio = pdata->scl_gpio;
+ i2c_dev->sda_gpio = pdata->sda_gpio;
i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
i2c_dev->adapter.owner = THIS_MODULE;
i2c_dev->adapter.class = I2C_CLASS_HWMON;
@@ -793,16 +1543,30 @@ static int tegra_i2c_probe(struct platform_device *pdev)
sizeof(i2c_dev->adapter.name));
i2c_dev->adapter.algo = &tegra_i2c_algo;
i2c_dev->adapter.dev.parent = &pdev->dev;
- i2c_dev->adapter.nr = pdev->id;
+ i2c_dev->adapter.nr = bus_num;
i2c_dev->adapter.dev.of_node = pdev->dev.of_node;
+ if (pdata->retries)
+ i2c_dev->adapter.retries = pdata->retries;
+ else
+ i2c_dev->adapter.retries = TEGRA_I2C_RETRIES;
+
+ if (pdata->timeout)
+ i2c_dev->adapter.timeout = pdata->timeout;
+
ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
if (ret) {
dev_err(&pdev->dev, "Failed to add I2C adapter\n");
return ret;
}
+ i2c_dev->pm_nb.notifier_call = tegra_i2c_pm_notifier;
+
+ tegra_register_pm_notifier(&i2c_dev->pm_nb);
+
of_i2c_register_devices(&i2c_dev->adapter);
+ pm_runtime_enable(&i2c_dev->adapter.dev);
+ tegra_i2c_gpio_init(i2c_dev);
return 0;
}
@@ -810,44 +1574,84 @@ static int tegra_i2c_probe(struct platform_device *pdev)
static int tegra_i2c_remove(struct platform_device *pdev)
{
struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+ tegra_unregister_pm_notifier(&i2c_dev->pm_nb);
i2c_del_adapter(&i2c_dev->adapter);
+ pm_runtime_disable(&i2c_dev->adapter.dev);
+
+ if (i2c_dev->is_clkon_always)
+ tegra_i2c_clock_disable(i2c_dev);
+
+ pm_runtime_disable(&pdev->dev);
return 0;
}
+static void tegra_i2c_shutdown(struct platform_device *pdev)
+{
+ struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+ i2c_dev->is_shutdown = true;
+}
+
#ifdef CONFIG_PM_SLEEP
-static int tegra_i2c_suspend(struct device *dev)
+static int __tegra_i2c_suspend_noirq(struct tegra_i2c_dev *i2c_dev)
{
- struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
+ i2c_dev->is_suspended = true;
+ if (i2c_dev->is_clkon_always)
+ tegra_i2c_clock_disable(i2c_dev);
+
+ return 0;
+}
+
+static int tegra_i2c_suspend_noirq(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
i2c_lock_adapter(&i2c_dev->adapter);
- i2c_dev->is_suspended = true;
+
+ __tegra_i2c_suspend_noirq(i2c_dev);
+
i2c_unlock_adapter(&i2c_dev->adapter);
return 0;
}
-static int tegra_i2c_resume(struct device *dev)
+
+static int __tegra_i2c_resume_noirq(struct tegra_i2c_dev *i2c_dev)
{
- struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
int ret;
- i2c_lock_adapter(&i2c_dev->adapter);
+ if (i2c_dev->is_clkon_always)
+ tegra_i2c_clock_enable(i2c_dev);
ret = tegra_i2c_init(i2c_dev);
-
- if (ret) {
- i2c_unlock_adapter(&i2c_dev->adapter);
+ if (ret)
return ret;
- }
i2c_dev->is_suspended = false;
+ return 0;
+}
+
+static int tegra_i2c_resume_noirq(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+ i2c_lock_adapter(&i2c_dev->adapter);
+
+ __tegra_i2c_resume_noirq(i2c_dev);
+
i2c_unlock_adapter(&i2c_dev->adapter);
return 0;
}
-static SIMPLE_DEV_PM_OPS(tegra_i2c_pm, tegra_i2c_suspend, tegra_i2c_resume);
+static const struct dev_pm_ops tegra_i2c_pm = {
+ .suspend_noirq = tegra_i2c_suspend_noirq,
+ .resume_noirq = tegra_i2c_resume_noirq,
+};
#define TEGRA_I2C_PM (&tegra_i2c_pm)
#else
#define TEGRA_I2C_PM NULL
@@ -856,10 +1660,12 @@ static SIMPLE_DEV_PM_OPS(tegra_i2c_pm, tegra_i2c_suspend, tegra_i2c_resume);
static struct platform_driver tegra_i2c_driver = {
.probe = tegra_i2c_probe,
.remove = tegra_i2c_remove,
+ .shutdown = tegra_i2c_shutdown,
+ .id_table = tegra_i2c_devtype,
.driver = {
.name = "tegra-i2c",
.owner = THIS_MODULE,
- .of_match_table = tegra_i2c_of_match,
+ .of_match_table = of_match_ptr(tegra_i2c_of_match),
.pm = TEGRA_I2C_PM,
},
};