lkml.org 
[lkml]   [2021]   [Sep]   [22]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v2 6/7] iio: adc: stm32-adc: add vrefint calibration support
    Date
    Add support of vrefint calibration.
    If a channel is labeled as vrefint, get vrefint calibration
    from non volatile memory for this channel.
    vrefint channel is exposed as a processed channel returning
    the actual value of vrefp:
    vrefp = 3.3 x vrefint_cal / vrefint_data

    A conversion on vrefint channel allows to update scale
    factor according to vrefint deviation, compared to vrefint
    calibration value.

    Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
    ---
    drivers/iio/adc/stm32-adc.c | 76 ++++++++++++++++++++++++++++++++++---
    1 file changed, 70 insertions(+), 6 deletions(-)

    diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
    index 5a7ac45a0725..c427e439bf4a 100644
    --- a/drivers/iio/adc/stm32-adc.c
    +++ b/drivers/iio/adc/stm32-adc.c
    @@ -21,6 +21,7 @@
    #include <linux/io.h>
    #include <linux/iopoll.h>
    #include <linux/module.h>
    +#include <linux/nvmem-consumer.h>
    #include <linux/platform_device.h>
    #include <linux/pm_runtime.h>
    #include <linux/of.h>
    @@ -41,6 +42,7 @@
    #define STM32_ADC_TIMEOUT_US 100000
    #define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
    #define STM32_ADC_HW_STOP_DELAY_MS 100
    +#define STM32_ADC_VREFINT_VOLTAGE 3300

    #define STM32_DMA_BUFFER_SIZE PAGE_SIZE

    @@ -137,6 +139,16 @@ struct stm32_adc_regs {
    int shift;
    };

    +/**
    + * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
    + * @vrefint_cal: vrefint calibration value from nvmem
    + * @vrefint_data: vrefint actual value
    + */
    +struct stm32_adc_vrefint {
    + u32 vrefint_cal;
    + u32 vrefint_data;
    +};
    +
    /**
    * struct stm32_adc_regspec - stm32 registers definition
    * @dr: data register offset
    @@ -186,6 +198,7 @@ struct stm32_adc;
    * @unprepare: optional unprepare routine (disable, power-down)
    * @irq_clear: routine to clear irqs
    * @smp_cycles: programmable sampling time (ADC clock cycles)
    + * @ts_vrefint_ns: vrefint minimum sampling time in ns
    */
    struct stm32_adc_cfg {
    const struct stm32_adc_regspec *regs;
    @@ -199,6 +212,7 @@ struct stm32_adc_cfg {
    void (*unprepare)(struct iio_dev *);
    void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
    const unsigned int *smp_cycles;
    + const unsigned int ts_vrefint_ns;
    };

    /**
    @@ -223,6 +237,7 @@ struct stm32_adc_cfg {
    * @pcsel: bitmask to preselect channels on some devices
    * @smpr_val: sampling time settings (e.g. smpr1 / smpr2)
    * @cal: optional calibration data on some devices
    + * @vrefint: internal reference voltage data
    * @chan_name: channel name array
    * @num_diff: number of differential channels
    * @int_ch: internal channel indexes array
    @@ -248,6 +263,7 @@ struct stm32_adc {
    u32 pcsel;
    u32 smpr_val[2];
    struct stm32_adc_calib cal;
    + struct stm32_adc_vrefint vrefint;
    char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
    u32 num_diff;
    int int_ch[STM32_ADC_INT_CH_NB];
    @@ -1343,6 +1359,7 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,

    switch (mask) {
    case IIO_CHAN_INFO_RAW:
    + case IIO_CHAN_INFO_PROCESSED:
    ret = iio_device_claim_direct_mode(indio_dev);
    if (ret)
    return ret;
    @@ -1350,6 +1367,10 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
    ret = stm32_adc_single_conv(indio_dev, chan, val);
    else
    ret = -EINVAL;
    +
    + if (mask == IIO_CHAN_INFO_PROCESSED && adc->vrefint.vrefint_cal)
    + *val = STM32_ADC_VREFINT_VOLTAGE * adc->vrefint.vrefint_cal / *val;
    +
    iio_device_release_direct_mode(indio_dev);
    return ret;

    @@ -1819,7 +1840,10 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
    chan->datasheet_name = name;
    chan->scan_index = scan_index;
    chan->indexed = 1;
    - chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
    + if (chan->channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT])
    + chan->info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED);
    + else
    + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
    chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
    BIT(IIO_CHAN_INFO_OFFSET);
    chan->scan_type.sign = 'u';
    @@ -1921,6 +1945,36 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
    return scan_index;
    }

    +static int stm32_adc_populate_int_ch(struct iio_dev *indio_dev, const char *ch_name,
    + int chan)
    +{
    + struct stm32_adc *adc = iio_priv(indio_dev);
    + u16 vrefint;
    + int i, ret;
    +
    + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
    + if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
    + adc->int_ch[i] = chan;
    +
    + if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT)
    + continue;
    +
    + /* Get calibration data for vrefint channel */
    + ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
    + if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
    + dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
    + return ret;
    + }
    + if (ret == -ENOENT)
    + dev_dbg(&indio_dev->dev, "vrefint calibration not found\n");
    + else
    + adc->vrefint.vrefint_cal = vrefint;
    + }
    + }
    +
    + return 0;
    +}
    +
    static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
    struct stm32_adc *adc,
    struct iio_chan_spec *channels)
    @@ -1929,7 +1983,7 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
    const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
    struct device_node *child;
    const char *name;
    - int val, scan_index = 0, ret, i;
    + int val, scan_index = 0, ret;
    bool differential;
    u32 vin[2];

    @@ -1949,10 +2003,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
    return -EINVAL;
    }
    strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
    - for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
    - if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
    - adc->int_ch[i] = val;
    - }
    + ret = stm32_adc_populate_int_ch(indio_dev, name, val);
    + if (ret)
    + goto err;
    } else if (ret != -EINVAL) {
    dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
    goto err;
    @@ -2059,6 +2112,16 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
    */
    of_property_read_u32_index(node, "st,min-sample-time-nsecs",
    i, &smp);
    +
    + /*
    + * For vrefint channel, ensure that the sampling time cannot
    + * be lower than the one specified in the datasheet
    + */
    + if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
    + smp < adc->cfg->ts_vrefint_ns) {
    + smp = adc->cfg->ts_vrefint_ns;
    + }
    +
    /* Prepare sampling time settings */
    stm32_adc_smpr_init(adc, channels[i].channel, smp);
    }
    @@ -2365,6 +2428,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
    .unprepare = stm32h7_adc_unprepare,
    .smp_cycles = stm32h7_adc_smp_cycles,
    .irq_clear = stm32h7_adc_irq_clear,
    + .ts_vrefint_ns = 4300,
    };

    static const struct of_device_id stm32_adc_of_match[] = {
    --
    2.17.1
    \
     
     \ /
      Last update: 2021-09-22 17:37    [W:5.954 / U:0.452 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site