]> Pileus Git - ~andy/linux/blobdiff - drivers/mfd/ti_am335x_tscadc.c
Linux 3.14
[~andy/linux] / drivers / mfd / ti_am335x_tscadc.c
index 88718abfb9ba0169090201e8fc04037ea4f32854..d4e860413bb54e1af55409dd7c8dc29e47747b02 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/sched.h>
 
 #include <linux/mfd/ti_am335x_tscadc.h>
 
@@ -48,32 +49,79 @@ static const struct regmap_config tscadc_regmap_config = {
        .val_bits = 32,
 };
 
-void am335x_tsc_se_update(struct ti_tscadc_dev *tsadc)
+void am335x_tsc_se_set_cache(struct ti_tscadc_dev *tsadc, u32 val)
 {
-       tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache);
+       unsigned long flags;
+
+       spin_lock_irqsave(&tsadc->reg_lock, flags);
+       tsadc->reg_se_cache = val;
+       if (tsadc->adc_waiting)
+               wake_up(&tsadc->reg_se_wait);
+       else if (!tsadc->adc_in_use)
+               tscadc_writel(tsadc, REG_SE, val);
+
+       spin_unlock_irqrestore(&tsadc->reg_lock, flags);
+}
+EXPORT_SYMBOL_GPL(am335x_tsc_se_set_cache);
+
+static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc)
+{
+       DEFINE_WAIT(wait);
+       u32 reg;
+
+       /*
+        * disable TSC steps so it does not run while the ADC is using it. If
+        * write 0 while it is running (it just started or was already running)
+        * then it completes all steps that were enabled and stops then.
+        */
+       tscadc_writel(tsadc, REG_SE, 0);
+       reg = tscadc_readl(tsadc, REG_ADCFSM);
+       if (reg & SEQ_STATUS) {
+               tsadc->adc_waiting = true;
+               prepare_to_wait(&tsadc->reg_se_wait, &wait,
+                               TASK_UNINTERRUPTIBLE);
+               spin_unlock_irq(&tsadc->reg_lock);
+
+               schedule();
+
+               spin_lock_irq(&tsadc->reg_lock);
+               finish_wait(&tsadc->reg_se_wait, &wait);
+
+               reg = tscadc_readl(tsadc, REG_ADCFSM);
+               WARN_ON(reg & SEQ_STATUS);
+               tsadc->adc_waiting = false;
+       }
+       tsadc->adc_in_use = true;
+}
+
+void am335x_tsc_se_set_once(struct ti_tscadc_dev *tsadc, u32 val)
+{
+       spin_lock_irq(&tsadc->reg_lock);
+       am335x_tscadc_need_adc(tsadc);
+
+       tscadc_writel(tsadc, REG_SE, val);
+       spin_unlock_irq(&tsadc->reg_lock);
 }
-EXPORT_SYMBOL_GPL(am335x_tsc_se_update);
+EXPORT_SYMBOL_GPL(am335x_tsc_se_set_once);
 
-void am335x_tsc_se_set(struct ti_tscadc_dev *tsadc, u32 val)
+void am335x_tsc_se_adc_done(struct ti_tscadc_dev *tsadc)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&tsadc->reg_lock, flags);
-       tsadc->reg_se_cache = tscadc_readl(tsadc, REG_SE);
-       tsadc->reg_se_cache |= val;
-       am335x_tsc_se_update(tsadc);
+       tsadc->adc_in_use = false;
+       tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache);
        spin_unlock_irqrestore(&tsadc->reg_lock, flags);
 }
-EXPORT_SYMBOL_GPL(am335x_tsc_se_set);
+EXPORT_SYMBOL_GPL(am335x_tsc_se_adc_done);
 
 void am335x_tsc_se_clr(struct ti_tscadc_dev *tsadc, u32 val)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&tsadc->reg_lock, flags);
-       tsadc->reg_se_cache = tscadc_readl(tsadc, REG_SE);
        tsadc->reg_se_cache &= ~val;
-       am335x_tsc_se_update(tsadc);
+       tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache);
        spin_unlock_irqrestore(&tsadc->reg_lock, flags);
 }
 EXPORT_SYMBOL_GPL(am335x_tsc_se_clr);
@@ -181,6 +229,8 @@ static      int ti_tscadc_probe(struct platform_device *pdev)
        }
 
        spin_lock_init(&tscadc->reg_lock);
+       init_waitqueue_head(&tscadc->reg_se_wait);
+
        pm_runtime_enable(&pdev->dev);
        pm_runtime_get_sync(&pdev->dev);
 
@@ -302,7 +352,6 @@ static int tscadc_resume(struct device *dev)
 
        if (tscadc_dev->tsc_cell != -1)
                tscadc_idle_config(tscadc_dev);
-       am335x_tsc_se_update(tscadc_dev);
        restore = tscadc_readl(tscadc_dev, REG_CTRL);
        tscadc_writel(tscadc_dev, REG_CTRL,
                        (restore | CNTRLREG_TSCSSENB));