summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses')
-rw-r--r--drivers/i2c/busses/Kconfig19
-rw-r--r--drivers/i2c/busses/Makefile5
-rw-r--r--drivers/i2c/busses/i2c-pca-gmi.c344
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c5
-rw-r--r--drivers/i2c/busses/i2c-sirf.c6
-rw-r--r--drivers/i2c/busses/i2c-slave-tegra.c1115
-rw-r--r--drivers/i2c/busses/i2c-tegra.c1082
7 files changed, 2438 insertions, 138 deletions
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,
},
};