Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/i2c/busses/i2c-msm.c b/drivers/i2c/busses/i2c-msm.c
new file mode 100644
index 0000000..7bf5314
--- /dev/null
+++ b/drivers/i2c/busses/i2c-msm.c
@@ -0,0 +1,797 @@
+/* drivers/i2c/busses/i2c-msm.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2009, Code Aurora Forum. 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.
+ *
+ * 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 */
+
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <mach/board.h>
+#include <linux/mutex.h>
+#include <linux/timer.h>
+#include <linux/remote_spinlock.h>
+#include <linux/pm_qos_params.h>
+#include <mach/gpio.h>
+
+
+enum {
+ I2C_WRITE_DATA = 0x00,
+ I2C_CLK_CTL = 0x04,
+ I2C_STATUS = 0x08,
+ I2C_READ_DATA = 0x0c,
+ I2C_INTERFACE_SELECT = 0x10,
+
+ I2C_WRITE_DATA_DATA_BYTE = 0xff,
+ I2C_WRITE_DATA_ADDR_BYTE = 1U << 8,
+ I2C_WRITE_DATA_LAST_BYTE = 1U << 9,
+
+ I2C_CLK_CTL_FS_DIVIDER_VALUE = 0xff,
+ I2C_CLK_CTL_HS_DIVIDER_VALUE = 7U << 8,
+
+ I2C_STATUS_WR_BUFFER_FULL = 1U << 0,
+ I2C_STATUS_RD_BUFFER_FULL = 1U << 1,
+ I2C_STATUS_BUS_ERROR = 1U << 2,
+ I2C_STATUS_PACKET_NACKED = 1U << 3,
+ I2C_STATUS_ARB_LOST = 1U << 4,
+ I2C_STATUS_INVALID_WRITE = 1U << 5,
+ I2C_STATUS_FAILED = 3U << 6,
+ I2C_STATUS_BUS_ACTIVE = 1U << 8,
+ I2C_STATUS_BUS_MASTER = 1U << 9,
+ I2C_STATUS_ERROR_MASK = 0xfc,
+
+ I2C_INTERFACE_SELECT_INTF_SELECT = 1U << 0,
+ I2C_INTERFACE_SELECT_SCL = 1U << 8,
+ I2C_INTERFACE_SELECT_SDA = 1U << 9,
+ I2C_STATUS_RX_DATA_STATE = 3U << 11,
+ I2C_STATUS_LOW_CLK_STATE = 3U << 13,
+};
+
+struct msm_i2c_dev {
+ struct device *dev;
+ void __iomem *base; /* virtual */
+ int irq;
+ struct clk *clk;
+ struct i2c_adapter adap_pri;
+ struct i2c_adapter adap_aux;
+
+ spinlock_t lock;
+
+ struct i2c_msg *msg;
+ int rem;
+ int pos;
+ int cnt;
+ int err;
+ int flush_cnt;
+ int rd_acked;
+ int one_bit_t;
+ remote_mutex_t r_lock;
+ int suspended;
+ struct mutex mlock;
+ struct msm_i2c_platform_data *pdata;
+ struct timer_list pwr_timer;
+ int clk_state;
+ void *complete;
+
+ struct pm_qos_request_list pm_qos_req;
+};
+
+static void
+msm_i2c_pwr_mgmt(struct msm_i2c_dev *dev, unsigned int state)
+{
+ dev->clk_state = state;
+ if (state != 0)
+ clk_enable(dev->clk);
+ else
+ clk_disable(dev->clk);
+}
+
+static void
+msm_i2c_pwr_timer(unsigned long data)
+{
+ struct msm_i2c_dev *dev = (struct msm_i2c_dev *) data;
+ dev_dbg(dev->dev, "I2C_Power: Inactivity based power management\n");
+ if (dev->clk_state == 1)
+ msm_i2c_pwr_mgmt(dev, 0);
+}
+
+#ifdef DEBUG
+static void
+dump_status(uint32_t status)
+{
+ printk("STATUS (0x%.8x): ", status);
+ if (status & I2C_STATUS_BUS_MASTER)
+ printk("MST ");
+ if (status & I2C_STATUS_BUS_ACTIVE)
+ printk("ACT ");
+ if (status & I2C_STATUS_INVALID_WRITE)
+ printk("INV_WR ");
+ if (status & I2C_STATUS_ARB_LOST)
+ printk("ARB_LST ");
+ if (status & I2C_STATUS_PACKET_NACKED)
+ printk("NAK ");
+ if (status & I2C_STATUS_BUS_ERROR)
+ printk("BUS_ERR ");
+ if (status & I2C_STATUS_RD_BUFFER_FULL)
+ printk("RD_FULL ");
+ if (status & I2C_STATUS_WR_BUFFER_FULL)
+ printk("WR_FULL ");
+ if (status & I2C_STATUS_FAILED)
+ printk("FAIL 0x%x", (status & I2C_STATUS_FAILED));
+ printk("\n");
+}
+#endif
+
+static irqreturn_t
+msm_i2c_interrupt(int irq, void *devid)
+{
+ struct msm_i2c_dev *dev = devid;
+ uint32_t status = readl(dev->base + I2C_STATUS);
+ int err = 0;
+
+#ifdef DEBUG
+ dump_status(status);
+#endif
+
+ spin_lock(&dev->lock);
+ if (!dev->msg) {
+ printk(KERN_ERR "%s: IRQ but nothing to do!\n", __func__);
+ spin_unlock(&dev->lock);
+ return IRQ_HANDLED;
+ }
+
+ if (status & I2C_STATUS_ERROR_MASK) {
+ err = -EIO;
+ goto out_err;
+ }
+
+ if (dev->msg->flags & I2C_M_RD) {
+ if (status & I2C_STATUS_RD_BUFFER_FULL) {
+
+ /*
+ * Theres something in the FIFO.
+ * Are we expecting data or flush crap?
+ */
+ if (dev->cnt) { /* DATA */
+ uint8_t *data = &dev->msg->buf[dev->pos];
+
+ /* This is in spin-lock. So there will be no
+ * scheduling between reading the second-last
+ * byte and writing LAST_BYTE to the controller.
+ * So extra read-cycle-clock won't be generated
+ * Per I2C MSM HW Specs: Write LAST_BYTE befure
+ * reading 2nd last byte
+ */
+ if (dev->cnt == 2)
+ writel(I2C_WRITE_DATA_LAST_BYTE,
+ dev->base + I2C_WRITE_DATA);
+ *data = readl(dev->base + I2C_READ_DATA);
+ dev->cnt--;
+ dev->pos++;
+ if (dev->msg->len == 1)
+ dev->rd_acked = 0;
+ if (dev->cnt == 0)
+ goto out_complete;
+
+ } else {
+ /* Now that extra read-cycle-clocks aren't
+ * generated, this becomes error condition
+ */
+ dev_err(dev->dev,
+ "read did not stop, status - %x\n",
+ status);
+ err = -EIO;
+ goto out_err;
+ }
+ } else if (dev->msg->len == 1 && dev->rd_acked == 0 &&
+ ((status & I2C_STATUS_RX_DATA_STATE) ==
+ I2C_STATUS_RX_DATA_STATE))
+ writel(I2C_WRITE_DATA_LAST_BYTE,
+ dev->base + I2C_WRITE_DATA);
+ } else {
+ uint16_t data;
+
+ if (status & I2C_STATUS_WR_BUFFER_FULL) {
+ dev_err(dev->dev,
+ "Write buffer full in ISR on write?\n");
+ err = -EIO;
+ goto out_err;
+ }
+
+ if (dev->cnt) {
+ /* Ready to take a byte */
+ data = dev->msg->buf[dev->pos];
+ if (dev->cnt == 1 && dev->rem == 1)
+ data |= I2C_WRITE_DATA_LAST_BYTE;
+
+ status = readl(dev->base + I2C_STATUS);
+ /*
+ * Due to a hardware timing issue, data line setup time
+ * may be reduced to less than recommended 250 ns.
+ * This happens when next byte is written in a
+ * particular window of clock line being low and master
+ * not stretching the clock line. Due to setup time
+ * violation, some slaves may miss first-bit of data, or
+ * misinterprete data as start condition.
+ * We introduce delay of just over 1/2 clock cycle to
+ * ensure master stretches the clock line thereby
+ * avoiding setup time violation. Delay is introduced
+ * only if I2C clock FSM is LOW. The delay is not needed
+ * if I2C clock FSM is HIGH or FORCED_LOW.
+ */
+ if ((status & I2C_STATUS_LOW_CLK_STATE) ==
+ I2C_STATUS_LOW_CLK_STATE)
+ udelay((dev->one_bit_t >> 1) + 1);
+ writel(data, dev->base + I2C_WRITE_DATA);
+ dev->pos++;
+ dev->cnt--;
+ } else
+ goto out_complete;
+ }
+
+ spin_unlock(&dev->lock);
+ return IRQ_HANDLED;
+
+ out_err:
+ dev->err = err;
+ out_complete:
+ complete(dev->complete);
+ spin_unlock(&dev->lock);
+ return IRQ_HANDLED;
+}
+
+static int
+msm_i2c_poll_writeready(struct msm_i2c_dev *dev)
+{
+ uint32_t retries = 0;
+
+ while (retries != 2000) {
+ uint32_t status = readl(dev->base + I2C_STATUS);
+
+ if (!(status & I2C_STATUS_WR_BUFFER_FULL))
+ return 0;
+ if (retries++ > 1000)
+ usleep_range(100, 200);
+ }
+ return -ETIMEDOUT;
+}
+
+static int
+msm_i2c_poll_notbusy(struct msm_i2c_dev *dev)
+{
+ uint32_t retries = 0;
+
+ while (retries != 2000) {
+ uint32_t status = readl(dev->base + I2C_STATUS);
+
+ if (!(status & I2C_STATUS_BUS_ACTIVE))
+ return 0;
+ if (retries++ > 1000)
+ usleep_range(100, 200);
+ }
+ return -ETIMEDOUT;
+}
+
+static int
+msm_i2c_recover_bus_busy(struct msm_i2c_dev *dev, struct i2c_adapter *adap)
+{
+ int i;
+ int gpio_clk;
+ int gpio_dat;
+ uint32_t status = readl(dev->base + I2C_STATUS);
+ bool gpio_clk_status = false;
+
+ if (!(status & (I2C_STATUS_BUS_ACTIVE | I2C_STATUS_WR_BUFFER_FULL)))
+ return 0;
+
+ dev->pdata->msm_i2c_config_gpio(adap->nr, 0);
+ /* Even adapter is primary and Odd adapter is AUX */
+ if (adap->nr % 2) {
+ gpio_clk = dev->pdata->aux_clk;
+ gpio_dat = dev->pdata->aux_dat;
+ } else {
+ gpio_clk = dev->pdata->pri_clk;
+ gpio_dat = dev->pdata->pri_dat;
+ }
+
+ disable_irq(dev->irq);
+ if (status & I2C_STATUS_RD_BUFFER_FULL) {
+ dev_warn(dev->dev, "Read buffer full, status %x, intf %x\n",
+ status, readl(dev->base + I2C_INTERFACE_SELECT));
+ writel(I2C_WRITE_DATA_LAST_BYTE, dev->base + I2C_WRITE_DATA);
+ readl(dev->base + I2C_READ_DATA);
+ } else if (status & I2C_STATUS_BUS_MASTER) {
+ dev_warn(dev->dev, "Still the bus master, status %x, intf %x\n",
+ status, readl(dev->base + I2C_INTERFACE_SELECT));
+ writel(I2C_WRITE_DATA_LAST_BYTE | 0xff,
+ dev->base + I2C_WRITE_DATA);
+ }
+
+ for (i = 0; i < 9; i++) {
+ if (gpio_get_value(gpio_dat) && gpio_clk_status)
+ break;
+ gpio_direction_output(gpio_clk, 0);
+ udelay(5);
+ gpio_direction_output(gpio_dat, 0);
+ udelay(5);
+ gpio_direction_input(gpio_clk);
+ udelay(5);
+ if (!gpio_get_value(gpio_clk))
+ usleep_range(20, 30);
+ if (!gpio_get_value(gpio_clk))
+ msleep(10);
+ gpio_clk_status = gpio_get_value(gpio_clk);
+ gpio_direction_input(gpio_dat);
+ udelay(5);
+ }
+ dev->pdata->msm_i2c_config_gpio(adap->nr, 1);
+ udelay(10);
+
+ status = readl(dev->base + I2C_STATUS);
+ if (!(status & I2C_STATUS_BUS_ACTIVE)) {
+ dev_info(dev->dev, "Bus busy cleared after %d clock cycles, "
+ "status %x, intf %x\n",
+ i, status, readl(dev->base + I2C_INTERFACE_SELECT));
+ enable_irq(dev->irq);
+ return 0;
+ }
+
+ dev_err(dev->dev, "Bus still busy, status %x, intf %x\n",
+ status, readl(dev->base + I2C_INTERFACE_SELECT));
+ enable_irq(dev->irq);
+ return -EBUSY;
+}
+
+static int
+msm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ DECLARE_COMPLETION_ONSTACK(complete);
+ struct msm_i2c_dev *dev = i2c_get_adapdata(adap);
+ int ret;
+ int rem = num;
+ uint16_t addr;
+ long timeout;
+ unsigned long flags;
+ int check_busy = 1;
+
+ del_timer_sync(&dev->pwr_timer);
+ mutex_lock(&dev->mlock);
+ if (dev->suspended) {
+ mutex_unlock(&dev->mlock);
+ return -EIO;
+ }
+
+ if (dev->clk_state == 0) {
+ dev_dbg(dev->dev, "I2C_Power: Enable I2C clock(s)\n");
+ msm_i2c_pwr_mgmt(dev, 1);
+ }
+
+ /* Don't allow power collapse until we release remote spinlock */
+ pm_qos_update_request(&dev->pm_qos_req, dev->pdata->pm_lat);
+ if (dev->pdata->rmutex) {
+ remote_mutex_lock(&dev->r_lock);
+ /* If other processor did some transactions, we may have
+ * interrupt pending. Clear it
+ */
+ irq_get_chip(dev->irq)->irq_ack(irq_get_irq_data(dev->irq));
+ }
+
+ if (adap == &dev->adap_pri)
+ writel(0, dev->base + I2C_INTERFACE_SELECT);
+ else
+ writel(I2C_INTERFACE_SELECT_INTF_SELECT,
+ dev->base + I2C_INTERFACE_SELECT);
+ enable_irq(dev->irq);
+ while (rem) {
+ addr = msgs->addr << 1;
+ if (msgs->flags & I2C_M_RD)
+ addr |= 1;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->msg = msgs;
+ dev->rem = rem;
+ dev->pos = 0;
+ dev->err = 0;
+ dev->flush_cnt = 0;
+ dev->cnt = msgs->len;
+ dev->complete = &complete;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (check_busy) {
+ ret = msm_i2c_poll_notbusy(dev);
+ if (ret)
+ ret = msm_i2c_recover_bus_busy(dev, adap);
+ if (ret) {
+ dev_err(dev->dev,
+ "Error waiting for notbusy\n");
+ goto out_err;
+ }
+ check_busy = 0;
+ }
+
+ if (rem == 1 && msgs->len == 0)
+ addr |= I2C_WRITE_DATA_LAST_BYTE;
+
+ /* Wait for WR buffer not full */
+ ret = msm_i2c_poll_writeready(dev);
+ if (ret) {
+ ret = msm_i2c_recover_bus_busy(dev, adap);
+ if (ret) {
+ dev_err(dev->dev,
+ "Error waiting for write ready before addr\n");
+ goto out_err;
+ }
+ }
+
+ /* special case for doing 1 byte read.
+ * There should be no scheduling between I2C controller becoming
+ * ready to read and writing LAST-BYTE to I2C controller
+ * This will avoid potential of I2C controller starting to latch
+ * another extra byte.
+ */
+ if ((msgs->len == 1) && (msgs->flags & I2C_M_RD)) {
+ uint32_t retries = 0;
+ spin_lock_irqsave(&dev->lock, flags);
+
+ writel(I2C_WRITE_DATA_ADDR_BYTE | addr,
+ dev->base + I2C_WRITE_DATA);
+
+ /* Poll for I2C controller going into RX_DATA mode to
+ * ensure controller goes into receive mode.
+ * Just checking write_buffer_full may not work since
+ * there is delay between the write-buffer becoming
+ * empty and the slave sending ACK to ensure I2C
+ * controller goes in receive mode to receive data.
+ */
+ while (retries != 2000) {
+ uint32_t status = readl(dev->base + I2C_STATUS);
+
+ if ((status & I2C_STATUS_RX_DATA_STATE)
+ == I2C_STATUS_RX_DATA_STATE)
+ break;
+ retries++;
+ }
+ if (retries >= 2000) {
+ dev->rd_acked = 0;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ /* 1-byte-reads from slow devices in interrupt
+ * context
+ */
+ goto wait_for_int;
+ }
+
+ dev->rd_acked = 1;
+ writel(I2C_WRITE_DATA_LAST_BYTE,
+ dev->base + I2C_WRITE_DATA);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ } else {
+ writel(I2C_WRITE_DATA_ADDR_BYTE | addr,
+ dev->base + I2C_WRITE_DATA);
+ }
+ /* Polling and waiting for write_buffer_empty is not necessary.
+ * Even worse, if we do, it can result in invalid status and
+ * error if interrupt(s) occur while polling.
+ */
+
+ /*
+ * Now that we've setup the xfer, the ISR will transfer the data
+ * and wake us up with dev->err set if there was an error
+ */
+wait_for_int:
+
+ timeout = wait_for_completion_timeout(&complete, HZ);
+ if (!timeout) {
+ dev_err(dev->dev, "Transaction timed out\n");
+ writel(I2C_WRITE_DATA_LAST_BYTE,
+ dev->base + I2C_WRITE_DATA);
+ msleep(100);
+ /* FLUSH */
+ readl(dev->base + I2C_READ_DATA);
+ readl(dev->base + I2C_STATUS);
+ ret = -ETIMEDOUT;
+ goto out_err;
+ }
+ if (dev->err) {
+ dev_err(dev->dev,
+ "(%04x) Error during data xfer (%d)\n",
+ addr, dev->err);
+ ret = dev->err;
+ goto out_err;
+ }
+
+ if (msgs->flags & I2C_M_RD)
+ check_busy = 1;
+
+ msgs++;
+ rem--;
+ }
+
+ ret = num;
+ out_err:
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->complete = NULL;
+ dev->msg = NULL;
+ dev->rem = 0;
+ dev->pos = 0;
+ dev->err = 0;
+ dev->flush_cnt = 0;
+ dev->cnt = 0;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ disable_irq(dev->irq);
+ if (dev->pdata->rmutex)
+ remote_mutex_unlock(&dev->r_lock);
+ pm_qos_update_request(&dev->pm_qos_req,
+ PM_QOS_DEFAULT_VALUE);
+ mod_timer(&dev->pwr_timer, (jiffies + 3*HZ));
+ mutex_unlock(&dev->mlock);
+ return ret;
+}
+
+static u32
+msm_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static const struct i2c_algorithm msm_i2c_algo = {
+ .master_xfer = msm_i2c_xfer,
+ .functionality = msm_i2c_func,
+};
+
+static int
+msm_i2c_probe(struct platform_device *pdev)
+{
+ struct msm_i2c_dev *dev;
+ struct resource *mem, *irq, *ioarea;
+ int ret;
+ int fs_div;
+ int hs_div;
+ int i2c_clk;
+ int clk_ctl;
+ struct clk *clk;
+ struct msm_i2c_platform_data *pdata;
+
+ printk(KERN_INFO "msm_i2c_probe\n");
+
+ /* NOTE: driver uses the static register mapping */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no mem resource?\n");
+ return -ENODEV;
+ }
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ return -ENODEV;
+ }
+
+ ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
+ pdev->name);
+ if (!ioarea) {
+ dev_err(&pdev->dev, "I2C region already claimed\n");
+ return -EBUSY;
+ }
+ clk = clk_get(&pdev->dev, "i2c_clk");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Could not get clock\n");
+ ret = PTR_ERR(clk);
+ goto err_clk_get_failed;
+ }
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "platform data not initialized\n");
+ ret = -ENOSYS;
+ goto err_clk_get_failed;
+ }
+ if (!pdata->msm_i2c_config_gpio) {
+ dev_err(&pdev->dev, "config_gpio function not initialized\n");
+ ret = -ENOSYS;
+ goto err_clk_get_failed;
+ }
+ /* We support frequencies upto FAST Mode(400KHz) */
+ if (pdata->clk_freq <= 0 || pdata->clk_freq > 400000) {
+ dev_err(&pdev->dev, "clock frequency not supported\n");
+ ret = -EIO;
+ goto err_clk_get_failed;
+ }
+
+ dev = kzalloc(sizeof(struct msm_i2c_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err_alloc_dev_failed;
+ }
+
+ dev->dev = &pdev->dev;
+ dev->irq = irq->start;
+ dev->clk = clk;
+ dev->pdata = pdata;
+ dev->base = ioremap(mem->start, (mem->end - mem->start) + 1);
+ if (!dev->base) {
+ ret = -ENOMEM;
+ goto err_ioremap_failed;
+ }
+
+ dev->one_bit_t = USEC_PER_SEC/pdata->clk_freq;
+ spin_lock_init(&dev->lock);
+ platform_set_drvdata(pdev, dev);
+
+ clk_enable(clk);
+
+ if (pdata->rmutex) {
+ struct remote_mutex_id rmid;
+ rmid.r_spinlock_id = pdata->rsl_id;
+ rmid.delay_us = 10000000/pdata->clk_freq;
+ if (remote_mutex_init(&dev->r_lock, &rmid) != 0)
+ pdata->rmutex = 0;
+ }
+ /* I2C_HS_CLK = I2C_CLK/(3*(HS_DIVIDER_VALUE+1) */
+ /* I2C_FS_CLK = I2C_CLK/(2*(FS_DIVIDER_VALUE+3) */
+ /* FS_DIVIDER_VALUE = ((I2C_CLK / I2C_FS_CLK) / 2) - 3 */
+ i2c_clk = 19200000; /* input clock */
+ fs_div = ((i2c_clk / pdata->clk_freq) / 2) - 3;
+ hs_div = 3;
+ clk_ctl = ((hs_div & 0x7) << 8) | (fs_div & 0xff);
+ writel(clk_ctl, dev->base + I2C_CLK_CTL);
+ printk(KERN_INFO "msm_i2c_probe: clk_ctl %x, %d Hz\n",
+ clk_ctl, i2c_clk / (2 * ((clk_ctl & 0xff) + 3)));
+
+ i2c_set_adapdata(&dev->adap_pri, dev);
+ dev->adap_pri.algo = &msm_i2c_algo;
+ strlcpy(dev->adap_pri.name,
+ "MSM I2C adapter-PRI",
+ sizeof(dev->adap_pri.name));
+
+ dev->adap_pri.nr = pdev->id;
+ ret = i2c_add_numbered_adapter(&dev->adap_pri);
+ if (ret) {
+ dev_err(&pdev->dev, "Primary i2c_add_adapter failed\n");
+ goto err_i2c_add_adapter_failed;
+ }
+
+ i2c_set_adapdata(&dev->adap_aux, dev);
+ dev->adap_aux.algo = &msm_i2c_algo;
+ strlcpy(dev->adap_aux.name,
+ "MSM I2C adapter-AUX",
+ sizeof(dev->adap_aux.name));
+
+ dev->adap_aux.nr = pdev->id + 1;
+ ret = i2c_add_numbered_adapter(&dev->adap_aux);
+ if (ret) {
+ dev_err(&pdev->dev, "auxiliary i2c_add_adapter failed\n");
+ i2c_del_adapter(&dev->adap_pri);
+ goto err_i2c_add_adapter_failed;
+ }
+ ret = request_irq(dev->irq, msm_i2c_interrupt,
+ IRQF_TRIGGER_RISING, pdev->name, dev);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq failed\n");
+ goto err_request_irq_failed;
+ }
+ pm_qos_add_request(&dev->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+ disable_irq(dev->irq);
+ dev->suspended = 0;
+ mutex_init(&dev->mlock);
+ dev->clk_state = 0;
+ /* Config GPIOs for primary and secondary lines */
+ pdata->msm_i2c_config_gpio(dev->adap_pri.nr, 1);
+ pdata->msm_i2c_config_gpio(dev->adap_aux.nr, 1);
+ clk_disable(dev->clk);
+ setup_timer(&dev->pwr_timer, msm_i2c_pwr_timer, (unsigned long) dev);
+
+ return 0;
+
+err_request_irq_failed:
+ i2c_del_adapter(&dev->adap_pri);
+ i2c_del_adapter(&dev->adap_aux);
+err_i2c_add_adapter_failed:
+ clk_disable(clk);
+ iounmap(dev->base);
+err_ioremap_failed:
+ kfree(dev);
+err_alloc_dev_failed:
+ clk_put(clk);
+err_clk_get_failed:
+ release_mem_region(mem->start, (mem->end - mem->start) + 1);
+ return ret;
+}
+
+static int
+msm_i2c_remove(struct platform_device *pdev)
+{
+ struct msm_i2c_dev *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ /* Grab mutex to ensure ongoing transaction is over */
+ mutex_lock(&dev->mlock);
+ dev->suspended = 1;
+ mutex_unlock(&dev->mlock);
+ mutex_destroy(&dev->mlock);
+ del_timer_sync(&dev->pwr_timer);
+ if (dev->clk_state != 0)
+ msm_i2c_pwr_mgmt(dev, 0);
+ platform_set_drvdata(pdev, NULL);
+ pm_qos_remove_request(&dev->pm_qos_req);
+ free_irq(dev->irq, dev);
+ i2c_del_adapter(&dev->adap_pri);
+ i2c_del_adapter(&dev->adap_aux);
+ clk_put(dev->clk);
+ iounmap(dev->base);
+ kfree(dev);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (mem)
+ release_mem_region(mem->start, (mem->end - mem->start) + 1);
+ return 0;
+}
+
+static int msm_i2c_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct msm_i2c_dev *dev = platform_get_drvdata(pdev);
+ /* Wait until current transaction finishes
+ * Make sure remote lock is released before we suspend
+ */
+ if (dev) {
+ /* Grab mutex to ensure ongoing transaction is over */
+ mutex_lock(&dev->mlock);
+ dev->suspended = 1;
+ mutex_unlock(&dev->mlock);
+ del_timer_sync(&dev->pwr_timer);
+ if (dev->clk_state != 0)
+ msm_i2c_pwr_mgmt(dev, 0);
+ }
+
+ return 0;
+}
+
+static int msm_i2c_resume(struct platform_device *pdev)
+{
+ struct msm_i2c_dev *dev = platform_get_drvdata(pdev);
+ dev->suspended = 0;
+ return 0;
+}
+
+static struct platform_driver msm_i2c_driver = {
+ .probe = msm_i2c_probe,
+ .remove = msm_i2c_remove,
+ .suspend = msm_i2c_suspend,
+ .resume = msm_i2c_resume,
+ .driver = {
+ .name = "msm_i2c",
+ .owner = THIS_MODULE,
+ },
+};
+
+/* I2C may be needed to bring up other drivers */
+static int __init
+msm_i2c_init_driver(void)
+{
+ return platform_driver_register(&msm_i2c_driver);
+}
+subsys_initcall(msm_i2c_init_driver);
+
+static void __exit msm_i2c_exit_driver(void)
+{
+ platform_driver_unregister(&msm_i2c_driver);
+}
+module_exit(msm_i2c_exit_driver);
+