Received: by 2002:a25:ab43:0:0:0:0:0 with SMTP id u61csp1579253ybi; Sun, 16 Jun 2019 08:10:22 -0700 (PDT) X-Google-Smtp-Source: APXvYqw7u7Sr2nEsBshvUm87IEl1CkLK8/s48mT023ISrK18xJBlW4HJEmPWqufotlejqYS/ECcl X-Received: by 2002:aa7:919a:: with SMTP id x26mr95574222pfa.134.1560697822165; Sun, 16 Jun 2019 08:10:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1560697822; cv=none; d=google.com; s=arc-20160816; b=lb/jWFuYKbyAxqLV0qBrEowgU6Cw88gKWt8EBr+9mfzHD4Q++VE00B5JIe6YAjYlAF GeOlHZ/GSsKBaDOQBZpxPzTsV4UirdFLRJApZjady+dLsxWJwYwHl26TowlHy66cvjgv FcQRBNCziECN+zY6a60JQkh3vvKXhFdt8uh1G4DQwVtroawWvc/ufE7mSE+PiTNzBpan /E7eMvzqKT80r4YCj4RykkOIUUhN9FqaZSdoEJSu0MND+u9TAUFlzdSx9zvhM3Y6Tu8y QrI2syBYU1e1kqu9OhTtcWKFzPqJfwqIChGyqrGNehofa2i7zolYBuhiYLzJ52zaoO1u dBYg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:subject:cc:to:from:date :dkim-signature; bh=H8wF9JfMZ3WE+EO7loQEPcbBUWsbdNw/sDUtY/Ey6RY=; b=MO/GMNGGBXVH+fISV9g8Mp3AJjeUSP+7y3sKGwAfYoJd/UwbBUtRUKWa9IjsBhcKpR I1l1qQ5uLkHYktJ8YZlKh80E6546gTtT7Dp335lgv62Iesp4XLtbAx4rxwKgDURMvgSB uR6fazkrrINL9l37o7+zicZqQ/g6vaIBBFmBYcmNGV6YO8hYebLXFTTFMZCD8RBQMwWZ T3W5igagXiC//1ccp8+KC2PuQONPLcadbPfnjZorqtG8pPgEB+L3rMTfeTAx5PnTOW9h 9h91Hkb7iSgc3cQtvN1mzGXeDbE7nffub8v7XtwmNpApfZnXrQagTzsr1K0w+KGM4Iys 1hfA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b="I3hQow/c"; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id s24si8237457pga.515.2019.06.16.08.09.55; Sun, 16 Jun 2019 08:10:22 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b="I3hQow/c"; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727230AbfFPPHk (ORCPT + 99 others); Sun, 16 Jun 2019 11:07:40 -0400 Received: from mail.kernel.org ([198.145.29.99]:35070 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725879AbfFPPHj (ORCPT ); Sun, 16 Jun 2019 11:07:39 -0400 Received: from archlinux (cpc149474-cmbg20-2-0-cust94.5-4.cable.virginm.net [82.4.196.95]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 846F72064B; Sun, 16 Jun 2019 15:07:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1560697658; bh=KjfP2Iy5IlnLbdApS3yWSLJRMp/gBbiDEhtqpQsQYY4=; h=Date:From:To:Cc:Subject:In-Reply-To:References:From; b=I3hQow/clmsLGIG5T5PZS7cv+UiL2irY+3nUWUUYMQeHw1rG0vsyFm9bQVRVZ554m YbwF957vdMGjkjs5VuaVkfP+G0HQIO0TYUDui+GSQ0UTJ/p/im78uQ8mqvjabEQ+7V 2sAAfACYoarHwEZiH7DCC57mauFjEUBRiZbSZYIw= Date: Sun, 16 Jun 2019 16:07:32 +0100 From: Jonathan Cameron To: Fabrice Gasnier Cc: , , , , , , , , , , , Subject: Re: [PATCH 2/3] iio: adc: stm32-adc: add analog switches supply control Message-ID: <20190616160732.124a1eb9@archlinux> In-Reply-To: <1560324276-681-3-git-send-email-fabrice.gasnier@st.com> References: <1560324276-681-1-git-send-email-fabrice.gasnier@st.com> <1560324276-681-3-git-send-email-fabrice.gasnier@st.com> X-Mailer: Claws Mail 3.17.3 (GTK+ 2.24.32; x86_64-pc-linux-gnu) MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Wed, 12 Jun 2019 09:24:35 +0200 Fabrice Gasnier wrote: > On stm32h7 and stm32mp1, the ADC inputs are multiplexed with analog > switches which have reduced performances when their supply is below 2.7V > (vdda by default): > - vdd supply can be selected if above 2.7V by setting ANASWVDD syscfg bit > (STM32MP1 only). > - Voltage booster can be used, to get full ADC performances by setting > BOOSTE/EN_BOOSTER syscfg bit (increases power consumption). > > Make this optional, since this is a trade-off between analog performance > and power consumption. > > Note: STM32H7 syscfg has a set and clear register for "BOOSTE" control. > STM32MP1 has separate set and clear registers pair to control EN_BOOSTER > and ANASWVDD bits. > > Signed-off-by: Fabrice Gasnier A few minor bits inline, but mostly seems fine to me. Jonathan > --- > drivers/iio/adc/stm32-adc-core.c | 232 ++++++++++++++++++++++++++++++++++++++- > 1 file changed, 230 insertions(+), 2 deletions(-) > > diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c > index 2327ec1..9d41b16 100644 > --- a/drivers/iio/adc/stm32-adc-core.c > +++ b/drivers/iio/adc/stm32-adc-core.c > @@ -14,9 +14,11 @@ > #include > #include > #include > +#include > #include > #include > #include > +#include > #include > #include > > @@ -51,6 +53,20 @@ > > #define STM32_ADC_CORE_SLEEP_DELAY_MS 2000 > > +/* SYSCFG registers */ > +#define STM32H7_SYSCFG_PMCR 0x04 > +#define STM32MP1_SYSCFG_PMCSETR 0x04 > +#define STM32MP1_SYSCFG_PMCCLRR 0x44 > + > +/* SYSCFG bit fields */ > +#define STM32H7_SYSCFG_BOOSTE_MASK BIT(8) > +#define STM32MP1_SYSCFG_ANASWVDD_MASK BIT(9) > + > +/* SYSCFG capability flags */ > +#define HAS_VBOOSTER BIT(0) > +#define HAS_ANASWVDD BIT(1) > +#define HAS_CLEAR_REG BIT(2) > + > /** > * stm32_adc_common_regs - stm32 common registers, compatible dependent data > * @csr: common status register offset > @@ -58,6 +74,11 @@ > * @eoc1: adc1 end of conversion flag in @csr > * @eoc2: adc2 end of conversion flag in @csr > * @eoc3: adc3 end of conversion flag in @csr > + * @has_syscfg: SYSCFG capability flags > + * @pmcr: SYSCFG_PMCSETR/SYSCFG_PMCR register offset > + * @pmcc: SYSCFG_PMCCLRR clear register offset > + * @booste_msk: SYSCFG BOOSTE / EN_BOOSTER bitmask in PMCR & PMCCLRR > + * @anaswvdd_msk: SYSCFG ANASWVDD bitmask in PMCR & PMCCLRR > */ > struct stm32_adc_common_regs { > u32 csr; > @@ -65,6 +86,11 @@ struct stm32_adc_common_regs { > u32 eoc1_msk; > u32 eoc2_msk; > u32 eoc3_msk; > + unsigned int has_syscfg; > + u32 pmcr; > + u32 pmcc; > + u32 booste_msk; > + u32 anaswvdd_msk; > }; > > struct stm32_adc_priv; > @@ -87,20 +113,26 @@ struct stm32_adc_priv_cfg { > * @domain: irq domain reference > * @aclk: clock reference for the analog circuitry > * @bclk: bus clock common for all ADCs, depends on part used > + * @vdd: vdd supply reference > + * @vdda: vdda supply reference > * @vref: regulator reference > * @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; > + struct regulator *vdd; > + struct regulator *vdda; > struct regulator *vref; > 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) > @@ -284,6 +316,22 @@ static const struct stm32_adc_common_regs stm32h7_adc_common_regs = { > .ccr = STM32H7_ADC_CCR, > .eoc1_msk = STM32H7_EOC_MST, > .eoc2_msk = STM32H7_EOC_SLV, > + .has_syscfg = HAS_VBOOSTER, > + .pmcr = STM32H7_SYSCFG_PMCR, > + .booste_msk = STM32H7_SYSCFG_BOOSTE_MASK, > +}; > + > +/* STM32MP1 common registers definitions */ > +static const struct stm32_adc_common_regs stm32mp1_adc_common_regs = { > + .csr = STM32H7_ADC_CSR, > + .ccr = STM32H7_ADC_CCR, > + .eoc1_msk = STM32H7_EOC_MST, > + .eoc2_msk = STM32H7_EOC_SLV, > + .has_syscfg = HAS_VBOOSTER | HAS_ANASWVDD | HAS_CLEAR_REG, Extra space after = > + .pmcr = STM32MP1_SYSCFG_PMCSETR, > + .pmcc = STM32MP1_SYSCFG_PMCCLRR, > + .booste_msk = STM32H7_SYSCFG_BOOSTE_MASK, > + .anaswvdd_msk = STM32MP1_SYSCFG_ANASWVDD_MASK, > }; > > /* ADC common interrupt for all instances */ > @@ -388,16 +436,145 @@ static void stm32_adc_irq_remove(struct platform_device *pdev, > } > } > > +static int stm32_adc_core_switches_supply_en(struct device *dev) > +{ > + struct stm32_adc_common *common = dev_get_drvdata(dev); > + struct stm32_adc_priv *priv = to_stm32_adc_priv(common); > + const struct stm32_adc_common_regs *regs = priv->cfg->regs; > + int ret, vdda, vdd = 0; > + u32 mask, clrmask, setmask = 0; > + > + /* > + * 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). > + * > + * This is optional, as this is a trade-off between analog performance > + * and power consumption. > + */ > + if (!regs->has_syscfg || !priv->vdda || !priv->syscfg) { > + dev_dbg(dev, "Not configuring analog switches\n"); > + return 0; > + } > + > + 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 vdda_dis; > + } > + vdda = ret; > + We only need to do the following block if vdaa is too low. Should probably not turn on vdd if there is not chance we are going to use it? > + if (priv->vdd && regs->has_syscfg & HAS_ANASWVDD) { > + ret = regulator_enable(priv->vdd); > + if (ret < 0) { > + dev_err(dev, "vdd enable failed %d\n", ret); > + goto vdda_dis; > + } > + > + ret = regulator_get_voltage(priv->vdd); > + if (ret < 0) { > + dev_err(dev, "vdd get voltage failed %d\n", ret); > + goto vdd_dis; > + } > + vdd = ret; > + } > + > + /* > + * 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 (vdda < 2700000) { > + if (vdd > 2700000) { > + dev_dbg(dev, "analog switches supplied by vdd\n"); > + setmask = regs->anaswvdd_msk; > + clrmask = regs->booste_msk; > + } else { > + dev_dbg(dev, "Enabling voltage booster\n"); > + setmask = regs->booste_msk; > + clrmask = regs->anaswvdd_msk; > + } > + } else { > + dev_dbg(dev, "analog switches supplied by vdda\n"); > + clrmask = regs->booste_msk | regs->anaswvdd_msk; > + } > + > + mask = regs->booste_msk | regs->anaswvdd_msk; > + if (regs->has_syscfg & HAS_CLEAR_REG) { > + ret = regmap_write(priv->syscfg, regs->pmcc, clrmask); > + if (ret) { > + dev_err(dev, "syscfg clear failed, %d\n", ret); > + goto vdd_dis; > + } > + mask = setmask; > + } > + > + ret = regmap_update_bits(priv->syscfg, regs->pmcr, mask, setmask); > + if (ret) { > + dev_err(dev, "syscfg update failed, %d\n", ret); > + goto vdd_dis; > + } > + > + /* Booster voltage can take up to 50 us to stabilize */ > + if (setmask & regs->booste_msk) > + usleep_range(50, 100); > + > + return ret; > + > +vdd_dis: > + if (priv->vdd && (regs->has_syscfg & HAS_ANASWVDD)) > + regulator_disable(priv->vdd); > +vdda_dis: > + regulator_disable(priv->vdda); > + > + return ret; > +} > + > +static void stm32_adc_core_switches_supply_dis(struct device *dev) > +{ > + struct stm32_adc_common *common = dev_get_drvdata(dev); > + struct stm32_adc_priv *priv = to_stm32_adc_priv(common); > + const struct stm32_adc_common_regs *regs = priv->cfg->regs; > + u32 mask = regs->booste_msk | regs->anaswvdd_msk; > + > + if (!regs->has_syscfg || !priv->vdda || !priv->syscfg) > + return; > + > + if (regs->has_syscfg & HAS_CLEAR_REG) > + regmap_write(priv->syscfg, regs->pmcc, mask); > + else > + regmap_update_bits(priv->syscfg, regs->pmcr, mask, 0); > + > + if (priv->vdd && (regs->has_syscfg & HAS_ANASWVDD)) > + regulator_disable(priv->vdd); > + > + regulator_disable(priv->vdda); > +} > + > 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 = stm32_adc_core_switches_supply_en(dev); > + if (ret < 0) > + return ret; > + > ret = regulator_enable(priv->vref); > if (ret < 0) { > dev_err(dev, "vref enable failed\n"); > - return ret; > + goto err_switches_disable; > } > > if (priv->bclk) { > @@ -425,6 +602,8 @@ static int stm32_adc_core_hw_start(struct device *dev) > clk_disable_unprepare(priv->bclk); > err_regulator_disable: > regulator_disable(priv->vref); > +err_switches_disable: > + stm32_adc_core_switches_supply_dis(dev); > > return ret; > } > @@ -441,6 +620,24 @@ static void stm32_adc_core_hw_stop(struct device *dev) > if (priv->bclk) > clk_disable_unprepare(priv->bclk); > regulator_disable(priv->vref); > + stm32_adc_core_switches_supply_dis(dev); > +} > + > +static int stm32_adc_core_syscfg_probe(struct device_node *np, > + struct stm32_adc_priv *priv) > +{ > + if (!priv->cfg->regs->has_syscfg) > + return 0; > + > + priv->syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); > + if (IS_ERR(priv->syscfg)) { > + /* Optional */ > + if (PTR_ERR(priv->syscfg) != -ENODEV) > + return PTR_ERR(priv->syscfg); > + priv->syscfg = NULL; > + } > + > + return 0; > } > > static int stm32_adc_probe(struct platform_device *pdev) > @@ -475,6 +672,30 @@ static int stm32_adc_probe(struct platform_device *pdev) > return ret; > } > > + priv->vdda = devm_regulator_get_optional(&pdev->dev, "vdda"); > + if (IS_ERR(priv->vdda)) { > + ret = PTR_ERR(priv->vdda); > + if (ret != -ENODEV) { > + if (ret != -EPROBE_DEFER) > + dev_err(&pdev->dev, "vdda get failed, %d\n", > + ret); > + return ret; > + } > + priv->vdda = NULL; > + } > + > + priv->vdd = devm_regulator_get_optional(&pdev->dev, "vdd"); > + if (IS_ERR(priv->vdd)) { > + ret = PTR_ERR(priv->vdd); > + if (ret != -ENODEV) { > + if (ret != -EPROBE_DEFER) > + dev_err(&pdev->dev, "vdd get failed, %d\n", > + ret); > + return ret; > + } > + priv->vdd = NULL; > + } > + > priv->aclk = devm_clk_get(&pdev->dev, "adc"); > if (IS_ERR(priv->aclk)) { > ret = PTR_ERR(priv->aclk); > @@ -495,6 +716,13 @@ static int stm32_adc_probe(struct platform_device *pdev) > priv->bclk = NULL; > } > > + ret = stm32_adc_core_syscfg_probe(np, priv); > + if (ret) { > + if (ret != -EPROBE_DEFER) > + dev_err(&pdev->dev, "Can't probe syscfg: %d\n", 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); > @@ -595,7 +823,7 @@ static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = { > }; > > static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = { > - .regs = &stm32h7_adc_common_regs, > + .regs = &stm32mp1_adc_common_regs, > .clk_sel = stm32h7_adc_clk_sel, > .max_clk_rate_hz = 40000000, > };