]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/mfd/ab8500-gpadc.c
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney...
[karo-tx-linux.git] / drivers / mfd / ab8500-gpadc.c
index 3fb1f40d6389235e45b35aec393b4bf606a1979c..b1f3561b023f0aaa4939bc469e3c0a40a718d0db 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
 #include <linux/delay.h>
+#include <linux/pm_runtime.h>
 #include <linux/platform_device.h>
 #include <linux/completion.h>
 #include <linux/regulator/consumer.h>
 /* This is used to not lose precision when dividing to get gain and offset */
 #define CALIB_SCALE                    1000
 
+/* Time in ms before disabling regulator */
+#define GPADC_AUDOSUSPEND_DELAY                1
+
+#define CONVERSION_TIME                        500 /* ms */
+
 enum cal_channels {
        ADC_INPUT_VMAIN = 0,
        ADC_INPUT_BTEMP,
@@ -102,10 +108,10 @@ struct adc_cal_data {
 
 /**
  * struct ab8500_gpadc - AB8500 GPADC device information
- * @chip_id                    ABB chip id
  * @dev:                       pointer to the struct device
  * @node:                      a list of AB8500 GPADCs, hence prepared for
                                reentrance
+ * @parent:                    pointer to the struct ab8500
  * @ab8500_gpadc_complete:     pointer to the struct completion, to indicate
  *                             the completion of gpadc conversion
  * @ab8500_gpadc_lock:         structure of type mutex
@@ -114,9 +120,9 @@ struct adc_cal_data {
  * @cal_data                   array of ADC calibration data structs
  */
 struct ab8500_gpadc {
-       u8 chip_id;
        struct device *dev;
        struct list_head node;
+       struct ab8500 *parent;
        struct completion ab8500_gpadc_complete;
        struct mutex ab8500_gpadc_lock;
        struct regulator *regu;
@@ -282,8 +288,9 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
                return -ENODEV;
 
        mutex_lock(&gpadc->ab8500_gpadc_lock);
+
        /* Enable VTVout LDO this is required for GPADC */
-       regulator_enable(gpadc->regu);
+       pm_runtime_get_sync(gpadc->dev);
 
        /* Check if ADC is not busy, lock and proceed */
        do {
@@ -332,7 +339,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
                        EN_BUF | EN_ICHAR);
                break;
        case BTEMP_BALL:
-               if (gpadc->chip_id >= AB8500_CUT3P0) {
+               if (!is_ab8500_2p0_or_earlier(gpadc->parent)) {
                        /* Turn on btemp pull-up on ABB 3.0 */
                        ret = abx500_mask_and_set_register_interruptible(
                                gpadc->dev,
@@ -344,7 +351,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
                  * Delay might be needed for ABB8500 cut 3.0, if not, remove
                  * when hardware will be available
                  */
-                       msleep(1);
+                       usleep_range(1000, 1000);
                        break;
                }
                /* Intentional fallthrough */
@@ -367,7 +374,8 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
                goto out;
        }
        /* wait for completion of conversion */
-       if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, 2*HZ)) {
+       if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete,
+                                        msecs_to_jiffies(CONVERSION_TIME))) {
                dev_err(gpadc->dev,
                        "timeout: didn't receive GPADC conversion interrupt\n");
                ret = -EINVAL;
@@ -397,8 +405,10 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
                dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n");
                goto out;
        }
-       /* Disable VTVout LDO this is required for GPADC */
-       regulator_disable(gpadc->regu);
+
+       pm_runtime_mark_last_busy(gpadc->dev);
+       pm_runtime_put_autosuspend(gpadc->dev);
+
        mutex_unlock(&gpadc->ab8500_gpadc_lock);
 
        return (high_data << 8) | low_data;
@@ -412,7 +422,9 @@ out:
         */
        (void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
                AB8500_GPADC_CTRL1_REG, DIS_GPADC);
-       regulator_disable(gpadc->regu);
+
+       pm_runtime_put(gpadc->dev);
+
        mutex_unlock(&gpadc->ab8500_gpadc_lock);
        dev_err(gpadc->dev,
                "gpadc_conversion: Failed to AD convert channel %d\n", channel);
@@ -571,6 +583,28 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
                gpadc->cal_data[ADC_INPUT_VBAT].offset);
 }
 
+static int ab8500_gpadc_runtime_suspend(struct device *dev)
+{
+       struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+
+       regulator_disable(gpadc->regu);
+       return 0;
+}
+
+static int ab8500_gpadc_runtime_resume(struct device *dev)
+{
+       struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+
+       regulator_enable(gpadc->regu);
+       return 0;
+}
+
+static int ab8500_gpadc_runtime_idle(struct device *dev)
+{
+       pm_runtime_suspend(dev);
+       return 0;
+}
+
 static int ab8500_gpadc_probe(struct platform_device *pdev)
 {
        int ret = 0;
@@ -591,6 +625,7 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
        }
 
        gpadc->dev = &pdev->dev;
+       gpadc->parent = dev_get_drvdata(pdev->dev.parent);
        mutex_init(&gpadc->ab8500_gpadc_lock);
 
        /* Initialize completion used to notify completion of conversion */
@@ -607,14 +642,6 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
                goto fail;
        }
 
-       /* Get Chip ID of the ABB ASIC  */
-       ret = abx500_get_chip_id(gpadc->dev);
-       if (ret < 0) {
-               dev_err(gpadc->dev, "failed to get chip ID\n");
-               goto fail_irq;
-       }
-       gpadc->chip_id = (u8) ret;
-
        /* VTVout LDO used to power up ab8500-GPADC */
        gpadc->regu = regulator_get(&pdev->dev, "vddadc");
        if (IS_ERR(gpadc->regu)) {
@@ -622,6 +649,16 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
                dev_err(gpadc->dev, "failed to get vtvout LDO\n");
                goto fail_irq;
        }
+
+       platform_set_drvdata(pdev, gpadc);
+
+       regulator_enable(gpadc->regu);
+
+       pm_runtime_set_autosuspend_delay(gpadc->dev, GPADC_AUDOSUSPEND_DELAY);
+       pm_runtime_use_autosuspend(gpadc->dev);
+       pm_runtime_set_active(gpadc->dev);
+       pm_runtime_enable(gpadc->dev);
+
        ab8500_gpadc_read_calibration_data(gpadc);
        list_add_tail(&gpadc->node, &ab8500_gpadc_list);
        dev_dbg(gpadc->dev, "probe success\n");
@@ -642,19 +679,34 @@ static int ab8500_gpadc_remove(struct platform_device *pdev)
        list_del(&gpadc->node);
        /* remove interrupt  - completion of Sw ADC conversion */
        free_irq(gpadc->irq, gpadc);
-       /* disable VTVout LDO that is being used by GPADC */
-       regulator_put(gpadc->regu);
+
+       pm_runtime_get_sync(gpadc->dev);
+       pm_runtime_disable(gpadc->dev);
+
+       regulator_disable(gpadc->regu);
+
+       pm_runtime_set_suspended(gpadc->dev);
+
+       pm_runtime_put_noidle(gpadc->dev);
+
        kfree(gpadc);
        gpadc = NULL;
        return 0;
 }
 
+static const struct dev_pm_ops ab8500_gpadc_pm_ops = {
+       SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend,
+                          ab8500_gpadc_runtime_resume,
+                          ab8500_gpadc_runtime_idle)
+};
+
 static struct platform_driver ab8500_gpadc_driver = {
        .probe = ab8500_gpadc_probe,
        .remove = ab8500_gpadc_remove,
        .driver = {
                .name = "ab8500-gpadc",
                .owner = THIS_MODULE,
+               .pm = &ab8500_gpadc_pm_ops,
        },
 };