summaryrefslogtreecommitdiff
path: root/drivers/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/Kconfig52
-rw-r--r--drivers/i2c/busses/Makefile4
-rw-r--r--drivers/i2c/busses/i2c-ns9xxx.c558
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c9
-rw-r--r--drivers/i2c/busses/i2c-stmp378x.c337
-rw-r--r--drivers/i2c/busses/mxc_i2c.c799
-rw-r--r--drivers/i2c/busses/mxc_i2c_hs.c549
-rw-r--r--drivers/i2c/busses/mxc_i2c_hs_reg.h97
-rw-r--r--drivers/i2c/busses/mxc_i2c_reg.h40
9 files changed, 2444 insertions, 1 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 7f95905bbb9d..0d9ad8e73026 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -160,6 +160,16 @@ config I2C_NFORCE2_S4985
This driver can also be built as a module. If so, the module
will be called i2c-nforce2-s4985.
+config I2C_NS9XXX
+ tristate "Digi ns9360, ns921x"
+ depends on PROCESSOR_NS9360 || PROCESSOR_NS921X
+ help
+ If you say yes to this option, support will be included for the
+ I2C interface included in Digi ns9360 and ns921x CPUs.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-ns9xxx.
+
config I2C_SIS5595
tristate "SiS 5595"
depends on PCI
@@ -328,6 +338,10 @@ config I2C_DAVINCI
config I2C_GPIO
tristate "GPIO-based bitbanging I2C"
depends on GENERIC_GPIO
+
+config I2C_PARPORT
+ tristate "Parallel port adapter"
+ depends on PARPORT
select I2C_ALGOBIT
help
This is a very simple bitbanging I2C driver utilizing the
@@ -391,9 +405,34 @@ config I2C_MPC
This driver can also be built as a module. If so, the module
will be called i2c-mpc.
+config I2C_MXC
+ tristate "MXC I2C support"
+ depends on I2C && ARCH_MXC
+ help
+ If you say yes to this option, support will be included for Freescale
+ MXC I2C modules.
+
+ This driver can also be built as a module.
+
+config I2C_MXC_HS
+ tristate "MXC HIGH SPEED I2C support"
+ depends on I2C && ARCH_MXC
+ help
+ If you say yes to this option, support will be included for Freescale
+ MXC HIGH SPEED I2C modules.
+
+ This driver can also be built as a module.
+
+config I2C_STMP378X
+ tristate "STMP378x I2C adapter"
+ depends on MACH_STMP378X
+ help
+ TBD
+
config I2C_MV64XXX
tristate "Marvell mv64xxx I2C Controller"
depends on (MV64X60 || PLAT_ORION) && EXPERIMENTAL
+
help
If you say yes to this option, support will be included for the
built-in I2C interface on the Marvell 64xxx line of host bridges.
@@ -461,6 +500,19 @@ config I2C_S3C2410
Say Y here to include support for I2C controller in the
Samsung S3C2410 based System-on-Chip devices.
+config I2C_S3C2410_ADAPTER_NR
+ int "S3C bus number for the I2C-adapter"
+ depends on I2C_S3C2410
+ default 1
+ help
+ This is the adapter/bus number for the I2C-adapter. This number can
+ be used by pre-been declared I2C-devices that will be probed
+ first when the I2C-bus driver is loaded.
+ For declaring this kind of static I2C-devices use the function
+ i2c_register_board_info (see drivers/i2c/i2c-core.c).
+
+
+
config I2C_SH7760
tristate "Renesas SH7760 I2C Controller"
depends on CPU_SUBTYPE_SH7760
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 0c2c4b26cdf1..2e0c0302c40c 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_I2C_I801) += i2c-i801.o
obj-$(CONFIG_I2C_ISCH) += i2c-isch.o
obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o
obj-$(CONFIG_I2C_NFORCE2_S4985) += i2c-nforce2-s4985.o
+obj-$(CONFIG_I2C_NS9XXX) += i2c-ns9xxx.o
obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o
obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o
obj-$(CONFIG_I2C_SIS630) += i2c-sis630.o
@@ -67,6 +68,9 @@ obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
obj-$(CONFIG_I2C_STUB) += i2c-stub.o
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o
+obj-$(CONFIG_I2C_MXC) += mxc_i2c.o
+obj-$(CONFIG_I2C_MXC_HS) += mxc_i2c_hs.o
+obj-$(CONFIG_I2C_STMP378X) += i2c-stmp378x.o
ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/i2c/busses/i2c-ns9xxx.c b/drivers/i2c/busses/i2c-ns9xxx.c
new file mode 100644
index 000000000000..0b6d1eb3239b
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ns9xxx.c
@@ -0,0 +1,558 @@
+/*
+ * linux/drivers/i2c/busses/i2c-ns9xxx.c
+ *
+ * based on old i2c-ns9xxx.c by Digi International Inc.
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/i2c-ns9xxx.h>
+#include <linux/platform_device.h>
+
+#include <asm/gpio.h>
+#include <asm/io.h>
+
+/* registers */
+#define I2C_CMD 0x00
+#define I2C_STATUS 0x00
+#define I2C_MASTERADDR 0x04
+#define I2C_SLAVEADDR 0x08
+#define I2C_CONFIG 0x0c
+
+/* command bit fields */
+#define I2C_CMD_TXVAL (1 << 13)
+#define I2C_MASTERADDR_7BIT 0
+#define I2C_MASTERADDR_10BIT 1
+
+/* configuration masks */
+#define I2C_CONFIG_CLREFMASK 0x000001ff
+#define I2C_MASTERADDR_ADDRMASK 0x000007ff
+
+/* configuration shifts */
+#define I2C_MASTERADDR_ADDRSHIFT 1
+
+/* shifted i2c commands */
+#define I2C_CMD_NOP 0
+#define I2C_CMD_READ (4 << 8)
+#define I2C_CMD_WRITE (5 << 8)
+#define I2C_CMD_STOP (6 << 8)
+
+/* interrupt causes */
+#define I2C_IRQ_MASK (0xf << 8)
+#define I2C_IRQ_ARBITLOST (1 << 8)
+#define I2C_IRQ_NOACK (2 << 8)
+#define I2C_IRQ_TXDATA (3 << 8)
+#define I2C_IRQ_RXDATA (4 << 8)
+#define I2C_IRQ_CMDACK (5 << 8)
+
+#define I2C_NORMALSPEED 100000
+#define I2C_HIGHSPEED 400000
+
+#define DRIVER_NAME "i2c-ns9xxx"
+
+enum i2c_int_state {
+ I2C_INT_AWAITING,
+ I2C_INT_OK,
+ I2C_INT_RETRY,
+ I2C_INT_ERROR,
+ I2C_INT_ABORT
+};
+
+struct ns9xxx_i2c {
+ struct i2c_adapter adap;
+ struct resource *mem;
+ struct clk *clk;
+
+ void __iomem *ioaddr;
+
+ spinlock_t lock;
+ wait_queue_head_t wait_q;
+
+ struct plat_ns9xxx_i2c *pdata;
+
+ char *buf;
+ int irq;
+ enum i2c_int_state state;
+};
+
+static int ns9xxx_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num);
+
+static u32 ns9xxx_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR
+ | I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE
+ | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA;
+}
+
+static struct i2c_algorithm ns9xxx_i2c_algo = {
+ .master_xfer = ns9xxx_i2c_xfer,
+ .functionality = ns9xxx_i2c_func,
+};
+
+static irqreturn_t ns9xxx_i2c_irq(int irqnr, void *dev_id)
+{
+ struct ns9xxx_i2c *dev_data = (struct ns9xxx_i2c *)dev_id;
+ unsigned int status;
+
+ /* acknowledge by reading */
+ status = readl(dev_data->ioaddr + I2C_CMD);
+
+ if (dev_data->state != I2C_INT_AWAITING)
+ return IRQ_HANDLED;
+
+ switch (status & I2C_IRQ_MASK) {
+ case I2C_IRQ_RXDATA:
+ spin_lock(&dev_data->lock);
+ if (dev_data->buf)
+ *dev_data->buf = status & 0xff;
+ spin_unlock(&dev_data->lock);
+ case I2C_IRQ_CMDACK:
+ case I2C_IRQ_TXDATA:
+ dev_data->state = I2C_INT_OK;
+ break;
+ case I2C_IRQ_NOACK:
+ writel(I2C_CMD_STOP, dev_data->ioaddr + I2C_CMD);
+ dev_data->state = I2C_INT_ABORT;
+ break;
+ case I2C_IRQ_ARBITLOST:
+ dev_data->state = I2C_INT_RETRY;
+ break;
+ default:
+ dev_data->state = I2C_INT_ERROR;
+ }
+
+ wake_up_interruptible(&dev_data->wait_q);
+
+ return IRQ_HANDLED;
+}
+
+static int ns9xxx_i2c_send_cmd(struct ns9xxx_i2c *dev_data, unsigned int cmd)
+{
+ dev_data->state = I2C_INT_AWAITING;
+ do {
+ writel(cmd, dev_data->ioaddr + I2C_CMD);
+ if (!wait_event_interruptible_timeout(dev_data->wait_q,
+ dev_data->state != I2C_INT_AWAITING,
+ dev_data->adap.timeout))
+ return -ETIMEDOUT;
+ } while (dev_data->state == I2C_INT_AWAITING);
+
+ if (dev_data->state != I2C_INT_OK)
+ return -EIO;
+
+ return 0;
+}
+
+static int ns9xxx_i2c_read(struct ns9xxx_i2c *dev_data, int count)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ while (count-- > 1) {
+ spin_lock_irqsave(&dev_data->lock, flags);
+ dev_data->buf++;
+ spin_unlock_irqrestore(&dev_data->lock, flags);
+
+ ret = ns9xxx_i2c_send_cmd(dev_data, I2C_CMD_NOP);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int ns9xxx_i2c_write(struct ns9xxx_i2c *dev_data,
+ const char *buf, int count)
+{
+ int ret = 0;
+
+ while (count--) {
+ ret = ns9xxx_i2c_send_cmd(dev_data,
+ I2C_CMD_NOP | I2C_CMD_TXVAL | *buf);
+ if (ret)
+ break;
+ buf++;
+ }
+
+ return ret;
+}
+
+static int ns9xxx_i2c_bitbang(struct ns9xxx_i2c *dev_data, struct i2c_msg *msg)
+{
+ int i, nr_bits, ret;
+
+ gpio_direction_output(dev_data->pdata->gpio_sda, 1);
+ gpio_direction_output(dev_data->pdata->gpio_scl, 1);
+ mdelay(10);
+
+ /* start */
+ gpio_set_value(dev_data->pdata->gpio_sda, 0);
+ mdelay(1);
+ gpio_set_value(dev_data->pdata->gpio_scl, 0);
+ mdelay(1);
+
+ nr_bits = (msg->flags & I2C_M_TEN) ? 10 : 7;
+ for (i = 0; i < nr_bits; i++) {
+ /* set data */
+ if (msg->addr & (1 << (nr_bits - i - 1)))
+ gpio_set_value(dev_data->pdata->gpio_sda, 1);
+ else
+ gpio_set_value(dev_data->pdata->gpio_sda, 1);
+ mdelay(1);
+
+ /* toggle clock */
+ gpio_set_value(dev_data->pdata->gpio_scl, 1);
+ mdelay(1);
+ gpio_set_value(dev_data->pdata->gpio_scl, 0);
+ mdelay(1);
+ }
+
+ /* read ack */
+ gpio_direction_input(dev_data->pdata->gpio_sda);
+ gpio_set_value(dev_data->pdata->gpio_scl, 1);
+ mdelay(1);
+ ret = gpio_get_value(dev_data->pdata->gpio_sda);
+
+ /* stop */
+ gpio_direction_output(dev_data->pdata->gpio_sda, 1);
+
+ return ret ? 0 : -ENODEV;
+}
+
+static int ns9xxx_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct ns9xxx_i2c *dev_data = (struct ns9xxx_i2c *)adap->algo_data;
+ int len, i, ret = 0, retry = 10;
+ unsigned long flags = 0;
+ unsigned int cmd, reg;
+ char *buf = NULL;
+
+ dev_data->state = I2C_INT_OK;
+
+ for (i = 0; i < num; i++) {
+ if (dev_data->state == I2C_INT_RETRY) {
+ ret = ns9xxx_i2c_send_cmd(dev_data, I2C_CMD_STOP);
+ --retry;
+ if (ret || !retry)
+ return -EIO;
+ }
+
+ len = msgs[i].len;
+ buf = msgs[i].buf;
+
+ spin_lock_irqsave(&dev_data->lock, flags);
+ dev_data->buf = buf;
+ spin_unlock_irqrestore(&dev_data->lock, flags);
+
+ if (msgs[i].len == 0) {
+ /* send using use bitbang mode */
+ ret = ns9xxx_i2c_bitbang(dev_data, &msgs[i]);
+ /* reset gpios to hardware i2c */
+ dev_data->pdata->gpio_configuration_func();
+ } else {
+ if (!(msgs[i].flags & I2C_M_NOSTART)) {
+ /* set device address */
+ reg = ((msgs[i].addr & I2C_MASTERADDR_ADDRMASK)
+ << I2C_MASTERADDR_ADDRSHIFT);
+
+ if (msgs[i].flags & I2C_M_TEN)
+ reg |= I2C_MASTERADDR_10BIT;
+ else
+ reg |= I2C_MASTERADDR_7BIT;
+
+ writel(reg, dev_data->ioaddr +
+ I2C_MASTERADDR);
+
+ if (msgs[i].flags & I2C_M_RD)
+ cmd = I2C_CMD_READ;
+ else {
+ cmd = I2C_CMD_WRITE | I2C_CMD_TXVAL;
+ cmd |= *buf;
+ len--;
+ buf++;
+ }
+
+ ret = ns9xxx_i2c_send_cmd(dev_data, cmd);
+ if (ret) {
+ if (dev_data->state == I2C_INT_RETRY) {
+ i = 0;
+ continue;
+ }
+ break;
+ }
+ }
+
+ if (msgs[i].flags & I2C_M_RD)
+ ret = ns9xxx_i2c_read(dev_data, len);
+ else
+ ret = ns9xxx_i2c_write(dev_data, buf, len);
+ if (ret) {
+ if (dev_data->state == I2C_INT_RETRY) {
+ i = 0;
+ continue;
+ }
+ break;
+ }
+ }
+ }
+
+ if (ns9xxx_i2c_send_cmd(dev_data, I2C_CMD_STOP)) {
+ /* sometimes interface gets stucked
+ * try to fix this by send "start, nop, start" */
+ ns9xxx_i2c_send_cmd(dev_data, I2C_CMD_NOP);
+ ns9xxx_i2c_send_cmd(dev_data, I2C_CMD_STOP);
+ }
+
+ spin_lock_irqsave(&dev_data->lock, flags);
+ dev_data->buf = NULL;
+ spin_unlock_irqrestore(&dev_data->lock, flags);
+
+ /* return ERROR or number of transmits */
+ return ((ret < 0) ? ret : i);
+}
+
+static int ns9xxx_i2c_set_clock(struct ns9xxx_i2c *dev_data, unsigned int freq)
+{
+ u32 config;
+
+ config = readl(dev_data->ioaddr + I2C_CONFIG) & ~I2C_CONFIG_CLREFMASK;
+
+ switch (freq) {
+ case I2C_NORMALSPEED:
+ config |= (((clk_get_rate(dev_data->clk) / (4 * freq))
+ - 4 - 3) / 2) & I2C_CONFIG_CLREFMASK;
+ break;
+ case I2C_HIGHSPEED:
+ config |= (((clk_get_rate(dev_data->clk) / (4 * freq))
+ - 4 - 24) * 2 / 3) & I2C_CONFIG_CLREFMASK;
+ break;
+ default:
+ pr_warning(DRIVER_NAME ": wrong clock configuration,"
+ " i2c won't work!\n");
+ return -EINVAL;
+ }
+
+ writel(config, dev_data->ioaddr + I2C_CONFIG);
+
+ return 0;
+}
+
+static int __devinit ns9xxx_i2c_probe(struct platform_device *pdev)
+{
+ struct ns9xxx_i2c *dev_data;
+ int ret;
+
+ dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
+ if (!dev_data) {
+ dev_dbg(&pdev->dev, "%s: err_alloc_dd\n", __func__);
+ ret = -ENOMEM;
+ goto err_alloc_dd;
+ }
+ platform_set_drvdata(pdev, dev_data);
+
+ dev_data->pdata = pdev->dev.platform_data;
+ if (!dev_data->pdata) {
+ dev_dbg(&pdev->dev, "%s: err_pdata\n", __func__);
+ ret = -ENOENT;
+ goto err_pdata;
+ }
+
+ snprintf(dev_data->adap.name, ARRAY_SIZE(dev_data->adap.name),
+ DRIVER_NAME);
+ dev_data->adap.owner = THIS_MODULE;
+ dev_data->adap.algo = &ns9xxx_i2c_algo;
+ dev_data->adap.algo_data = dev_data;
+ dev_data->adap.retries = 1;
+ dev_data->adap.timeout = HZ / 10;
+ dev_data->adap.class = I2C_CLASS_HWMON;
+ dev_data->buf = NULL;
+
+ spin_lock_init(&dev_data->lock);
+ init_waitqueue_head(&dev_data->wait_q);
+
+ dev_data->irq = platform_get_irq(pdev, 0);
+ if (dev_data->irq <= 0) {
+ dev_dbg(&pdev->dev, "%s: err_irq\n", __func__);
+ ret = -ENOENT;
+ goto err_irq;
+ }
+
+ dev_data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!dev_data->mem) {
+ dev_dbg(&pdev->dev, "%s: err_mem\n", __func__);
+ ret = -ENOENT;
+ goto err_mem;
+ }
+
+ if (!request_mem_region(dev_data->mem->start,
+ dev_data->mem->end - dev_data->mem->start + 1,
+ DRIVER_NAME)) {
+ dev_dbg(&pdev->dev, "%s: err_req_mem\n", __func__);
+ ret = -EBUSY;
+ goto err_req_mem;
+ }
+
+ dev_data->ioaddr = ioremap(dev_data->mem->start,
+ dev_data->mem->end - dev_data->mem->start + 1);
+ if (dev_data->ioaddr <= 0) {
+ dev_dbg(&pdev->dev, "%s: err_map_mem\n", __func__);
+ ret = -EBUSY;
+ goto err_map_mem;
+ } else
+ dev_dbg(&pdev->dev, "mapped I2C interface to virtual address"
+ "0x%x\n", (int)dev_data->ioaddr);
+
+ if (gpio_request(dev_data->pdata->gpio_scl, DRIVER_NAME)) {
+ dev_dbg(&pdev->dev, "%s: err_gpio_scl\n", __func__);
+ ret = -EBUSY;
+ goto err_gpio_scl;
+ }
+
+ if (gpio_request(dev_data->pdata->gpio_sda, DRIVER_NAME)) {
+ dev_dbg(&pdev->dev, "%s: err_gpio_sda\n", __func__);
+ ret = -EBUSY;
+ goto err_gpio_sda;
+ }
+
+ dev_data->clk = clk_get(&pdev->dev, DRIVER_NAME);
+ if (IS_ERR(dev_data->clk)) {
+ dev_dbg(&pdev->dev, "%s: err_clk_get\n", __func__);
+ ret = PTR_ERR(dev_data->clk);
+ goto err_clk_get;
+ }
+
+ ret = clk_enable(dev_data->clk);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_clk_enable\n", __func__);
+ goto err_clk_enable;
+ }
+
+ /* configure i2c interface */
+ if (!dev_data->pdata->gpio_configuration_func) {
+ dev_dbg(&pdev->dev, "%s: err_cfg_gpio\n", __func__);
+ ret = -ENOENT;
+ goto err_cfg_gpio;
+ }
+ dev_data->pdata->gpio_configuration_func();
+
+ /* Set spike filter width to maximum value to workaround communication
+ * problems on the cc9p9360 module */
+ writel(readl(dev_data->ioaddr + I2C_CONFIG) | (0xf << 9),
+ dev_data->ioaddr + I2C_CONFIG);
+
+ if (dev_data->pdata->speed)
+ ret = ns9xxx_i2c_set_clock(dev_data, dev_data->pdata->speed);
+ else
+ ret = ns9xxx_i2c_set_clock(dev_data, I2C_NORMALSPEED);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_set_clk\n", __func__);
+ goto err_set_clk;
+ }
+
+ ret = request_irq(dev_data->irq, ns9xxx_i2c_irq, 0,
+ DRIVER_NAME, dev_data);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_req_irq\n", __func__);
+ ret = -EBUSY;
+ goto err_req_irq;
+ }
+
+ ret = i2c_add_numbered_adapter(&dev_data->adap);
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "%s: err_add_adap\n", __func__);
+ goto err_add_adap;
+ }
+
+ dev_info(&pdev->dev, "NS9XXX I2C adapter\n");
+
+ return 0;
+
+err_add_adap:
+ free_irq(dev_data->irq, dev_data);
+err_req_irq:
+err_set_clk:
+err_cfg_gpio:
+ clk_disable(dev_data->clk);
+err_clk_enable:
+ clk_put(dev_data->clk);
+err_clk_get:
+ gpio_free(dev_data->pdata->gpio_sda);
+err_gpio_sda:
+ gpio_free(dev_data->pdata->gpio_scl);
+err_gpio_scl:
+ iounmap(dev_data->ioaddr);
+err_map_mem:
+ release_mem_region(dev_data->mem->start,
+ dev_data->mem->end - dev_data->mem->start + 1);
+err_req_mem:
+err_mem:
+err_irq:
+err_pdata:
+err_alloc_dd:
+ kfree(dev_data);
+
+ return ret;
+}
+
+static int __devexit ns9xxx_i2c_remove(struct platform_device *pdev)
+{
+ struct ns9xxx_i2c *dev_data = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&dev_data->adap);
+
+ free_irq(dev_data->irq, dev_data);
+
+ clk_disable(dev_data->clk);
+ clk_put(dev_data->clk);
+
+ gpio_free(dev_data->pdata->gpio_sda);
+ gpio_free(dev_data->pdata->gpio_scl);
+
+ iounmap(dev_data->ioaddr);
+ release_mem_region(dev_data->mem->start,
+ dev_data->mem->end - dev_data->mem->start + 1);
+
+ kfree(dev_data);
+
+ return 0;
+}
+
+static struct platform_driver ns9xxx_i2c_driver = {
+ .probe = ns9xxx_i2c_probe,
+ .remove = __devexit_p(ns9xxx_i2c_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init ns9xxx_i2c_init(void)
+{
+ return platform_driver_register(&ns9xxx_i2c_driver);
+}
+
+static void __exit ns9xxx_i2c_exit(void)
+{
+ platform_driver_unregister(&ns9xxx_i2c_driver);
+}
+
+module_init(ns9xxx_i2c_init);
+module_exit(ns9xxx_i2c_exit);
+
+MODULE_AUTHOR("Matthias Ludwig");
+MODULE_DESCRIPTION("Digi NS9xxx I2C Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index b7434d24904e..622caefd6f50 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -666,7 +666,7 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
pdata = s3c24xx_i2c_get_platformdata(i2c->adap.dev.parent);
clkin /= 1000; /* clkin now in KHz */
-
+
dev_dbg(i2c->dev, "pdata %p, freq %lu %lu..%lu\n",
pdata, pdata->bus_freq, pdata->min_freq, pdata->max_freq);
@@ -689,6 +689,13 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
for (; start > end; start--) {
freq = s3c24xx_i2c_calcdivisor(clkin, start, &div1, &divs);
+ /*
+ * If the clock is higher than the requested, continue searching
+ * (Luis Galdos)
+ */
+ if (pdata->bus_freq && (freq * 1000 > pdata->bus_freq))
+ continue;
+
if (freq_acceptable(freq, start))
goto found;
}
diff --git a/drivers/i2c/busses/i2c-stmp378x.c b/drivers/i2c/busses/i2c-stmp378x.c
new file mode 100644
index 000000000000..4a01b55f5f07
--- /dev/null
+++ b/drivers/i2c/busses/i2c-stmp378x.c
@@ -0,0 +1,337 @@
+/*
+ * Freescale STMP378X I2C bus driver
+ *
+ * Author: Dmitrij Frasenyak <sed@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+/* #define DEBUG */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <mach/regs-i2c.h>
+#include <mach/regs-apbx.h>
+#include <mach/i2c.h>
+
+static void reset_i2c_module(void)
+{
+ int count;
+ count = 1000;
+ HW_I2C_CTRL0_SET(BM_I2C_CTRL0_SFTRST);
+ udelay(10); /* Reseting the module can take multiple clocks.*/
+ while (--count && (!(HW_I2C_CTRL0_RD() & BM_I2C_CTRL0_CLKGATE)))
+ udelay(1);
+
+ if (!count) {
+ printk(KERN_ERR "timeout reseting the module\n");
+ BUG();
+ }
+
+ /* take controller out of reset */
+ HW_I2C_CTRL0_CLR(BM_I2C_CTRL0_SFTRST | BM_I2C_CTRL0_CLKGATE);
+ udelay(10);
+ HW_I2C_CTRL1_SET(0x0000FF00); /* Wil catch all error (IRQ mask) */
+
+}
+
+/*
+ * Low level master read/write transaction.
+ */
+static int stmp378x_i2c_xfer_msg(struct i2c_adapter *adap,
+ struct i2c_msg *msg, int stop)
+{
+ struct stmp378x_i2c_dev *dev = i2c_get_adapdata(adap);
+ int err;
+
+ init_completion(&dev->cmd_complete);
+ dev->cmd_err = 0;
+
+ dev_dbg(dev->dev, " Start XFER ===>\n");
+ dev_dbg(dev->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
+ msg->addr, msg->len, msg->flags, stop);
+
+ if ((msg->len == 0) || (msg->len > (PAGE_SIZE - 1)))
+ return -EINVAL;
+
+ if (msg->flags & I2C_M_RD) {
+ hw_i2c_setup_read(msg->addr ,
+ msg->buf ,
+ msg->len,
+ stop ? BM_I2C_CTRL0_POST_SEND_STOP : 0);
+
+ hw_i2c_run(1); /* read */
+ } else {
+ hw_i2c_setup_write(msg->addr ,
+ msg->buf ,
+ msg->len,
+ stop ? BM_I2C_CTRL0_POST_SEND_STOP : 0);
+
+ hw_i2c_run(0); /* write */
+ }
+
+ err = wait_for_completion_interruptible_timeout(
+ &dev->cmd_complete,
+ msecs_to_jiffies(1000)
+ );
+
+ if (err < 0) {
+ dev_dbg(dev->dev, "controler is timed out\n");
+ return -ETIMEDOUT;
+ }
+ if ((!dev->cmd_err) && (msg->flags & I2C_M_RD))
+ hw_i2c_finish_read(msg->buf, msg->len);
+
+ dev_dbg(dev->dev, "<============= Done with err=%d\n", dev->cmd_err);
+
+
+ return dev->cmd_err;
+}
+
+
+static int
+stmp378x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ int i;
+ int err;
+
+ if (!msgs->len)
+ return -EINVAL;
+
+ for (i = 0; i < num; i++) {
+ err = stmp378x_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
+ if (err)
+ break;
+ }
+
+ if (err == 0)
+ err = num;
+
+ return err;
+}
+
+static u32
+stmp378x_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+/*
+ * Debug. Don't need dma_irq for the final version
+ */
+
+static irqreturn_t
+stmp378x_i2c_dma_isr(int this_irq, void *dev_id)
+{
+ hw_i2c_clear_dma_interrupt();
+ return IRQ_HANDLED;
+
+}
+
+#define I2C_IRQ_MASK 0x000000FF
+
+static irqreturn_t
+stmp378x_i2c_isr(int this_irq, void *dev_id)
+{
+ struct stmp378x_i2c_dev *dev = dev_id;
+ u32 stat;
+ u32 done_mask =
+ BM_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ |
+ BM_I2C_CTRL1_BUS_FREE_IRQ ;
+
+ stat = HW_I2C_CTRL1_RD() & I2C_IRQ_MASK;
+ if (!stat)
+ return IRQ_NONE;
+
+ if (stat & BM_I2C_CTRL1_NO_SLAVE_ACK_IRQ) {
+ dev->cmd_err = -EREMOTEIO;
+
+ /*
+ * Stop DMA
+ * Clear NAK
+ */
+ HW_I2C_CTRL1_SET(BM_I2C_CTRL1_CLR_GOT_A_NAK);
+ hw_i2c_reset_dma();
+ reset_i2c_module();
+
+ complete(&dev->cmd_complete);
+
+ goto done;
+ }
+
+/* Don't care about BM_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ */
+ if (stat & (
+ BM_I2C_CTRL1_EARLY_TERM_IRQ |
+ BM_I2C_CTRL1_MASTER_LOSS_IRQ |
+ BM_I2C_CTRL1_SLAVE_STOP_IRQ |
+ BM_I2C_CTRL1_SLAVE_IRQ
+ )) {
+ dev->cmd_err = -EIO;
+ complete(&dev->cmd_complete);
+ goto done;
+ }
+ if ((stat & done_mask) == done_mask)
+ complete(&dev->cmd_complete);
+
+
+done:
+ HW_I2C_CTRL1_CLR(stat);
+ return IRQ_HANDLED;
+}
+
+static const struct i2c_algorithm stmp378x_i2c_algo = {
+ .master_xfer = stmp378x_i2c_xfer,
+ .functionality = stmp378x_i2c_func,
+};
+
+
+static int
+stmp378x_i2c_probe(struct platform_device *pdev)
+{
+ struct stmp378x_i2c_dev *dev;
+ struct i2c_adapter *adap;
+ struct resource *irq;
+ int err = 0;
+
+ /* NOTE: driver uses the static register mapping */
+ dev = kzalloc(sizeof(struct stmp378x_i2c_dev), GFP_KERNEL);
+ if (!dev) {
+ dev_err(&pdev->dev, "no mem \n");
+ return -ENOMEM;
+ }
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); /* Error */
+ if (!irq) {
+ dev_err(&pdev->dev, "no err_irq resource\n");
+ err = -ENODEV;
+ goto nores;
+ }
+ dev->irq_err = irq->start;
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1); /* DMA */
+ if (!irq) {
+ dev_err(&pdev->dev, "no dma_irq resource\n");
+ err = -ENODEV;
+ goto nores;
+ }
+
+ dev->irq_dma = irq->start;
+ dev->dev = &pdev->dev;
+
+ err = request_irq(dev->irq_err, stmp378x_i2c_isr, 0, pdev->name, dev);
+ if (err) {
+ dev_err(&pdev->dev, "Can't get IRQ\n");
+ goto no_err_irq;
+ }
+
+ err = request_irq(dev->irq_dma,
+ stmp378x_i2c_dma_isr,
+ 0, pdev->name, dev);
+ if (err) {
+ dev_err(&pdev->dev, "Can't get IRQ\n");
+ goto no_dma_irq;
+ }
+
+ err = hw_i2c_init(&pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "HW Init failed\n");
+ goto init_failed;
+ }
+
+ HW_I2C_CTRL1_SET(0x0000FF00); /* Will catch all error (IRQ mask) */
+
+ adap = &dev->adapter;
+ i2c_set_adapdata(adap, dev);
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_HWMON;
+ strncpy(adap->name, "378x I2C adapter", sizeof(adap->name));
+ adap->algo = &stmp378x_i2c_algo;
+ adap->dev.parent = &pdev->dev;
+
+ adap->nr = pdev->id;
+ err = i2c_add_numbered_adapter(adap);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to add adapter\n");
+ goto no_i2c_adapter;
+
+ }
+
+ return 0;
+
+no_i2c_adapter:
+ hw_i2c_stop(dev->dev);
+init_failed:
+ free_irq(dev->irq_dma, dev);
+no_dma_irq:
+ free_irq(dev->irq_err, dev);
+no_err_irq:
+nores:
+ kfree(dev);
+ return err;
+}
+
+static int
+stmp378x_i2c_remove(struct platform_device *pdev)
+{
+ struct stmp378x_i2c_dev *dev = platform_get_drvdata(pdev);
+ int res;
+
+ res = i2c_del_adapter(&dev->adapter);
+ if (res)
+ return -EBUSY;
+
+ hw_i2c_stop(dev->dev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ free_irq(dev->irq_err, dev);
+ free_irq(dev->irq_dma, dev);
+
+ kfree(dev);
+ return 0;
+}
+
+static struct platform_driver stmp378x_i2c_driver = {
+ .probe = stmp378x_i2c_probe,
+ .remove = __devexit_p(stmp378x_i2c_remove),
+ .driver = {
+ .name = "i2c_stmp",
+ .owner = THIS_MODULE,
+ },
+};
+
+/* I2C may be needed to bring up other drivers */
+
+static int __init stmp378x_i2c_init_driver(void)
+{
+ return platform_driver_register(&stmp378x_i2c_driver);
+}
+subsys_initcall(stmp378x_i2c_init_driver);
+
+static void __exit stmp378x_i2c_exit_driver(void)
+{
+ platform_driver_unregister(&stmp378x_i2c_driver);
+}
+module_exit(stmp378x_i2c_exit_driver);
+
+MODULE_AUTHOR("old_chap@embeddedalley.com");
+MODULE_DESCRIPTION("IIC for Freescale STMP378x");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/mxc_i2c.c b/drivers/i2c/busses/mxc_i2c.c
new file mode 100644
index 000000000000..f051419f02bc
--- /dev/null
+++ b/drivers/i2c/busses/mxc_i2c.c
@@ -0,0 +1,799 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mxc_i2c.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC I2C buses.
+ *
+ * Based on i2c driver algorithm for PCF8584 adapters
+ *
+ * @ingroup MXCI2C
+ */
+
+/*
+ * Include Files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include "mxc_i2c_reg.h"
+
+/*!
+ * In case the MXC device has multiple I2C modules, this structure is used to
+ * store information specific to each I2C module.
+ */
+typedef struct {
+ /*!
+ * This structure is used to identify the physical i2c bus along with
+ * the access algorithms necessary to access it.
+ */
+ struct i2c_adapter adap;
+
+ /*!
+ * This waitqueue is used to wait for the data transfer to complete.
+ */
+ wait_queue_head_t wq;
+
+ /*!
+ * The base address of the I2C device.
+ */
+ unsigned long membase;
+
+ /*!
+ * The interrupt number used by the I2C device.
+ */
+ int irq;
+
+ /*!
+ * The default clock divider value to be used.
+ */
+ unsigned int clkdiv;
+
+ /*!
+ * The clock source for the device.
+ */
+ struct clk *clk;
+
+ /*!
+ * The current power state of the device
+ */
+ bool low_power;
+
+ /*!
+ * Boolean to indicate if data was transferred
+ */
+ bool transfer_done;
+
+ /*!
+ * Boolean to indicate if we received an ACK for the data transmitted
+ */
+ bool tx_success;
+} mxc_i2c_device;
+
+struct clk_div_table {
+ int reg_value;
+ int div;
+};
+
+static const struct clk_div_table i2c_clk_table[] = {
+ {0x20, 22}, {0x21, 24}, {0x22, 26}, {0x23, 28},
+ {0, 30}, {1, 32}, {0x24, 32}, {2, 36},
+ {0x25, 36}, {0x26, 40}, {3, 42}, {0x27, 44},
+ {4, 48}, {0x28, 48}, {5, 52}, {0x29, 56},
+ {6, 60}, {0x2A, 64}, {7, 72}, {0x2B, 72},
+ {8, 80}, {0x2C, 80}, {9, 88}, {0x2D, 96},
+ {0xA, 104}, {0x2E, 112}, {0xB, 128}, {0x2F, 128},
+ {0xC, 144}, {0xD, 160}, {0x30, 160}, {0xE, 192},
+ {0x31, 192}, {0x32, 224}, {0xF, 240}, {0x33, 256},
+ {0x10, 288}, {0x11, 320}, {0x34, 320}, {0x12, 384},
+ {0x35, 384}, {0x36, 448}, {0x13, 480}, {0x37, 512},
+ {0x14, 576}, {0x15, 640}, {0x38, 640}, {0x16, 768},
+ {0x39, 768}, {0x3A, 896}, {0x17, 960}, {0x3B, 1024},
+ {0x18, 1152}, {0x19, 1280}, {0x3C, 1280}, {0x1A, 1536},
+ {0x3D, 1536}, {0x3E, 1792}, {0x1B, 1920}, {0x3F, 2048},
+ {0x1C, 2304}, {0x1D, 2560}, {0x1E, 3072}, {0x1F, 3840},
+ {0, 0}
+};
+
+extern void gpio_i2c_active(int i2c_num);
+extern void gpio_i2c_inactive(int i2c_num);
+
+/*!
+ * Transmit a \b STOP signal to the slave device.
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ */
+static void mxc_i2c_stop(mxc_i2c_device * dev)
+{
+ unsigned int cr, sr;
+ int retry = 16;
+
+ cr = readw(dev->membase + MXC_I2CR);
+ cr &= ~(MXC_I2CR_MSTA | MXC_I2CR_MTX);
+ writew(cr, dev->membase + MXC_I2CR);
+
+ /* Wait till the Bus Busy bit is reset */
+ sr = readw(dev->membase + MXC_I2SR);
+ while (retry-- && ((sr & MXC_I2SR_IBB))) {
+ udelay(3);
+ sr = readw(dev->membase + MXC_I2SR);
+ }
+ if (retry <= 0)
+ dev_err(&dev->adap.dev, "Could not set I2C Bus Busy bit"
+ " to zero.\n");
+}
+
+/*!
+ * Wait for the transmission of the data byte to complete. This function waits
+ * till we get a signal from the interrupt service routine indicating completion
+ * of the address cycle or we time out.
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ * @param trans_flag transfer flag
+ *
+ *
+ * @return The function returns 0 on success or -1 if an ack was not received
+ */
+
+static int mxc_i2c_wait_for_tc(mxc_i2c_device * dev, int trans_flag)
+{
+ int retry = 16;
+
+ while (retry-- && !dev->transfer_done) {
+ wait_event_interruptible_timeout(dev->wq,
+ dev->transfer_done,
+ dev->adap.timeout);
+ }
+ dev->transfer_done = false;
+
+ if (retry <= 0) {
+ /* Unable to send data */
+ dev_err(&dev->adap.dev, "Data not transmitted\n");
+ return -1;
+ }
+
+ if (!dev->tx_success) {
+ /* An ACK was not received for transmitted byte */
+ dev_err(&dev->adap.dev, "ACK not received \n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * Transmit a \b START signal to the slave device.
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ * @param *msg pointer to a message structure that contains the slave
+ * address
+ *
+ * @return The function returns EBUSY on failure, 0 on success.
+ */
+static int mxc_i2c_start(mxc_i2c_device *dev, struct i2c_msg *msg)
+{
+ volatile unsigned int cr, sr;
+ unsigned int addr_trans;
+ int retry = 16;
+
+ /*
+ * Set the slave address and the requested transfer mode
+ * in the data register
+ */
+ addr_trans = msg->addr << 1;
+ if (msg->flags & I2C_M_RD) {
+ addr_trans |= 0x01;
+ }
+
+ /* Set the Master bit */
+ cr = readw(dev->membase + MXC_I2CR);
+ cr |= MXC_I2CR_MSTA;
+ writew(cr, dev->membase + MXC_I2CR);
+
+ /* Wait till the Bus Busy bit is set */
+ sr = readw(dev->membase + MXC_I2SR);
+ while (retry-- && (!(sr & MXC_I2SR_IBB))) {
+ udelay(3);
+ sr = readw(dev->membase + MXC_I2SR);
+ }
+ if (retry <= 0) {
+ dev_err(&dev->adap.dev, "Could not grab Bus ownership\n");
+ return -EBUSY;
+ }
+
+ /* Set the Transmit bit */
+ cr = readw(dev->membase + MXC_I2CR);
+ cr |= MXC_I2CR_MTX;
+ writew(cr, dev->membase + MXC_I2CR);
+
+ writew(addr_trans, dev->membase + MXC_I2DR);
+ return 0;
+}
+
+/*!
+ * Transmit a \b REPEAT START to the slave device
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ * @param *msg pointer to a message structure that contains the slave
+ * address
+ */
+static void mxc_i2c_repstart(mxc_i2c_device *dev, struct i2c_msg *msg)
+{
+ volatile unsigned int cr, sr;
+ unsigned int addr_trans;
+ int retry = 16;
+
+ /*
+ * Set the slave address and the requested transfer mode
+ * in the data register
+ */
+ addr_trans = msg->addr << 1;
+ if (msg->flags & I2C_M_RD) {
+ addr_trans |= 0x01;
+ }
+ cr = readw(dev->membase + MXC_I2CR);
+ cr |= MXC_I2CR_RSTA;
+ writew(cr, dev->membase + MXC_I2CR);
+ /* Wait till the Bus Busy bit is set */
+ sr = readw(dev->membase + MXC_I2SR);
+ while (retry-- && (!(sr & MXC_I2SR_IBB))) {
+ udelay(3);
+ sr = readw(dev->membase + MXC_I2SR);
+ }
+ if (retry <= 0) {
+ dev_err(&dev->adap.dev, "Could not grab Bus ownership\n");
+ return;
+ }
+ writew(addr_trans, dev->membase + MXC_I2DR);
+}
+
+/*!
+ * Read the received data. The function waits till data is available or times
+ * out. Generates a stop signal if this is the last message to be received.
+ * Sends an ack for all the bytes received except the last byte.
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ * @param *msg pointer to a message structure that contains the slave
+ * address and a pointer to the receive buffer
+ * @param last indicates that this is the last message to be received
+ * @param addr_comp flag indicates that we just finished the address cycle
+ *
+ * @return The function returns the number of bytes read or -1 on time out.
+ */
+static int mxc_i2c_readbytes(mxc_i2c_device * dev, struct i2c_msg *msg,
+ int last, int addr_comp)
+{
+ int i;
+ char *buf = msg->buf;
+ int len = msg->len;
+ volatile unsigned int cr;
+
+ cr = readw(dev->membase + MXC_I2CR);
+ /*
+ * Clear MTX to switch to receive mode.
+ */
+ cr &= ~MXC_I2CR_MTX;
+ /*
+ * Clear the TXAK bit to gen an ack when receiving only one byte.
+ */
+ if (len == 1) {
+ cr |= MXC_I2CR_TXAK;
+ } else {
+ cr &= ~MXC_I2CR_TXAK;
+ }
+ writew(cr, dev->membase + MXC_I2CR);
+ /*
+ * Dummy read only at the end of an address cycle
+ */
+ if (addr_comp > 0) {
+ readw(dev->membase + MXC_I2DR);
+ }
+
+ for (i = 0; i < len; i++) {
+ /*
+ * Wait for data transmission to complete
+ */
+ if (mxc_i2c_wait_for_tc(dev, msg->flags)) {
+ mxc_i2c_stop(dev);
+ return -1;
+ }
+ /*
+ * Do not generate an ACK for the last byte
+ */
+ if (i == (len - 2)) {
+ cr = readw(dev->membase + MXC_I2CR);
+ cr |= MXC_I2CR_TXAK;
+ writew(cr, dev->membase + MXC_I2CR);
+ } else if (i == (len - 1)) {
+ if (last) {
+ mxc_i2c_stop(dev);
+ }
+ }
+ /* Read the data */
+ *buf++ = readw(dev->membase + MXC_I2DR);
+ }
+
+ return i;
+}
+
+/*!
+ * Write the data to the data register. Generates a stop signal if this is
+ * the last message to be sent or if no ack was received for the data sent.
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ * @param *msg pointer to a message structure that contains the slave
+ * address and data to be sent
+ * @param last indicates that this is the last message to be received
+ *
+ * @return The function returns the number of bytes written or -1 on time out
+ * or if no ack was received for the data that was sent.
+ */
+static int mxc_i2c_writebytes(mxc_i2c_device * dev, struct i2c_msg *msg,
+ int last)
+{
+ int i;
+ char *buf = msg->buf;
+ int len = msg->len;
+ volatile unsigned int cr;
+
+ cr = readw(dev->membase + MXC_I2CR);
+ /* Set MTX to switch to transmit mode */
+ cr |= MXC_I2CR_MTX;
+ writew(cr, dev->membase + MXC_I2CR);
+
+ for (i = 0; i < len; i++) {
+ /*
+ * Write the data
+ */
+ writew(*buf++, dev->membase + MXC_I2DR);
+ if (mxc_i2c_wait_for_tc(dev, msg->flags)) {
+ mxc_i2c_stop(dev);
+ return -1;
+ }
+ }
+ if (last > 0) {
+ mxc_i2c_stop(dev);
+ }
+
+ return i;
+}
+
+/*!
+ * Function enables the I2C module and initializes the registers.
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ * @param trans_flag transfer flag
+ */
+static void mxc_i2c_module_en(mxc_i2c_device * dev, int trans_flag)
+{
+ clk_enable(dev->clk);
+ /* Set the frequency divider */
+ writew(dev->clkdiv, dev->membase + MXC_IFDR);
+ /* Clear the status register */
+ writew(0x0, dev->membase + MXC_I2SR);
+ /* Enable I2C and its interrupts */
+ writew(MXC_I2CR_IEN, dev->membase + MXC_I2CR);
+ writew(MXC_I2CR_IEN | MXC_I2CR_IIEN, dev->membase + MXC_I2CR);
+}
+
+/*!
+ * Disables the I2C module.
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ */
+static void mxc_i2c_module_dis(mxc_i2c_device * dev)
+{
+ writew(0x0, dev->membase + MXC_I2CR);
+ clk_disable(dev->clk);
+}
+
+/*!
+ * The function is registered in the adapter structure. It is called when an MXC
+ * driver wishes to transfer data to a device connected to the I2C device.
+ *
+ * @param adap adapter structure for the MXC i2c device
+ * @param msgs[] array of messages to be transferred to the device
+ * @param num number of messages to be transferred to the device
+ *
+ * @return The function returns the number of messages transferred,
+ * \b -EREMOTEIO on I2C failure and a 0 if the num argument is
+ * less than 0.
+ */
+static int mxc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
+ int num)
+{
+ mxc_i2c_device *dev = (mxc_i2c_device *) (i2c_get_adapdata(adap));
+ int i, ret = 0, addr_comp = 0;
+ volatile unsigned int sr;
+ int retry = 5;
+
+ if (dev->low_power) {
+ dev_err(&dev->adap.dev, "I2C Device in low power mode\n");
+ return -EREMOTEIO;
+ }
+
+ if (num < 1) {
+ return 0;
+ }
+
+ mxc_i2c_module_en(dev, msgs[0].flags);
+ sr = readw(dev->membase + MXC_I2SR);
+ /*
+ * Check bus state
+ */
+
+ while ((sr & MXC_I2SR_IBB) && retry--) {
+ udelay(5);
+ sr = readw(dev->membase + MXC_I2SR);
+ }
+
+ if ((sr & MXC_I2SR_IBB) && retry < 0) {
+ mxc_i2c_module_dis(dev);
+ dev_err(&dev->adap.dev, "Bus busy\n");
+ return -EREMOTEIO;
+ }
+
+ //gpio_i2c_active(dev->adap.id);
+ dev->transfer_done = false;
+ dev->tx_success = false;
+ for (i = 0; i < num && ret >= 0; i++) {
+ addr_comp = 0;
+ /*
+ * Send the slave address and transfer direction in the
+ * address cycle
+ */
+ if (i == 0) {
+ /*
+ * Send a start or repeat start signal
+ */
+ if (mxc_i2c_start(dev, &msgs[0]))
+ return -EREMOTEIO;
+ /* Wait for the address cycle to complete */
+ if (mxc_i2c_wait_for_tc(dev, msgs[0].flags)) {
+ mxc_i2c_stop(dev);
+ //gpio_i2c_inactive(dev->adap.id);
+ mxc_i2c_module_dis(dev);
+ return -EREMOTEIO;
+ }
+ addr_comp = 1;
+ } else {
+ /*
+ * Generate repeat start only if required i.e the address
+ * changed or the transfer direction changed
+ */
+ if ((msgs[i].addr != msgs[i - 1].addr) ||
+ ((msgs[i].flags & I2C_M_RD) !=
+ (msgs[i - 1].flags & I2C_M_RD))) {
+ mxc_i2c_repstart(dev, &msgs[i]);
+ /* Wait for the address cycle to complete */
+ if (mxc_i2c_wait_for_tc(dev, msgs[i].flags)) {
+ mxc_i2c_stop(dev);
+ //gpio_i2c_inactive(dev->adap.id);
+ mxc_i2c_module_dis(dev);
+ return -EREMOTEIO;
+ }
+ addr_comp = 1;
+ }
+ }
+
+ /* Transfer the data */
+ if (msgs[i].flags & I2C_M_RD) {
+ /* Read the data */
+ ret = mxc_i2c_readbytes(dev, &msgs[i], (i + 1 == num),
+ addr_comp);
+ if (ret < 0) {
+ dev_err(&dev->adap.dev, "mxc_i2c_readbytes:"
+ " fail.\n");
+ break;
+ }
+ } else {
+ /* Write the data */
+ ret = mxc_i2c_writebytes(dev, &msgs[i], (i + 1 == num));
+ if (ret < 0) {
+ dev_err(&dev->adap.dev, "mxc_i2c_writebytes:"
+ " fail.\n");
+ break;
+ }
+ }
+ }
+
+ //gpio_i2c_inactive(dev->adap.id);
+ mxc_i2c_module_dis(dev);
+ /*
+ * Decrease by 1 as we do not want Start message to be included in
+ * the count
+ */
+ return (i < 0 ? ret : i);
+}
+
+/*!
+ * Returns the i2c functionality supported by this driver.
+ *
+ * @param adap adapter structure for this i2c device
+ *
+ * @return Returns the functionality that is supported.
+ */
+static u32 mxc_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+/*!
+ * Stores the pointers for the i2c algorithm functions. The algorithm functions
+ * is used by the i2c bus driver to talk to the i2c bus
+ */
+static struct i2c_algorithm mxc_i2c_algorithm = {
+ .master_xfer = mxc_i2c_xfer,
+ .functionality = mxc_i2c_func
+};
+
+/*!
+ * Interrupt Service Routine. It signals to the process about the data transfer
+ * completion. Also sets a flag if bus arbitration is lost.
+ * @param irq the interrupt number
+ * @param dev_id driver private data
+ *
+ * @return The function returns \b IRQ_HANDLED.
+ */
+static irqreturn_t mxc_i2c_handler(int irq, void *dev_id)
+{
+ mxc_i2c_device *dev = dev_id;
+ volatile unsigned int sr, cr;
+
+ sr = readw(dev->membase + MXC_I2SR);
+ cr = readw(dev->membase + MXC_I2CR);
+
+ /*
+ * Clear the interrupt bit
+ */
+ writew(0x0, dev->membase + MXC_I2SR);
+
+ if (sr & MXC_I2SR_IAL) {
+ dev_err(&dev->adap.dev, "Bus Arbitration lost\n");
+ } else {
+ /* Interrupt due byte transfer completion */
+ dev->tx_success = true;
+ /* Check if RXAK is received in Transmit mode */
+ if ((cr & MXC_I2CR_MTX) && (sr & MXC_I2SR_RXAK)) {
+ dev->tx_success = false;
+ }
+ dev->transfer_done = true;
+ wake_up_interruptible(&dev->wq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * This function is called to put the I2C adapter in a low power state. Refer to the
+ * document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure used to give information on which I2C
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function returns 0 on success and -1 on failure.
+ */
+static int mxci2c_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ mxc_i2c_device *mxcdev = platform_get_drvdata(pdev);
+ volatile unsigned int sr = 0;
+
+ if (mxcdev == NULL) {
+ return -1;
+ }
+
+ /* Prevent further calls to be processed */
+ mxcdev->low_power = true;
+ /* Wait till we finish the current transfer */
+ sr = readw(mxcdev->membase + MXC_I2SR);
+ while (sr & MXC_I2SR_IBB) {
+ msleep(10);
+ sr = readw(mxcdev->membase + MXC_I2SR);
+ }
+ gpio_i2c_inactive(mxcdev->adap.id);
+
+ return 0;
+}
+
+/*!
+ * This function is called to bring the I2C adapter back from a low power state. Refer
+ * to the document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure used to give information on which I2C
+ * to resume
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+static int mxci2c_resume(struct platform_device *pdev)
+{
+ mxc_i2c_device *mxcdev = platform_get_drvdata(pdev);
+
+ if (mxcdev == NULL)
+ return -1;
+
+ mxcdev->low_power = false;
+ gpio_i2c_active(mxcdev->adap.id);
+
+ return 0;
+}
+
+/*!
+ * This function is called during the driver binding process.
+ *
+ * @param pdev the device structure used to store device specific
+ * information that is used by the suspend, resume and remove
+ * functions
+ *
+ * @return The function always returns 0.
+ */
+static int mxci2c_probe(struct platform_device *pdev)
+{
+ mxc_i2c_device *mxc_i2c;
+ struct mxc_i2c_platform_data *i2c_plat_data = pdev->dev.platform_data;
+ struct resource *res;
+ int id = pdev->id;
+ u32 clk_freq;
+ int ret = 0;
+ int i;
+
+ mxc_i2c = kzalloc(sizeof(mxc_i2c_device), GFP_KERNEL);
+ if (!mxc_i2c) {
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ ret = -ENODEV;
+ goto err1;
+ }
+ mxc_i2c->membase = IO_ADDRESS(res->start);
+
+ /*
+ * Request the I2C interrupt
+ */
+ mxc_i2c->irq = platform_get_irq(pdev, 0);
+ if (mxc_i2c->irq < 0) {
+ ret = mxc_i2c->irq;
+ goto err1;
+ }
+
+ ret = request_irq(mxc_i2c->irq, mxc_i2c_handler,
+ 0, pdev->name, mxc_i2c);
+ if (ret < 0) {
+ goto err1;
+ }
+
+ init_waitqueue_head(&mxc_i2c->wq);
+
+ mxc_i2c->low_power = false;
+
+ gpio_i2c_active(id);
+
+ mxc_i2c->clk = clk_get(&pdev->dev, "i2c_clk");
+ clk_freq = clk_get_rate(mxc_i2c->clk);
+ mxc_i2c->clkdiv = -1;
+ if (i2c_plat_data->i2c_clk) {
+ /* Calculate divider and round up any fractional part */
+ int div = (clk_freq + i2c_plat_data->i2c_clk - 1) /
+ i2c_plat_data->i2c_clk;
+ for (i = 0; i2c_clk_table[i].div != 0; i++) {
+ if (i2c_clk_table[i].div >= div) {
+ mxc_i2c->clkdiv = i2c_clk_table[i].reg_value;
+ break;
+ }
+ }
+ }
+ if (mxc_i2c->clkdiv == -1) {
+ i--;
+ mxc_i2c->clkdiv = 0x1F; /* Use max divider */
+ }
+ dev_dbg(&pdev->dev, "i2c speed is %d/%d = %d bps, reg val = 0x%02X\n",
+ clk_freq, i2c_clk_table[i].div,
+ clk_freq / i2c_clk_table[i].div, mxc_i2c->clkdiv);
+
+ /*
+ * Set the adapter information
+ */
+ strlcpy(mxc_i2c->adap.name, pdev->name, 48);
+ mxc_i2c->adap.id = mxc_i2c->adap.nr = id;
+ mxc_i2c->adap.algo = &mxc_i2c_algorithm;
+ mxc_i2c->adap.timeout = 1;
+ platform_set_drvdata(pdev, mxc_i2c);
+ i2c_set_adapdata(&mxc_i2c->adap, mxc_i2c);
+ if ((ret = i2c_add_numbered_adapter(&mxc_i2c->adap)) < 0) {
+ goto err2;
+ }
+
+ printk(KERN_INFO "MXC I2C driver\n");
+ return 0;
+
+ err2:
+ free_irq(mxc_i2c->irq, mxc_i2c);
+ gpio_i2c_inactive(id);
+ err1:
+ dev_err(&pdev->dev, "failed to probe i2c adapter\n");
+ kfree(mxc_i2c);
+ return ret;
+}
+
+/*!
+ * Dissociates the driver from the I2C device.
+ *
+ * @param pdev the device structure used to give information on which I2C
+ * to remove
+ *
+ * @return The function always returns 0.
+ */
+static int mxci2c_remove(struct platform_device *pdev)
+{
+ mxc_i2c_device *mxc_i2c = platform_get_drvdata(pdev);
+ int id = pdev->id;
+
+ free_irq(mxc_i2c->irq, mxc_i2c);
+ i2c_del_adapter(&mxc_i2c->adap);
+ gpio_i2c_inactive(id);
+ clk_put(mxc_i2c->clk);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxci2c_driver = {
+ .driver = {
+ .name = "mxc_i2c",
+ .owner = THIS_MODULE,
+ },
+ .probe = mxci2c_probe,
+ .remove = mxci2c_remove,
+ .suspend_late = mxci2c_suspend,
+ .resume_early = mxci2c_resume,
+};
+
+/*!
+ * Function requests the interrupts and registers the i2c adapter structures.
+ *
+ * @return The function returns 0 on success and a non-zero value on failure.
+ */
+static int __init mxc_i2c_init(void)
+{
+ /* Register the device driver structure. */
+ return platform_driver_register(&mxci2c_driver);
+}
+
+/*!
+ * This function is used to cleanup all resources before the driver exits.
+ */
+static void __exit mxc_i2c_exit(void)
+{
+ platform_driver_unregister(&mxci2c_driver);
+}
+
+subsys_initcall(mxc_i2c_init);
+module_exit(mxc_i2c_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC I2C driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/mxc_i2c_hs.c b/drivers/i2c/busses/mxc_i2c_hs.c
new file mode 100644
index 000000000000..b9132351359e
--- /dev/null
+++ b/drivers/i2c/busses/mxc_i2c_hs.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include "mxc_i2c_hs_reg.h"
+
+typedef struct {
+ struct device *dev;
+
+ unsigned long reg_base_virt;
+ unsigned long reg_base_phy;
+ int irq;
+ unsigned int speed;
+ struct clk *ipg_clk;
+ struct clk *serial_clk;
+ bool low_power;
+
+ struct i2c_msg *msg;
+ int index;
+} mxc_i2c_hs;
+
+struct clk_div_table {
+ int reg_value;
+ int div;
+};
+
+static const struct clk_div_table i2c_clk_table[] = {
+ {0x0, 16}, {0x1, 18}, {0x2, 20}, {0x3, 22},
+ {0x20, 24}, {0x21, 26}, {0x22, 28}, {0x23, 30},
+ {0x4, 32}, {0x5, 36}, {0x6, 40}, {0x7, 44},
+ {0x24, 48}, {0x25, 52}, {0x26, 56}, {0x27, 60},
+ {0x8, 64}, {0x9, 72}, {0xa, 80}, {0xb, 88},
+ {0x28, 96}, {0x29, 104}, {0x2a, 112}, {0x2b, 120},
+ {0xc, 128}, {0xd, 144}, {0xe, 160}, {0xf, 176},
+ {0x2c, 192}, {0x2d, 208}, {0x2e, 224}, {0x2f, 240},
+ {0x10, 256}, {0x11, 288}, {0x12, 320}, {0x13, 352},
+ {0x30, 384}, {0x31, 416}, {0x32, 448}, {0x33, 480},
+ {0x14, 512}, {0x15, 576}, {0x16, 640}, {0x17, 704},
+ {0x34, 768}, {0x35, 832}, {0x36, 896}, {0x37, 960},
+ {0x18, 1024}, {0x19, 1152}, {0x1a, 1280}, {0x1b, 1408},
+ {0x38, 1536}, {0x39, 1664}, {0x3a, 1792}, {0x3b, 1920},
+ {0x1c, 2048}, {0x1d, 2304}, {0x1e, 2560}, {0x1f, 2816},
+ {0x3c, 3072}, {0x3d, 3328}, {0x3E, 3584}, {0x3F, 3840},
+ {-1, -1}
+};
+
+static struct i2c_adapter *adap;
+
+extern void gpio_i2c_hs_inactive(void);
+extern void gpio_i2c_hs_active(void);
+
+static u16 reg_read(mxc_i2c_hs *i2c_hs, u32 reg_offset)
+{
+ return __raw_readw(i2c_hs->reg_base_virt + reg_offset);
+}
+
+static void reg_write(mxc_i2c_hs *i2c_hs, u32 reg_offset, u16 data)
+{
+ __raw_writew(data, i2c_hs->reg_base_virt + reg_offset);
+}
+
+static void reg_set_mask(mxc_i2c_hs *i2c_hs, u32 reg_offset, u16 mask)
+{
+ u16 value;
+
+ value = reg_read(i2c_hs, reg_offset);
+ value |= mask;
+ reg_write(i2c_hs, reg_offset, value);
+}
+static void reg_clear_mask(mxc_i2c_hs *i2c_hs, u32 reg_offset, u16 mask)
+{
+ u16 value;
+
+ value = reg_read(i2c_hs, reg_offset);
+ value &= ~mask;
+ reg_write(i2c_hs, reg_offset, value);
+}
+
+static void mxci2c_hs_set_div(mxc_i2c_hs *i2c_hs)
+{
+ unsigned long clk_freq;
+ int i;
+ int div = -1;;
+
+ clk_freq = clk_get_rate(i2c_hs->serial_clk);
+ if (i2c_hs->speed) {
+ div = (clk_freq + i2c_hs->speed - 1) / i2c_hs->speed;
+ for (i = 0; i2c_clk_table[i].div >= 0; i++) {
+ if (i2c_clk_table[i].div >= div) {
+ div = i2c_clk_table[i].reg_value;
+ reg_write(i2c_hs, HIFSFDR, div);
+ break;
+ }
+ }
+ }
+}
+
+static int mxci2c_hs_enable(mxc_i2c_hs *i2c_hs)
+{
+ gpio_i2c_hs_active();
+ clk_enable(i2c_hs->ipg_clk);
+ clk_enable(i2c_hs->serial_clk);
+ mxci2c_hs_set_div(i2c_hs);
+ reg_write(i2c_hs, HICR, reg_read(i2c_hs, HICR) | HICR_HIEN);
+
+ return 0;
+}
+
+static int mxci2c_hs_disable(mxc_i2c_hs *i2c_hs)
+{
+ reg_write(i2c_hs, HICR, reg_read(i2c_hs, HICR) & (~HICR_HIEN));
+ clk_disable(i2c_hs->ipg_clk);
+ clk_disable(i2c_hs->serial_clk);
+
+ return 0;
+}
+
+static int mxci2c_hs_bus_busy(mxc_i2c_hs *i2c_hs)
+{
+ u16 value;
+ int retry = 1000;
+
+ while (retry--) {
+ value = reg_read(i2c_hs, HISR);
+ if (value & HISR_HIBB) {
+ udelay(1);
+ } else {
+ break;
+ }
+ }
+
+ if (retry <= 0) {
+ dev_dbg(NULL, "%s: Bus Busy!\n", __func__);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int mxci2c_hs_start(mxc_i2c_hs *i2c_hs, int repeat_start, u16 address)
+{
+ u16 mask;
+ int ret = 0;
+
+ mxci2c_hs_bus_busy(i2c_hs);
+
+ /*7 bit address */
+ reg_clear_mask(i2c_hs, HICR, HICR_ADDR_MODE);
+
+ /*send start */
+ if (repeat_start)
+ mask = HICR_RSTA;
+ else
+ mask = HICR_MSTA;
+ reg_set_mask(i2c_hs, HICR, mask);
+
+ return ret;
+}
+
+static int mxci2c_hs_stop(mxc_i2c_hs *i2c_hs)
+{
+ reg_clear_mask(i2c_hs, HICR, HICR_MSTA);
+ reg_clear_mask(i2c_hs, HICR, HICR_HIIEN);
+
+ return 0;
+}
+
+static int mxci2c_wait_writefifo(mxc_i2c_hs *i2c_hs)
+{
+ int i, num, left;
+ int retry, ret = 0;
+
+ retry = 10000;
+ while (retry--) {
+ udelay(10);
+ if (reg_read(i2c_hs, HISR) & (HISR_TDE | HISR_TDC_ZERO)) {
+ if (i2c_hs->index < i2c_hs->msg->len) {
+ left = i2c_hs->msg->len - i2c_hs->index;
+ num =
+ (left >
+ HITFR_MAX_COUNT) ? HITFR_MAX_COUNT : left;
+ for (i = 0; i < num; i++) {
+ reg_write(i2c_hs, HITDR,
+ i2c_hs->msg->buf[i2c_hs->
+ index + i]);
+ }
+ i2c_hs->index += num;
+ } else {
+ if (reg_read(i2c_hs, HISR) & HISR_TDC_ZERO) {
+ msleep(1);
+ break;
+ }
+ }
+ }
+ }
+
+ if (retry <= 0) {
+ printk(KERN_ERR "%s:wait error\n", __func__);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int mxci2c_wait_readfifo(mxc_i2c_hs *i2c_hs)
+{
+ int i, num, left;
+ int retry, ret = 0;
+ u16 value;
+
+ retry = 10000;
+ while (retry--) {
+ udelay(10);
+ value = reg_read(i2c_hs, HISR);
+ if (value & (HISR_RDF | HISR_RDC_ZERO)) {
+ if (i2c_hs->index < i2c_hs->msg->len) {
+ left = i2c_hs->msg->len - i2c_hs->index;
+ num =
+ (left >
+ HITFR_MAX_COUNT) ? HITFR_MAX_COUNT : left;
+ for (i = 0; i < num; i++) {
+ i2c_hs->msg->buf[i2c_hs->index + i] =
+ reg_read(i2c_hs, HIRDR);
+ }
+ i2c_hs->index += num;
+ } else {
+ if (value & HISR_RDC_ZERO) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (retry <= 0) {
+ printk(KERN_ERR "%s:wait error\n", __func__);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int mxci2c_hs_read(mxc_i2c_hs *i2c_hs, int repeat_start,
+ struct i2c_msg *msg)
+{
+ int ret;
+
+ if (msg->len > HIRDCR_MAX_COUNT) {
+ printk(KERN_ERR "%s: error: msg too long, max longth 256\n",
+ __func__);
+ return -1;
+ }
+
+ ret = 0;
+ i2c_hs->msg = msg;
+ i2c_hs->index = 0;
+
+ /*set address */
+ reg_write(i2c_hs, HIMADR, HIMADR_LSB_ADR(msg->addr));
+
+ /*receive mode */
+ reg_clear_mask(i2c_hs, HICR, HICR_MTX);
+
+ reg_clear_mask(i2c_hs, HICR, HICR_HIIEN);
+
+ /*FIFO*/ reg_set_mask(i2c_hs, HIRFR, HIRFR_RFEN | HIRFR_RFWM(7));
+ reg_set_mask(i2c_hs, HIRFR, HIRFR_RFLSH);
+
+ /*TDCR*/
+ reg_write(i2c_hs, HIRDCR, HIRDCR_RDC_EN | HIRDCR_RDC(msg->len));
+
+ mxci2c_hs_start(i2c_hs, repeat_start, msg->addr);
+
+ ret = mxci2c_wait_readfifo(i2c_hs);
+
+ if (ret < 0)
+ return ret;
+ else
+ return msg->len;
+}
+
+static int mxci2c_hs_write(mxc_i2c_hs *i2c_hs, int repeat_start,
+ struct i2c_msg *msg)
+{
+ int ret, i;
+
+ if (msg->len > HITDCR_MAX_COUNT) {
+ printk(KERN_ERR "%s: error: msg too long, max longth 256\n",
+ __func__);
+ return -1;
+ }
+
+ ret = 0;
+ i2c_hs->msg = msg;
+ i2c_hs->index = 0;
+
+ /*set address */
+ reg_write(i2c_hs, HIMADR, HIMADR_LSB_ADR(msg->addr));
+
+ /*transmit mode */
+ reg_set_mask(i2c_hs, HICR, HICR_MTX);
+
+ reg_clear_mask(i2c_hs, HICR, HICR_HIIEN);
+
+ /* TDCR */
+ reg_write(i2c_hs, HITDCR, HITDCR_TDC_EN | HITDCR_TDC(msg->len));
+
+ /* FIFO */
+ reg_set_mask(i2c_hs, HITFR, HITFR_TFEN);
+ reg_set_mask(i2c_hs, HITFR, HITFR_TFLSH);
+
+ if (msg->len > HITFR_MAX_COUNT)
+ i2c_hs->index = HITFR_MAX_COUNT;
+ else {
+ i2c_hs->index = msg->len;
+ }
+
+ for (i = 0; i < i2c_hs->index; i++) {
+ reg_write(i2c_hs, HITDR, msg->buf[i]);
+ }
+
+ mxci2c_hs_start(i2c_hs, repeat_start, msg->addr);
+
+ ret = mxci2c_wait_writefifo(i2c_hs);
+
+ if (ret < 0)
+ return ret;
+ else
+ return msg->len;
+}
+
+static int mxci2c_hs_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
+ int num)
+{
+ int i;
+ int ret = -EIO;
+
+ mxc_i2c_hs *i2c_hs = (mxc_i2c_hs *) (i2c_get_adapdata(adap));
+
+ if (i2c_hs->low_power) {
+ dev_err(&adap->dev, "I2C Device in low power mode\n");
+ return -EREMOTEIO;
+ }
+
+ if (num < 1) {
+ return 0;
+ }
+
+ mxci2c_hs_enable(i2c_hs);
+
+ for (i = 0; i < num; i++) {
+ if (msgs[i].flags & I2C_M_RD) {
+ ret = mxci2c_hs_read(i2c_hs, 0, &msgs[i]);
+ if (ret < 0)
+ break;
+ } else {
+ ret = mxci2c_hs_write(i2c_hs, 0, &msgs[i]);
+ if (ret < 0)
+ break;
+ }
+ mxci2c_hs_stop(i2c_hs);
+ }
+ mxci2c_hs_stop(i2c_hs);
+
+ mxci2c_hs_disable(i2c_hs);
+
+ if (ret < 0)
+ return ret;
+
+ return i;
+}
+
+static u32 mxci2c_hs_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+/*!
+ * Stores the pointers for the i2c algorithm functions. The algorithm functions
+ * is used by the i2c bus driver to talk to the i2c bus
+ */
+static struct i2c_algorithm mxci2c_hs_algorithm = {
+ .master_xfer = mxci2c_hs_xfer,
+ .functionality = mxci2c_hs_func
+};
+
+static int mxci2c_hs_probe(struct platform_device *pdev)
+{
+ mxc_i2c_hs *i2c_hs;
+ struct mxc_i2c_platform_data *i2c_plat_data = pdev->dev.platform_data;
+ struct resource *res;
+ int id = pdev->id;
+ int ret = 0;
+
+ i2c_hs = kzalloc(sizeof(mxc_i2c_hs), GFP_KERNEL);
+ if (!i2c_hs) {
+ return -ENOMEM;
+ }
+
+ i2c_hs->dev = &pdev->dev;
+
+ i2c_hs->speed = i2c_plat_data->i2c_clk;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ ret = -ENODEV;
+ goto err1;
+ }
+ i2c_hs->reg_base_virt = IO_ADDRESS(res->start);
+ i2c_hs->reg_base_phy = res->start;
+
+ i2c_hs->ipg_clk = clk_get(&pdev->dev, "hsi2c_clk");
+ i2c_hs->serial_clk = clk_get(&pdev->dev, "hsi2c_serial_clk");
+
+ /*
+ * Request the I2C interrupt
+ */
+ i2c_hs->irq = platform_get_irq(pdev, 0);
+ if (i2c_hs->irq < 0) {
+ ret = i2c_hs->irq;
+ goto err1;
+ }
+
+ i2c_hs->low_power = false;
+
+ /*
+ * Set the adapter information
+ */
+ adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
+ if (!adap) {
+ ret = -ENODEV;
+ goto err1;
+ }
+ strlcpy(adap->name, pdev->name, 48);
+ adap->id = adap->nr = id;
+ adap->algo = &mxci2c_hs_algorithm;
+ adap->timeout = 1;
+ platform_set_drvdata(pdev, i2c_hs);
+ i2c_set_adapdata(adap, i2c_hs);
+ ret = i2c_add_numbered_adapter(adap);
+ if (ret < 0) {
+ goto err2;
+ }
+
+ printk(KERN_INFO "MXC HS I2C driver\n");
+ return 0;
+
+ err2:
+ kfree(adap);
+ err1:
+ dev_err(&pdev->dev, "failed to probe high speed i2c adapter\n");
+ kfree(i2c_hs);
+ return ret;
+}
+
+static int mxci2c_hs_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ mxc_i2c_hs *i2c_hs = platform_get_drvdata(pdev);
+
+ if (i2c_hs == NULL) {
+ return -1;
+ }
+
+ /* Prevent further calls to be processed */
+ i2c_hs->low_power = true;
+
+ gpio_i2c_hs_inactive();
+
+ return 0;
+}
+
+static int mxci2c_hs_resume(struct platform_device *pdev)
+{
+ mxc_i2c_hs *i2c_hs = platform_get_drvdata(pdev);
+
+ if (i2c_hs == NULL)
+ return -1;
+
+ i2c_hs->low_power = false;
+ gpio_i2c_hs_active();
+
+ return 0;
+}
+
+static int mxci2c_hs_remove(struct platform_device *pdev)
+{
+ mxc_i2c_hs *i2c_hs = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(adap);
+ gpio_i2c_hs_inactive();
+ platform_set_drvdata(pdev, NULL);
+ kfree(i2c_hs);
+ return 0;
+}
+
+static struct platform_driver mxci2c_hs_driver = {
+ .driver = {
+ .name = "mxc_i2c_hs",
+ .owner = THIS_MODULE,
+ },
+ .probe = mxci2c_hs_probe,
+ .remove = mxci2c_hs_remove,
+ .suspend = mxci2c_hs_suspend,
+ .resume = mxci2c_hs_resume,
+};
+
+/*!
+ * Function requests the interrupts and registers the i2c adapter structures.
+ *
+ * @return The function returns 0 on success and a non-zero value on failure.
+ */
+static int __init mxci2c_hs_init(void)
+{
+ /* Register the device driver structure. */
+ return platform_driver_register(&mxci2c_hs_driver);
+}
+
+/*!
+ * This function is used to cleanup all resources before the driver exits.
+ */
+static void __exit mxci2c_hs_exit(void)
+{
+ platform_driver_unregister(&mxci2c_hs_driver);
+}
+
+subsys_initcall(mxci2c_hs_init);
+module_exit(mxci2c_hs_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC HIGH SPEED I2C driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/mxc_i2c_hs_reg.h b/drivers/i2c/busses/mxc_i2c_hs_reg.h
new file mode 100644
index 000000000000..fe6bb9a8f1ff
--- /dev/null
+++ b/drivers/i2c/busses/mxc_i2c_hs_reg.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef __MXC_I2C_HS_REG_H__
+#define __MXC_I2C_HS_REG_H__
+
+#define HISADR 0x00
+
+#define HIMADR 0x04
+#define HIMADR_LSB_ADR(x) ((x) << 1)
+#define HIMADR_MSB_ADR(x) (((x) & 0x7) << 8)
+
+#define HICR 0x08
+#define HICR_HIEN 0x1
+#define HICR_DMA_EN_RX 0x2
+#define HICR_DMA_EN_TR 0x4
+#define HICR_RSTA 0x8
+#define HICR_TXAK 0x10
+#define HICR_MTX 0x20
+#define HICR_MSTA 0x40
+#define HICR_HIIEN 0x80
+#define HICR_ADDR_MODE 0x100
+#define HICR_MST_CODE(x) (((x)&0x7) << 9)
+#define HICR_HSM_EN 0x1000
+#define HICR_SAMC(x) (((x)&0x3) << 13)
+#define SAMC_7_10 0
+#define SMAC_7 1
+#define SMAC_10 2
+
+#define HISR 0x0c
+#define HISR_RDF 0x1
+#define HISR_TDE 0x2
+#define HISR_HIAAS 0x4
+#define HISR_HIAL 0x8
+#define HISR_BTD 0x10
+#define HISR_RDC_ZERO 0x20
+#define HISR_TDC_ZERO 0x40
+#define HISR_RXAK 0x80
+#define HISR_HIBB 0x100
+#define HISR_SRW 0x200
+#define HISR_SADDR_MODE 0x400
+#define HISR_SHS_MODE 0x800
+
+#define HIIMR 0x10
+#define HIIMR_RDF 0x1
+#define HIIMR_TDE 0x2
+#define HIIMR_AAS 0x4
+#define HIIMR_AL 0x8
+#define HIIMR_BTD 0x10
+#define HIIMR_RDC 0x20
+#define HIIMR_TDC 0x40
+#define HIIMR_RXAK 0x80
+
+#define HITDR 0x14
+
+#define HIRDR 0x18
+
+#define HIFSFDR 0x1c
+
+#define HIHSFDR 0x20
+
+#define HITFR 0x24
+#define HITFR_TFEN 0x1
+#define HITFR_TFLSH 0x2
+#define HITFR_TFWM(x) (((x) & 0x7) << 2)
+#define HITFR_TFC(x) (((x) >> 8) & 0xF)
+#define HITFR_MAX_COUNT 8
+
+#define HIRFR 0x28
+#define HIRFR_RFEN 0x1
+#define HIRFR_RFLSH 0x2
+#define HIRFR_RFWM(x) (((x) & 0x7) << 2)
+#define HIRFR_RFC(x) (((x) >> 8) & 0xF)
+#define HIRFR_MAX_COUNT 8
+
+#define HITDCR 0x2c
+#define HITDCR_TDC(x) ((x) & 0xFF)
+#define HITDCR_TDC_EN 0x100
+#define HITDCR_TDC_RSTA 0x200
+#define HITDCR_MAX_COUNT 0xFF
+
+#define HIRDCR 0x30
+#define HIRDCR_RDC(x) ((x) & 0xFF)
+#define HIRDCR_RDC_EN 0x100
+#define HIRDCR_RDC_RSTA 0x200
+#define HIRDCR_MAX_COUNT 0xFF
+
+#endif
diff --git a/drivers/i2c/busses/mxc_i2c_reg.h b/drivers/i2c/busses/mxc_i2c_reg.h
new file mode 100644
index 000000000000..4fc76c8aa811
--- /dev/null
+++ b/drivers/i2c/busses/mxc_i2c_reg.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef __MXC_I2C_REG_H__
+#define __MXC_I2C_REG_H__
+
+/* Address offsets of the I2C registers */
+#define MXC_IADR 0x00 /* Address Register */
+#define MXC_IFDR 0x04 /* Freq div register */
+#define MXC_I2CR 0x08 /* Control regsiter */
+#define MXC_I2SR 0x0C /* Status register */
+#define MXC_I2DR 0x10 /* Data I/O register */
+
+/* Bit definitions of I2CR */
+#define MXC_I2CR_IEN 0x0080
+#define MXC_I2CR_IIEN 0x0040
+#define MXC_I2CR_MSTA 0x0020
+#define MXC_I2CR_MTX 0x0010
+#define MXC_I2CR_TXAK 0x0008
+#define MXC_I2CR_RSTA 0x0004
+
+/* Bit definitions of I2SR */
+#define MXC_I2SR_ICF 0x0080
+#define MXC_I2SR_IAAS 0x0040
+#define MXC_I2SR_IBB 0x0020
+#define MXC_I2SR_IAL 0x0010
+#define MXC_I2SR_SRW 0x0004
+#define MXC_I2SR_IIF 0x0002
+#define MXC_I2SR_RXAK 0x0001
+
+#endif /* __MXC_I2C_REG_H__ */