forked from Minki/linux
cc06e67d8f
Enable overrun interrupt on STM32 ADC. In case data register hasn't been read (by CPU or DMA), overrun condition is detected when there's new conversion data available. Stop grabbing data and log an error message. Use a threaded irq to avoid printing the error message from hard irq context. Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
863 lines
22 KiB
C
863 lines
22 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* This file is part of STM32 ADC driver
|
|
*
|
|
* Copyright (C) 2016, STMicroelectronics - All Rights Reserved
|
|
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
|
|
*
|
|
* Inspired from: fsl-imx25-tsadc
|
|
*
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irqchip/chained_irq.h>
|
|
#include <linux/irqdesc.h>
|
|
#include <linux/irqdomain.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "stm32-adc-core.h"
|
|
|
|
#define STM32_ADC_CORE_SLEEP_DELAY_MS 2000
|
|
|
|
/* SYSCFG registers */
|
|
#define STM32MP1_SYSCFG_PMCSETR 0x04
|
|
#define STM32MP1_SYSCFG_PMCCLRR 0x44
|
|
|
|
/* SYSCFG bit fields */
|
|
#define STM32MP1_SYSCFG_ANASWVDD_MASK BIT(9)
|
|
|
|
/* SYSCFG capability flags */
|
|
#define HAS_VBOOSTER BIT(0)
|
|
#define HAS_ANASWVDD BIT(1)
|
|
|
|
/**
|
|
* struct stm32_adc_common_regs - stm32 common registers
|
|
* @csr: common status register offset
|
|
* @ccr: common control register offset
|
|
* @eoc1_msk: adc1 end of conversion flag in @csr
|
|
* @eoc2_msk: adc2 end of conversion flag in @csr
|
|
* @eoc3_msk: adc3 end of conversion flag in @csr
|
|
* @ier: interrupt enable register offset for each adc
|
|
* @eocie_msk: end of conversion interrupt enable mask in @ier
|
|
*/
|
|
struct stm32_adc_common_regs {
|
|
u32 csr;
|
|
u32 ccr;
|
|
u32 eoc1_msk;
|
|
u32 eoc2_msk;
|
|
u32 eoc3_msk;
|
|
u32 ier;
|
|
u32 eocie_msk;
|
|
};
|
|
|
|
struct stm32_adc_priv;
|
|
|
|
/**
|
|
* struct stm32_adc_priv_cfg - stm32 core compatible configuration data
|
|
* @regs: common registers for all instances
|
|
* @clk_sel: clock selection routine
|
|
* @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
|
|
* @has_syscfg: SYSCFG capability flags
|
|
*/
|
|
struct stm32_adc_priv_cfg {
|
|
const struct stm32_adc_common_regs *regs;
|
|
int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
|
|
u32 max_clk_rate_hz;
|
|
unsigned int has_syscfg;
|
|
};
|
|
|
|
/**
|
|
* struct stm32_adc_priv - stm32 ADC core private data
|
|
* @irq: irq(s) for ADC block
|
|
* @domain: irq domain reference
|
|
* @aclk: clock reference for the analog circuitry
|
|
* @bclk: bus clock common for all ADCs, depends on part used
|
|
* @max_clk_rate: desired maximum clock rate
|
|
* @booster: booster supply reference
|
|
* @vdd: vdd supply reference
|
|
* @vdda: vdda analog supply reference
|
|
* @vref: regulator reference
|
|
* @vdd_uv: vdd supply voltage (microvolts)
|
|
* @vdda_uv: vdda supply voltage (microvolts)
|
|
* @cfg: compatible configuration data
|
|
* @common: common data for all ADC instances
|
|
* @ccr_bak: backup CCR in low power mode
|
|
* @syscfg: reference to syscon, system control registers
|
|
*/
|
|
struct stm32_adc_priv {
|
|
int irq[STM32_ADC_MAX_ADCS];
|
|
struct irq_domain *domain;
|
|
struct clk *aclk;
|
|
struct clk *bclk;
|
|
u32 max_clk_rate;
|
|
struct regulator *booster;
|
|
struct regulator *vdd;
|
|
struct regulator *vdda;
|
|
struct regulator *vref;
|
|
int vdd_uv;
|
|
int vdda_uv;
|
|
const struct stm32_adc_priv_cfg *cfg;
|
|
struct stm32_adc_common common;
|
|
u32 ccr_bak;
|
|
struct regmap *syscfg;
|
|
};
|
|
|
|
static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
|
|
{
|
|
return container_of(com, struct stm32_adc_priv, common);
|
|
}
|
|
|
|
/* STM32F4 ADC internal common clock prescaler division ratios */
|
|
static int stm32f4_pclk_div[] = {2, 4, 6, 8};
|
|
|
|
/**
|
|
* stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler
|
|
* @pdev: platform device
|
|
* @priv: stm32 ADC core private data
|
|
* Select clock prescaler used for analog conversions, before using ADC.
|
|
*/
|
|
static int stm32f4_adc_clk_sel(struct platform_device *pdev,
|
|
struct stm32_adc_priv *priv)
|
|
{
|
|
unsigned long rate;
|
|
u32 val;
|
|
int i;
|
|
|
|
/* stm32f4 has one clk input for analog (mandatory), enforce it here */
|
|
if (!priv->aclk) {
|
|
dev_err(&pdev->dev, "No 'adc' clock found\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
rate = clk_get_rate(priv->aclk);
|
|
if (!rate) {
|
|
dev_err(&pdev->dev, "Invalid clock rate: 0\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
|
|
if ((rate / stm32f4_pclk_div[i]) <= priv->max_clk_rate)
|
|
break;
|
|
}
|
|
if (i >= ARRAY_SIZE(stm32f4_pclk_div)) {
|
|
dev_err(&pdev->dev, "adc clk selection failed\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
priv->common.rate = rate / stm32f4_pclk_div[i];
|
|
val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
|
|
val &= ~STM32F4_ADC_ADCPRE_MASK;
|
|
val |= i << STM32F4_ADC_ADCPRE_SHIFT;
|
|
writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR);
|
|
|
|
dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n",
|
|
priv->common.rate / 1000);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* struct stm32h7_adc_ck_spec - specification for stm32h7 adc clock
|
|
* @ckmode: ADC clock mode, Async or sync with prescaler.
|
|
* @presc: prescaler bitfield for async clock mode
|
|
* @div: prescaler division ratio
|
|
*/
|
|
struct stm32h7_adc_ck_spec {
|
|
u32 ckmode;
|
|
u32 presc;
|
|
int div;
|
|
};
|
|
|
|
static const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = {
|
|
/* 00: CK_ADC[1..3]: Asynchronous clock modes */
|
|
{ 0, 0, 1 },
|
|
{ 0, 1, 2 },
|
|
{ 0, 2, 4 },
|
|
{ 0, 3, 6 },
|
|
{ 0, 4, 8 },
|
|
{ 0, 5, 10 },
|
|
{ 0, 6, 12 },
|
|
{ 0, 7, 16 },
|
|
{ 0, 8, 32 },
|
|
{ 0, 9, 64 },
|
|
{ 0, 10, 128 },
|
|
{ 0, 11, 256 },
|
|
/* HCLK used: Synchronous clock modes (1, 2 or 4 prescaler) */
|
|
{ 1, 0, 1 },
|
|
{ 2, 0, 2 },
|
|
{ 3, 0, 4 },
|
|
};
|
|
|
|
static int stm32h7_adc_clk_sel(struct platform_device *pdev,
|
|
struct stm32_adc_priv *priv)
|
|
{
|
|
u32 ckmode, presc, val;
|
|
unsigned long rate;
|
|
int i, div;
|
|
|
|
/* stm32h7 bus clock is common for all ADC instances (mandatory) */
|
|
if (!priv->bclk) {
|
|
dev_err(&pdev->dev, "No 'bus' clock found\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
/*
|
|
* stm32h7 can use either 'bus' or 'adc' clock for analog circuitry.
|
|
* So, choice is to have bus clock mandatory and adc clock optional.
|
|
* If optional 'adc' clock has been found, then try to use it first.
|
|
*/
|
|
if (priv->aclk) {
|
|
/*
|
|
* Asynchronous clock modes (e.g. ckmode == 0)
|
|
* From spec: PLL output musn't exceed max rate
|
|
*/
|
|
rate = clk_get_rate(priv->aclk);
|
|
if (!rate) {
|
|
dev_err(&pdev->dev, "Invalid adc clock rate: 0\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
|
|
ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
|
|
presc = stm32h7_adc_ckmodes_spec[i].presc;
|
|
div = stm32h7_adc_ckmodes_spec[i].div;
|
|
|
|
if (ckmode)
|
|
continue;
|
|
|
|
if ((rate / div) <= priv->max_clk_rate)
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* Synchronous clock modes (e.g. ckmode is 1, 2 or 3) */
|
|
rate = clk_get_rate(priv->bclk);
|
|
if (!rate) {
|
|
dev_err(&pdev->dev, "Invalid bus clock rate: 0\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
|
|
ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
|
|
presc = stm32h7_adc_ckmodes_spec[i].presc;
|
|
div = stm32h7_adc_ckmodes_spec[i].div;
|
|
|
|
if (!ckmode)
|
|
continue;
|
|
|
|
if ((rate / div) <= priv->max_clk_rate)
|
|
goto out;
|
|
}
|
|
|
|
dev_err(&pdev->dev, "adc clk selection failed\n");
|
|
return -EINVAL;
|
|
|
|
out:
|
|
/* rate used later by each ADC instance to control BOOST mode */
|
|
priv->common.rate = rate / div;
|
|
|
|
/* Set common clock mode and prescaler */
|
|
val = readl_relaxed(priv->common.base + STM32H7_ADC_CCR);
|
|
val &= ~(STM32H7_CKMODE_MASK | STM32H7_PRESC_MASK);
|
|
val |= ckmode << STM32H7_CKMODE_SHIFT;
|
|
val |= presc << STM32H7_PRESC_SHIFT;
|
|
writel_relaxed(val, priv->common.base + STM32H7_ADC_CCR);
|
|
|
|
dev_dbg(&pdev->dev, "Using %s clock/%d source at %ld kHz\n",
|
|
ckmode ? "bus" : "adc", div, priv->common.rate / 1000);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* STM32F4 common registers definitions */
|
|
static const struct stm32_adc_common_regs stm32f4_adc_common_regs = {
|
|
.csr = STM32F4_ADC_CSR,
|
|
.ccr = STM32F4_ADC_CCR,
|
|
.eoc1_msk = STM32F4_EOC1 | STM32F4_OVR1,
|
|
.eoc2_msk = STM32F4_EOC2 | STM32F4_OVR2,
|
|
.eoc3_msk = STM32F4_EOC3 | STM32F4_OVR3,
|
|
.ier = STM32F4_ADC_CR1,
|
|
.eocie_msk = STM32F4_EOCIE | STM32F4_OVRIE,
|
|
};
|
|
|
|
/* STM32H7 common registers definitions */
|
|
static const struct stm32_adc_common_regs stm32h7_adc_common_regs = {
|
|
.csr = STM32H7_ADC_CSR,
|
|
.ccr = STM32H7_ADC_CCR,
|
|
.eoc1_msk = STM32H7_EOC_MST | STM32H7_OVR_MST,
|
|
.eoc2_msk = STM32H7_EOC_SLV | STM32H7_OVR_SLV,
|
|
.ier = STM32H7_ADC_IER,
|
|
.eocie_msk = STM32H7_EOCIE | STM32H7_OVRIE,
|
|
};
|
|
|
|
static const unsigned int stm32_adc_offset[STM32_ADC_MAX_ADCS] = {
|
|
0, STM32_ADC_OFFSET, STM32_ADC_OFFSET * 2,
|
|
};
|
|
|
|
static unsigned int stm32_adc_eoc_enabled(struct stm32_adc_priv *priv,
|
|
unsigned int adc)
|
|
{
|
|
u32 ier, offset = stm32_adc_offset[adc];
|
|
|
|
ier = readl_relaxed(priv->common.base + offset + priv->cfg->regs->ier);
|
|
|
|
return ier & priv->cfg->regs->eocie_msk;
|
|
}
|
|
|
|
/* ADC common interrupt for all instances */
|
|
static void stm32_adc_irq_handler(struct irq_desc *desc)
|
|
{
|
|
struct stm32_adc_priv *priv = irq_desc_get_handler_data(desc);
|
|
struct irq_chip *chip = irq_desc_get_chip(desc);
|
|
u32 status;
|
|
|
|
chained_irq_enter(chip, desc);
|
|
status = readl_relaxed(priv->common.base + priv->cfg->regs->csr);
|
|
|
|
/*
|
|
* End of conversion may be handled by using IRQ or DMA. There may be a
|
|
* race here when two conversions complete at the same time on several
|
|
* ADCs. EOC may be read 'set' for several ADCs, with:
|
|
* - an ADC configured to use DMA (EOC triggers the DMA request, and
|
|
* is then automatically cleared by DR read in hardware)
|
|
* - an ADC configured to use IRQs (EOCIE bit is set. The handler must
|
|
* be called in this case)
|
|
* So both EOC status bit in CSR and EOCIE control bit must be checked
|
|
* before invoking the interrupt handler (e.g. call ISR only for
|
|
* IRQ-enabled ADCs).
|
|
*/
|
|
if (status & priv->cfg->regs->eoc1_msk &&
|
|
stm32_adc_eoc_enabled(priv, 0))
|
|
generic_handle_irq(irq_find_mapping(priv->domain, 0));
|
|
|
|
if (status & priv->cfg->regs->eoc2_msk &&
|
|
stm32_adc_eoc_enabled(priv, 1))
|
|
generic_handle_irq(irq_find_mapping(priv->domain, 1));
|
|
|
|
if (status & priv->cfg->regs->eoc3_msk &&
|
|
stm32_adc_eoc_enabled(priv, 2))
|
|
generic_handle_irq(irq_find_mapping(priv->domain, 2));
|
|
|
|
chained_irq_exit(chip, desc);
|
|
};
|
|
|
|
static int stm32_adc_domain_map(struct irq_domain *d, unsigned int irq,
|
|
irq_hw_number_t hwirq)
|
|
{
|
|
irq_set_chip_data(irq, d->host_data);
|
|
irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void stm32_adc_domain_unmap(struct irq_domain *d, unsigned int irq)
|
|
{
|
|
irq_set_chip_and_handler(irq, NULL, NULL);
|
|
irq_set_chip_data(irq, NULL);
|
|
}
|
|
|
|
static const struct irq_domain_ops stm32_adc_domain_ops = {
|
|
.map = stm32_adc_domain_map,
|
|
.unmap = stm32_adc_domain_unmap,
|
|
.xlate = irq_domain_xlate_onecell,
|
|
};
|
|
|
|
static int stm32_adc_irq_probe(struct platform_device *pdev,
|
|
struct stm32_adc_priv *priv)
|
|
{
|
|
struct device_node *np = pdev->dev.of_node;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < STM32_ADC_MAX_ADCS; i++) {
|
|
priv->irq[i] = platform_get_irq(pdev, i);
|
|
if (priv->irq[i] < 0) {
|
|
/*
|
|
* At least one interrupt must be provided, make others
|
|
* optional:
|
|
* - stm32f4/h7 shares a common interrupt.
|
|
* - stm32mp1, has one line per ADC (either for ADC1,
|
|
* ADC2 or both).
|
|
*/
|
|
if (i && priv->irq[i] == -ENXIO)
|
|
continue;
|
|
|
|
return priv->irq[i];
|
|
}
|
|
}
|
|
|
|
priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
|
|
&stm32_adc_domain_ops,
|
|
priv);
|
|
if (!priv->domain) {
|
|
dev_err(&pdev->dev, "Failed to add irq domain\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for (i = 0; i < STM32_ADC_MAX_ADCS; i++) {
|
|
if (priv->irq[i] < 0)
|
|
continue;
|
|
irq_set_chained_handler(priv->irq[i], stm32_adc_irq_handler);
|
|
irq_set_handler_data(priv->irq[i], priv);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void stm32_adc_irq_remove(struct platform_device *pdev,
|
|
struct stm32_adc_priv *priv)
|
|
{
|
|
int hwirq;
|
|
unsigned int i;
|
|
|
|
for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
|
|
irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
|
|
irq_domain_remove(priv->domain);
|
|
|
|
for (i = 0; i < STM32_ADC_MAX_ADCS; i++) {
|
|
if (priv->irq[i] < 0)
|
|
continue;
|
|
irq_set_chained_handler(priv->irq[i], NULL);
|
|
}
|
|
}
|
|
|
|
static int stm32_adc_core_switches_supply_en(struct stm32_adc_priv *priv,
|
|
struct device *dev)
|
|
{
|
|
int ret;
|
|
|
|
/*
|
|
* On STM32H7 and STM32MP1, the ADC inputs are multiplexed with analog
|
|
* switches (via PCSEL) which have reduced performances when their
|
|
* supply is below 2.7V (vdda by default):
|
|
* - Voltage booster can be used, to get full ADC performances
|
|
* (increases power consumption).
|
|
* - Vdd can be used to supply them, if above 2.7V (STM32MP1 only).
|
|
*
|
|
* Recommended settings for ANASWVDD and EN_BOOSTER:
|
|
* - vdda < 2.7V but vdd > 2.7V: ANASWVDD = 1, EN_BOOSTER = 0 (stm32mp1)
|
|
* - vdda < 2.7V and vdd < 2.7V: ANASWVDD = 0, EN_BOOSTER = 1
|
|
* - vdda >= 2.7V: ANASWVDD = 0, EN_BOOSTER = 0 (default)
|
|
*/
|
|
if (priv->vdda_uv < 2700000) {
|
|
if (priv->syscfg && priv->vdd_uv > 2700000) {
|
|
ret = regulator_enable(priv->vdd);
|
|
if (ret < 0) {
|
|
dev_err(dev, "vdd enable failed %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = regmap_write(priv->syscfg,
|
|
STM32MP1_SYSCFG_PMCSETR,
|
|
STM32MP1_SYSCFG_ANASWVDD_MASK);
|
|
if (ret < 0) {
|
|
regulator_disable(priv->vdd);
|
|
dev_err(dev, "vdd select failed, %d\n", ret);
|
|
return ret;
|
|
}
|
|
dev_dbg(dev, "analog switches supplied by vdd\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (priv->booster) {
|
|
/*
|
|
* This is optional, as this is a trade-off between
|
|
* analog performance and power consumption.
|
|
*/
|
|
ret = regulator_enable(priv->booster);
|
|
if (ret < 0) {
|
|
dev_err(dev, "booster enable failed %d\n", ret);
|
|
return ret;
|
|
}
|
|
dev_dbg(dev, "analog switches supplied by booster\n");
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Fallback using vdda (default), nothing to do */
|
|
dev_dbg(dev, "analog switches supplied by vdda (%d uV)\n",
|
|
priv->vdda_uv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void stm32_adc_core_switches_supply_dis(struct stm32_adc_priv *priv)
|
|
{
|
|
if (priv->vdda_uv < 2700000) {
|
|
if (priv->syscfg && priv->vdd_uv > 2700000) {
|
|
regmap_write(priv->syscfg, STM32MP1_SYSCFG_PMCCLRR,
|
|
STM32MP1_SYSCFG_ANASWVDD_MASK);
|
|
regulator_disable(priv->vdd);
|
|
return;
|
|
}
|
|
if (priv->booster)
|
|
regulator_disable(priv->booster);
|
|
}
|
|
}
|
|
|
|
static int stm32_adc_core_hw_start(struct device *dev)
|
|
{
|
|
struct stm32_adc_common *common = dev_get_drvdata(dev);
|
|
struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
|
|
int ret;
|
|
|
|
ret = regulator_enable(priv->vdda);
|
|
if (ret < 0) {
|
|
dev_err(dev, "vdda enable failed %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = regulator_get_voltage(priv->vdda);
|
|
if (ret < 0) {
|
|
dev_err(dev, "vdda get voltage failed, %d\n", ret);
|
|
goto err_vdda_disable;
|
|
}
|
|
priv->vdda_uv = ret;
|
|
|
|
ret = stm32_adc_core_switches_supply_en(priv, dev);
|
|
if (ret < 0)
|
|
goto err_vdda_disable;
|
|
|
|
ret = regulator_enable(priv->vref);
|
|
if (ret < 0) {
|
|
dev_err(dev, "vref enable failed\n");
|
|
goto err_switches_dis;
|
|
}
|
|
|
|
if (priv->bclk) {
|
|
ret = clk_prepare_enable(priv->bclk);
|
|
if (ret < 0) {
|
|
dev_err(dev, "bus clk enable failed\n");
|
|
goto err_regulator_disable;
|
|
}
|
|
}
|
|
|
|
if (priv->aclk) {
|
|
ret = clk_prepare_enable(priv->aclk);
|
|
if (ret < 0) {
|
|
dev_err(dev, "adc clk enable failed\n");
|
|
goto err_bclk_disable;
|
|
}
|
|
}
|
|
|
|
writel_relaxed(priv->ccr_bak, priv->common.base + priv->cfg->regs->ccr);
|
|
|
|
return 0;
|
|
|
|
err_bclk_disable:
|
|
if (priv->bclk)
|
|
clk_disable_unprepare(priv->bclk);
|
|
err_regulator_disable:
|
|
regulator_disable(priv->vref);
|
|
err_switches_dis:
|
|
stm32_adc_core_switches_supply_dis(priv);
|
|
err_vdda_disable:
|
|
regulator_disable(priv->vdda);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void stm32_adc_core_hw_stop(struct device *dev)
|
|
{
|
|
struct stm32_adc_common *common = dev_get_drvdata(dev);
|
|
struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
|
|
|
|
/* Backup CCR that may be lost (depends on power state to achieve) */
|
|
priv->ccr_bak = readl_relaxed(priv->common.base + priv->cfg->regs->ccr);
|
|
if (priv->aclk)
|
|
clk_disable_unprepare(priv->aclk);
|
|
if (priv->bclk)
|
|
clk_disable_unprepare(priv->bclk);
|
|
regulator_disable(priv->vref);
|
|
stm32_adc_core_switches_supply_dis(priv);
|
|
regulator_disable(priv->vdda);
|
|
}
|
|
|
|
static int stm32_adc_core_switches_probe(struct device *dev,
|
|
struct stm32_adc_priv *priv)
|
|
{
|
|
struct device_node *np = dev->of_node;
|
|
int ret;
|
|
|
|
/* Analog switches supply can be controlled by syscfg (optional) */
|
|
priv->syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
|
|
if (IS_ERR(priv->syscfg)) {
|
|
ret = PTR_ERR(priv->syscfg);
|
|
if (ret != -ENODEV) {
|
|
if (ret != -EPROBE_DEFER)
|
|
dev_err(dev, "Can't probe syscfg: %d\n", ret);
|
|
return ret;
|
|
}
|
|
priv->syscfg = NULL;
|
|
}
|
|
|
|
/* Booster can be used to supply analog switches (optional) */
|
|
if (priv->cfg->has_syscfg & HAS_VBOOSTER &&
|
|
of_property_read_bool(np, "booster-supply")) {
|
|
priv->booster = devm_regulator_get_optional(dev, "booster");
|
|
if (IS_ERR(priv->booster)) {
|
|
ret = PTR_ERR(priv->booster);
|
|
if (ret != -ENODEV) {
|
|
if (ret != -EPROBE_DEFER)
|
|
dev_err(dev, "can't get booster %d\n",
|
|
ret);
|
|
return ret;
|
|
}
|
|
priv->booster = NULL;
|
|
}
|
|
}
|
|
|
|
/* Vdd can be used to supply analog switches (optional) */
|
|
if (priv->cfg->has_syscfg & HAS_ANASWVDD &&
|
|
of_property_read_bool(np, "vdd-supply")) {
|
|
priv->vdd = devm_regulator_get_optional(dev, "vdd");
|
|
if (IS_ERR(priv->vdd)) {
|
|
ret = PTR_ERR(priv->vdd);
|
|
if (ret != -ENODEV) {
|
|
if (ret != -EPROBE_DEFER)
|
|
dev_err(dev, "can't get vdd %d\n", ret);
|
|
return ret;
|
|
}
|
|
priv->vdd = NULL;
|
|
}
|
|
}
|
|
|
|
if (priv->vdd) {
|
|
ret = regulator_enable(priv->vdd);
|
|
if (ret < 0) {
|
|
dev_err(dev, "vdd enable failed %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = regulator_get_voltage(priv->vdd);
|
|
if (ret < 0) {
|
|
dev_err(dev, "vdd get voltage failed %d\n", ret);
|
|
regulator_disable(priv->vdd);
|
|
return ret;
|
|
}
|
|
priv->vdd_uv = ret;
|
|
|
|
regulator_disable(priv->vdd);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stm32_adc_probe(struct platform_device *pdev)
|
|
{
|
|
struct stm32_adc_priv *priv;
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct resource *res;
|
|
u32 max_rate;
|
|
int ret;
|
|
|
|
if (!pdev->dev.of_node)
|
|
return -ENODEV;
|
|
|
|
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
|
if (!priv)
|
|
return -ENOMEM;
|
|
platform_set_drvdata(pdev, &priv->common);
|
|
|
|
priv->cfg = (const struct stm32_adc_priv_cfg *)
|
|
of_match_device(dev->driver->of_match_table, dev)->data;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
priv->common.base = devm_ioremap_resource(&pdev->dev, res);
|
|
if (IS_ERR(priv->common.base))
|
|
return PTR_ERR(priv->common.base);
|
|
priv->common.phys_base = res->start;
|
|
|
|
priv->vdda = devm_regulator_get(&pdev->dev, "vdda");
|
|
if (IS_ERR(priv->vdda)) {
|
|
ret = PTR_ERR(priv->vdda);
|
|
if (ret != -EPROBE_DEFER)
|
|
dev_err(&pdev->dev, "vdda get failed, %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
priv->vref = devm_regulator_get(&pdev->dev, "vref");
|
|
if (IS_ERR(priv->vref)) {
|
|
ret = PTR_ERR(priv->vref);
|
|
dev_err(&pdev->dev, "vref get failed, %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
priv->aclk = devm_clk_get(&pdev->dev, "adc");
|
|
if (IS_ERR(priv->aclk)) {
|
|
ret = PTR_ERR(priv->aclk);
|
|
if (ret != -ENOENT) {
|
|
dev_err(&pdev->dev, "Can't get 'adc' clock\n");
|
|
return ret;
|
|
}
|
|
priv->aclk = NULL;
|
|
}
|
|
|
|
priv->bclk = devm_clk_get(&pdev->dev, "bus");
|
|
if (IS_ERR(priv->bclk)) {
|
|
ret = PTR_ERR(priv->bclk);
|
|
if (ret != -ENOENT) {
|
|
dev_err(&pdev->dev, "Can't get 'bus' clock\n");
|
|
return ret;
|
|
}
|
|
priv->bclk = NULL;
|
|
}
|
|
|
|
ret = stm32_adc_core_switches_probe(dev, priv);
|
|
if (ret)
|
|
return ret;
|
|
|
|
pm_runtime_get_noresume(dev);
|
|
pm_runtime_set_active(dev);
|
|
pm_runtime_set_autosuspend_delay(dev, STM32_ADC_CORE_SLEEP_DELAY_MS);
|
|
pm_runtime_use_autosuspend(dev);
|
|
pm_runtime_enable(dev);
|
|
|
|
ret = stm32_adc_core_hw_start(dev);
|
|
if (ret)
|
|
goto err_pm_stop;
|
|
|
|
ret = regulator_get_voltage(priv->vref);
|
|
if (ret < 0) {
|
|
dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
|
|
goto err_hw_stop;
|
|
}
|
|
priv->common.vref_mv = ret / 1000;
|
|
dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
|
|
|
|
ret = of_property_read_u32(pdev->dev.of_node, "st,max-clk-rate-hz",
|
|
&max_rate);
|
|
if (!ret)
|
|
priv->max_clk_rate = min(max_rate, priv->cfg->max_clk_rate_hz);
|
|
else
|
|
priv->max_clk_rate = priv->cfg->max_clk_rate_hz;
|
|
|
|
ret = priv->cfg->clk_sel(pdev, priv);
|
|
if (ret < 0)
|
|
goto err_hw_stop;
|
|
|
|
ret = stm32_adc_irq_probe(pdev, priv);
|
|
if (ret < 0)
|
|
goto err_hw_stop;
|
|
|
|
ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
|
|
if (ret < 0) {
|
|
dev_err(&pdev->dev, "failed to populate DT children\n");
|
|
goto err_irq_remove;
|
|
}
|
|
|
|
pm_runtime_mark_last_busy(dev);
|
|
pm_runtime_put_autosuspend(dev);
|
|
|
|
return 0;
|
|
|
|
err_irq_remove:
|
|
stm32_adc_irq_remove(pdev, priv);
|
|
err_hw_stop:
|
|
stm32_adc_core_hw_stop(dev);
|
|
err_pm_stop:
|
|
pm_runtime_disable(dev);
|
|
pm_runtime_set_suspended(dev);
|
|
pm_runtime_put_noidle(dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int stm32_adc_remove(struct platform_device *pdev)
|
|
{
|
|
struct stm32_adc_common *common = platform_get_drvdata(pdev);
|
|
struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
|
|
|
|
pm_runtime_get_sync(&pdev->dev);
|
|
of_platform_depopulate(&pdev->dev);
|
|
stm32_adc_irq_remove(pdev, priv);
|
|
stm32_adc_core_hw_stop(&pdev->dev);
|
|
pm_runtime_disable(&pdev->dev);
|
|
pm_runtime_set_suspended(&pdev->dev);
|
|
pm_runtime_put_noidle(&pdev->dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_PM)
|
|
static int stm32_adc_core_runtime_suspend(struct device *dev)
|
|
{
|
|
stm32_adc_core_hw_stop(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stm32_adc_core_runtime_resume(struct device *dev)
|
|
{
|
|
return stm32_adc_core_hw_start(dev);
|
|
}
|
|
#endif
|
|
|
|
static const struct dev_pm_ops stm32_adc_core_pm_ops = {
|
|
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
|
pm_runtime_force_resume)
|
|
SET_RUNTIME_PM_OPS(stm32_adc_core_runtime_suspend,
|
|
stm32_adc_core_runtime_resume,
|
|
NULL)
|
|
};
|
|
|
|
static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = {
|
|
.regs = &stm32f4_adc_common_regs,
|
|
.clk_sel = stm32f4_adc_clk_sel,
|
|
.max_clk_rate_hz = 36000000,
|
|
};
|
|
|
|
static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = {
|
|
.regs = &stm32h7_adc_common_regs,
|
|
.clk_sel = stm32h7_adc_clk_sel,
|
|
.max_clk_rate_hz = 36000000,
|
|
.has_syscfg = HAS_VBOOSTER,
|
|
};
|
|
|
|
static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
|
|
.regs = &stm32h7_adc_common_regs,
|
|
.clk_sel = stm32h7_adc_clk_sel,
|
|
.max_clk_rate_hz = 40000000,
|
|
.has_syscfg = HAS_VBOOSTER | HAS_ANASWVDD,
|
|
};
|
|
|
|
static const struct of_device_id stm32_adc_of_match[] = {
|
|
{
|
|
.compatible = "st,stm32f4-adc-core",
|
|
.data = (void *)&stm32f4_adc_priv_cfg
|
|
}, {
|
|
.compatible = "st,stm32h7-adc-core",
|
|
.data = (void *)&stm32h7_adc_priv_cfg
|
|
}, {
|
|
.compatible = "st,stm32mp1-adc-core",
|
|
.data = (void *)&stm32mp1_adc_priv_cfg
|
|
}, {
|
|
},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
|
|
|
|
static struct platform_driver stm32_adc_driver = {
|
|
.probe = stm32_adc_probe,
|
|
.remove = stm32_adc_remove,
|
|
.driver = {
|
|
.name = "stm32-adc-core",
|
|
.of_match_table = stm32_adc_of_match,
|
|
.pm = &stm32_adc_core_pm_ops,
|
|
},
|
|
};
|
|
module_platform_driver(stm32_adc_driver);
|
|
|
|
MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
|
|
MODULE_DESCRIPTION("STMicroelectronics STM32 ADC core driver");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_ALIAS("platform:stm32-adc-core");
|