diff options
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/Kconfig | 10 | ||||
-rw-r--r-- | drivers/i2c/Makefile | 2 | ||||
-rw-r--r-- | drivers/i2c/algos/Kconfig | 3 | ||||
-rw-r--r-- | drivers/i2c/algos/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/algos/i2c-algo-busclear.c | 102 | ||||
-rw-r--r-- | drivers/i2c/algos/i2c-algo-pca.c | 439 | ||||
-rw-r--r-- | drivers/i2c/busses/Kconfig | 19 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 5 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-pca-gmi.c | 344 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-s3c2410.c | 5 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-sirf.c | 6 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-slave-tegra.c | 1115 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-tegra.c | 1082 | ||||
-rwxr-xr-x | drivers/i2c/i2c-slave.c | 281 | ||||
-rw-r--r-- | drivers/i2c/muxes/i2c-mux-pca954x.c | 104 |
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; } |