Merge branch 'for-linus/2640/i2c' of git://git.fluff.org/bjdooks/linux
[pandora-kernel.git] / drivers / i2c / busses / i2c-nomadik.c
index dbd93b2..0c731ca 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/i2c.h>
@@ -23,6 +22,7 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
 
 #include <plat/i2c.h>
 
 /* maximum threshold value */
 #define MAX_I2C_FIFO_THRESHOLD 15
 
-/* per-transfer delay, required for the hardware to stabilize */
-#define I2C_DELAY              150
-
 enum i2c_status {
        I2C_NOP,
        I2C_ON_GOING,
@@ -210,7 +207,7 @@ static int flush_i2c_fifo(struct nmk_i2c_dev *dev)
        writel((I2C_CR_FTX | I2C_CR_FRX), dev->virtbase + I2C_CR);
 
        for (i = 0; i < LOOP_ATTEMPTS; i++) {
-               timeout = jiffies + msecs_to_jiffies(dev->adap.timeout);
+               timeout = jiffies + dev->adap.timeout;
 
                while (!time_after(jiffies, timeout)) {
                        if ((readl(dev->virtbase + I2C_CR) &
@@ -254,8 +251,6 @@ static int init_hw(struct nmk_i2c_dev *dev)
 {
        int stat;
 
-       clk_enable(dev->clk);
-
        stat = flush_i2c_fifo(dev);
        if (stat)
                goto exit;
@@ -270,14 +265,6 @@ static int init_hw(struct nmk_i2c_dev *dev)
        dev->cli.operation = I2C_NO_OPERATION;
 
 exit:
-       /* TODO: Why disable clocks after init hw? */
-       clk_disable(dev->clk);
-       /*
-        * TODO: What is this delay for?
-        * Must be pretty pointless since the hw block
-        * is frozen. Or?
-        */
-       udelay(I2C_DELAY);
        return stat;
 }
 
@@ -431,7 +418,7 @@ static int read_i2c(struct nmk_i2c_dev *dev)
                        dev->virtbase + I2C_IMSCR);
 
        timeout = wait_for_completion_interruptible_timeout(
-               &dev->xfer_complete, msecs_to_jiffies(dev->adap.timeout));
+               &dev->xfer_complete, dev->adap.timeout);
 
        if (timeout < 0) {
                dev_err(&dev->pdev->dev,
@@ -441,14 +428,32 @@ static int read_i2c(struct nmk_i2c_dev *dev)
        }
 
        if (timeout == 0) {
-               /* controller has timedout, re-init the h/w */
-               dev_err(&dev->pdev->dev, "controller timed out, re-init h/w\n");
-               (void) init_hw(dev);
+               /* Controller timed out */
+               dev_err(&dev->pdev->dev, "read from slave 0x%x timed out\n",
+                               dev->cli.slave_adr);
                status = -ETIMEDOUT;
        }
        return status;
 }
 
+static void fill_tx_fifo(struct nmk_i2c_dev *dev, int no_bytes)
+{
+       int count;
+
+       for (count = (no_bytes - 2);
+                       (count > 0) &&
+                       (dev->cli.count != 0);
+                       count--) {
+               /* write to the Tx FIFO */
+               writeb(*dev->cli.buffer,
+                       dev->virtbase + I2C_TFR);
+               dev->cli.buffer++;
+               dev->cli.count--;
+               dev->cli.xfer_bytes++;
+       }
+
+}
+
 /**
  * write_i2c() - Write data to I2C client.
  * @dev: private data of I2C Driver
@@ -476,8 +481,13 @@ static int write_i2c(struct nmk_i2c_dev *dev)
        init_completion(&dev->xfer_complete);
 
        /* enable interrupts by settings the masks */
-       irq_mask = (I2C_IT_TXFNE | I2C_IT_TXFOVR |
-                       I2C_IT_MAL | I2C_IT_BERR);
+       irq_mask = (I2C_IT_TXFOVR | I2C_IT_MAL | I2C_IT_BERR);
+
+       /* Fill the TX FIFO with transmit data */
+       fill_tx_fifo(dev, MAX_I2C_FIFO_THRESHOLD);
+
+       if (dev->cli.count != 0)
+               irq_mask |= I2C_IT_TXFNE;
 
        /*
         * check if we want to transfer a single or multiple bytes, if so
@@ -495,7 +505,7 @@ static int write_i2c(struct nmk_i2c_dev *dev)
                        dev->virtbase + I2C_IMSCR);
 
        timeout = wait_for_completion_interruptible_timeout(
-               &dev->xfer_complete, msecs_to_jiffies(dev->adap.timeout));
+               &dev->xfer_complete, dev->adap.timeout);
 
        if (timeout < 0) {
                dev_err(&dev->pdev->dev,
@@ -505,15 +515,60 @@ static int write_i2c(struct nmk_i2c_dev *dev)
        }
 
        if (timeout == 0) {
-               /* controller has timedout, re-init the h/w */
-               dev_err(&dev->pdev->dev, "controller timed out, re-init h/w\n");
-               (void) init_hw(dev);
+               /* Controller timed out */
+               dev_err(&dev->pdev->dev, "write to slave 0x%x timed out\n",
+                               dev->cli.slave_adr);
                status = -ETIMEDOUT;
        }
 
        return status;
 }
 
+/**
+ * nmk_i2c_xfer_one() - transmit a single I2C message
+ * @dev: device with a message encoded into it
+ * @flags: message flags
+ */
+static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags)
+{
+       int status;
+
+       if (flags & I2C_M_RD) {
+               /* read operation */
+               dev->cli.operation = I2C_READ;
+               status = read_i2c(dev);
+       } else {
+               /* write operation */
+               dev->cli.operation = I2C_WRITE;
+               status = write_i2c(dev);
+       }
+
+       if (status || (dev->result)) {
+               u32 i2c_sr;
+               u32 cause;
+
+               i2c_sr = readl(dev->virtbase + I2C_SR);
+               /*
+                * Check if the controller I2C operation status
+                * is set to ABORT(11b).
+                */
+               if (((i2c_sr >> 2) & 0x3) == 0x3) {
+                       /* get the abort cause */
+                       cause = (i2c_sr >> 4) & 0x7;
+                       dev_err(&dev->pdev->dev, "%s\n", cause
+                               >= ARRAY_SIZE(abort_causes) ?
+                               "unknown reason" :
+                               abort_causes[cause]);
+               }
+
+               (void) init_hw(dev);
+
+               status = status ? status : dev->result;
+       }
+
+       return status;
+}
+
 /**
  * nmk_i2c_xfer() - I2C transfer function used by kernel framework
  * @i2c_adap: Adapter pointer to the controller
@@ -566,63 +621,51 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
 {
        int status;
        int i;
-       u32 cause;
        struct nmk_i2c_dev *dev = i2c_get_adapdata(i2c_adap);
+       int j;
 
        dev->busy = true;
 
        if (dev->regulator)
                regulator_enable(dev->regulator);
-
-       status = init_hw(dev);
-       if (status)
-               goto out2;
+       pm_runtime_get_sync(&dev->pdev->dev);
 
        clk_enable(dev->clk);
 
-       /* setup the i2c controller */
-       setup_i2c_controller(dev);
-
-       for (i = 0; i < num_msgs; i++) {
-               if (unlikely(msgs[i].flags & I2C_M_TEN)) {
-                       dev_err(&dev->pdev->dev, "10 bit addressing"
-                                       "not supported\n");
+       status = init_hw(dev);
+       if (status)
+               goto out;
 
-                       status = -EINVAL;
-                       goto out;
-               }
-               dev->cli.slave_adr      = msgs[i].addr;
-               dev->cli.buffer         = msgs[i].buf;
-               dev->cli.count          = msgs[i].len;
-               dev->stop = (i < (num_msgs - 1)) ? 0 : 1;
-               dev->result = 0;
-
-               if (msgs[i].flags & I2C_M_RD) {
-                       /* it is a read operation */
-                       dev->cli.operation = I2C_READ;
-                       status = read_i2c(dev);
-               } else {
-                       /* write operation */
-                       dev->cli.operation = I2C_WRITE;
-                       status = write_i2c(dev);
-               }
-               if (status || (dev->result)) {
-                       /* get the abort cause */
-                       cause = (readl(dev->virtbase + I2C_SR) >> 4) & 0x7;
-                       dev_err(&dev->pdev->dev, "%s\n",
-                               cause >= ARRAY_SIZE(abort_causes)
-                               ? "unknown reason" : abort_causes[cause]);
+       /* Attempt three times to send the message queue */
+       for (j = 0; j < 3; j++) {
+               /* setup the i2c controller */
+               setup_i2c_controller(dev);
 
-                       status = status ? status : dev->result;
+               for (i = 0; i < num_msgs; i++) {
+                       if (unlikely(msgs[i].flags & I2C_M_TEN)) {
+                               dev_err(&dev->pdev->dev, "10 bit addressing"
+                                               "not supported\n");
 
-                       goto out;
+                               status = -EINVAL;
+                               goto out;
+                       }
+                       dev->cli.slave_adr      = msgs[i].addr;
+                       dev->cli.buffer         = msgs[i].buf;
+                       dev->cli.count          = msgs[i].len;
+                       dev->stop = (i < (num_msgs - 1)) ? 0 : 1;
+                       dev->result = 0;
+
+                       status = nmk_i2c_xfer_one(dev, msgs[i].flags);
+                       if (status != 0)
+                               break;
                }
-               udelay(I2C_DELAY);
+               if (status == 0)
+                       break;
        }
 
 out:
        clk_disable(dev->clk);
-out2:
+       pm_runtime_put_sync(&dev->pdev->dev);
        if (dev->regulator)
                regulator_disable(dev->regulator);
 
@@ -687,17 +730,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
                         */
                        disable_interrupts(dev, I2C_IT_TXFNE);
                } else {
-                       for (count = (MAX_I2C_FIFO_THRESHOLD - tft - 2);
-                                       (count > 0) &&
-                                       (dev->cli.count != 0);
-                                       count--) {
-                               /* write to the Tx FIFO */
-                               writeb(*dev->cli.buffer,
-                                       dev->virtbase + I2C_TFR);
-                               dev->cli.buffer++;
-                               dev->cli.count--;
-                               dev->cli.xfer_bytes++;
-                       }
+                       fill_tx_fifo(dev, (MAX_I2C_FIFO_THRESHOLD - tft));
                        /*
                         * if done, close the transfer by disabling the
                         * corresponding TXFNE interrupt
@@ -750,13 +783,8 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
                        }
                }
 
-               i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MTD);
-               i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MTDWS);
-
-               disable_interrupts(dev,
-                               (I2C_IT_TXFNE | I2C_IT_TXFE | I2C_IT_TXFF
-                                       | I2C_IT_TXFOVR | I2C_IT_RXFNF
-                                       | I2C_IT_RXFF | I2C_IT_RXFE));
+               disable_all_interrupts(dev);
+               clear_all_interrupts(dev);
 
                if (dev->cli.count) {
                        dev->result = -EIO;
@@ -828,19 +856,36 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
 
 
 #ifdef CONFIG_PM
-static int nmk_i2c_suspend(struct platform_device *pdev, pm_message_t mesg)
+static int nmk_i2c_suspend(struct device *dev)
 {
-       struct nmk_i2c_dev *dev = platform_get_drvdata(pdev);
+       struct platform_device *pdev = to_platform_device(dev);
+       struct nmk_i2c_dev *nmk_i2c = platform_get_drvdata(pdev);
 
-       if (dev->busy)
+       if (nmk_i2c->busy)
                return -EBUSY;
-       else
-               return 0;
+
+       return 0;
+}
+
+static int nmk_i2c_resume(struct device *dev)
+{
+       return 0;
 }
 #else
 #define nmk_i2c_suspend        NULL
+#define nmk_i2c_resume NULL
 #endif
 
+/*
+ * We use noirq so that we suspend late and resume before the wakeup interrupt
+ * to ensure that we do the !pm_runtime_suspended() check in resume before
+ * there has been a regular pm runtime resume (via pm_runtime_get_sync()).
+ */
+static const struct dev_pm_ops nmk_i2c_pm = {
+       .suspend_noirq  = nmk_i2c_suspend,
+       .resume_noirq   = nmk_i2c_resume,
+};
+
 static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap)
 {
        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
@@ -902,6 +947,9 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
                dev->regulator = NULL;
        }
 
+       pm_suspend_ignore_children(&pdev->dev, true);
+       pm_runtime_enable(&pdev->dev);
+
        dev->clk = clk_get(&pdev->dev, NULL);
        if (IS_ERR(dev->clk)) {
                dev_err(&pdev->dev, "could not get i2c clock\n");
@@ -914,7 +962,8 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
        adap->owner     = THIS_MODULE;
        adap->class     = I2C_CLASS_HWMON | I2C_CLASS_SPD;
        adap->algo      = &nmk_i2c_algo;
-       adap->timeout   = pdata->timeout ? pdata->timeout : 20000;
+       adap->timeout   = pdata->timeout ? msecs_to_jiffies(pdata->timeout) :
+               msecs_to_jiffies(20000);
        snprintf(adap->name, sizeof(adap->name),
                 "Nomadik I2C%d at %lx", pdev->id, (unsigned long)res->start);
 
@@ -946,6 +995,7 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
  err_no_clk:
        if (dev->regulator)
                regulator_put(dev->regulator);
+       pm_runtime_disable(&pdev->dev);
        free_irq(dev->irq, dev);
  err_irq:
        iounmap(dev->virtbase);
@@ -978,6 +1028,7 @@ static int __devexit nmk_i2c_remove(struct platform_device *pdev)
        clk_put(dev->clk);
        if (dev->regulator)
                regulator_put(dev->regulator);
+       pm_runtime_disable(&pdev->dev);
        platform_set_drvdata(pdev, NULL);
        kfree(dev);
 
@@ -988,10 +1039,10 @@ static struct platform_driver nmk_i2c_driver = {
        .driver = {
                .owner = THIS_MODULE,
                .name = DRIVER_NAME,
+               .pm = &nmk_i2c_pm,
        },
        .probe = nmk_i2c_probe,
        .remove = __devexit_p(nmk_i2c_remove),
-       .suspend = nmk_i2c_suspend,
 };
 
 static int __init nmk_i2c_init(void)