]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/iio/adc/exynos_adc.c
Merge remote-tracking branch 'staging/staging-next'
[karo-tx-linux.git] / drivers / iio / adc / exynos_adc.c
index affa93f517893b1c6db2ece1c1e83afeea1828aa..010578f1d76264e68dec04fbd361b6958003f5cb 100644 (file)
@@ -82,7 +82,7 @@ enum adc_version {
 #define ADC_CON_EN_START       (1u << 0)
 #define ADC_DATX_MASK          0xFFF
 
-#define EXYNOS_ADC_TIMEOUT     (msecs_to_jiffies(1000))
+#define EXYNOS_ADC_TIMEOUT     (msecs_to_jiffies(100))
 
 struct exynos_adc {
        void __iomem            *regs;
@@ -112,6 +112,30 @@ static inline unsigned int exynos_adc_get_version(struct platform_device *pdev)
        return (unsigned int)match->data;
 }
 
+static void exynos_adc_hw_init(struct exynos_adc *info)
+{
+       u32 con1, con2;
+
+       if (info->version == ADC_V2) {
+               con1 = ADC_V2_CON1_SOFT_RESET;
+               writel(con1, ADC_V2_CON1(info->regs));
+
+               con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL |
+                       ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0);
+               writel(con2, ADC_V2_CON2(info->regs));
+
+               /* Enable interrupts */
+               writel(1, ADC_V2_INT_EN(info->regs));
+       } else {
+               /* set default prescaler values and Enable prescaler */
+               con1 =  ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN;
+
+               /* Enable 12-bit ADC resolution */
+               con1 |= ADC_V1_CON_RES;
+               writel(con1, ADC_V1_CON(info->regs));
+       }
+}
+
 static int exynos_read_raw(struct iio_dev *indio_dev,
                                struct iio_chan_spec const *chan,
                                int *val,
@@ -121,11 +145,13 @@ static int exynos_read_raw(struct iio_dev *indio_dev,
        struct exynos_adc *info = iio_priv(indio_dev);
        unsigned long timeout;
        u32 con1, con2;
+       int ret;
 
        if (mask != IIO_CHAN_INFO_RAW)
                return -EINVAL;
 
        mutex_lock(&indio_dev->mlock);
+       reinit_completion(&info->completion);
 
        /* Select the channel to be used and Trigger conversion */
        if (info->version == ADC_V2) {
@@ -145,16 +171,21 @@ static int exynos_read_raw(struct iio_dev *indio_dev,
                                ADC_V1_CON(info->regs));
        }
 
-       timeout = wait_for_completion_interruptible_timeout
+       timeout = wait_for_completion_timeout
                        (&info->completion, EXYNOS_ADC_TIMEOUT);
-       *val = info->value;
+       if (timeout == 0) {
+               dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n");
+               exynos_adc_hw_init(info);
+               ret = -ETIMEDOUT;
+       } else {
+               *val = info->value;
+               *val2 = 0;
+               ret = IIO_VAL_INT;
+       }
 
        mutex_unlock(&indio_dev->mlock);
 
-       if (timeout == 0)
-               return -ETIMEDOUT;
-
-       return IIO_VAL_INT;
+       return ret;
 }
 
 static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
@@ -226,30 +257,6 @@ static int exynos_adc_remove_devices(struct device *dev, void *c)
        return 0;
 }
 
-static void exynos_adc_hw_init(struct exynos_adc *info)
-{
-       u32 con1, con2;
-
-       if (info->version == ADC_V2) {
-               con1 = ADC_V2_CON1_SOFT_RESET;
-               writel(con1, ADC_V2_CON1(info->regs));
-
-               con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL |
-                       ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0);
-               writel(con2, ADC_V2_CON2(info->regs));
-
-               /* Enable interrupts */
-               writel(1, ADC_V2_INT_EN(info->regs));
-       } else {
-               /* set default prescaler values and Enable prescaler */
-               con1 =  ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN;
-
-               /* Enable 12-bit ADC resolution */
-               con1 |= ADC_V1_CON_RES;
-               writel(con1, ADC_V1_CON(info->regs));
-       }
-}
-
 static int exynos_adc_probe(struct platform_device *pdev)
 {
        struct exynos_adc *info = NULL;
@@ -290,32 +297,30 @@ static int exynos_adc_probe(struct platform_device *pdev)
 
        init_completion(&info->completion);
 
-       ret = request_irq(info->irq, exynos_adc_isr,
-                                       0, dev_name(&pdev->dev), info);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
-                                                       info->irq);
-               return ret;
-       }
-
-       writel(1, info->enable_reg);
-
        info->clk = devm_clk_get(&pdev->dev, "adc");
        if (IS_ERR(info->clk)) {
                dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
                                                        PTR_ERR(info->clk));
-               ret = PTR_ERR(info->clk);
-               goto err_irq;
+               return PTR_ERR(info->clk);
        }
 
        info->vdd = devm_regulator_get(&pdev->dev, "vdd");
        if (IS_ERR(info->vdd)) {
                dev_err(&pdev->dev, "failed getting regulator, err = %ld\n",
                                                        PTR_ERR(info->vdd));
-               ret = PTR_ERR(info->vdd);
-               goto err_irq;
+               return PTR_ERR(info->vdd);
        }
 
+       ret = regulator_enable(info->vdd);
+       if (ret)
+               return ret;
+
+       ret = clk_prepare_enable(info->clk);
+       if (ret)
+               goto err_disable_reg;
+
+       writel(1, info->enable_reg);
+
        info->version = exynos_adc_get_version(pdev);
 
        platform_set_drvdata(pdev, indio_dev);
@@ -332,16 +337,18 @@ static int exynos_adc_probe(struct platform_device *pdev)
        else
                indio_dev->num_channels = MAX_ADC_V2_CHANNELS;
 
+       ret = request_irq(info->irq, exynos_adc_isr,
+                                       0, dev_name(&pdev->dev), info);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
+                                                       info->irq);
+               goto err_disable_clk;
+       }
+
        ret = iio_device_register(indio_dev);
        if (ret)
                goto err_irq;
 
-       ret = regulator_enable(info->vdd);
-       if (ret)
-               goto err_iio_dev;
-
-       clk_prepare_enable(info->clk);
-
        exynos_adc_hw_init(info);
 
        ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev);
@@ -355,12 +362,14 @@ static int exynos_adc_probe(struct platform_device *pdev)
 err_of_populate:
        device_for_each_child(&indio_dev->dev, NULL,
                                exynos_adc_remove_devices);
-       regulator_disable(info->vdd);
-       clk_disable_unprepare(info->clk);
-err_iio_dev:
        iio_device_unregister(indio_dev);
 err_irq:
        free_irq(info->irq, info);
+err_disable_clk:
+       writel(0, info->enable_reg);
+       clk_disable_unprepare(info->clk);
+err_disable_reg:
+       regulator_disable(info->vdd);
        return ret;
 }
 
@@ -371,11 +380,11 @@ static int exynos_adc_remove(struct platform_device *pdev)
 
        device_for_each_child(&indio_dev->dev, NULL,
                                exynos_adc_remove_devices);
-       regulator_disable(info->vdd);
-       clk_disable_unprepare(info->clk);
-       writel(0, info->enable_reg);
        iio_device_unregister(indio_dev);
        free_irq(info->irq, info);
+       writel(0, info->enable_reg);
+       clk_disable_unprepare(info->clk);
+       regulator_disable(info->vdd);
 
        return 0;
 }
@@ -397,8 +406,8 @@ static int exynos_adc_suspend(struct device *dev)
                writel(con, ADC_V1_CON(info->regs));
        }
 
-       clk_disable_unprepare(info->clk);
        writel(0, info->enable_reg);
+       clk_disable_unprepare(info->clk);
        regulator_disable(info->vdd);
 
        return 0;
@@ -414,9 +423,11 @@ static int exynos_adc_resume(struct device *dev)
        if (ret)
                return ret;
 
-       writel(1, info->enable_reg);
-       clk_prepare_enable(info->clk);
+       ret = clk_prepare_enable(info->clk);
+       if (ret)
+               return ret;
 
+       writel(1, info->enable_reg);
        exynos_adc_hw_init(info);
 
        return 0;