iio: adc: stm32-adc: add vrefint calibration support
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> Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com> Link: https://lore.kernel.org/r/20211014131228.4692-7-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
parent
aec6e0d8f0
commit
0e346b2cfa
@ -21,6 +21,7 @@
|
|||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/iopoll.h>
|
#include <linux/iopoll.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/nvmem-consumer.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
@ -41,6 +42,7 @@
|
|||||||
#define STM32_ADC_TIMEOUT_US 100000
|
#define STM32_ADC_TIMEOUT_US 100000
|
||||||
#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
|
#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
|
||||||
#define STM32_ADC_HW_STOP_DELAY_MS 100
|
#define STM32_ADC_HW_STOP_DELAY_MS 100
|
||||||
|
#define STM32_ADC_VREFINT_VOLTAGE 3300
|
||||||
|
|
||||||
#define STM32_DMA_BUFFER_SIZE PAGE_SIZE
|
#define STM32_DMA_BUFFER_SIZE PAGE_SIZE
|
||||||
|
|
||||||
@ -137,6 +139,16 @@ struct stm32_adc_regs {
|
|||||||
int shift;
|
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
|
* struct stm32_adc_regspec - stm32 registers definition
|
||||||
* @dr: data register offset
|
* @dr: data register offset
|
||||||
@ -186,6 +198,7 @@ struct stm32_adc;
|
|||||||
* @unprepare: optional unprepare routine (disable, power-down)
|
* @unprepare: optional unprepare routine (disable, power-down)
|
||||||
* @irq_clear: routine to clear irqs
|
* @irq_clear: routine to clear irqs
|
||||||
* @smp_cycles: programmable sampling time (ADC clock cycles)
|
* @smp_cycles: programmable sampling time (ADC clock cycles)
|
||||||
|
* @ts_vrefint_ns: vrefint minimum sampling time in ns
|
||||||
*/
|
*/
|
||||||
struct stm32_adc_cfg {
|
struct stm32_adc_cfg {
|
||||||
const struct stm32_adc_regspec *regs;
|
const struct stm32_adc_regspec *regs;
|
||||||
@ -199,6 +212,7 @@ struct stm32_adc_cfg {
|
|||||||
void (*unprepare)(struct iio_dev *);
|
void (*unprepare)(struct iio_dev *);
|
||||||
void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
|
void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
|
||||||
const unsigned int *smp_cycles;
|
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
|
* @pcsel: bitmask to preselect channels on some devices
|
||||||
* @smpr_val: sampling time settings (e.g. smpr1 / smpr2)
|
* @smpr_val: sampling time settings (e.g. smpr1 / smpr2)
|
||||||
* @cal: optional calibration data on some devices
|
* @cal: optional calibration data on some devices
|
||||||
|
* @vrefint: internal reference voltage data
|
||||||
* @chan_name: channel name array
|
* @chan_name: channel name array
|
||||||
* @num_diff: number of differential channels
|
* @num_diff: number of differential channels
|
||||||
* @int_ch: internal channel indexes array
|
* @int_ch: internal channel indexes array
|
||||||
@ -248,6 +263,7 @@ struct stm32_adc {
|
|||||||
u32 pcsel;
|
u32 pcsel;
|
||||||
u32 smpr_val[2];
|
u32 smpr_val[2];
|
||||||
struct stm32_adc_calib cal;
|
struct stm32_adc_calib cal;
|
||||||
|
struct stm32_adc_vrefint vrefint;
|
||||||
char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
|
char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
|
||||||
u32 num_diff;
|
u32 num_diff;
|
||||||
int int_ch[STM32_ADC_INT_CH_NB];
|
int int_ch[STM32_ADC_INT_CH_NB];
|
||||||
@ -1339,6 +1355,7 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
|
|||||||
|
|
||||||
switch (mask) {
|
switch (mask) {
|
||||||
case IIO_CHAN_INFO_RAW:
|
case IIO_CHAN_INFO_RAW:
|
||||||
|
case IIO_CHAN_INFO_PROCESSED:
|
||||||
ret = iio_device_claim_direct_mode(indio_dev);
|
ret = iio_device_claim_direct_mode(indio_dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@ -1346,6 +1363,10 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
|
|||||||
ret = stm32_adc_single_conv(indio_dev, chan, val);
|
ret = stm32_adc_single_conv(indio_dev, chan, val);
|
||||||
else
|
else
|
||||||
ret = -EINVAL;
|
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);
|
iio_device_release_direct_mode(indio_dev);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -1815,7 +1836,10 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
|
|||||||
chan->datasheet_name = name;
|
chan->datasheet_name = name;
|
||||||
chan->scan_index = scan_index;
|
chan->scan_index = scan_index;
|
||||||
chan->indexed = 1;
|
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) |
|
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
|
||||||
BIT(IIO_CHAN_INFO_OFFSET);
|
BIT(IIO_CHAN_INFO_OFFSET);
|
||||||
chan->scan_type.sign = 'u';
|
chan->scan_type.sign = 'u';
|
||||||
@ -1917,6 +1941,36 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
|
|||||||
return scan_index;
|
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) {
|
||||||
|
return dev_err_probe(&indio_dev->dev, ret,
|
||||||
|
"nvmem access error\n");
|
||||||
|
}
|
||||||
|
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,
|
static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
|
||||||
struct stm32_adc *adc,
|
struct stm32_adc *adc,
|
||||||
struct iio_chan_spec *channels)
|
struct iio_chan_spec *channels)
|
||||||
@ -1925,7 +1979,7 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
|
|||||||
const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
|
const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
const char *name;
|
const char *name;
|
||||||
int val, scan_index = 0, ret, i;
|
int val, scan_index = 0, ret;
|
||||||
bool differential;
|
bool differential;
|
||||||
u32 vin[2];
|
u32 vin[2];
|
||||||
|
|
||||||
@ -1945,10 +1999,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
|
strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
|
||||||
for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
|
ret = stm32_adc_populate_int_ch(indio_dev, name, val);
|
||||||
if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
|
if (ret)
|
||||||
adc->int_ch[i] = val;
|
goto err;
|
||||||
}
|
|
||||||
} else if (ret != -EINVAL) {
|
} else if (ret != -EINVAL) {
|
||||||
dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
|
dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
|
||||||
goto err;
|
goto err;
|
||||||
@ -2055,6 +2108,14 @@ 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",
|
of_property_read_u32_index(node, "st,min-sample-time-nsecs",
|
||||||
i, &smp);
|
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 = max(smp, adc->cfg->ts_vrefint_ns);
|
||||||
|
|
||||||
/* Prepare sampling time settings */
|
/* Prepare sampling time settings */
|
||||||
stm32_adc_smpr_init(adc, channels[i].channel, smp);
|
stm32_adc_smpr_init(adc, channels[i].channel, smp);
|
||||||
}
|
}
|
||||||
@ -2361,6 +2422,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
|
|||||||
.unprepare = stm32h7_adc_unprepare,
|
.unprepare = stm32h7_adc_unprepare,
|
||||||
.smp_cycles = stm32h7_adc_smp_cycles,
|
.smp_cycles = stm32h7_adc_smp_cycles,
|
||||||
.irq_clear = stm32h7_adc_irq_clear,
|
.irq_clear = stm32h7_adc_irq_clear,
|
||||||
|
.ts_vrefint_ns = 4300,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id stm32_adc_of_match[] = {
|
static const struct of_device_id stm32_adc_of_match[] = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user