lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAMz4kuLVtdbBaNBY6e8F9fPuSBo60HGxzJ2wsW-dDV9-iOsfDg@mail.gmail.com>
Date:   Sun, 17 Jun 2018 16:03:04 +0800
From:   Baolin Wang <baolin.wang@...aro.org>
To:     Jonathan Cameron <jic23@...nel.org>
Cc:     Rob Herring <robh+dt@...nel.org>,
        Mark Rutland <mark.rutland@....com>,
        Hartmut Knaack <knaack.h@....de>,
        Lars-Peter Clausen <lars@...afoo.de>,
        Peter Meerwald-Stadler <pmeerw@...erw.net>,
        freeman.liu@...eadtrum.com, Mark Brown <broonie@...nel.org>,
        DTML <devicetree@...r.kernel.org>, linux-iio@...r.kernel.org,
        LKML <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH v2 2/2] iio: adc: Add Spreadtrum SC27XX PMICs ADC support

Hi Jonathan,

On 17 June 2018 at 02:35, Jonathan Cameron <jic23@...nel.org> wrote:
> On Fri, 15 Jun 2018 15:03:36 +0800
> Baolin Wang <baolin.wang@...aro.org> wrote:
>
>> From: Freeman Liu <freeman.liu@...eadtrum.com>
>>
>> The Spreadtrum SC27XX PMICs ADC controller contains 32 channels,
>> which is used to sample voltages with 12 bits conversion.
>>
>> Signed-off-by: Freeman Liu <freeman.liu@...eadtrum.com>
>> Signed-off-by: Baolin Wang <baolin.wang@...aro.org>
>
> 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 <linux/hwspinlock.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +
>> +/* 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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ