summaryrefslogtreecommitdiff
path: root/drivers/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/Kconfig10
-rw-r--r--drivers/i2c/Makefile2
-rw-r--r--drivers/i2c/algos/Kconfig3
-rw-r--r--drivers/i2c/algos/Makefile1
-rw-r--r--drivers/i2c/algos/i2c-algo-busclear.c102
-rw-r--r--drivers/i2c/algos/i2c-algo-pca.c439
-rw-r--r--drivers/i2c/busses/Kconfig19
-rw-r--r--drivers/i2c/busses/Makefile5
-rw-r--r--drivers/i2c/busses/i2c-pca-gmi.c344
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c5
-rw-r--r--drivers/i2c/busses/i2c-sirf.c6
-rw-r--r--drivers/i2c/busses/i2c-slave-tegra.c1115
-rw-r--r--drivers/i2c/busses/i2c-tegra.c1082
-rwxr-xr-xdrivers/i2c/i2c-slave.c281
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c104
15 files changed, 3346 insertions, 172 deletions
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index e380c6eef3af..6331123b40d2 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -59,6 +59,16 @@ config I2C_MUX
source drivers/i2c/muxes/Kconfig
+config I2C_SLAVE
+ bool "I2C slave driver support"
+ default n
+ help
+ Say Y here if you want the I2C slave functionality in the driver.
+ The external system will be master and the system on which this
+ driver is running act as i2c slave.
+ This drivers supports read/write data from master devices through
+ I2C.
+
config I2C_HELPER_AUTO
bool "Autoselect pertinent helper modules"
default y
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 1722f50f2473..572cab8fc2ae 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -1,12 +1,14 @@
#
# Makefile for the i2c core.
#
+GCOV_PROFILE := y
obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
obj-$(CONFIG_I2C) += i2c-core.o
obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
obj-$(CONFIG_I2C_MUX) += i2c-mux.o
+obj-$(CONFIG_I2C_SLAVE) += i2c-slave.o
obj-y += algos/ busses/ muxes/
obj-$(CONFIG_I2C_STUB) += i2c-stub.o
diff --git a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig
index f1cfe7e5508b..d5f8ae533da6 100644
--- a/drivers/i2c/algos/Kconfig
+++ b/drivers/i2c/algos/Kconfig
@@ -14,4 +14,7 @@ config I2C_ALGOPCF
config I2C_ALGOPCA
tristate "I2C PCA 9564 interfaces"
+config I2C_ALGO_BUSCLEAR
+ bool
+
endmenu
diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile
index 215303f60d61..253b96d13f3c 100644
--- a/drivers/i2c/algos/Makefile
+++ b/drivers/i2c/algos/Makefile
@@ -5,5 +5,6 @@
obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o
obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o
obj-$(CONFIG_I2C_ALGOPCA) += i2c-algo-pca.o
+obj-$(CONFIG_I2C_ALGO_BUSCLEAR) += i2c-algo-busclear.o
ccflags-$(CONFIG_I2C_DEBUG_ALGO) := -DDEBUG
diff --git a/drivers/i2c/algos/i2c-algo-busclear.c b/drivers/i2c/algos/i2c-algo-busclear.c
new file mode 100644
index 000000000000..ade921ea457a
--- /dev/null
+++ b/drivers/i2c/algos/i2c-algo-busclear.c
@@ -0,0 +1,102 @@
+/*
+ * i2c-algo-busclear: Bus clear logic implementation based on GPIO
+ *
+ * Copyright (c) 2013, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/export.h>
+
+/*I2C controller supports eight-byte burst transfer*/
+#define RETRY_MAX_COUNT (9 * 8 + 1)
+
+int i2c_algo_busclear_gpio(struct device *dev, int scl_gpio, int scl_gpio_flags,
+ int sda_gpio, int sda_gpio_flags,
+ int max_retry_clock, int clock_speed_hz)
+{
+ int ret;
+ int retry_clk = min(max_retry_clock, RETRY_MAX_COUNT);
+ int recovered_successfully = 0;
+ int val;
+ int clk_delay = DIV_ROUND_UP(1000000, 2 * clock_speed_hz);
+ int clk_count = 0;
+
+ if ((!gpio_is_valid(scl_gpio)) || !gpio_is_valid((sda_gpio))) {
+ dev_err(dev, "GPIOs are not valid\n");
+ return -EINVAL;
+ }
+
+ ret = gpio_request_one(scl_gpio,
+ scl_gpio_flags | GPIOF_OUT_INIT_HIGH, "scl_gpio");
+ if (ret < 0) {
+ dev_err(dev, "GPIO request for scl_gpio failed, err = %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = gpio_request_one(sda_gpio,
+ sda_gpio_flags | GPIOF_IN, "sda_gpio");
+ if (ret < 0) {
+ dev_err(dev, "GPIO request for sda_gpio failed, err = %d\n",
+ ret);
+ gpio_free(scl_gpio);
+ return ret;
+ }
+
+ gpio_direction_input(sda_gpio);
+ while (retry_clk--) {
+ clk_count++;
+ gpio_direction_output(scl_gpio, 0);
+ udelay(clk_delay);
+ gpio_direction_output(scl_gpio, 1);
+ udelay(clk_delay);
+
+ /* check whether sda struct low release */
+ val = gpio_get_value(sda_gpio);
+ if (val) {
+ /* send START */
+ gpio_direction_output(sda_gpio, 0);
+ udelay(clk_delay);
+
+ /* send STOP in next clock cycle */
+ gpio_direction_output(scl_gpio, 0);
+ udelay(clk_delay);
+ gpio_direction_output(scl_gpio, 1);
+ udelay(clk_delay);
+ gpio_direction_output(sda_gpio, 1);
+ udelay(clk_delay);
+ recovered_successfully = 1;
+ break;
+ }
+ }
+
+ gpio_free(scl_gpio);
+ gpio_free(sda_gpio);
+
+ if (!recovered_successfully) {
+ dev_err(dev, "arb lost recovered failed with total clk %d\n",
+ clk_count);
+ return -EINVAL;
+ }
+ dev_err(dev, "arb lost recovered in clock count %d\n", clk_count);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(i2c_algo_busclear_gpio);
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c
index f892a424009b..c97a15a8222a 100644
--- a/drivers/i2c/algos/i2c-algo-pca.c
+++ b/drivers/i2c/algos/i2c-algo-pca.c
@@ -2,7 +2,8 @@
* i2c-algo-pca.c i2c driver algorithms for PCA9564 adapters
* Copyright (C) 2004 Arcom Control Systems
* Copyright (C) 2008 Pengutronix
- *
+ * Copyright (C) 2013 NVIDIA Corporation
+
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -29,17 +30,38 @@
#include <linux/i2c.h>
#include <linux/i2c-algo-pca.h>
-#define DEB1(fmt, args...) do { if (i2c_debug >= 1) \
- printk(KERN_DEBUG fmt, ## args); } while (0)
-#define DEB2(fmt, args...) do { if (i2c_debug >= 2) \
- printk(KERN_DEBUG fmt, ## args); } while (0)
-#define DEB3(fmt, args...) do { if (i2c_debug >= 3) \
- printk(KERN_DEBUG fmt, ## args); } while (0)
+#define DEB1(fmt, args...) \
+do { \
+ if (i2c_debug >= 1) \
+ printk(KERN_DEBUG fmt, ## args); \
+} while (0)
+
+#define DEB2(fmt, args...) \
+do { \
+ if (i2c_debug >= 2) \
+ printk(KERN_DEBUG fmt, ## args); \
+} while (0)
+
+#define DEB3(fmt, args...) \
+do { \
+ if (i2c_debug >= 3) \
+ printk(KERN_DEBUG fmt, ## args); \
+} while (0)
static int i2c_debug;
#define pca_outw(adap, reg, val) adap->write_byte(adap->data, reg, val)
#define pca_inw(adap, reg) adap->read_byte(adap->data, reg)
+#define pca_lock(adap) \
+do { \
+ if (adap->request_access) \
+ adap->request_access(adap->data); \
+} while (0)
+#define pca_unlock(adap) \
+do { \
+ if (adap->release_access) \
+ adap->release_access(adap->data); \
+} while (0)
#define pca_status(adap) pca_inw(adap, I2C_PCA_STA)
#define pca_clock(adap) adap->i2c_clock
@@ -72,6 +94,10 @@ static int pca_start(struct i2c_algo_pca_data *adap)
DEB2("=== START\n");
sta |= I2C_PCA_CON_STA;
sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI);
+ if (adap->mode == I2C_PCA_BUFFERED_MODE)
+ sta |= I2C_PCA_CON_MODE;
+
+ DEB2("I2C PCA CON ::%x\n", sta);
pca_set_con(adap, sta);
return pca_wait(adap);
}
@@ -87,6 +113,9 @@ static int pca_repeated_start(struct i2c_algo_pca_data *adap)
DEB2("=== REPEATED START\n");
sta |= I2C_PCA_CON_STA;
sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI);
+ if (adap->mode == I2C_PCA_BUFFERED_MODE)
+ sta |= I2C_PCA_CON_MODE;
+
pca_set_con(adap, sta);
return pca_wait(adap);
}
@@ -106,6 +135,10 @@ static void pca_stop(struct i2c_algo_pca_data *adap)
DEB2("=== STOP\n");
sta |= I2C_PCA_CON_STO;
sta &= ~(I2C_PCA_CON_STA|I2C_PCA_CON_SI);
+
+ if (adap->mode == I2C_PCA_BUFFERED_MODE)
+ sta |= I2C_PCA_CON_MODE;
+
pca_set_con(adap, sta);
}
@@ -134,6 +167,63 @@ static int pca_address(struct i2c_algo_pca_data *adap,
return pca_wait(adap);
}
+static int pca_setup_rx_buffered(struct i2c_algo_pca_data *adap,
+ struct i2c_msg *msg)
+{
+ int sta = pca_get_con(adap);
+ int ret;
+ int addr = ((0x7f & msg->addr) << 1);
+ if (msg->flags & I2C_M_RD)
+ addr |= 1;
+ DEB2("=== SLAVE ADDRESS %#04x+%c=%#04x\n",
+ msg->addr, msg->flags & I2C_M_RD ? 'R' : 'W', addr);
+
+ pca_outw(adap, I2C_PCA_DAT, addr);
+
+ sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI);
+ sta |= I2C_PCA_CON_MODE;
+
+ pca_set_con(adap, sta);
+ ret = pca_wait(adap);
+ return ret;
+
+}
+
+static void pca_rx_buffer(struct i2c_algo_pca_data *adap,
+ struct i2c_msg *msg, int transfer_len, int index)
+{
+ int i;
+ for (i = 0; i < transfer_len; i++)
+ msg->buf[index + i] = pca_inw(adap, I2C_PCA_DAT);
+
+}
+
+static int pca_tx_buffer(struct i2c_algo_pca_data *adap,
+ struct i2c_msg *msg, int transfer_len, int index, int restart)
+{
+ int sta = pca_get_con(adap);
+ int i = 0;
+ int ret;
+ if (restart) {
+ int addr = ((0x7f & msg->addr) << 1);
+ if (msg->flags & I2C_M_RD)
+ addr |= 1;
+ DEB2("=== SLAVE ADDRESS %#04x+%c=%#04x\n",
+ msg->addr, msg->flags & I2C_M_RD ? 'R' : 'W', addr);
+
+ pca_outw(adap, I2C_PCA_DAT, addr);
+ }
+ for (i = 0; i < transfer_len; i++)
+ pca_outw(adap, I2C_PCA_DAT, msg->buf[i + index]);
+
+ sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI);
+ sta |= I2C_PCA_CON_MODE;
+
+ pca_set_con(adap, sta);
+ ret = pca_wait(adap);
+ return ret;
+
+}
/*
* Transmit a byte.
*
@@ -183,6 +273,265 @@ static int pca_rx_ack(struct i2c_algo_pca_data *adap,
return pca_wait(adap);
}
+
+static void pca_set_count(struct i2c_algo_pca_data *adap, int count,
+ char last_buf)
+{
+
+ pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_ICOUNT);
+ if (last_buf)
+ pca_outw(adap, I2C_PCA_IND, count | I2C_PCA_CON_AA);
+ else
+ pca_outw(adap, I2C_PCA_IND, count);
+}
+
+static int pca_xfer_buffered(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msgs,
+ int num)
+{
+ struct i2c_algo_pca_data *adap = i2c_adap->algo_data;
+ struct i2c_msg *msg = NULL;
+ int curmsg;
+ int numbytes = 0;
+ int state;
+ int ret;
+ int completed = 1;
+ unsigned long timeout = jiffies + i2c_adap->timeout;
+ int transfer_len = 0;
+ int last_buf = 0;
+ state = 0;
+
+ pca_lock(adap);
+ state = pca_status(adap);
+ while (state != 0xf8) {
+ if (time_before(jiffies, timeout)) {
+ pca_unlock(adap);
+ msleep(10);
+
+ pca_lock(adap);
+ state = pca_status(adap);
+ } else {
+ dev_dbg(&i2c_adap->dev, "bus is not idle. status is " \
+ "%#04x\n", state);
+ pca_unlock(adap);
+ return -EBUSY;
+ }
+ }
+
+ DEB1("{{{ XFER %d messages\n", num);
+
+ if (i2c_debug >= 2) {
+ for (curmsg = 0; curmsg < num; curmsg++) {
+ int addr, i;
+ msg = &msgs[curmsg];
+
+ addr = (0x7f & msg->addr) ;
+
+ if (msg->flags & I2C_M_RD)
+ printk(KERN_INFO " [%02d] RD %d bytes from %#02x [%#02x, ...]\n",
+ curmsg, msg->len, addr, (addr << 1) | 1);
+ else {
+ printk(KERN_INFO " [%02d] WR %d bytes to %#02x [%#02x%s",
+ curmsg, msg->len, addr, addr << 1,
+ msg->len == 0 ? "" : ", ");
+ for (i = 0; i < msg->len; i++)
+ printk("%#04x%s", msg->buf[i],
+ i == msg->len - 1 ? "" : ", ");
+ printk("]\n");
+ }
+ }
+ }
+ curmsg = 0;
+ ret = -EIO;
+ while (curmsg < num) {
+ state = pca_status(adap);
+ msg = &msgs[curmsg];
+
+ DEB3("STATE is 0x%02x\n", state);
+ switch (state) {
+ case 0xf8: /* On reset or stop the bus is idle */
+ /* Increse len by 1 byte for slave address */
+ if (msg->len > (adap->buf_size))
+ pca_set_count(adap, adap->buf_size + 1, 0);
+ else
+ pca_set_count(adap, msg->len + 1,
+ msg->flags & I2C_M_RD ? 1 : 0);
+
+ completed = pca_start(adap);
+ break;
+
+ case 0x08: /* A START condition has been transmitted */
+ case 0x10: /* A repeated start condition has been transmitted */
+
+ if (msg->len > adap->buf_size) {
+ last_buf = 0;
+ transfer_len = adap->buf_size;
+ } else {
+ last_buf = 1;
+ transfer_len = msg->len;
+ }
+ if (msg->flags & I2C_M_RD) {
+ pca_set_count(adap,
+ transfer_len,
+ msg->flags & I2C_M_RD ? last_buf : 0);
+ pca_setup_rx_buffered(adap, msg);
+ } else {
+ pca_tx_buffer(adap, msg, transfer_len, numbytes, 1);
+ numbytes += transfer_len;
+ }
+ break;
+ case 0x18:
+ /* SLA+W has been transmitted; ACK has been received */
+ case 0x28:
+ /* Data byte in I2CDAT has been transmitted; ACK has been received */
+
+ if (numbytes < msg->len) {
+ int remaining = msg->len - numbytes;
+
+ if (remaining > adap->buf_size)
+ transfer_len = adap->buf_size;
+ else
+ transfer_len = remaining;
+
+ pca_set_count(adap, transfer_len, 0);
+
+ pca_tx_buffer(adap, msg, transfer_len, numbytes, 0);
+ numbytes += transfer_len;
+ break;
+
+ }
+ curmsg++; numbytes = 0;
+
+ if (curmsg == num)
+ pca_stop(adap);
+ else
+ completed = pca_repeated_start(adap);
+ break;
+
+ case 0x20:
+ /* SLA+W has been transmitted; NOT ACK has been received */
+ DEB2("NOT ACK received after SLA+W\n");
+ pca_stop(adap);
+ ret = -ENXIO;
+ goto out;
+
+ case 0x40:
+ /* SLA+R has been transmitted; ACK has been received */
+ completed = pca_rx_ack(adap, msg->len > 1);
+ break;
+
+ case 0x50:
+ /* Data bytes has been received; ACK has been returned */
+
+ pca_rx_buffer(adap, msg, transfer_len, numbytes);
+ numbytes += transfer_len;
+
+ if (numbytes < msg->len) {
+ int remaining = msg->len - numbytes;
+ if (remaining > adap->buf_size) {
+ last_buf = 0;
+ transfer_len = adap->buf_size;
+ } else {
+ last_buf = 1;
+ transfer_len = remaining;
+ }
+
+ pca_set_count(adap, transfer_len,
+ msg->flags & I2C_M_RD ? last_buf : 0);
+ pca_setup_rx_buffered(adap, msg);
+ break;
+
+ }
+ curmsg++; numbytes = 0;
+ if (curmsg == num)
+ pca_stop(adap);
+ else
+ completed = pca_repeated_start(adap);
+ break;
+
+ case 0x48:
+ /* SLA+R has been transmitted; NOT ACK has been received */
+ DEB2("NOT ACK received after SLA+R\n");
+ pca_stop(adap);
+ ret = -ENXIO;
+ goto out;
+
+ case 0x30:
+ /* Data byte in I2CDAT has been transmitted; NOT ACK has been received*/
+ DEB2("NOT ACK received after data byte\n");
+ pca_stop(adap);
+ goto out;
+
+ case 0x38:
+ /* Arbitration lost during SLA+W, SLA+R or data bytes */
+ DEB2("Arbitration lost\n");
+ /*
+ * The PCA9564 data sheet (2006-09-01) says "A
+ * START condition will be transmitted when the
+ * bus becomes free (STOP or SCL and SDA high)"
+ * when the STA bit is set (p. 11).
+ *
+ * In case this won't work, try pca_reset()
+ * instead.
+ */
+ pca_start(adap);
+ goto out;
+
+ case 0x58:
+ /* Data byte has been received; NOT ACK has been returned */
+ pca_rx_buffer(adap, msg, transfer_len, numbytes);
+ numbytes += transfer_len;
+
+
+ if (numbytes == msg->len) {
+ curmsg++; numbytes = 0;
+ if (curmsg == num)
+ pca_stop(adap);
+ else
+ completed = pca_repeated_start(adap);
+ } else {
+ DEB2("NOT ACK sent after data byte received. " \
+ "Not final byte. numbytes %d. len %d\n", \
+ numbytes, msg->len);
+ pca_stop(adap);
+ goto out;
+ }
+ break;
+ case 0x70: /* Bus error - SDA stuck low */
+ DEB2("BUS ERROR - SDA Stuck low\n");
+ pca_reset(adap);
+ goto out;
+ case 0x90: /* Bus error - SCL stuck low */
+ DEB2("BUS ERROR - SCL Stuck low\n");
+ pca_reset(adap);
+ goto out;
+ case 0x00:
+ /* Bus error during master or slave mode due to illegal START or
+ STOP condition */
+ DEB2("BUS ERROR - Illegal START or STOP\n");
+ pca_reset(adap);
+ goto out;
+ default:
+ dev_err(&i2c_adap->dev, "unhandled SIO state 0x%02x\n", state);
+ break;
+ }
+
+ if (!completed)
+ goto out;
+ }
+
+ ret = curmsg;
+ out:
+ DEB1("}}} transferred %d/%d messages. " \
+ "status is %#04x. control is %#04x\n",
+ curmsg, num, pca_status(adap),
+ pca_get_con(adap));
+
+ pca_unlock(adap);
+
+ return ret;
+}
+
static int pca_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg *msgs,
int num)
@@ -200,7 +549,7 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
if (time_before(jiffies, timeout)) {
msleep(10);
} else {
- dev_dbg(&i2c_adap->dev, "bus is not idle. status is "
+ dev_dbg(&i2c_adap->dev, "bus is not idle. status is " \
"%#04x\n", state);
return -EBUSY;
}
@@ -223,12 +572,12 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
curmsg, msg->len, addr, addr << 1,
msg->len == 0 ? "" : ", ");
for (i = 0; i < msg->len; i++)
- printk("%#04x%s", msg->buf[i], i == msg->len - 1 ? "" : ", ");
+ printk("%#04x%s", msg->buf[i],
+ i == msg->len - 1 ? "" : ", ");
printk("]\n");
}
}
}
-
curmsg = 0;
ret = -EIO;
while (curmsg < num) {
@@ -247,8 +596,10 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
completed = pca_address(adap, msg);
break;
- case 0x18: /* SLA+W has been transmitted; ACK has been received */
- case 0x28: /* Data byte in I2CDAT has been transmitted; ACK has been received */
+ case 0x18:
+ /* SLA+W has been transmitted; ACK has been received */
+ case 0x28:
+ /* Data byte in I2CDAT has been transmitted; ACK has been received */
if (numbytes < msg->len) {
completed = pca_tx_byte(adap,
msg->buf[numbytes]);
@@ -262,17 +613,20 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
completed = pca_repeated_start(adap);
break;
- case 0x20: /* SLA+W has been transmitted; NOT ACK has been received */
+ case 0x20:
+ /* SLA+W has been transmitted; NOT ACK has been received */
DEB2("NOT ACK received after SLA+W\n");
pca_stop(adap);
ret = -ENXIO;
goto out;
- case 0x40: /* SLA+R has been transmitted; ACK has been received */
+ case 0x40:
+ /* SLA+R has been transmitted; ACK has been received */
completed = pca_rx_ack(adap, msg->len > 1);
break;
- case 0x50: /* Data bytes has been received; ACK has been returned */
+ case 0x50:
+ /* Data bytes has been received; ACK has been returned */
if (numbytes < msg->len) {
pca_rx_byte(adap, &msg->buf[numbytes], 1);
numbytes++;
@@ -287,18 +641,22 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
completed = pca_repeated_start(adap);
break;
- case 0x48: /* SLA+R has been transmitted; NOT ACK has been received */
+ case 0x48:
+ /* SLA+R has been transmitted; NOT ACK has been received */
DEB2("NOT ACK received after SLA+R\n");
pca_stop(adap);
ret = -ENXIO;
goto out;
- case 0x30: /* Data byte in I2CDAT has been transmitted; NOT ACK has been received */
+ case 0x30:
+ /* Data byte in I2CDAT has been transmitted;
+ * NOT ACK has been received */
DEB2("NOT ACK received after data byte\n");
pca_stop(adap);
goto out;
- case 0x38: /* Arbitration lost during SLA+W, SLA+R or data bytes */
+ case 0x38:
+ /* Arbitration lost during SLA+W, SLA+R or data bytes */
DEB2("Arbitration lost\n");
/*
* The PCA9564 data sheet (2006-09-01) says "A
@@ -312,7 +670,9 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
pca_start(adap);
goto out;
- case 0x58: /* Data byte has been received; NOT ACK has been returned */
+ case 0x58:
+ /* Data byte has been received;
+ * NOT ACK has been returned */
if (numbytes == msg->len - 1) {
pca_rx_byte(adap, &msg->buf[numbytes], 0);
curmsg++; numbytes = 0;
@@ -321,7 +681,7 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
else
completed = pca_repeated_start(adap);
} else {
- DEB2("NOT ACK sent after data byte received. "
+ DEB2("NOT ACK sent after data byte received. " \
"Not final byte. numbytes %d. len %d\n",
numbytes, msg->len);
pca_stop(adap);
@@ -336,12 +696,16 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
DEB2("BUS ERROR - SCL Stuck low\n");
pca_reset(adap);
goto out;
- case 0x00: /* Bus error during master or slave mode due to illegal START or STOP condition */
+ case 0x00:
+ /* Bus error during master or slave mode due to illegal START
+ or STOP condition */
DEB2("BUS ERROR - Illegal START or STOP\n");
pca_reset(adap);
goto out;
default:
- dev_err(&i2c_adap->dev, "unhandled SIO state 0x%02x\n", state);
+ dev_err(&i2c_adap->dev,
+ "unhandled SIO state 0x%02x\n",
+ state);
break;
}
@@ -351,7 +715,7 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
ret = curmsg;
out:
- DEB1("}}} transferred %d/%d messages. "
+ DEB1("}}} transferred %d/%d messages. " \
"status is %#04x. control is %#04x\n",
curmsg, num, pca_status(adap),
pca_get_con(adap));
@@ -368,6 +732,11 @@ static const struct i2c_algorithm pca_algo = {
.functionality = pca_func,
};
+static const struct i2c_algorithm pca_algo_buffered = {
+ .master_xfer = pca_xfer_buffered,
+ .functionality = pca_func,
+};
+
static unsigned int pca_probe_chip(struct i2c_adapter *adap)
{
struct i2c_algo_pca_data *pca_data = adap->algo_data;
@@ -395,12 +764,11 @@ static int pca_init(struct i2c_adapter *adap)
{
struct i2c_algo_pca_data *pca_data = adap->algo_data;
- adap->algo = &pca_algo;
-
if (pca_probe_chip(adap) == I2C_PCA_CHIP_9564) {
static int freqs[] = {330, 288, 217, 146, 88, 59, 44, 36};
int clock;
+ adap->algo = &pca_algo;
if (pca_data->i2c_clock > 7) {
switch (pca_data->i2c_clock) {
case 330000:
@@ -434,12 +802,12 @@ static int pca_init(struct i2c_adapter *adap)
pca_data->i2c_clock = I2C_PCA_CON_59kHz;
}
} else {
- printk(KERN_WARNING "%s: "
- "Choosing the clock frequency based on "
- "index is deprecated."
+ printk(KERN_WARNING "%s: " \
+ "Choosing the clock frequency based on " \
+ "index is deprecated." \
" Use the nominal frequency.\n", adap->name);
}
-
+ pca_data->mode = I2C_PCA_BYTE_MODE;
pca_reset(pca_data);
clock = pca_clock(pca_data);
@@ -461,15 +829,16 @@ static int pca_init(struct i2c_adapter *adap)
* maximum clock rate for each mode
*/
int raise_fall_time;
+ adap->algo = &pca_algo_buffered;
if (pca_data->i2c_clock > 1265800) {
- printk(KERN_WARNING "%s: I2C clock speed too high."
+ printk(KERN_WARNING "%s: I2C clock speed too high." \
" Using 1265.8kHz.\n", adap->name);
pca_data->i2c_clock = 1265800;
}
if (pca_data->i2c_clock < 60300) {
- printk(KERN_WARNING "%s: I2C clock speed too low."
+ printk(KERN_WARNING "%s: I2C clock speed too low." \
" Using 60.3kHz.\n", adap->name);
pca_data->i2c_clock = 60300;
}
@@ -526,6 +895,9 @@ static int pca_init(struct i2c_adapter *adap)
pca_outw(pca_data, I2C_PCA_IND, thi);
pca_set_con(pca_data, I2C_PCA_CON_ENSIO);
+ pca_data->mode = I2C_PCA_BUFFERED_MODE;
+ pca_data->buf_size = I2C_PCA_CHIP_9665_BUFSIZE - 1;
+
}
udelay(500); /* 500 us for oscilator to stabilise */
@@ -559,8 +931,9 @@ int i2c_pca_add_numbered_bus(struct i2c_adapter *adap)
}
EXPORT_SYMBOL(i2c_pca_add_numbered_bus);
-MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>, "
- "Wolfram Sang <w.sang@pengutronix.de>");
+MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>, " \
+ "Wolfram Sang <w.sang@pengutronix.de>" \
+ "Nitin Sehgal <nsehgal@nvidia.com>");
MODULE_DESCRIPTION("I2C-Bus PCA9564/PCA9665 algorithm");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 4faf02b3657d..d2c4cb3521ac 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -710,10 +710,20 @@ config I2C_STU300
config I2C_TEGRA
tristate "NVIDIA Tegra internal I2C controller"
depends on ARCH_TEGRA
+ select I2C_ALGO_BUSCLEAR
+ select I2C_ALGOBIT
help
If you say yes to this option, support will be included for the
I2C controller embedded in NVIDIA Tegra SOCs
+config I2C_SLAVE_TEGRA
+ tristate "NVIDIA Tegra internal I2C slave controller"
+ depends on ARCH_TEGRA && I2C_SLAVE
+ default n
+ help
+ If you say yes to this option, support will be included for the
+ I2C slave controller embedded in NVIDIA Tegra SOCs
+
config I2C_VERSATILE
tristate "ARM Versatile/Realview I2C bus support"
depends on ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS
@@ -953,4 +963,13 @@ config SCx200_ACB
This support is also available as a module. If so, the module
will be called scx200_acb.
+config I2C_PCA_GMI
+ tristate "PCA9564/PCA9665 on an GMI bus"
+ select I2C_ALGOPCA
+ select TEGRA_GMI
+ default n
+ help
+ This driver supports boards using the Philips PCA9564/PCA9665 parallel
+ bus to I2C bus controller connected on Nvidia Tegra GMI bus.
+
endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 8f4fc23b85b1..04fb39ed83b4 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -1,6 +1,7 @@
#
# Makefile for the i2c bus drivers.
#
+GCOV_PROFILE := y
# ACPI drivers
obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o
@@ -69,7 +70,10 @@ obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
+CFLAGS_i2c-tegra.o = -Werror
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
+CFLAGS_i2c-slave-tegra.o = -Werror
+obj-$(CONFIG_I2C_SLAVE_TEGRA) += i2c-slave-tegra.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
@@ -88,6 +92,7 @@ obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o
obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
+obj-$(CONFIG_I2C_PCA_GMI) += i2c-pca-gmi.o
obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o
diff --git a/drivers/i2c/busses/i2c-pca-gmi.c b/drivers/i2c/busses/i2c-pca-gmi.c
new file mode 100644
index 000000000000..a98fe6701e3f
--- /dev/null
+++ b/drivers/i2c/busses/i2c-pca-gmi.c
@@ -0,0 +1,344 @@
+/*
+ * i2c-pca-gmi.c driver for PCA9564 on ISA boards
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * drivers/i2c/busses/i2c-pca-gmi.c
+ *
+ * I2C GMI Bus driver for for PCA9564 on ISA boards
+*/
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-pca.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <asm/irq.h>
+#include <linux/platform_data/tegra_nor.h>
+#include <linux/gpio.h>
+#include <linux/tegra_snor.h>
+
+#ifdef CONFIG_TEGRA_GMI_ACCESS_CONTROL
+#include <linux/tegra_gmi_access.h>
+#endif
+
+#include "../../../arch/arm/mach-tegra/iomap.h"
+
+#define DRV_NAME "i2c-pca-gmi"
+
+static int irq = -1;
+
+/* Data sheet recommends 59kHz for 100kHz operation due to variation
+ * in the actual clock rate */
+static int clock = 1265800;
+static struct i2c_adapter pca_gmi_ops;
+static wait_queue_head_t pca_wait;
+
+struct tegra_gmi_pca_info {
+ struct tegra_nor_chip_parms *plat;
+ struct device *dev;
+ void __iomem *base;
+ u32 init_config;
+ u32 timing0_default, timing1_default;
+ u32 timing0_read, timing1_read;
+ int gpio_num;
+#ifdef CONFIG_TEGRA_GMI_ACCESS_CONTROL
+ u32 gmiFmLockHandle;
+#endif
+
+};
+
+static struct tegra_gmi_pca_info *info;
+
+static void pca_gmi_request_access(void *pd)
+{
+#ifdef CONFIG_TEGRA_GMI_ACCESS_CONTROL
+ request_gmi_access(info->gmiFmLockHandle);
+#endif
+}
+
+static void pca_gmi_release_access(void *pd)
+{
+#ifdef CONFIG_TEGRA_GMI_ACCESS_CONTROL
+ release_gmi_access();
+#endif
+}
+static inline unsigned long snor_tegra_readl(struct tegra_gmi_pca_info *tsnor,
+ unsigned long reg)
+{
+ return readl(tsnor->base + reg);
+}
+
+static inline void snor_tegra_writel(struct tegra_gmi_pca_info *tsnor,
+ unsigned long val, unsigned long reg)
+{
+ writel(val, tsnor->base + reg);
+}
+
+
+static void pca_gmi_writebyte(void *pd, int reg, int val)
+{
+ struct tegra_nor_chip_parms *chip_parm = info->plat;
+ struct cs_info *csinfo = &chip_parm->csinfo;
+ unsigned int *ptr = csinfo->virt;
+ struct gpio_state *state = &csinfo->gpio_cs;
+
+ snor_tegra_writel(info, info->init_config, TEGRA_SNOR_CONFIG_REG);
+ snor_tegra_writel(info, info->timing1_read, TEGRA_SNOR_TIMING1_REG);
+ snor_tegra_writel(info, info->timing0_read, TEGRA_SNOR_TIMING0_REG);
+
+ gpio_set_value(state[0].gpio_num, state[0].value);
+ __raw_writeb(val, ptr + reg);
+}
+static int pca_gmi_readbyte(void *pd, int reg)
+{
+ int res;
+ struct tegra_nor_chip_parms *chip_parm = info->plat;
+ struct cs_info *csinfo = &chip_parm->csinfo;
+ unsigned int *ptr = csinfo->virt;
+ struct gpio_state *state = &csinfo->gpio_cs;
+
+ snor_tegra_writel(info, info->init_config, TEGRA_SNOR_CONFIG_REG);
+ snor_tegra_writel(info, info->timing1_read, TEGRA_SNOR_TIMING1_REG);
+ snor_tegra_writel(info, info->timing0_read, TEGRA_SNOR_TIMING0_REG);
+ gpio_set_value(state[0].gpio_num, state[0].value);
+
+ res = __raw_readb(ptr + reg);
+ return res;
+}
+
+static int pca_gmi_waitforcompletion(void *pd)
+{
+ unsigned long timeout;
+ long ret;
+
+ if (irq > -1) {
+ ret = wait_event_timeout(pca_wait,
+ pca_gmi_readbyte(pd, I2C_PCA_CON)
+ & I2C_PCA_CON_SI, pca_gmi_ops.timeout);
+ } else {
+ /* Do polling */
+ timeout = jiffies + pca_gmi_ops.timeout;
+ do {
+ ret = time_before(jiffies, timeout);
+ if (pca_gmi_readbyte(pd, I2C_PCA_CON)
+ & I2C_PCA_CON_SI)
+ break;
+ udelay(100);
+ } while (ret);
+ }
+ return ret > 0;
+}
+
+static void pca_gmi_resetchip(void *pd)
+{
+/* apparently only an external reset will do it. not a lot can be done */
+ printk(KERN_WARNING "DRIVER: Haven't figured out how" \
+ "to do a reset yet\n");
+}
+
+static irqreturn_t pca_handler(int this_irq, void *dev_id)
+{
+ static int gpio_state;
+
+ if (gpio_state == 0)
+ gpio_state = 1;
+ else
+ gpio_state = 0;
+
+ gpio_set_value(info->gpio_num, gpio_state);
+
+ wake_up(&pca_wait);
+ return IRQ_HANDLED;
+}
+
+static struct i2c_algo_pca_data pca_gmi_data = {
+ /* .data intentionally left NULL, not needed with ISA */
+ .write_byte = pca_gmi_writebyte,
+ .read_byte = pca_gmi_readbyte,
+ .wait_for_completion = pca_gmi_waitforcompletion,
+ .reset_chip = pca_gmi_resetchip,
+ .request_access = pca_gmi_request_access,
+ .release_access = pca_gmi_release_access,
+};
+
+static struct i2c_adapter pca_gmi_ops = {
+ .owner = THIS_MODULE,
+ .algo_data = &pca_gmi_data,
+ .name = "PCA9564/PCA9665 GMI Adapter",
+ .timeout = HZ,
+};
+
+
+static int tegra_gmi_controller_init(struct tegra_gmi_pca_info *info)
+{
+ struct tegra_nor_chip_parms *chip_parm = info->plat;
+ struct cs_info *csinfo = &chip_parm->csinfo;
+
+ u32 width = chip_parm->BusWidth;
+ u32 config = 0;
+
+ config |= TEGRA_SNOR_CONFIG_DEVICE_MODE(0);
+ config |= TEGRA_SNOR_CONFIG_SNOR_CS(csinfo->cs);
+ config &= ~TEGRA_SNOR_CONFIG_DEVICE_TYPE;
+ config |= TEGRA_SNOR_CONFIG_WP; /* Enable writes */
+
+ switch (width) {
+ case 2:
+ config &= ~TEGRA_SNOR_CONFIG_WORDWIDE; /* 16 bit */
+ break;
+ case 4:
+ config |= TEGRA_SNOR_CONFIG_WORDWIDE; /* 32 bit */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (chip_parm->MuxMode) {
+ case NorMuxMode_ADNonMux:
+ config &= ~TEGRA_SNOR_CONFIG_MUX_MODE;
+ break;
+ case NorMuxMode_ADMux:
+ config |= TEGRA_SNOR_CONFIG_MUX_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ info->init_config = config;
+
+ info->timing0_default = chip_parm->timing_default.timing0;
+ info->timing0_read = chip_parm->timing_read.timing0;
+ info->timing1_default = chip_parm->timing_default.timing1;
+ info->timing1_read = chip_parm->timing_read.timing1;
+ return 0;
+}
+
+
+static int tegra_gmi_pca_probe(struct platform_device *pdev)
+{
+ int err = 0, err_gpio = 0;
+ int err_int = 0;
+ struct tegra_nor_chip_parms *plat = pdev->dev.platform_data;
+ struct cs_info *csinfo = &plat->csinfo;
+ struct gpio_state *state = &csinfo->gpio_cs;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+
+ if (!plat) {
+ pr_err("%s: no platform device info\n", __func__);
+ err = -EINVAL;
+ goto fail;
+ }
+ info = devm_kzalloc(dev, sizeof(struct tegra_gmi_pca_info),
+ GFP_KERNEL);
+ if (!info) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ info->base = ((void __iomem *)IO_APB_VIRT +
+ (TEGRA_SNOR_BASE - IO_APB_PHYS));
+ info->plat = plat;
+ info->dev = dev;
+
+ /* Intialise the SNOR controller before probe */
+ err = tegra_gmi_controller_init(info);
+ if (err) {
+ dev_err(dev, "Error initializing controller\n");
+ goto fail;
+ }
+ platform_set_drvdata(pdev, info);
+
+ init_waitqueue_head(&pca_wait);
+
+ err_gpio = gpio_request(state[0].gpio_num, state[0].label);
+ gpio_direction_output(state[0].gpio_num, 0);
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res) {
+ dev_err(dev, "GPIO %d can't be used as interrupt\n",
+ res->start);
+ return -ENODEV;
+ }
+
+ err_int = gpio_request(res->start, NULL);
+ gpio_direction_output(res->start, 0);
+ irq = gpio_to_irq(res->start);
+ info->gpio_num = res->start;
+
+ if (irq <= 0) {
+ printk(KERN_ALERT "GPIO %d can't be used as interrupt"
+ , res->start);
+ goto fail;
+ }
+
+ if (irq > -1) {
+ if (request_irq(irq, pca_handler, IRQF_TRIGGER_FALLING,
+ "i2c-pca-gmi", &pca_gmi_ops) < 0) {
+ dev_err(dev, "Request irq%d failed\n", irq);
+ goto fail;
+ }
+ }
+
+#ifdef CONFIG_TEGRA_GMI_ACCESS_CONTROL
+ info->gmiFmLockHandle = register_gmi_device("gmi-i2c-pca", 0);
+#endif
+
+ pca_gmi_data.i2c_clock = clock;
+ pca_gmi_request_access(NULL);
+
+ if (i2c_pca_add_bus(&pca_gmi_ops) < 0) {
+ pca_gmi_release_access(NULL);
+ dev_err(dev, "Failed to add i2c bus\n");
+ goto out_irq;
+ }
+
+ pca_gmi_release_access(NULL);
+
+ return 0;
+
+out_irq:
+ if (irq > -1)
+ free_irq(irq, &pca_gmi_ops);
+
+fail:
+ pr_err("Tegra GMI PCA probe failed\n");
+ return err;
+}
+
+static struct platform_driver __refdata tegra_gmi_pca_driver = {
+ .probe = tegra_gmi_pca_probe,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(tegra_gmi_pca_driver);
+
+MODULE_DESCRIPTION("ISA base PCA9564/PCA9665 driver");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index cab1c91b75a3..6e8ee92ab553 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -1082,6 +1082,11 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
/* map the registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "cannot find IO resource\n");
+ return -ENOENT;
+ }
+
i2c->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(i2c->regs))
diff --git a/drivers/i2c/busses/i2c-sirf.c b/drivers/i2c/busses/i2c-sirf.c
index a63c7d506836..5a7ad240bd26 100644
--- a/drivers/i2c/busses/i2c-sirf.c
+++ b/drivers/i2c/busses/i2c-sirf.c
@@ -303,6 +303,12 @@ static int i2c_sirfsoc_probe(struct platform_device *pdev)
adap->class = I2C_CLASS_HWMON;
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (mem_res == NULL) {
+ dev_err(&pdev->dev, "Unable to get MEM resource\n");
+ err = -EINVAL;
+ goto out;
+ }
+
siic->base = devm_ioremap_resource(&pdev->dev, mem_res);
if (IS_ERR(siic->base)) {
err = PTR_ERR(siic->base);
diff --git a/drivers/i2c/busses/i2c-slave-tegra.c b/drivers/i2c/busses/i2c-slave-tegra.c
new file mode 100644
index 000000000000..afd3e3521efe
--- /dev/null
+++ b/drivers/i2c/busses/i2c-slave-tegra.c
@@ -0,0 +1,1115 @@
+/*
+ * drivers/i2c/busses/i2c-slave-tegra.c
+ * I2c slave driver for the Nvidia's tegra SOC.
+ *
+ * Copyright (c) 2009-2011, NVIDIA Corporation.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*#define DEBUG 1*/
+/*#define VERBOSE_DEBUG 1*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/i2c-tegra.h>
+#include <linux/i2c-slave.h>
+#include <linux/module.h>
+#include <asm/unaligned.h>
+#include <linux/clk/tegra.h>
+#include <mach/pinmux.h>
+#include <linux/pm_runtime.h>
+#define BYTES_PER_FIFO_WORD 4
+#define to_jiffies(msecs) msecs_to_jiffies(msecs)
+
+#define I2C_CNFG 0x000
+#define I2C_CNFG_PACKET_MODE_EN (1<<10)
+#define I2C_CNFG_NEW_MASTER_FSM (1<<11)
+#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12
+#define I2C_CNFG_DEBOUNCE_CNT_MASK (0x7)
+
+#define I2C_STATUS 0x01C
+
+#define I2C_SLV_CNFG 0x020
+#define I2C_SLV_CNFG_NEWSL (1<<2)
+#define I2C_SLV_CNFG_ENABLE_SL (1<<3)
+#define I2C_SLV_CNFG_PKT_MODE_EN (1<<4)
+#define I2C_SLV_CNFG_FIFO_XFER_EN (1<<20)
+#define I2C_SLV_CNFG_ACK_LAST_BYTE (1<<6)
+#define I2C_SLV_CNFG_ACK_LAST_BYTE_VALID (1<<7)
+
+#define I2C_SLV_ADDR1 0x02c
+#define I2C_SLV_ADDR1_ADDR_SHIFT 0x0
+
+#define I2C_SLV_ADDR2 0x030
+#define I2C_SLV_ADDR2_ADDR0_HI_SHIFT 0x1
+#define I2C_SLV_ADDR2_ADDR0_MASK 0x7
+#define I2C_SLV_ADDR2_ADDR0_TEN_BIT_ADDR_MODE 0x1
+
+#define I2C_SLV_INT_MASK 0x040
+
+#define I2C_TX_FIFO 0x050
+#define I2C_RX_FIFO 0x054
+#define I2C_PACKET_TRANSFER_STATUS 0x058
+
+#define I2C_FIFO_CONTROL 0x05c
+#define I2C_FIFO_CONTROL_SLV_TX_FLUSH (1<<9)
+#define I2C_FIFO_CONTROL_SLV_RX_FLUSH (1<<8)
+#define I2C_FIFO_CONTROL_SLV_TX_TRIG_SHIFT 13
+#define I2C_FIFO_CONTROL_SLV_TX_TRIG_MASK (0x7 << 13)
+#define I2C_FIFO_CONTROL_SLV_RX_TRIG_SHIFT 10
+#define I2C_FIFO_CONTROL_SLV_RX_TRIG_MASK (1 << 10)
+
+#define I2C_FIFO_STATUS 0x060
+#define I2C_FIFO_STATUS_SLV_TX_MASK (0xF << 20)
+#define I2C_FIFO_STATUS_SLV_TX_SHIFT 20
+#define I2C_FIFO_STATUS_SLV_RX_MASK (0x0F << 16)
+#define I2C_FIFO_STATUS_SLV_RX_SHIFT 16
+
+#define I2C_INT_MASK 0x064
+#define I2C_INT_STATUS 0x068
+#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)
+#define I2C_INT_RX_FIFO_UNDERFLOW (1<<4)
+#define I2C_INT_NO_ACK (1<<3)
+#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_INT_SLV_PKT_XFER_ERR (1 << 25)
+#define I2C_INT_SLV_TX_BUFFER_REQ (1 << 24)
+#define I2C_INT_SLV_RX_BUFFER_FILLED (1 << 23)
+#define I2C_INT_SLV_PACKET_XFER_COMPLETE (1 << 22)
+#define I2C_INT_SLV_TFIFO_OVF_REQ (1 << 21)
+#define I2C_INT_SLV_RFIFO_UNF_REQ (1 << 20)
+#define I2C_INT_SLV_TFIFO_DATA_REQ (1 << 17)
+#define I2C_INT_SLV_RFIFO_DATA_REQ (1 << 16)
+
+#define I2C_SLV_TX_FIFO 0x078
+#define I2C_SLV_RX_FIFO 0x07c
+
+#define I2C_SLV_PACKET_STATUS 0x80
+#define I2C_SLV_PACKET_STATUS_BYTENUM_SHIFT 4
+#define I2C_SLV_PACKET_STATUS_BYTENUM_MASK 0xFFF0
+
+#define I2C_CLK_DIVISOR 0x06c
+
+#define DVC_CTRL_REG1 0x000
+#define DVC_CTRL_REG1_INTR_EN (1<<10)
+#define DVC_CTRL_REG2 0x004
+#define DVC_CTRL_REG3 0x008
+#define DVC_CTRL_REG3_SW_PROG (1<<26)
+#define DVC_CTRL_REG3_I2C_DONE_INTR_EN (1<<30)
+#define DVC_STATUS 0x00c
+#define DVC_STATUS_I2C_DONE_INTR (1<<30)
+
+#define I2C_ERR_NONE 0x00
+#define I2C_ERR_NO_ACK 0x01
+#define I2C_ERR_ARBITRATION_LOST 0x02
+#define I2C_ERR_UNKNOWN_INTERRUPT 0x04
+
+#define PACKET_HEADER0_HEADER_SIZE_SHIFT 28
+#define PACKET_HEADER0_PACKET_ID_SHIFT 16
+#define PACKET_HEADER0_CONT_ID_SHIFT 12
+#define PACKET_HEADER0_PROTOCOL_I2C (1<<4)
+
+#define I2C_HEADER_HIGHSPEED_MODE (1<<22)
+#define I2C_HEADER_CONT_ON_NAK (1<<21)
+#define I2C_HEADER_SEND_START_BYTE (1<<20)
+#define I2C_HEADER_READ (1<<19)
+#define I2C_HEADER_10BIT_ADDR (1<<18)
+#define I2C_HEADER_IE_ENABLE (1<<17)
+#define I2C_HEADER_REPEAT_START (1<<16)
+#define I2C_HEADER_MASTER_ADDR_SHIFT 12
+#define I2C_HEADER_SLAVE_ADDR_SHIFT 1
+
+#define I2C_FIFO_DEPTH 8
+/* Transfer state of the i2c slave */
+#define TRANSFER_STATE_NONE 0
+#define TRANSFER_STATE_READ 1
+#define TRANSFER_STATE_WRITE 2
+
+#define I2C_SLV_TRANS_PREMATURE_END I2C_INT_SLV_PKT_XFER_ERR
+
+#define I2C_SLV_TRANS_ALL_XFER_END I2C_INT_SLV_PACKET_XFER_COMPLETE
+
+#define I2C_SLV_TRANS_END \
+ (I2C_INT_SLV_PKT_XFER_ERR | I2C_INT_SLV_PACKET_XFER_COMPLETE)
+
+#define I2C_INT_STATUS_RX_BUFFER_FILLED I2C_INT_SLV_RX_BUFFER_FILLED
+
+#define I2C_INT_STATUS_RX_DATA_AVAILABLE \
+ (I2C_INT_SLV_RX_BUFFER_FILLED | I2C_INT_SLV_RFIFO_DATA_REQ)
+
+#define I2C_INT_STATUS_TX_BUFFER_REQUEST \
+ (I2C_INT_SLV_TX_BUFFER_REQ | I2C_INT_SLV_TFIFO_DATA_REQ)
+
+#define I2C_SLV_ERRORS_INT_MASK (I2C_INT_SLV_TFIFO_OVF_REQ | \
+ I2C_INT_SLV_RFIFO_UNF_REQ | I2C_INT_SLV_PKT_XFER_ERR)
+
+#define I2C_SLV_DEFAULT_INT_MASK (I2C_INT_SLV_TFIFO_OVF_REQ | \
+ I2C_INT_SLV_RFIFO_UNF_REQ | I2C_INT_SLV_PKT_XFER_ERR | \
+ I2C_INT_SLV_RX_BUFFER_FILLED | I2C_INT_SLV_TX_BUFFER_REQ)
+
+struct tegra_i2c_slave_dev;
+
+struct tegra_i2c_slave_bus {
+ struct tegra_i2c_slave_dev *dev;
+ const struct tegra_pingroup_config *pinmux;
+ int mux_len;
+ unsigned long bus_clk_rate;
+ struct i2c_slave_adapter slv_adap;
+};
+
+struct tegra_i2c_slave_dev {
+ struct device *dev;
+ struct clk *clk;
+ struct resource *iomem;
+ void __iomem *base;
+ int cont_id;
+ int irq;
+ spinlock_t lock;
+ struct completion rx_msg_complete;
+ struct completion tx_msg_complete;
+ bool is_rx_waiting;
+ bool is_tx_waiting;
+ u8 *rx_msg_buff;
+ int rx_msg_buf_size;
+ int rx_msg_head;
+ int rx_msg_tail;
+ u8 *tx_msg_buff;
+ int tx_msg_buf_size;
+ int tx_msg_head;
+ int tx_msg_tail;
+ bool is_slave_started;
+ int slave_add;
+ bool is_ten_bit_addr;
+ u32 dummy_word;
+ unsigned long rx_pack_hdr1;
+ unsigned long rx_pack_hdr2;
+ unsigned long rx_pack_hdr3;
+ int curr_transfer;
+ unsigned long int_mask;
+ int nack_packet_count;
+ bool is_first_byte_read_wait;
+ int curr_packet_bytes_read;
+ unsigned int cont_status;
+ bool is_dummy_char_cycle;
+ unsigned long curr_packet_tx_tail;
+ const struct tegra_pingroup_config *pin_mux;
+ int bus_clk;
+ struct tegra_i2c_slave_bus bus;
+};
+
+#define get_space_count(rInd, wInd, maxsize) \
+ (((wInd > rInd) ? (maxsize - wInd + rInd) : (rInd - wInd)) - 1)
+
+#define get_data_count(rInd, wInd, maxsize) \
+ ((wInd >= rInd) ? (wInd - rInd) : (maxsize - rInd + wInd - 1))
+
+static void set_tx_trigger_level(struct tegra_i2c_slave_dev *i2c_dev, int trig)
+{
+ unsigned long fifo_control = readl(i2c_dev->base + I2C_FIFO_CONTROL);
+ if (trig) {
+ fifo_control &= ~I2C_FIFO_CONTROL_SLV_TX_TRIG_MASK;
+ fifo_control |= (trig-1) << I2C_FIFO_CONTROL_SLV_TX_TRIG_SHIFT;
+ writel(fifo_control, i2c_dev->base + I2C_FIFO_CONTROL);
+ }
+}
+
+static void set_rx_trigger_level(struct tegra_i2c_slave_dev *i2c_dev, int trig)
+{
+ unsigned long fifo_control = readl(i2c_dev->base + I2C_FIFO_CONTROL);
+ if (trig) {
+ fifo_control &= ~I2C_FIFO_CONTROL_SLV_RX_TRIG_MASK;
+ fifo_control |= (trig-1) << I2C_FIFO_CONTROL_SLV_RX_TRIG_SHIFT;
+ writel(fifo_control, i2c_dev->base + I2C_FIFO_CONTROL);
+ }
+}
+
+static void reset_slave_tx_fifo(struct tegra_i2c_slave_dev *i2c_dev)
+{
+ unsigned long fifo_control = readl(i2c_dev->base + I2C_FIFO_CONTROL);
+ unsigned long timeout_count = 1000;
+
+ writel(fifo_control | I2C_FIFO_CONTROL_SLV_TX_FLUSH,
+ i2c_dev->base + I2C_FIFO_CONTROL);
+ while (timeout_count--) {
+ fifo_control = readl(i2c_dev->base + I2C_FIFO_CONTROL);
+ if (!(fifo_control & I2C_FIFO_CONTROL_SLV_TX_FLUSH))
+ break;
+ udelay(1);
+ }
+ if (!timeout_count) {
+ dev_err(i2c_dev->dev, "Not able to flush tx fifo\n");
+ BUG();
+ }
+}
+
+static void do_tx_fifo_empty(struct tegra_i2c_slave_dev *i2c_dev,
+ unsigned long *empty_count)
+{
+ unsigned long fifo_status = readl(i2c_dev->base + I2C_FIFO_STATUS);
+ unsigned long tx_fifo_empty_count;
+
+ tx_fifo_empty_count = (fifo_status & I2C_FIFO_STATUS_SLV_TX_MASK) >>
+ I2C_FIFO_STATUS_SLV_TX_SHIFT;
+ if (tx_fifo_empty_count < I2C_FIFO_DEPTH)
+ reset_slave_tx_fifo(i2c_dev);
+ if (empty_count)
+ *empty_count = tx_fifo_empty_count;
+}
+
+static void get_packet_headers(struct tegra_i2c_slave_dev *i2c_dev, u32 msg_len,
+ u32 flags, unsigned long *packet_header1,
+ unsigned long *packet_header2, unsigned long *packet_header3)
+{
+ unsigned long packet_header;
+ *packet_header1 = (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);
+ *packet_header2 = msg_len-1;
+ if (i2c_dev->is_ten_bit_addr)
+ packet_header = i2c_dev->slave_add | I2C_HEADER_10BIT_ADDR;
+ else
+ packet_header = i2c_dev->slave_add <<
+ I2C_HEADER_SLAVE_ADDR_SHIFT;
+
+ if (flags & I2C_M_RD)
+ packet_header |= I2C_HEADER_READ;
+
+ *packet_header3 = packet_header;
+}
+
+static void configure_i2c_slave_packet_mode(struct tegra_i2c_slave_dev *i2c_dev)
+{
+ unsigned long i2c_config;
+ i2c_config = I2C_CNFG_PACKET_MODE_EN | I2C_CNFG_NEW_MASTER_FSM;
+ i2c_config |= (2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT);
+ writel(i2c_config, i2c_dev->base + I2C_CNFG);
+}
+
+static void configure_i2c_slave_address(struct tegra_i2c_slave_dev *i2c_dev)
+{
+
+ unsigned long slave_add_reg;
+ unsigned long i2c_slv_config;
+ unsigned long slave_add;
+
+ if (i2c_dev->is_ten_bit_addr) {
+ slave_add = i2c_dev->slave_add & 0xFF;
+ slave_add_reg = readl(i2c_dev->base + I2C_SLV_ADDR1);
+ slave_add_reg &= ~(0xFF);
+ slave_add_reg |= slave_add << I2C_SLV_ADDR1_ADDR_SHIFT;
+ writel(slave_add_reg, i2c_dev->base + I2C_SLV_ADDR1);
+
+ slave_add = (i2c_dev->slave_add >> 8) & 0x3;
+ slave_add_reg = readl(i2c_dev->base + I2C_SLV_ADDR2);
+ slave_add_reg &= ~I2C_SLV_ADDR2_ADDR0_MASK;
+ slave_add_reg |= slave_add |
+ I2C_SLV_ADDR2_ADDR0_TEN_BIT_ADDR_MODE;
+ writel(slave_add_reg, i2c_dev->base + I2C_SLV_ADDR2);
+ } else {
+ slave_add = (i2c_dev->slave_add & 0x3FF);
+ slave_add_reg = readl(i2c_dev->base + I2C_SLV_ADDR1);
+ slave_add_reg &= ~(0x3FF);
+ slave_add_reg |= slave_add << I2C_SLV_ADDR1_ADDR_SHIFT;
+ writel(slave_add_reg, i2c_dev->base + I2C_SLV_ADDR1);
+
+ slave_add_reg = readl(i2c_dev->base + I2C_SLV_ADDR2);
+ slave_add_reg &= ~I2C_SLV_ADDR2_ADDR0_MASK;
+ writel(slave_add_reg, i2c_dev->base + I2C_SLV_ADDR2);
+ }
+
+ i2c_slv_config = I2C_SLV_CNFG_NEWSL;
+ if (i2c_dev->slave_add) {
+ i2c_slv_config = I2C_SLV_CNFG_ENABLE_SL |
+ I2C_SLV_CNFG_PKT_MODE_EN |
+ I2C_SLV_CNFG_FIFO_XFER_EN;
+ }
+ writel(i2c_slv_config, i2c_dev->base + I2C_SLV_CNFG);
+}
+
+static void copy_rx_data(struct tegra_i2c_slave_dev *i2c_dev, u8 rcv_char)
+{
+ if (get_space_count(i2c_dev->rx_msg_tail, i2c_dev->rx_msg_head,
+ i2c_dev->rx_msg_buf_size)){
+ i2c_dev->rx_msg_buff[i2c_dev->rx_msg_head++] = rcv_char;
+ if (i2c_dev->rx_msg_head == i2c_dev->rx_msg_buf_size)
+ i2c_dev->rx_msg_head = 0;
+ } else {
+ dev_warn(i2c_dev->dev, "The slave rx buffer is full, ignoring "
+ "new receive data\n");
+ }
+}
+
+static void handle_packet_first_byte_read(struct tegra_i2c_slave_dev *i2c_dev)
+{
+ unsigned long fifo_status;
+ int filled_slots;
+ unsigned long i2c_sl_config;
+ unsigned long recv_data;
+
+ fifo_status = readl(i2c_dev->base + I2C_FIFO_STATUS);
+ filled_slots = (fifo_status & I2C_FIFO_STATUS_SLV_RX_MASK) >>
+ I2C_FIFO_STATUS_SLV_RX_SHIFT;
+
+ writel(I2C_INT_STATUS_RX_DATA_AVAILABLE,
+ i2c_dev->base + I2C_INT_STATUS);
+ if (unlikely(filled_slots != 1)) {
+ dev_err(i2c_dev->dev, "Unexpected number of filed slots %d",
+ filled_slots);
+ BUG();
+ }
+ recv_data = readl(i2c_dev->base + I2C_SLV_RX_FIFO);
+ copy_rx_data(i2c_dev, (u8)recv_data);
+
+ i2c_dev->is_first_byte_read_wait = false;
+ i2c_dev->curr_transfer = TRANSFER_STATE_READ;
+ i2c_dev->curr_packet_bytes_read = 0;
+
+ /* Write packet Header */
+ writel(i2c_dev->rx_pack_hdr1, i2c_dev->base + I2C_SLV_TX_FIFO);
+ writel(i2c_dev->rx_pack_hdr2, i2c_dev->base + I2C_SLV_TX_FIFO);
+ writel(i2c_dev->rx_pack_hdr3, i2c_dev->base + I2C_SLV_TX_FIFO);
+
+ set_rx_trigger_level(i2c_dev, 4);
+ i2c_dev->int_mask |= I2C_INT_SLV_RFIFO_DATA_REQ;
+ writel(i2c_dev->int_mask, i2c_dev->base + I2C_INT_MASK);
+
+ /* Ack the master */
+ i2c_sl_config = readl(i2c_dev->base + I2C_SLV_CNFG);
+ i2c_sl_config |= I2C_SLV_CNFG_ACK_LAST_BYTE |
+ I2C_SLV_CNFG_ACK_LAST_BYTE_VALID;
+ writel(i2c_sl_config, i2c_dev->base + I2C_SLV_CNFG);
+}
+
+static void handle_packet_byte_read(struct tegra_i2c_slave_dev *i2c_dev)
+{
+ unsigned long fifo_status;
+ int i, j;
+ int filled_slots;
+ unsigned long recv_data;
+ int curr_xfer_size;
+
+ fifo_status = readl(i2c_dev->base + I2C_FIFO_STATUS);
+ filled_slots = (fifo_status & I2C_FIFO_STATUS_SLV_RX_MASK) >>
+ I2C_FIFO_STATUS_SLV_RX_SHIFT;
+
+ curr_xfer_size = BYTES_PER_FIFO_WORD * filled_slots;
+ if (i2c_dev->cont_status & I2C_SLV_TRANS_PREMATURE_END) {
+ curr_xfer_size = readl(i2c_dev->base + I2C_SLV_PACKET_STATUS);
+ curr_xfer_size =
+ (curr_xfer_size & I2C_SLV_PACKET_STATUS_BYTENUM_MASK) >>
+ I2C_SLV_PACKET_STATUS_BYTENUM_SHIFT;
+
+ BUG_ON(filled_slots != ((curr_xfer_size -
+ i2c_dev->curr_packet_bytes_read + 3) >> 2));
+ curr_xfer_size -= i2c_dev->curr_packet_bytes_read;
+ }
+
+ i2c_dev->curr_packet_bytes_read += curr_xfer_size;
+ for (i = 0; i < filled_slots; ++i) {
+ recv_data = readl(i2c_dev->base + I2C_SLV_RX_FIFO);
+ for (j = 0; j < BYTES_PER_FIFO_WORD; ++j) {
+ copy_rx_data(i2c_dev, (u8)(recv_data >> j*8));
+ curr_xfer_size--;
+ if (!curr_xfer_size)
+ break;
+ }
+ }
+ if (i2c_dev->cont_status & I2C_SLV_TRANS_PREMATURE_END) {
+ writel(I2C_SLV_TRANS_END | I2C_INT_STATUS_RX_BUFFER_FILLED,
+ i2c_dev->base + I2C_INT_STATUS);
+
+ i2c_dev->is_first_byte_read_wait = true;
+ i2c_dev->curr_transfer = TRANSFER_STATE_NONE;
+ i2c_dev->curr_packet_bytes_read = 0;
+ set_rx_trigger_level(i2c_dev, 1);
+ writel(0, i2c_dev->base + I2C_SLV_INT_MASK);
+ i2c_dev->int_mask = I2C_SLV_DEFAULT_INT_MASK;
+ writel(i2c_dev->int_mask, i2c_dev->base + I2C_INT_MASK);
+ }
+}
+
+static void handle_rx_interrupt(struct tegra_i2c_slave_dev *i2c_dev)
+{
+ if (i2c_dev->is_first_byte_read_wait)
+ handle_packet_first_byte_read(i2c_dev);
+ else
+ handle_packet_byte_read(i2c_dev);
+
+ if (i2c_dev->is_rx_waiting) {
+ complete(&i2c_dev->rx_msg_complete);
+ i2c_dev->is_rx_waiting = false;
+ }
+}
+
+static void handle_tx_transaction_end(struct tegra_i2c_slave_dev *i2c_dev)
+{
+ unsigned long curr_packet_size;
+
+ i2c_dev->curr_transfer = TRANSFER_STATE_NONE;
+ curr_packet_size = readl(i2c_dev->base + I2C_SLV_PACKET_STATUS);
+ curr_packet_size =
+ (curr_packet_size & I2C_SLV_PACKET_STATUS_BYTENUM_MASK) >>
+ I2C_SLV_PACKET_STATUS_BYTENUM_SHIFT;
+
+ /* Get transfer count from request size.*/
+ if ((curr_packet_size == 0) &&
+ (i2c_dev->cont_status & I2C_SLV_TRANS_ALL_XFER_END) &&
+ (!(i2c_dev->cont_status & I2C_SLV_TRANS_PREMATURE_END))) {
+ if (!i2c_dev->is_dummy_char_cycle)
+ i2c_dev->tx_msg_tail = i2c_dev->curr_packet_tx_tail;
+ } else {
+ if (!i2c_dev->is_dummy_char_cycle) {
+ i2c_dev->tx_msg_tail += curr_packet_size;
+ if (i2c_dev->tx_msg_tail >= i2c_dev->tx_msg_buf_size)
+ i2c_dev->tx_msg_tail -=
+ i2c_dev->tx_msg_buf_size;
+ }
+ }
+ writel(I2C_SLV_TRANS_END, i2c_dev->base + I2C_INT_STATUS);
+
+ i2c_dev->curr_transfer = TRANSFER_STATE_NONE;
+ set_tx_trigger_level(i2c_dev, 1);
+ writel(0, i2c_dev->base + I2C_SLV_INT_MASK);
+ i2c_dev->int_mask = I2C_SLV_DEFAULT_INT_MASK;
+ writel(i2c_dev->int_mask, i2c_dev->base + I2C_INT_MASK);
+ if (i2c_dev->is_tx_waiting) {
+ complete(&i2c_dev->tx_msg_complete);
+ i2c_dev->is_tx_waiting = false;
+ }
+}
+
+static void handle_tx_trigger_int(struct tegra_i2c_slave_dev *i2c_dev)
+{
+ unsigned long fifo_status;
+ int empty_slots;
+ int i, j;
+ int data_available;
+ unsigned long header1, header2, header3;
+ unsigned long tx_data;
+ int word_to_write;
+ int bytes_remain;
+ int bytes_in_curr_word;
+ int tx_tail;
+ int packet_len;
+
+ fifo_status = readl(i2c_dev->base + I2C_FIFO_STATUS);
+ empty_slots = (fifo_status & I2C_FIFO_STATUS_SLV_TX_MASK) >>
+ I2C_FIFO_STATUS_SLV_TX_SHIFT;
+ BUG_ON(empty_slots <= 3);
+ if (i2c_dev->curr_transfer == TRANSFER_STATE_NONE) {
+ empty_slots -= 3;
+
+ /* Clear the tfifo request. */
+ writel(I2C_INT_STATUS_TX_BUFFER_REQUEST,
+ i2c_dev->base + I2C_INT_STATUS);
+
+ /* Get Number of bytes it can transfer in current */
+ data_available = get_data_count(i2c_dev->tx_msg_tail,
+ i2c_dev->tx_msg_head, i2c_dev->tx_msg_buf_size);
+ if (data_available)
+ packet_len = min(empty_slots*BYTES_PER_FIFO_WORD,
+ data_available);
+ else
+ packet_len = empty_slots*BYTES_PER_FIFO_WORD;
+
+ get_packet_headers(i2c_dev, packet_len, I2C_M_RD,
+ &header1, &header2, &header3);
+
+ /* Write packet Header */
+ writel(header1, i2c_dev->base + I2C_SLV_TX_FIFO);
+ writel(header2, i2c_dev->base + I2C_SLV_TX_FIFO);
+ writel(header3, i2c_dev->base + I2C_SLV_TX_FIFO);
+
+ fifo_status = readl(i2c_dev->base + I2C_FIFO_STATUS);
+ if (data_available) {
+ word_to_write = (packet_len + 3) >> 2;
+ bytes_remain = packet_len;
+ tx_tail = i2c_dev->tx_msg_tail;
+ for (i = 0; i < word_to_write; i++) {
+ bytes_in_curr_word =
+ min(bytes_remain, BYTES_PER_FIFO_WORD);
+ tx_data = 0;
+ for (j = 0; j < bytes_in_curr_word; ++j) {
+ tx_data |= (i2c_dev->tx_msg_buff[
+ tx_tail++]<<(j*8));
+ if (tx_tail >= i2c_dev->tx_msg_buf_size)
+ tx_tail = 0;
+ }
+ writel(tx_data, i2c_dev->base +
+ I2C_SLV_TX_FIFO);
+ bytes_remain -= bytes_in_curr_word;
+ }
+ i2c_dev->curr_packet_tx_tail = tx_tail;
+ i2c_dev->is_dummy_char_cycle = false;
+ } else {
+ i2c_dev->curr_packet_tx_tail = i2c_dev->tx_msg_tail;
+ for (i = 0; i < empty_slots; i++)
+ writel(i2c_dev->dummy_word,
+ i2c_dev->base + I2C_SLV_TX_FIFO);
+ i2c_dev->is_dummy_char_cycle = true;
+ }
+
+ i2c_dev->curr_transfer = TRANSFER_STATE_WRITE;
+ i2c_dev->int_mask &= ~I2C_INT_SLV_TFIFO_DATA_REQ;
+ i2c_dev->int_mask |= I2C_SLV_TRANS_END;
+ writel(i2c_dev->int_mask, i2c_dev->base + I2C_INT_MASK);
+ } else {
+ dev_err(i2c_dev->dev, "I2cSlaveIsr(): Illegal transfer at "
+ "this point\n");
+ BUG();
+ }
+}
+
+static void handle_tx_interrupt(struct tegra_i2c_slave_dev *i2c_dev)
+{
+ if (i2c_dev->cont_status & I2C_SLV_TRANS_END)
+ handle_tx_transaction_end(i2c_dev);
+ else
+ handle_tx_trigger_int(i2c_dev);
+}
+
+static irqreturn_t tegra_i2c_slave_isr(int irq, void *dev_id)
+{
+ struct tegra_i2c_slave_dev *i2c_dev = dev_id;
+ unsigned long flags;
+
+ /* Read the Interrupt status register & PKT_STATUS */
+ i2c_dev->cont_status = readl(i2c_dev->base + I2C_INT_STATUS);
+
+ dev_dbg(i2c_dev->dev, "ISR ContStatus 0x%08x\n", i2c_dev->cont_status);
+ spin_lock_irqsave(&i2c_dev->lock, flags);
+
+ if ((i2c_dev->cont_status & I2C_INT_STATUS_RX_DATA_AVAILABLE) ||
+ (i2c_dev->curr_transfer == TRANSFER_STATE_READ)) {
+ handle_rx_interrupt(i2c_dev);
+ goto Done;
+ }
+
+ if ((i2c_dev->cont_status & I2C_INT_STATUS_TX_BUFFER_REQUEST) ||
+ (i2c_dev->curr_transfer == TRANSFER_STATE_WRITE)) {
+ handle_tx_interrupt(i2c_dev);
+ goto Done;
+ }
+
+ dev_err(i2c_dev->dev, "Tegra I2c Slave got unwanted interrupt "
+ "IntStatus 0x%08x\n", i2c_dev->cont_status);
+ BUG();
+
+Done:
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int tegra_i2c_slave_start(struct i2c_slave_adapter *slv_adap, int addr,
+ int is_ten_bit_addr, unsigned char dummy_char)
+{
+ struct tegra_i2c_slave_bus *i2c_bus = i2c_get_slave_adapdata(slv_adap);
+ struct tegra_i2c_slave_dev *i2c_dev = i2c_bus->dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&i2c_dev->lock, flags);
+ if (i2c_dev->is_slave_started) {
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ return -EBUSY;
+ }
+
+ i2c_dev->rx_msg_buff = (u8 *)(i2c_dev+1);
+ i2c_dev->rx_msg_head = 0;
+ i2c_dev->rx_msg_tail = 0;
+ i2c_dev->is_rx_waiting = false;
+
+ i2c_dev->tx_msg_head = 0;
+ i2c_dev->tx_msg_tail = 0;
+ i2c_dev->is_tx_waiting = true;
+
+ i2c_dev->dummy_word = (dummy_char << 8) | dummy_char;
+ i2c_dev->dummy_word |= i2c_dev->dummy_word << 16;
+
+ i2c_dev->slave_add = addr;
+ i2c_dev->is_ten_bit_addr = is_ten_bit_addr;
+
+ get_packet_headers(i2c_dev, 4096, 0, &i2c_dev->rx_pack_hdr1,
+ &i2c_dev->rx_pack_hdr2, &i2c_dev->rx_pack_hdr3);
+
+ pm_runtime_get_sync(i2c_dev->dev);
+ configure_i2c_slave_packet_mode(i2c_dev);
+ configure_i2c_slave_address(i2c_dev);
+ do_tx_fifo_empty(i2c_dev, NULL);
+ set_rx_trigger_level(i2c_dev, 1);
+ writel(0, i2c_dev->base + I2C_SLV_INT_MASK);
+
+ if (i2c_bus->pinmux)
+ tegra_pinmux_config_tristate_table(i2c_bus->pinmux,
+ i2c_bus->mux_len, TEGRA_TRI_NORMAL);
+
+ i2c_dev->curr_transfer = 0;
+ i2c_dev->is_slave_started = true;
+ i2c_dev->int_mask = I2C_SLV_DEFAULT_INT_MASK;
+ i2c_dev->is_first_byte_read_wait = true;
+ writel(i2c_dev->int_mask, i2c_dev->base + I2C_INT_MASK);
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ return 0;
+}
+
+static void tegra_i2c_slave_stop(struct i2c_slave_adapter *slv_adap,
+ int is_buffer_clear)
+{
+ struct tegra_i2c_slave_bus *i2c_bus = i2c_get_slave_adapdata(slv_adap);
+ struct tegra_i2c_slave_dev *i2c_dev = i2c_bus->dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&i2c_dev->lock, flags);
+ if (!i2c_dev->is_slave_started) {
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ return;
+ }
+
+ i2c_dev->slave_add = 0;
+ i2c_dev->is_ten_bit_addr = false;
+ configure_i2c_slave_address(i2c_dev);
+ writel(0, i2c_dev->base + I2C_SLV_INT_MASK);
+ writel(0, i2c_dev->base + I2C_INT_MASK);
+ i2c_dev->curr_transfer = 0;
+ i2c_dev->is_slave_started = false;
+ pm_runtime_put_sync(i2c_dev->dev);
+ if (is_buffer_clear) {
+ i2c_dev->rx_msg_head = 0;
+ i2c_dev->rx_msg_tail = 0;
+ i2c_dev->is_rx_waiting = false;
+ i2c_dev->tx_msg_head = 0;
+ i2c_dev->tx_msg_tail = 0;
+ i2c_dev->is_tx_waiting = false;
+ }
+ if (i2c_bus->pinmux)
+ tegra_pinmux_config_tristate_table(i2c_bus->pinmux,
+ i2c_bus->mux_len, TEGRA_TRI_TRISTATE);
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+}
+
+static int tegra_i2c_slave_send(struct i2c_slave_adapter *slv_adap,
+ const char *buf, int count)
+{
+ struct tegra_i2c_slave_bus *i2c_bus = i2c_get_slave_adapdata(slv_adap);
+ struct tegra_i2c_slave_dev *i2c_dev = i2c_bus->dev;
+ unsigned long flags;
+ unsigned long space_available;
+ int i;
+
+ spin_lock_irqsave(&i2c_dev->lock, flags);
+ if (!i2c_dev->is_slave_started) {
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ return -EPERM;
+ }
+
+ space_available = get_space_count(i2c_dev->tx_msg_tail,
+ i2c_dev->tx_msg_head, i2c_dev->tx_msg_buf_size);
+ if (space_available < count) {
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ return 0;
+ }
+
+ for (i = 0; i < count; ++i) {
+ i2c_dev->tx_msg_buff[i2c_dev->tx_msg_head++] = *buf++;
+ if (i2c_dev->tx_msg_head >= i2c_dev->tx_msg_buf_size)
+ i2c_dev->tx_msg_head = 0;
+ }
+ i2c_dev->is_tx_waiting = false;
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ return count;
+}
+
+static int tegra_i2c_slave_get_tx_status(struct i2c_slave_adapter *slv_adap,
+ int timeout_ms)
+{
+ struct tegra_i2c_slave_bus *i2c_bus = i2c_get_slave_adapdata(slv_adap);
+ struct tegra_i2c_slave_dev *i2c_dev = i2c_bus->dev;
+ unsigned long flags;
+ unsigned long data_available;
+
+ spin_lock_irqsave(&i2c_dev->lock, flags);
+ if (!i2c_dev->is_slave_started) {
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ return -EPERM;
+ }
+
+ data_available = get_data_count(i2c_dev->tx_msg_tail,
+ i2c_dev->tx_msg_head, i2c_dev->tx_msg_buf_size);
+ if (!data_available) {
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ return 0;
+ }
+
+ INIT_COMPLETION(i2c_dev->tx_msg_complete);
+ if (timeout_ms)
+ i2c_dev->is_tx_waiting = true;
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ if (timeout_ms) {
+ wait_for_completion_timeout(&i2c_dev->tx_msg_complete,
+ to_jiffies(timeout_ms));
+ spin_lock_irqsave(&i2c_dev->lock, flags);
+ i2c_dev->is_tx_waiting = false;
+ data_available = get_data_count(i2c_dev->tx_msg_tail,
+ i2c_dev->tx_msg_head,
+ i2c_dev->tx_msg_buf_size);
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ if (data_available)
+ return -ETIMEDOUT;
+ }
+ return data_available;
+}
+
+/*
+ * Timeoutms = 0, MinBytesRead = 0, read without waiting.
+ * Timeoutms = 0, MinBytesRead != 0, block till min bytes read.
+ * Timeoutms != 0, wait till timeout to read data..
+ * Timeoutms = INF, wait till all req bytes read.
+ */
+
+static int tegra_i2c_slave_recv(struct i2c_slave_adapter *slv_adap, char *buf,
+ int count, int min_count, int timeout_ms)
+{
+ struct tegra_i2c_slave_bus *i2c_bus = i2c_get_slave_adapdata(slv_adap);
+ struct tegra_i2c_slave_dev *i2c_dev = i2c_bus->dev;
+ unsigned long flags;
+ int data_available;
+ int bytes_copy;
+ int i;
+ int read_count = 0;
+ bool is_inf_wait = false;
+ int run_count = 0;
+
+ spin_lock_irqsave(&i2c_dev->lock, flags);
+ if (!i2c_dev->is_slave_started) {
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ return -EPERM;
+ }
+
+ do {
+ data_available = get_data_count(i2c_dev->rx_msg_tail,
+ i2c_dev->rx_msg_head, i2c_dev->rx_msg_buf_size);
+
+ bytes_copy = min(data_available, count);
+
+ if (!data_available) {
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ return 0;
+ }
+ for (i = 0; i < bytes_copy; ++i) {
+ *buf++ = i2c_dev->rx_msg_buff[i2c_dev->rx_msg_tail++];
+ if (i2c_dev->rx_msg_tail >= i2c_dev->rx_msg_buf_size)
+ i2c_dev->rx_msg_tail = 0;
+ read_count++;
+ }
+ if (!timeout_ms) {
+ if ((!min_count) || (read_count >= min_count))
+ break;
+ is_inf_wait = true;
+ } else {
+ if ((read_count == count) || run_count)
+ break;
+ }
+ i2c_dev->is_rx_waiting = true;
+ INIT_COMPLETION(i2c_dev->rx_msg_complete);
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ if (is_inf_wait)
+ wait_for_completion(&i2c_dev->rx_msg_complete);
+ else
+ wait_for_completion_timeout(&i2c_dev->rx_msg_complete,
+ to_jiffies(timeout_ms));
+
+ spin_lock_irqsave(&i2c_dev->lock, flags);
+ } while (1);
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ i2c_dev->is_rx_waiting = false;
+ return read_count;
+}
+
+static int tegra_i2c_slave_flush_buffer(struct i2c_slave_adapter *slv_adap,
+ int is_flush_tx_buffer, int is_flush_rx_buffer)
+{
+ struct tegra_i2c_slave_bus *i2c_bus = i2c_get_slave_adapdata(slv_adap);
+ struct tegra_i2c_slave_dev *i2c_dev = i2c_bus->dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&i2c_dev->lock, flags);
+ if (!i2c_dev->is_slave_started) {
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ return -EPERM;
+ }
+ if (is_flush_tx_buffer) {
+ i2c_dev->tx_msg_head = 0;
+ i2c_dev->tx_msg_tail = 0;
+ }
+ if (is_flush_rx_buffer) {
+ i2c_dev->rx_msg_head = 0;
+ i2c_dev->rx_msg_tail = 0;
+ }
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ return 0;
+}
+
+static int tegra_i2c_slave_get_nack_cycle(struct i2c_slave_adapter *slv_adap,
+ int is_cout_reset)
+{
+ struct tegra_i2c_slave_bus *i2c_bus = i2c_get_slave_adapdata(slv_adap);
+ struct tegra_i2c_slave_dev *i2c_dev = i2c_bus->dev;
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&i2c_dev->lock, flags);
+ if (!i2c_dev->is_slave_started) {
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ dev_dbg(i2c_dev->dev, "The slave bus is already started\n");
+ return -EPERM;
+ }
+
+ retval = i2c_dev->nack_packet_count;
+ if (is_cout_reset)
+ i2c_dev->nack_packet_count = 0;
+
+ spin_unlock_irqrestore(&i2c_dev->lock, flags);
+ return retval;
+}
+
+static const struct i2c_slave_algorithm tegra_i2c_slave_algo = {
+ .slave_start = tegra_i2c_slave_start,
+ .slave_stop = tegra_i2c_slave_stop,
+ .slave_send = tegra_i2c_slave_send,
+ .slave_get_tx_status = tegra_i2c_slave_get_tx_status,
+ .slave_recv = tegra_i2c_slave_recv,
+ .slave_flush_buffer = tegra_i2c_slave_flush_buffer,
+ .slave_get_nack_cycle = tegra_i2c_slave_get_nack_cycle,
+};
+
+static int tegra_i2c_slave_probe(struct platform_device *pdev)
+{
+ struct tegra_i2c_slave_dev *i2c_dev;
+ struct tegra_i2c_slave_bus *i2c_bus = NULL;
+ struct tegra_i2c_slave_platform_data *pdata = pdev->dev.platform_data;
+ struct resource *res;
+ struct resource *iomem;
+ struct clk *clk;
+ void *base;
+ int irq;
+ int ret = 0;
+ int rx_buffer_size;
+ int tx_buffer_size;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data?\n");
+ return -ENODEV;
+ }
+
+ if (pdata->adapter_nr < 0) {
+ dev_err(&pdev->dev, "invalid platform data?\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no mem resource?\n");
+ return -ENODEV;
+ }
+ iomem = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (!iomem) {
+ dev_err(&pdev->dev, "I2C region already claimed\n");
+ return -EBUSY;
+ }
+
+ base = ioremap(iomem->start, resource_size(iomem));
+ if (!base) {
+ dev_err(&pdev->dev, "Can't ioremap I2C region\n");
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ ret = -ENODEV;
+ goto err_iounmap;
+ }
+ irq = res->start;
+
+ clk = clk_get(&pdev->dev, NULL);
+ if (!clk) {
+ ret = -ENODEV;
+ goto err_release_region;
+ }
+
+ rx_buffer_size = (pdata->max_rx_buffer_size)?:4096;
+ tx_buffer_size = (pdata->max_tx_buffer_size)?:4096;
+ i2c_dev = kzalloc(sizeof(struct tegra_i2c_slave_dev) +
+ rx_buffer_size + tx_buffer_size, GFP_KERNEL);
+ if (!i2c_dev) {
+ ret = -ENOMEM;
+ goto err_clk_put;
+ }
+
+ i2c_dev->base = base;
+ i2c_dev->clk = clk;
+ i2c_dev->iomem = iomem;
+ i2c_dev->irq = irq;
+ i2c_dev->cont_id = pdev->id;
+ i2c_dev->dev = &pdev->dev;
+ i2c_dev->bus_clk = pdata->bus_clk_rate?: 100000;
+ i2c_dev->rx_msg_buff = (u8 *)(i2c_dev+1);
+ i2c_dev->rx_msg_buf_size = rx_buffer_size;
+ i2c_dev->rx_msg_head = 0;
+ i2c_dev->rx_msg_tail = 0;
+ i2c_dev->is_rx_waiting = 0;
+ i2c_dev->tx_msg_buff = i2c_dev->rx_msg_buff + rx_buffer_size;
+ i2c_dev->tx_msg_buf_size = tx_buffer_size;
+ i2c_dev->tx_msg_head = 0;
+ i2c_dev->tx_msg_tail = 0;
+ i2c_dev->is_tx_waiting = 0;
+
+ i2c_dev->is_slave_started = false;
+ spin_lock_init(&i2c_dev->lock);
+
+ init_completion(&i2c_dev->rx_msg_complete);
+ init_completion(&i2c_dev->tx_msg_complete);
+
+ platform_set_drvdata(pdev, i2c_dev);
+
+ ret = request_irq(i2c_dev->irq, tegra_i2c_slave_isr, IRQF_DISABLED,
+ pdev->name, i2c_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq);
+ goto err_free;
+ }
+
+ i2c_bus = &i2c_dev->bus;
+ i2c_bus->dev = i2c_dev;
+ i2c_bus->pinmux = pdata->pinmux;
+ i2c_bus->mux_len = pdata->bus_mux_len;
+ i2c_bus->bus_clk_rate = pdata->bus_clk_rate ?: 100000;
+
+ i2c_bus->slv_adap.slv_algo = &tegra_i2c_slave_algo;
+ i2c_bus->slv_adap.owner = THIS_MODULE;
+ i2c_bus->slv_adap.class = I2C_CLASS_HWMON;
+ strlcpy(i2c_bus->slv_adap.name, "Tegra I2C SLAVE adapter",
+ sizeof(i2c_bus->slv_adap.name));
+ i2c_bus->slv_adap.parent_dev = &pdev->dev;
+ i2c_bus->slv_adap.dev = NULL;
+ i2c_bus->slv_adap.nr = pdata->adapter_nr;
+ ret = i2c_add_slave_adapter(&i2c_bus->slv_adap, true);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to add I2C adapter\n");
+ goto err_free_irq;
+ }
+ i2c_set_slave_adapdata(&i2c_bus->slv_adap, i2c_bus);
+ dev_dbg(&pdev->dev, "%s() suucess\n", __func__);
+ pm_runtime_enable(i2c_dev->dev);
+ return 0;
+
+err_free_irq:
+ free_irq(i2c_dev->irq, i2c_dev);
+err_free:
+ kfree(i2c_dev);
+err_clk_put:
+ clk_put(clk);
+err_release_region:
+ release_mem_region(iomem->start, resource_size(iomem));
+err_iounmap:
+ iounmap(base);
+ dev_dbg(&pdev->dev, "%s() failed %d\n", __func__, ret);
+ return ret;
+}
+
+static int tegra_i2c_slave_remove(struct platform_device *pdev)
+{
+ struct tegra_i2c_slave_dev *i2c_dev = platform_get_drvdata(pdev);
+
+ i2c_del_slave_adapter(&i2c_dev->bus.slv_adap);
+ pm_runtime_disable(i2c_dev->dev);
+ free_irq(i2c_dev->irq, i2c_dev);
+ clk_put(i2c_dev->clk);
+ release_mem_region(i2c_dev->iomem->start,
+ resource_size(i2c_dev->iomem));
+ iounmap(i2c_dev->base);
+ kfree(i2c_dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tegra_i2c_slave_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return 0;
+}
+
+static int tegra_i2c_slave_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
+#if defined(CONFIG_PM_RUNTIME)
+static int tegra_i2c_slave_runtime_idle(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tegra_i2c_slave_dev *i2c_dev = platform_get_drvdata(pdev);
+ clk_disable_unprepare(i2c_dev->clk);
+ return 0;
+}
+static int tegra_i2c_slave_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tegra_i2c_slave_dev *i2c_dev = platform_get_drvdata(pdev);
+ clk_prepare_enable(i2c_dev->clk);
+ return 0;
+}
+static const struct dev_pm_ops tegra_i2c_slave_dev_pm_ops = {
+ .runtime_idle = tegra_i2c_slave_runtime_idle,
+ .runtime_resume = tegra_i2c_slave_runtime_resume,
+};
+#endif
+
+static struct platform_driver tegra_i2c_slave_driver = {
+ .probe = tegra_i2c_slave_probe,
+ .remove = tegra_i2c_slave_remove,
+#ifdef CONFIG_PM
+ .suspend = tegra_i2c_slave_suspend,
+ .resume = tegra_i2c_slave_resume,
+#endif
+ .driver = {
+ .name = "tegra-i2c-slave",
+ .owner = THIS_MODULE,
+#if defined(CONFIG_PM_RUNTIME)
+ .pm = &tegra_i2c_slave_dev_pm_ops,
+#endif
+ },
+};
+
+static int __init tegra_i2c_slave_init_driver(void)
+{
+ return platform_driver_register(&tegra_i2c_slave_driver);
+}
+
+static void __exit tegra_i2c_slave_exit_driver(void)
+{
+ platform_driver_unregister(&tegra_i2c_slave_driver);
+}
+subsys_initcall(tegra_i2c_slave_init_driver);
+module_exit(tegra_i2c_slave_exit_driver);
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,
},
};
diff --git a/drivers/i2c/i2c-slave.c b/drivers/i2c/i2c-slave.c
new file mode 100755
index 000000000000..280a860cd2e8
--- /dev/null
+++ b/drivers/i2c/i2c-slave.c
@@ -0,0 +1,281 @@
+/*
+ * i2c-slave.c - a device driver for the iic-slave bus interface.
+ *
+ * Copyright (c) 2009-2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-slave.h>
+struct i2c_slave_priv {
+ struct i2c_adapter master_adap;
+ struct i2c_slave_adapter *slave_adap;
+ struct i2c_algorithm master_algo;
+};
+
+/**
+ * i2c_slave_send - Sends data to master. When master issues a read cycle, the
+ * data is sent by the slave.
+ * This function copies the client data into the slave tx buffer and return to
+ * client. This is not a blocking call. Data will be sent to master later once
+ * slave got the master-ready cycle transfer.
+ * if there is no sufficient space to write the client buffer, it will return
+ * error. it will not write partial data.
+ * @client: Handle to i2c-slave client.
+ * @buf: Data that will be written to the master
+ * @count: How many bytes to write.
+ *
+ * Returns negative errno, or else the number of bytes written.
+ */
+int i2c_slave_send(struct i2c_client *client, const char *buf, int count)
+{
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_slave_priv *priv = adap->algo_data;
+
+ if (!(adap->algo->functionality(adap) & I2C_FUNC_I2C_SLAVE_SUPPORT))
+ BUG();
+
+ if (priv->slave_adap->slv_algo->slave_send)
+ return priv->slave_adap->slv_algo->slave_send(priv->slave_adap,
+ buf, count);
+ return -ENODEV;
+}
+EXPORT_SYMBOL(i2c_slave_send);
+
+/**
+ * i2c_slave_get_tx_status - Get amount of data available in tx buffer. If there
+ * is still data in tx buffer then wait for given time to transfer complete
+ * for a give timeout.
+ * @client: Handle to i2c-slave client.
+ * @timeout_ms: Time to wait for transfer to complete.
+ *
+ * Returns negative errno, or else the number of bytes remaining in tx buffer.
+ */
+int i2c_slave_get_tx_status(struct i2c_client *client, int timeout_ms)
+{
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_slave_priv *priv = adap->algo_data;
+
+ if (!(adap->algo->functionality(adap) & I2C_FUNC_I2C_SLAVE_SUPPORT))
+ BUG();
+
+ if (priv->slave_adap->slv_algo->slave_get_tx_status)
+ return priv->slave_adap->slv_algo->slave_get_tx_status(
+ priv->slave_adap, timeout_ms);
+ return -ENODEV;
+}
+EXPORT_SYMBOL(i2c_slave_get_tx_status);
+
+/**
+ * i2c_slave_recv - Receive data from master. The data receive from master is
+ * stored on slave rx buffer. When this api will be called, the data will be
+ * copied from the slave rx buffer to client buffer. If requested amount (count)
+ * of data is not available then it will wait for either min_count to be receive
+ * or timeout whatever first.
+ *
+ * if timeout_ms = 0, then wait for min_count data to be read.
+ * if timoue_ms non zero then wait for the data till timeout happen.
+ * @client: Handle to i2c-slave client.
+ * @buf: Data that will be read from the master
+ * @count: How many bytes to read.
+ * @min_count: Block till read min_count of data.
+ * @timeout_ms: Time to wait for read to be complete.
+ *
+ * Returns negative errno, or else the number of bytes read.
+ */
+int i2c_slave_recv(struct i2c_client *client, char *buf, int count,
+ int min_count, int timeout_ms)
+{
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_slave_priv *priv = adap->algo_data;
+
+ if (!(adap->algo->functionality(adap) & I2C_FUNC_I2C_SLAVE_SUPPORT))
+ BUG();
+
+ if (priv->slave_adap->slv_algo->slave_recv)
+ return priv->slave_adap->slv_algo->slave_recv(priv->slave_adap,
+ buf, count, min_count, timeout_ms);
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL(i2c_slave_recv);
+
+/**
+ * i2c_slave_start - Start the i2c slave to receive/transmit data.
+ * After this i2c controller starts responding master.
+ * The dummy-char will send to master if there is no data to send on slave tx
+ * buffer.
+ * @client: Handle to i2c-slave client.
+ * @dummy_char: Data which will be send to master if there is no data to be send
+ * in slave tx buffer.
+ *
+ * Returns negative errno, or else 0 for success.
+ */
+int i2c_slave_start(struct i2c_client *client, unsigned char dummy_char)
+{
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_slave_priv *priv = adap->algo_data;
+ int slave_add;
+ int is_10bit_addr;
+
+ if (!(adap->algo->functionality(adap) & I2C_FUNC_I2C_SLAVE_SUPPORT))
+ BUG();
+ slave_add = client->addr;
+ is_10bit_addr = (client->flags & I2C_CLIENT_TEN) ? 1 : 0;
+ if (priv->slave_adap->slv_algo->slave_start)
+ return priv->slave_adap->slv_algo->slave_start(priv->slave_adap,
+ slave_add, is_10bit_addr, dummy_char);
+ return -ENODEV;
+}
+EXPORT_SYMBOL(i2c_slave_start);
+
+/**
+ * i2c_slave_stop - Stop slave to receive/transmit data.
+ * After this i2c controller stops responding master.
+ * @client: Handle to i2c-slave client.
+ * @is_buffer_clear: Reset the tx and rx slave buffer or not.
+ */
+void i2c_slave_stop(struct i2c_client *client, int is_buffer_clear)
+{
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_slave_priv *priv = adap->algo_data;
+
+ if (!(adap->algo->functionality(adap) & I2C_FUNC_I2C_SLAVE_SUPPORT))
+ BUG();
+
+ if (priv->slave_adap->slv_algo->slave_stop)
+ return priv->slave_adap->slv_algo->slave_stop(priv->slave_adap,
+ is_buffer_clear);
+}
+EXPORT_SYMBOL(i2c_slave_stop);
+
+/**
+ * i2c_slave_flush_buffer - Flush the receive and transmit buffer.
+ * @client: Handle to i2c-slave client.
+ * @is_flush_tx_buffer: Reset the tx slave buffer or not.
+ * @is_flush_rx_buffer: Reset the rx slave buffer or not.
+ *
+ * Returns negative errno, or else 0 for success.
+ */
+int i2c_slave_flush_buffer(struct i2c_client *client,
+ int is_flush_tx_buffer, int is_flush_rx_buffer)
+{
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_slave_priv *priv = adap->algo_data;
+
+ if (!(adap->algo->functionality(adap) & I2C_FUNC_I2C_SLAVE_SUPPORT))
+ BUG();
+
+ if (priv->slave_adap->slv_algo->slave_flush_buffer)
+ return priv->slave_adap->slv_algo->slave_flush_buffer(
+ priv->slave_adap, is_flush_tx_buffer,
+ is_flush_rx_buffer);
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL(i2c_slave_flush_buffer);
+
+/**
+ * i2c_slave_get_nack_cycle - Get the number of master read cycle on which
+ * dummy char sent. This is the way to find that how much cycle slave sent the
+ * NACK packet.
+ *
+ * @client: Handle to i2c-slave client.
+ * @is_cout_reset: Reset the nack count or not.
+ *
+ * Returns negative errno, or else 0 for success.
+ */
+int i2c_slave_get_nack_cycle(struct i2c_client *client,
+ int is_cout_reset)
+{
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_slave_priv *priv = adap->algo_data;
+
+ if (!(adap->algo->functionality(adap) & I2C_FUNC_I2C_SLAVE_SUPPORT))
+ BUG();
+
+ if (priv->slave_adap->slv_algo->slave_get_nack_cycle)
+ return priv->slave_adap->slv_algo->slave_get_nack_cycle(
+ priv->slave_adap, is_cout_reset);
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL(i2c_slave_get_nack_cycle);
+
+static u32 i2c_slave_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C_SLAVE_SUPPORT;
+}
+
+int i2c_add_slave_adapter(struct i2c_slave_adapter *slv_adap, bool force_nr)
+{
+ struct i2c_slave_priv *priv;
+ int ret;
+
+ priv = kzalloc(sizeof(struct i2c_slave_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* Set up private adapter data */
+ priv->slave_adap = slv_adap;
+ slv_adap->parent_data = priv;
+
+ priv->master_algo.functionality = i2c_slave_func;
+
+ /* Now fill out new adapter structure */
+ snprintf(priv->master_adap.name, sizeof(priv->master_adap.name),
+ "i2c-%d-slave", slv_adap->nr);
+ priv->master_adap.owner = THIS_MODULE;
+ priv->master_adap.class = slv_adap->class;
+ priv->master_adap.algo = &priv->master_algo;
+ priv->master_adap.algo_data = priv;
+ priv->master_adap.dev.parent = slv_adap->parent_dev;
+
+ if (force_nr) {
+ priv->master_adap.nr = slv_adap->nr;
+ ret = i2c_add_numbered_adapter(&priv->master_adap);
+ } else {
+ ret = i2c_add_adapter(&priv->master_adap);
+ }
+ if (ret < 0) {
+ dev_err(slv_adap->parent_dev,
+ "failed to add slave-adapter (error=%d)\n", ret);
+ kfree(priv);
+ return ret;
+ }
+ slv_adap->dev = &priv->master_adap.dev;
+ dev_info(slv_adap->parent_dev, "Added slave i2c bus %d\n",
+ i2c_adapter_id(&priv->master_adap));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(i2c_add_slave_adapter);
+
+int i2c_del_slave_adapter(struct i2c_slave_adapter *slv_adap)
+{
+ struct i2c_slave_priv *priv = slv_adap->parent_data;
+ int ret;
+
+ ret = i2c_del_adapter(&priv->master_adap);
+ if (ret < 0)
+ return ret;
+ kfree(priv);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(i2c_del_slave_adapter);
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index a531d801dbe4..15bf2ae32320 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -41,6 +41,9 @@
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/delay.h>
#include <linux/i2c/pca954x.h>
@@ -62,6 +65,8 @@ struct pca954x {
struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS];
u8 last_chan; /* last register value */
+ struct regulator *vcc_reg;
+ struct regulator *i2c_reg;
};
struct chip_desc {
@@ -123,6 +128,26 @@ static int pca954x_reg_write(struct i2c_adapter *adap,
struct i2c_client *client, u8 val)
{
int ret = -ENODEV;
+ struct pca954x *data = i2c_get_clientdata(client);
+
+ /* Increase ref count for pca954x vcc */
+ if (data->vcc_reg) {
+ ret = regulator_enable(data->vcc_reg);
+ if (ret) {
+ dev_err(&client->dev, "%s: failed to enable vcc\n",
+ __func__);
+ goto vcc_regulator_failed;
+ }
+ }
+ /* Increase ref count for pca954x vcc_i2c */
+ if (data->i2c_reg) {
+ ret = regulator_enable(data->i2c_reg);
+ if (ret) {
+ dev_err(&client->dev, "%s: failed to enable vcc_i2c\n",
+ __func__);
+ goto i2c_regulator_failed;
+ }
+ }
if (adap->algo->master_xfer) {
struct i2c_msg msg;
@@ -142,6 +167,15 @@ static int pca954x_reg_write(struct i2c_adapter *adap,
val, I2C_SMBUS_BYTE, &data);
}
+ /* Decrease ref count for pca954x vcc_i2c */
+ if (data->i2c_reg)
+ regulator_disable(data->i2c_reg);
+
+i2c_regulator_failed:
+ /* Decrease ref count for pca954x vcc */
+ if (data->vcc_reg)
+ regulator_disable(data->vcc_reg);
+vcc_regulator_failed:
return ret;
}
@@ -201,15 +235,71 @@ static int pca954x_probe(struct i2c_client *client,
i2c_set_clientdata(client, data);
+ /* Get regulator pointer for pca954x vcc */
+ data->vcc_reg = regulator_get(&client->dev, "vcc");
+ if (PTR_ERR(data->vcc_reg) == -EPROBE_DEFER)
+ data->vcc_reg = NULL;
+ else if (IS_ERR(data->vcc_reg)) {
+ dev_err(&client->dev, "%s: failed to get vcc\n",
+ __func__);
+ ret = PTR_ERR(data->vcc_reg);
+ goto exit_free;
+ }
+ /* Get regulator pointer for pca954x vcc_i2c */
+ data->i2c_reg = regulator_get(&client->dev, "vcc_i2c");
+ if (PTR_ERR(data->i2c_reg) == -EPROBE_DEFER)
+ data->i2c_reg = NULL;
+ else if (IS_ERR(data->i2c_reg)) {
+ dev_err(&client->dev, "%s: failed to get vcc_i2c\n",
+ __func__);
+ ret = PTR_ERR(data->i2c_reg);
+ regulator_put(data->vcc_reg);
+ goto exit_free;
+ }
+
+ /* Increase ref count for pca954x vcc */
+ if (data->vcc_reg) {
+ pr_info("%s: enable vcc\n", __func__);
+ ret = regulator_enable(data->vcc_reg);
+ if (ret) {
+ dev_err(&client->dev, "%s: failed to enable vcc\n",
+ __func__);
+ goto exit_regulator_put;
+ }
+ }
+ /* Increase ref count for pca954x vcc_i2c */
+ if (data->i2c_reg) {
+ pr_info("%s: enable vcc_i2c\n", __func__);
+ ret = regulator_enable(data->i2c_reg);
+ if (ret) {
+ dev_err(&client->dev, "%s: failed to enable vcc_i2c\n",
+ __func__);
+ goto exit_vcc_regulator_disable;
+ }
+ }
+
+ /*
+ * Power-On Reset takes time.
+ * I2C is ready after Power-On Reset.
+ */
+ msleep(1);
+
/* Write the mux register at addr to verify
* that the mux is in fact present. This also
* initializes the mux to disconnected state.
*/
if (i2c_smbus_write_byte(client, 0) < 0) {
dev_warn(&client->dev, "probe failed\n");
- goto exit_free;
+ goto exit_regulator_disable;
}
+ /* Decrease ref count for pca954x vcc */
+ if (data->vcc_reg)
+ regulator_disable(data->vcc_reg);
+ /* Decrease ref count for pca954x vcc_i2c */
+ if (data->i2c_reg)
+ regulator_disable(data->i2c_reg);
+
data->type = id->driver_data;
data->last_chan = 0; /* force the first selection */
@@ -252,6 +342,15 @@ static int pca954x_probe(struct i2c_client *client,
virt_reg_failed:
for (num--; num >= 0; num--)
i2c_del_mux_adapter(data->virt_adaps[num]);
+exit_regulator_disable:
+ if (data->i2c_reg)
+ regulator_disable(data->i2c_reg);
+exit_vcc_regulator_disable:
+ if (data->vcc_reg)
+ regulator_disable(data->vcc_reg);
+exit_regulator_put:
+ regulator_put(data->i2c_reg);
+ regulator_put(data->vcc_reg);
exit_free:
kfree(data);
err:
@@ -270,6 +369,9 @@ static int pca954x_remove(struct i2c_client *client)
data->virt_adaps[i] = NULL;
}
+ regulator_put(data->i2c_reg);
+ regulator_put(data->vcc_reg);
+
kfree(data);
return 0;
}