diff options
Diffstat (limited to 'drivers/mfd/ti_am335x_tscadc.c')
| -rw-r--r-- | drivers/mfd/ti_am335x_tscadc.c | 141 |
1 files changed, 90 insertions, 51 deletions
diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index b003a16ba22..dd4bf581622 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -14,7 +14,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/err.h> #include <linux/io.h> @@ -24,6 +23,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,29 +48,80 @@ 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_update); +EXPORT_SYMBOL_GPL(am335x_tsc_se_set_cache); -void am335x_tsc_se_set(struct ti_tscadc_dev *tsadc, u32 val) +static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc) { - spin_lock(&tsadc->reg_lock); - tsadc->reg_se_cache |= val; - spin_unlock(&tsadc->reg_lock); + DEFINE_WAIT(wait); + u32 reg; - am335x_tsc_se_update(tsadc); + /* + * 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; } -EXPORT_SYMBOL_GPL(am335x_tsc_se_set); + +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_set_once); + +void am335x_tsc_se_adc_done(struct ti_tscadc_dev *tsadc) +{ + unsigned long flags; + + spin_lock_irqsave(&tsadc->reg_lock, flags); + 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_adc_done); void am335x_tsc_se_clr(struct ti_tscadc_dev *tsadc, u32 val) { - spin_lock(&tsadc->reg_lock); - tsadc->reg_se_cache &= ~val; - spin_unlock(&tsadc->reg_lock); + unsigned long flags; - am335x_tsc_se_update(tsadc); + spin_lock_irqsave(&tsadc->reg_lock, flags); + tsadc->reg_se_cache &= ~val; + tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache); + spin_unlock_irqrestore(&tsadc->reg_lock, flags); } EXPORT_SYMBOL_GPL(am335x_tsc_se_clr); @@ -95,7 +146,7 @@ static int ti_tscadc_probe(struct platform_device *pdev) const __be32 *cur; u32 val; int err, ctrl; - int clk_value, clock_rate; + int clock_rate; int tsc_wires = 0, adc_channels = 0, total_channels; int readouts = 0; @@ -132,12 +183,6 @@ static int ti_tscadc_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no memory resource defined.\n"); - return -EINVAL; - } - /* Allocate memory for device */ tscadc = devm_kzalloc(&pdev->dev, sizeof(struct ti_tscadc_dev), GFP_KERNEL); @@ -154,19 +199,10 @@ static int ti_tscadc_probe(struct platform_device *pdev) } else tscadc->irq = err; - res = devm_request_mem_region(&pdev->dev, - res->start, resource_size(res), pdev->name); - if (!res) { - dev_err(&pdev->dev, "failed to reserve registers.\n"); - return -EBUSY; - } - - tscadc->tscadc_base = devm_ioremap(&pdev->dev, - res->start, resource_size(res)); - if (!tscadc->tscadc_base) { - dev_err(&pdev->dev, "failed to map registers.\n"); - return -ENOMEM; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tscadc->tscadc_base)) + return PTR_ERR(tscadc->tscadc_base); tscadc->regmap_tscadc = devm_regmap_init_mmio(&pdev->dev, tscadc->tscadc_base, &tscadc_regmap_config); @@ -177,6 +213,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); @@ -196,25 +234,22 @@ static int ti_tscadc_probe(struct platform_device *pdev) } clock_rate = clk_get_rate(clk); clk_put(clk); - clk_value = clock_rate / ADC_CLK; - if (clk_value < MAX_CLK_DIV) { - dev_err(&pdev->dev, "clock input less than min clock requirement\n"); - err = -EINVAL; - goto err_disable_clk; - } + tscadc->clk_div = clock_rate / ADC_CLK; + /* TSCADC_CLKDIV needs to be configured to the value minus 1 */ - clk_value = clk_value - 1; - tscadc_writel(tscadc, REG_CLKDIV, clk_value); + tscadc->clk_div--; + tscadc_writel(tscadc, REG_CLKDIV, tscadc->clk_div); /* Set the control register bits */ ctrl = CNTRLREG_STEPCONFIGWRT | - CNTRLREG_TSCENB | - CNTRLREG_STEPID | - CNTRLREG_4WIRE; + CNTRLREG_STEPID; + if (tsc_wires > 0) + ctrl |= CNTRLREG_4WIRE | CNTRLREG_TSCENB; tscadc_writel(tscadc, REG_CTRL, ctrl); /* Set register bits for Idle Config Mode */ - tscadc_idle_config(tscadc); + if (tsc_wires > 0) + tscadc_idle_config(tscadc); /* Enable the TSC module enable bit */ ctrl = tscadc_readl(tscadc, REG_CTRL); @@ -294,15 +329,19 @@ static int tscadc_resume(struct device *dev) pm_runtime_get_sync(dev); /* context restore */ - ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_TSCENB | - CNTRLREG_STEPID | CNTRLREG_4WIRE; + ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_STEPID; + if (tscadc_dev->tsc_cell != -1) + ctrl |= CNTRLREG_TSCENB | CNTRLREG_4WIRE; tscadc_writel(tscadc_dev, REG_CTRL, ctrl); - tscadc_idle_config(tscadc_dev); - am335x_tsc_se_update(tscadc_dev); + + if (tscadc_dev->tsc_cell != -1) + tscadc_idle_config(tscadc_dev); restore = tscadc_readl(tscadc_dev, REG_CTRL); tscadc_writel(tscadc_dev, REG_CTRL, (restore | CNTRLREG_TSCSSENB)); + tscadc_writel(tscadc_dev, REG_CLKDIV, tscadc_dev->clk_div); + return 0; } @@ -326,7 +365,7 @@ static struct platform_driver ti_tscadc_driver = { .name = "ti_am3359-tscadc", .owner = THIS_MODULE, .pm = TSCADC_PM_OPS, - .of_match_table = of_match_ptr(ti_tscadc_dt_ids), + .of_match_table = ti_tscadc_dt_ids, }, .probe = ti_tscadc_probe, .remove = ti_tscadc_remove, |
