Received: by 2002:ac0:a5b6:0:0:0:0:0 with SMTP id m51-v6csp2655255imm; Sun, 17 Jun 2018 01:04:19 -0700 (PDT) X-Google-Smtp-Source: ADUXVKLW8lLkTTldzJxbm2clPd9LLZ7CjNSVn54gnHOfhAwcbpm5tlsaA/z52hzoxtCCXz5bIMkx X-Received: by 2002:a62:5788:: with SMTP id i8-v6mr8723564pfj.175.1529222659561; Sun, 17 Jun 2018 01:04:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1529222659; cv=none; d=google.com; s=arc-20160816; b=opjj0MpYHK8z4APjcw/y6wuQc0HvE610bCgF0BnO+sBUMFSe7uLbgfvNiLcRfpW7af BV+1vMpX9kLc4raTnSOb4iS8ogRfXS89O5MyqwfmyVs4RB7Q8Oa7RoRtNUiXZHwZPbNX 66H+c/3oZp3POMYtdbO4IIpGbXXbn2j6aTm+j1Kr5co0xWC9a2A56yQHOTQWm6x88lIe MOK84Dm0/9iKxCMESIAC3LuSTCmjUs5TO0hkuZWVV0VaNqXE5T5z9qOP/BERsQtXQ9yQ yEwWmHVDcJjGms+tXCKdNHUXi4YZIRtbhIq6VnAMBPo82y3/DJcfb9+PWCigypBIjEIW olNw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:subject:message-id:date:from :references:in-reply-to:mime-version:dkim-signature :arc-authentication-results; bh=KxkSHvKyb9izYCMdIwU8ovn1hTQdBsdAaGsqIm+qyDc=; b=j0SMEpzaPfDl1wEsPFzU9xmWgROTxTP2SPWK0psYF8Q32zi8yT/+lS0qscLey+gLgl 1UmeJ4oLWmgIsEjkhJtvSt4u3Wh0VPiJfqhi5g7nTzzqzPW/FRl8k/KscuhHM1Zv4F1h SCDB2Wu6+AcMsKzlTZFG3QmoPSK4bp8l1MZpSdPeo1ICsYjdaDsbqhYHQOE9SbVGGNaS y9c2P9jW2ouuulUO/jNXk+8OvbWOMD8XTdl1UzGhSmclrcaNRvX6wAgajYTXEsqpIaKm uacqlZT9HnXtopUxV0UsE5YE5DDCaTx+WBNuPYHbl2i+d8h/gSRdx1rTWQm/40YModo/ YfRQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b="SHBYf/pS"; 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=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id s1-v6si10115901pga.296.2018.06.17.01.03.53; Sun, 17 Jun 2018 01:04:19 -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=@linaro.org header.s=google header.b="SHBYf/pS"; 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=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932584AbeFQIDK (ORCPT + 99 others); Sun, 17 Jun 2018 04:03:10 -0400 Received: from mail-ot0-f196.google.com ([74.125.82.196]:38402 "EHLO mail-ot0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932451AbeFQIDG (ORCPT ); Sun, 17 Jun 2018 04:03:06 -0400 Received: by mail-ot0-f196.google.com with SMTP id p95-v6so15361734ota.5 for ; Sun, 17 Jun 2018 01:03:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=KxkSHvKyb9izYCMdIwU8ovn1hTQdBsdAaGsqIm+qyDc=; b=SHBYf/pS1SHNVs/XNb7kyrShVJNCOkTU4F9MUEawj7pi5M5PMkLTe6KL+Mx9XZx/DG CgQ01CiDU+D7qIj5Oi6eQTbJcPKqyFd/lZZALZrtahjAmxKqNPFJ59l/I8kRPsh6oxZv Bz5ElSdYSafO1HsMeMjz/HDPsRRMzYiuQmpCs= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=KxkSHvKyb9izYCMdIwU8ovn1hTQdBsdAaGsqIm+qyDc=; b=fV6TkOCt8kqiOPRUPPQ/TxVmSBUbQ6nWV0LgByrOwKwfDDkbhOGYinYlzUgkxTDnAr Lg1WC9eUwY6an0ETLJXd+jSgBnQ57aWUzpAzkYAzGtWyp30n2sV7pFFcbFcytSefjZxp 9b7Mp7lE/Z0+Nsk55UoE+gaSRd5lviE3R35+Up28TWNtVAM9Sl+mjHRZx8bmN8ESX/NG DT3KXI1FblSmV9Tz5OEpU73opPIJdU3PbXZdIXXAiBiYdeZT86IMRykVz85syIYuBr4M P4QjVhRCt6CSy/Yq5z1wptCygXvCCwL9hqXFYIukxF7uUttVU7gJi2LmIeuHg/uyLBX+ pf4A== X-Gm-Message-State: APt69E2tQ1chavp6XWquJb4wRC0kihS5YxdEsFb5E4h+2RXSwTun9yOL fYHz6TrfooxJqWljZlY7VMs07q6IPalliNnrcfJeIA== X-Received: by 2002:a9d:11f4:: with SMTP id y49-v6mr5323596oty.141.1529222585507; Sun, 17 Jun 2018 01:03:05 -0700 (PDT) MIME-Version: 1.0 Received: by 2002:a9d:2d44:0:0:0:0:0 with HTTP; Sun, 17 Jun 2018 01:03:04 -0700 (PDT) In-Reply-To: <20180616193540.6ed717f7@archlinux> References: <9b6743bb6782041b7fec9ed0e166faf2b6456de4.1529040864.git.baolin.wang@linaro.org> <5728839377cefd20cdb95913b43dbdab530c1e81.1529040864.git.baolin.wang@linaro.org> <20180616193540.6ed717f7@archlinux> From: Baolin Wang Date: Sun, 17 Jun 2018 16:03:04 +0800 Message-ID: Subject: Re: [PATCH v2 2/2] iio: adc: Add Spreadtrum SC27XX PMICs ADC support To: Jonathan Cameron Cc: Rob Herring , Mark Rutland , Hartmut Knaack , Lars-Peter Clausen , Peter Meerwald-Stadler , freeman.liu@spreadtrum.com, Mark Brown , DTML , linux-iio@vger.kernel.org, LKML Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi Jonathan, On 17 June 2018 at 02:35, Jonathan Cameron wrote: > On Fri, 15 Jun 2018 15:03:36 +0800 > Baolin Wang wrote: > >> From: Freeman Liu >> >> The Spreadtrum SC27XX PMICs ADC controller contains 32 channels, >> which is used to sample voltages with 12 bits conversion. >> >> Signed-off-by: Freeman Liu >> Signed-off-by: Baolin Wang > > Hi, > > There are some race conditions around the probe and remove. > More care is needed when we have a mixture of managed and unmanaged cleanup > like here. Thanks to point the race issue. > > I'm not understanding the way you have exposed a simple _raw and _scale > attributes with what looks to be different scaling to that applied > in _processed. As I say below, we should not have both of those interface > options anyway. The ABI is that (X_raw + X_offset)*X_scale = X_processed. > (with defaults of X_scale = 1 and X_offset = 0). See below comments. > > Please rename to avoid using wild cards in the name. That's gone > wrong so many times in the past you wouldn't believe it! > Hmm Awkward though if the MFD is already upstream. Ah well, I guess > for consistency we should follow that and groan when it goes wrong. Can I rename to be 'sprd-pmic-adc.c'? I can not rename it as 'sc2731-adc', we have differnet PMICs (SC2730, SC2731, SC2720 etc.), but they are all integrated the same ADC controller. >> --- >> Changes since v1: >> - Add const for static structures definition. >> - Change SC27XX_ADC_TO_VOLTAGE macro to be one function. >> - Move channel scale accessing into mutex protection. >> - Fix some typos. >> --- >> drivers/iio/adc/Kconfig | 10 + >> drivers/iio/adc/Makefile | 1 + >> drivers/iio/adc/sc27xx_adc.c | 547 ++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 558 insertions(+) >> create mode 100644 drivers/iio/adc/sc27xx_adc.c >> >> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig >> index 9da7907..985b73e 100644 >> --- a/drivers/iio/adc/Kconfig >> +++ b/drivers/iio/adc/Kconfig >> @@ -621,6 +621,16 @@ config ROCKCHIP_SARADC >> To compile this driver as a module, choose M here: the >> module will be called rockchip_saradc. >> >> +config SC27XX_ADC >> + tristate "Spreadtrum SC27xx series PMICs ADC" >> + depends on MFD_SC27XX_PMIC || COMPILE_TEST >> + help >> + Say yes here to build support for the integrated ADC inside the >> + Spreadtrum SC27xx series PMICs. >> + >> + This driver can also be built as a module. If so, the module >> + will be called sc27xx_adc. >> + >> config SPEAR_ADC >> tristate "ST SPEAr ADC" >> depends on PLAT_SPEAR || COMPILE_TEST >> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile >> index 28a9423..03db7b5 100644 >> --- a/drivers/iio/adc/Makefile >> +++ b/drivers/iio/adc/Makefile >> @@ -59,6 +59,7 @@ obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o >> obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o >> obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o >> obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o >> +obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o >> obj-$(CONFIG_SPEAR_ADC) += spear_adc.o >> obj-$(CONFIG_STX104) += stx104.o >> obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o >> diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c >> new file mode 100644 >> index 0000000..52e5b74 >> --- /dev/null >> +++ b/drivers/iio/adc/sc27xx_adc.c > > In general (i.e. when we notice in time) we don't allow wild cards in names. > Far too many times we did this in the past and ended up with later parts > that fitted the name, but could not be supported by the driver. > > The convention is to name everything after the first part supported. > So here, sc2731. (I relaxed my thoughts on this later having seen the mfd > has this naming - so there are no ideal options left..) Like I explained above, maybe change to 'sprd_pmic_adc.c', is this OK for you? > >> @@ -0,0 +1,547 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +// Copyright (C) 2018 Spreadtrum Communications Inc. >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +/* PMIC global registers definition */ >> +#define SC27XX_MODULE_EN 0xc08 > Please avoid wild cards. This goes wrong an awful lot of the time > when a company comes out with an incompatible part that fits in the > existing wild cards. Sure. > >> +#define SC27XX_MODULE_ADC_EN BIT(5) >> +#define SC27XX_ARM_CLK_EN 0xc10 >> +#define SC27XX_CLK_ADC_EN BIT(5) >> +#define SC27XX_CLK_ADC_CLK_EN BIT(6) >> + >> +/* ADC controller registers definition */ >> +#define SC27XX_ADC_CTL 0x0 >> +#define SC27XX_ADC_CH_CFG 0x4 >> +#define SC27XX_ADC_DATA 0x4c >> +#define SC27XX_ADC_INT_EN 0x50 >> +#define SC27XX_ADC_INT_CLR 0x54 >> +#define SC27XX_ADC_INT_STS 0x58 >> +#define SC27XX_ADC_INT_RAW 0x5c >> + >> +/* Bits and mask definition for SC27XX_ADC_CTL register */ >> +#define SC27XX_ADC_EN BIT(0) >> +#define SC27XX_ADC_CHN_RUN BIT(1) >> +#define SC27XX_ADC_12BIT_MODE BIT(2) >> +#define SC27XX_ADC_RUN_NUM_MASK GENMASK(7, 4) >> +#define SC27XX_ADC_RUN_NUM_SHIFT 4 >> + >> +/* Bits and mask definition for SC27XX_ADC_CH_CFG register */ >> +#define SC27XX_ADC_CHN_ID_MASK GENMASK(4, 0) >> +#define SC27XX_ADC_SCALE_MASK GENMASK(10, 8) >> +#define SC27XX_ADC_SCALE_SHIFT 8 >> + >> +/* Bits definitions for SC27XX_ADC_INT_EN registers */ >> +#define SC27XX_ADC_IRQ_EN BIT(0) >> + >> +/* Bits definitions for SC27XX_ADC_INT_CLR registers */ >> +#define SC27XX_ADC_IRQ_CLR BIT(0) >> + >> +/* Mask definition for SC27XX_ADC_DATA register */ >> +#define SC27XX_ADC_DATA_MASK GENMASK(11, 0) >> + >> +/* Timeout (ms) for the trylock of hardware spinlocks */ >> +#define SC27XX_ADC_HWLOCK_TIMEOUT 5000 >> + >> +/* Maximum ADC channel number */ >> +#define SC27XX_ADC_CHANNEL_MAX 32 >> + >> +/* ADC voltage ratio definition */ >> +#define SC27XX_VOLT_RATIO(n, d) \ >> + (((n) << SC27XX_RATIO_NUMERATOR_OFFSET) | (d)) >> +#define SC27XX_RATIO_NUMERATOR_OFFSET 16 >> +#define SC27XX_RATIO_DENOMINATOR_MASK GENMASK(15, 0) >> + >> +struct sc27xx_adc_data { >> + struct device *dev; >> + struct regmap *regmap; >> + /* >> + * One hardware spinlock to synchronize between the multiple >> + * subsystems which will access the unique ADC controller. >> + */ >> + struct hwspinlock *hwlock; >> + struct completion completion; >> + int channel_scale[SC27XX_ADC_CHANNEL_MAX]; >> + int (*get_volt_ratio)(int channel, int scale); >> + u32 base; >> + int value; >> + int irq; >> +}; >> + >> +struct sc27xx_adc_linear_graph { >> + int volt0; >> + int adc0; >> + int volt1; >> + int adc1; >> +}; >> + >> +/* >> + * According to the datasheet, we can convert one ADC value to one voltage value >> + * through 2 points in the linear graph. If the voltage is less than 1.2v, we >> + * should use the small-scale graph, and if more than 1.2v, we should use the >> + * big-scale graph. >> + */ >> +static const struct sc27xx_adc_linear_graph big_scale_graph = { >> + 4200, 3310, >> + 3600, 2832, >> +}; >> + >> +static const struct sc27xx_adc_linear_graph small_scale_graph = { >> + 1000, 3413, >> + 100, 341, >> +}; >> + >> +static int sc27xx_adc_2731_ratio(int channel, int scale) >> +{ >> + switch (channel) { >> + case 1: >> + case 2: >> + case 3: >> + case 4: >> + return scale ? SC27XX_VOLT_RATIO(400, 1025) : >> + SC27XX_VOLT_RATIO(1, 1); >> + case 5: >> + return SC27XX_VOLT_RATIO(7, 29); >> + case 6: >> + return SC27XX_VOLT_RATIO(375, 9000); >> + case 7: >> + case 8: >> + return scale ? SC27XX_VOLT_RATIO(100, 125) : >> + SC27XX_VOLT_RATIO(1, 1); >> + case 19: >> + return SC27XX_VOLT_RATIO(1, 3); >> + default: >> + return SC27XX_VOLT_RATIO(1, 1); >> + } >> + return SC27XX_VOLT_RATIO(1, 1); >> +} >> + >> +static int sc27xx_adc_read(struct sc27xx_adc_data *data, int channel, >> + int scale, int *val) >> +{ >> + int ret; >> + u32 tmp; >> + >> + reinit_completion(&data->completion); >> + >> + ret = hwspin_lock_timeout_raw(data->hwlock, SC27XX_ADC_HWLOCK_TIMEOUT); >> + if (ret) { >> + dev_err(data->dev, "timeout to get the hwspinlock\n"); >> + return ret; >> + } >> + >> + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL, >> + SC27XX_ADC_EN, SC27XX_ADC_EN); >> + if (ret) >> + goto unlock_adc; >> + >> + /* Configure the channel id and scale */ >> + tmp = (scale << SC27XX_ADC_SCALE_SHIFT) & SC27XX_ADC_SCALE_MASK; >> + tmp |= channel & SC27XX_ADC_CHN_ID_MASK; >> + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CH_CFG, >> + SC27XX_ADC_CHN_ID_MASK | SC27XX_ADC_SCALE_MASK, >> + tmp); >> + if (ret) >> + goto disable_adc; >> + >> + /* Select 12bit conversion mode, and only sample 1 time */ >> + tmp = SC27XX_ADC_12BIT_MODE; >> + tmp |= (0 << SC27XX_ADC_RUN_NUM_SHIFT) & SC27XX_ADC_RUN_NUM_MASK; >> + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL, >> + SC27XX_ADC_RUN_NUM_MASK | SC27XX_ADC_12BIT_MODE, >> + tmp); >> + if (ret) >> + goto disable_adc; >> + >> + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL, >> + SC27XX_ADC_CHN_RUN, SC27XX_ADC_CHN_RUN); >> + if (ret) >> + goto disable_adc; >> + >> + wait_for_completion(&data->completion); >> + >> +disable_adc: >> + regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL, >> + SC27XX_ADC_EN, 0); >> +unlock_adc: >> + hwspin_unlock_raw(data->hwlock); >> + >> + if (!ret) >> + *val = data->value; >> + >> + return ret; >> +} >> + >> +static irqreturn_t sc27xx_adc_isr(int irq, void *dev_id) >> +{ >> + struct sc27xx_adc_data *data = dev_id; >> + int ret; >> + >> + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_CLR, >> + SC27XX_ADC_IRQ_CLR, SC27XX_ADC_IRQ_CLR); >> + if (ret) >> + return IRQ_RETVAL(ret); >> + >> + ret = regmap_read(data->regmap, data->base + SC27XX_ADC_DATA, >> + &data->value); >> + if (ret) >> + return IRQ_RETVAL(ret); >> + >> + data->value &= SC27XX_ADC_DATA_MASK; >> + complete(&data->completion); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static void sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data, >> + int channel, int scale, >> + u32 *div_numerator, u32 *div_denominator) >> +{ >> + u32 ratio = data->get_volt_ratio(channel, scale); >> + >> + *div_numerator = ratio >> SC27XX_RATIO_NUMERATOR_OFFSET; >> + *div_denominator = ratio & SC27XX_RATIO_DENOMINATOR_MASK; >> +} >> + >> +static int sc27xx_adc_to_volt(const struct sc27xx_adc_linear_graph *graph, >> + int raw_adc) >> +{ >> + int tmp; >> + >> + tmp = (graph->volt0 - graph->volt1) * (raw_adc - graph->adc1); >> + tmp /= (graph->adc0 - graph->adc1); >> + tmp += graph->volt1; >> + >> + return tmp < 0 ? 0 : tmp; >> +} >> + >> +static int sc27xx_adc_convert_volt(struct sc27xx_adc_data *data, int channel, >> + int scale, int raw_adc) >> +{ >> + u32 numerator, denominator; >> + u32 volt; >> + >> + /* >> + * Convert ADC values to voltage values according to the linear graph, >> + * and channel 5 and channel 1 has been calibrated, so we can just >> + * return the voltage values calculated by the linear graph. But other >> + * channels need be calculated to the real voltage values with the >> + * voltage ratio. >> + */ >> + switch (channel) { >> + case 5: >> + return sc27xx_adc_to_volt(&big_scale_graph, raw_adc); >> + >> + case 1: >> + return sc27xx_adc_to_volt(&small_scale_graph, raw_adc); >> + >> + default: >> + volt = sc27xx_adc_to_volt(&small_scale_graph, raw_adc); >> + break; >> + } > > This looks a lot more complex than simple scaling that is indicated by the > raw and scale attributes? They can't both be right.. Since this is special for our ADC controller, we have 2 channels that has been calibrated in hardware, but for other none-calibrated-channels, we should care about the channel voltage ratio when converting to a real voltage values, that is because some channel's voltage is larger so we need one voltage ratio to sample the ADC values. >> + >> + sc27xx_adc_volt_ratio(data, channel, scale, &numerator, &denominator); >> + >> + return (volt * denominator + numerator / 2) / numerator; >> +} >> + >> +static int sc27xx_adc_read_processed(struct sc27xx_adc_data *data, >> + int channel, int scale, int *val) >> +{ >> + int ret, raw_adc; >> + >> + ret = sc27xx_adc_read(data, channel, scale, &raw_adc); >> + if (ret) >> + return ret; >> + >> + *val = sc27xx_adc_convert_volt(data, channel, scale, raw_adc); >> + return 0; >> +} >> + >> +static int sc27xx_adc_read_raw(struct iio_dev *indio_dev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long mask) >> +{ >> + struct sc27xx_adc_data *data = iio_priv(indio_dev); >> + int scale, ret, tmp; >> + >> + switch (mask) { >> + case IIO_CHAN_INFO_RAW: >> + case IIO_CHAN_INFO_AVERAGE_RAW: >> + mutex_lock(&indio_dev->mlock); >> + scale = data->channel_scale[chan->channel]; >> + ret = sc27xx_adc_read(data, chan->channel, scale, &tmp); >> + mutex_unlock(&indio_dev->mlock); >> + >> + if (ret) >> + return ret; >> + >> + *val = tmp; >> + return IIO_VAL_INT; >> + >> + case IIO_CHAN_INFO_PROCESSED: >> + mutex_lock(&indio_dev->mlock); >> + scale = data->channel_scale[chan->channel]; >> + ret = sc27xx_adc_read_processed(data, chan->channel, scale, >> + &tmp); > > To keep to the rule of 'one way to read a value' we don't tend to support > both raw and processed. The only exception is made for devices where we got > this wrong in the first place and so have to support both to avoid a potential > regression due to ABI changes. > > If it is a simple linear scaling (like here I think) then the preferred option > is to not supply the processed version. Just do raw. Unfortunately, we can not use the formula ( (X_raw + X_offset)*X_scale = X_processed) for our ADC controller to get a processed value. Firstly, the ADC hardware will do the sampling with the scale value. Secondly we should convert a raw value to a voltage value by the linear graph table, for some channels, we should also use the channel voltage ratio to get a real voltage value. So I think we should keep our special processed approach for consumers. >> + mutex_unlock(&indio_dev->mlock); >> + >> + if (ret) >> + return ret; >> + >> + *val = tmp; >> + return IIO_VAL_INT; >> + >> + case IIO_CHAN_INFO_SCALE: >> + mutex_lock(&indio_dev->mlock); >> + *val = data->channel_scale[chan->channel]; >> + mutex_unlock(&indio_dev->mlock); >> + return IIO_VAL_INT; >> + >> + default: >> + return -EINVAL; >> + } >> +} >> + >> +static int sc27xx_adc_write_raw(struct iio_dev *indio_dev, >> + struct iio_chan_spec const *chan, >> + int val, int val2, long mask) >> +{ >> + struct sc27xx_adc_data *data = iio_priv(indio_dev); >> + >> + switch (mask) { >> + case IIO_CHAN_INFO_SCALE: >> + mutex_lock(&indio_dev->mlock); >> + data->channel_scale[chan->channel] = val; >> + mutex_unlock(&indio_dev->mlock); > > This is rather heavy weight locking for an integer write that will > be atomic (I hope!) anyway. Hence I don't think you need to > lock around this value at all. > > It 'might' change during a read, but given you take a snapshot > of it anyway I'm not sure why that would matter? OK, I can remove the lock. > >> + return IIO_VAL_INT; >> + >> + default: >> + return -EINVAL; >> + } >> +} >> + >> +static const struct iio_info sc27xx_info = { >> + .read_raw = &sc27xx_adc_read_raw, >> + .write_raw = &sc27xx_adc_write_raw, >> +}; >> + >> +#define SC27XX_ADC_CHANNEL(index) { \ >> + .type = IIO_VOLTAGE, \ >> + .channel = index, \ >> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ >> + BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ >> + BIT(IIO_CHAN_INFO_PROCESSED) | \ >> + BIT(IIO_CHAN_INFO_SCALE), \ >> + .datasheet_name = "CH##index", \ >> + .indexed = 1, \ >> +} >> + >> +static const struct iio_chan_spec sc27xx_channels[] = { >> + SC27XX_ADC_CHANNEL(0), >> + SC27XX_ADC_CHANNEL(1), >> + SC27XX_ADC_CHANNEL(2), >> + SC27XX_ADC_CHANNEL(3), >> + SC27XX_ADC_CHANNEL(4), >> + SC27XX_ADC_CHANNEL(5), >> + SC27XX_ADC_CHANNEL(6), >> + SC27XX_ADC_CHANNEL(7), >> + SC27XX_ADC_CHANNEL(8), >> + SC27XX_ADC_CHANNEL(9), >> + SC27XX_ADC_CHANNEL(10), >> + SC27XX_ADC_CHANNEL(11), >> + SC27XX_ADC_CHANNEL(12), >> + SC27XX_ADC_CHANNEL(13), >> + SC27XX_ADC_CHANNEL(14), >> + SC27XX_ADC_CHANNEL(15), >> + SC27XX_ADC_CHANNEL(16), >> + SC27XX_ADC_CHANNEL(17), >> + SC27XX_ADC_CHANNEL(18), >> + SC27XX_ADC_CHANNEL(19), >> + SC27XX_ADC_CHANNEL(20), >> + SC27XX_ADC_CHANNEL(21), >> + SC27XX_ADC_CHANNEL(22), >> + SC27XX_ADC_CHANNEL(23), >> + SC27XX_ADC_CHANNEL(24), >> + SC27XX_ADC_CHANNEL(25), >> + SC27XX_ADC_CHANNEL(26), >> + SC27XX_ADC_CHANNEL(27), >> + SC27XX_ADC_CHANNEL(28), >> + SC27XX_ADC_CHANNEL(29), >> + SC27XX_ADC_CHANNEL(30), >> + SC27XX_ADC_CHANNEL(31), >> +}; >> + >> +static int sc27xx_adc_enable(struct sc27xx_adc_data *data) >> +{ >> + int ret; >> + >> + ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN, >> + SC27XX_MODULE_ADC_EN, SC27XX_MODULE_ADC_EN); >> + if (ret) >> + return ret; > Hmm. We allow this function to return errors, but don't really clean up properly > if it happens. > > So errors in later paths than this one should ensure this is undone. The state > on exit from this function (when an error occurs) should be as close as possible > to the state on entry. > > Now things get interesting if the failure indicates we probably have a hardware > failure, but it would still be nice to be consistent and try and unwind. You are right, we should ensure previous operations are undone. Will fix in next version. >> + >> + /* Enable ADC work clock and controller clock */ >> + ret = regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN, >> + SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, >> + SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN); >> + if (ret) >> + return ret; >> + >> + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_EN, >> + SC27XX_ADC_IRQ_EN, SC27XX_ADC_IRQ_EN); >> + if (ret) >> + return ret; >> + >> + return regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_CLR, >> + SC27XX_ADC_IRQ_CLR, SC27XX_ADC_IRQ_CLR); >> +} >> + >> +static void sc27xx_adc_disable(struct sc27xx_adc_data *data) >> +{ >> + regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_EN, >> + SC27XX_ADC_IRQ_EN, 0); >> + >> + /* Disable ADC work clock and controller clock */ >> + regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN, >> + SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, 0); >> + >> + regmap_update_bits(data->regmap, SC27XX_MODULE_EN, >> + SC27XX_MODULE_ADC_EN, 0); >> +} >> + >> +static int sc27xx_adc_probe(struct platform_device *pdev) >> +{ >> + struct device_node *np = pdev->dev.of_node; >> + struct sc27xx_adc_data *sc27xx_data; >> + struct iio_dev *indio_dev; >> + const void *data; >> + int ret; >> + >> + data = of_device_get_match_data(&pdev->dev); >> + if (!data) { >> + dev_err(&pdev->dev, "failed to get match data\n"); >> + return -EINVAL; >> + } >> + >> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*sc27xx_data)); >> + if (!indio_dev) >> + return -ENOMEM; >> + >> + sc27xx_data = iio_priv(indio_dev); >> + >> + sc27xx_data->regmap = dev_get_regmap(pdev->dev.parent, NULL); >> + if (!sc27xx_data->regmap) { >> + dev_err(&pdev->dev, "failed to get ADC regmap\n"); >> + return -ENODEV; >> + } >> + >> + ret = of_property_read_u32(np, "reg", &sc27xx_data->base); >> + if (ret) { >> + dev_err(&pdev->dev, "failed to get ADC base address\n"); >> + return ret; >> + } >> + >> + sc27xx_data->irq = platform_get_irq(pdev, 0); >> + if (sc27xx_data->irq < 0) { >> + dev_err(&pdev->dev, "failed to get ADC irq number\n"); >> + return sc27xx_data->irq; >> + } >> + >> + ret = of_hwspin_lock_get_id(np, 0); >> + if (ret < 0) { >> + dev_err(&pdev->dev, "failed to get hwspinlock id\n"); >> + return ret; >> + } >> + >> + sc27xx_data->hwlock = hwspin_lock_request_specific(ret); >> + if (!sc27xx_data->hwlock) { >> + dev_err(&pdev->dev, "failed to request hwspinlock\n"); >> + return -ENXIO; >> + } >> + >> + init_completion(&sc27xx_data->completion); >> + >> + /* >> + * Different PMIC ADC controllers can have different channel voltage >> + * ratios, so we should save the implementation of getting voltage >> + * ratio for corresponding PMIC ADC in the device data structure. >> + */ >> + sc27xx_data->get_volt_ratio = data; >> + sc27xx_data->dev = &pdev->dev; >> + >> + ret = sc27xx_adc_enable(sc27xx_data); >> + if (ret) { >> + dev_err(&pdev->dev, "failed to enable ADC module\n"); >> + goto free_hwlock; >> + } >> + >> + ret = devm_request_threaded_irq(&pdev->dev, sc27xx_data->irq, NULL, >> + sc27xx_adc_isr, IRQF_ONESHOT, >> + pdev->name, sc27xx_data); > > This worries me from a race point of view as well. You shouldn't have > an interrupt still in use once the adc_disable in the remove is > called. It might be safe, but it's not immediately obvious that it > is. As such I'd rather we didn't use the managed interrupt request here. > So use request_threaded_irq and free_irq as appropriate instead. Thanks for pointing this issue out and will fix in next version. > > >> + if (ret) { >> + dev_err(&pdev->dev, "failed to request ADC irq\n"); >> + goto disable_adc; >> + } >> + >> + indio_dev->dev.parent = &pdev->dev; >> + indio_dev->name = dev_name(&pdev->dev); >> + indio_dev->modes = INDIO_DIRECT_MODE; >> + indio_dev->info = &sc27xx_info; >> + indio_dev->channels = sc27xx_channels; >> + indio_dev->num_channels = ARRAY_SIZE(sc27xx_channels); >> + ret = devm_iio_device_register(&pdev->dev, indio_dev); >> + if (ret) { > The moment I see any unwinding happening after a devm call I know > there is probably a race. > > Here the race is that you will be turning the ADC off and unlocking > before the userspace interface is removed (as devm unwind will happen > after remove is finished). > > You'll have to use the non managed version of iio_device_register > and unwind by hand to avoid this. > > Or you could if you prefer register some additional actions with devm > core to call the adc disable and hw_spinlock_free as appropriate. That is a good idea and I will add some additional actions with devm to avoid the race issue. > >> + dev_err(&pdev->dev, "could not register iio (ADC)"); >> + goto disable_adc; >> + } >> + >> + platform_set_drvdata(pdev, indio_dev); > Generally put a blank line before simple returns like this, > Aids readability (a very small amount!) Sure. > >> + return 0; >> + >> +disable_adc: >> + sc27xx_adc_disable(sc27xx_data); >> +free_hwlock: >> + hwspin_lock_free(sc27xx_data->hwlock); >> + return ret; >> +} >> + >> +static int sc27xx_adc_remove(struct platform_device *pdev) >> +{ >> + struct iio_dev *indio_dev = platform_get_drvdata(pdev); >> + struct sc27xx_adc_data *sc27xx_data = iio_priv(indio_dev); >> + >> + sc27xx_adc_disable(sc27xx_data); >> + hwspin_lock_free(sc27xx_data->hwlock); > > blank line here please. Sure. > >> + return 0; >> +} >> + >> +static const struct of_device_id sc27xx_adc_of_match[] = { >> + { >> + .compatible = "sprd,sc2731-adc", >> + .data = (void *)&sc27xx_adc_2731_ratio, > > There is no need to cast to (void *) Implicit casting too > and from void pointers is always fine under the c spec. Yes, this is redundant. > > However, for cleanness I would put that function pointer into > a const structure. Chances are you'll need something else in there > for some future feature and this would make it a lot cleaner. > > Also, a little unusual todo this when submitting a driver > supporting only one part. I'm fine leaving it if you confirm there > are other parts going to be supported by this driver in the near future. > Otherwise drop this unnecessary abstraction. Make sense and I will remove it. Very appreciate for your comments. -- Baolin.wang Best Regards