Commit f54e9f2b authored by Stefan Agner's avatar Stefan Agner Committed by Jonathan Cameron

iio: adc: vf610: use ADC clock within specification

Depending on conversion mode used, the ADC clock (ADCK) needs
to be below a maximum frequency. According to Vybrid's data
sheet this is 20MHz for the low power conversion mode.

The ADC clock is depending on input clock, which is the bus
clock by default. Vybrid SoC are typically clocked at at 400MHz
or 500MHz, which leads to 66MHz or 83MHz bus clock respectively.
Hence, a divider of 8 is required to stay below the specified
maximum clock of 20MHz.

Due to the different bus clock speeds, the resulting sampling
frequency is not static. Hence use the ADC clock and calculate
the actual available sampling frequency dynamically.

This fixes bogous values observed on some 500MHz clocked Vybrid
SoC. The resulting value usually showed Bit 9 being stuck at 1,
or 0, which lead to a value of +/-512.
Signed-off-by: default avatarStefan Agner <stefan@agner.ch>
Acked-by: default avatarFugang Duan <B38611@freescale.com>
Cc: <Stable@vger.kernel.org>
Signed-off-by: default avatarJonathan Cameron <jic23@kernel.org>
parent bbc45f3a
...@@ -141,9 +141,13 @@ struct vf610_adc { ...@@ -141,9 +141,13 @@ struct vf610_adc {
struct regulator *vref; struct regulator *vref;
struct vf610_adc_feature adc_feature; struct vf610_adc_feature adc_feature;
u32 sample_freq_avail[5];
struct completion completion; struct completion completion;
}; };
static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
#define VF610_ADC_CHAN(_idx, _chan_type) { \ #define VF610_ADC_CHAN(_idx, _chan_type) { \
.type = (_chan_type), \ .type = (_chan_type), \
.indexed = 1, \ .indexed = 1, \
...@@ -180,35 +184,47 @@ static const struct iio_chan_spec vf610_adc_iio_channels[] = { ...@@ -180,35 +184,47 @@ static const struct iio_chan_spec vf610_adc_iio_channels[] = {
/* sentinel */ /* sentinel */
}; };
/* static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
* ADC sample frequency, unit is ADCK cycles. {
* ADC clk source is ipg clock, which is the same as bus clock. unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk);
* int i;
* ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder)
* SFCAdder: fixed to 6 ADCK cycles /*
* AverageNum: 1, 4, 8, 16, 32 samples for hardware average. * Calculate ADC sample frequencies
* BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode * Sample time unit is ADCK cycles. ADCK clk source is ipg clock,
* LSTAdder(Long Sample Time): fixed to 3 ADCK cycles * which is the same as bus clock.
* *
* By default, enable 12 bit resolution mode, clock source * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder)
* set to ipg clock, So get below frequency group: * SFCAdder: fixed to 6 ADCK cycles
*/ * AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
static const u32 vf610_sample_freq_avail[5] = * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
{1941176, 559332, 286957, 145374, 73171}; * LSTAdder(Long Sample Time): fixed to 3 ADCK cycles
*/
adck_rate = ipg_rate / info->adc_feature.clk_div;
for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++)
info->sample_freq_avail[i] =
adck_rate / (6 + vf610_hw_avgs[i] * (25 + 3));
}
static inline void vf610_adc_cfg_init(struct vf610_adc *info) static inline void vf610_adc_cfg_init(struct vf610_adc *info)
{ {
struct vf610_adc_feature *adc_feature = &info->adc_feature;
/* set default Configuration for ADC controller */ /* set default Configuration for ADC controller */
info->adc_feature.clk_sel = VF610_ADCIOC_BUSCLK_SET; adc_feature->clk_sel = VF610_ADCIOC_BUSCLK_SET;
info->adc_feature.vol_ref = VF610_ADCIOC_VR_VREF_SET; adc_feature->vol_ref = VF610_ADCIOC_VR_VREF_SET;
adc_feature->calibration = true;
adc_feature->ovwren = true;
adc_feature->res_mode = 12;
adc_feature->sample_rate = 1;
adc_feature->lpm = true;
info->adc_feature.calibration = true; /* Use a save ADCK which is below 20MHz on all devices */
info->adc_feature.ovwren = true; adc_feature->clk_div = 8;
info->adc_feature.clk_div = 1; vf610_adc_calculate_rates(info);
info->adc_feature.res_mode = 12;
info->adc_feature.sample_rate = 1;
info->adc_feature.lpm = true;
} }
static void vf610_adc_cfg_post_set(struct vf610_adc *info) static void vf610_adc_cfg_post_set(struct vf610_adc *info)
...@@ -290,12 +306,10 @@ static void vf610_adc_cfg_set(struct vf610_adc *info) ...@@ -290,12 +306,10 @@ static void vf610_adc_cfg_set(struct vf610_adc *info)
cfg_data = readl(info->regs + VF610_REG_ADC_CFG); cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
/* low power configuration */
cfg_data &= ~VF610_ADC_ADLPC_EN; cfg_data &= ~VF610_ADC_ADLPC_EN;
if (adc_feature->lpm) if (adc_feature->lpm)
cfg_data |= VF610_ADC_ADLPC_EN; cfg_data |= VF610_ADC_ADLPC_EN;
/* disable high speed */
cfg_data &= ~VF610_ADC_ADHSC_EN; cfg_data &= ~VF610_ADC_ADHSC_EN;
writel(cfg_data, info->regs + VF610_REG_ADC_CFG); writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
...@@ -435,10 +449,27 @@ static irqreturn_t vf610_adc_isr(int irq, void *dev_id) ...@@ -435,10 +449,27 @@ static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1941176, 559332, 286957, 145374, 73171"); static ssize_t vf610_show_samp_freq_avail(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct vf610_adc *info = iio_priv(dev_to_iio_dev(dev));
size_t len = 0;
int i;
for (i = 0; i < ARRAY_SIZE(info->sample_freq_avail); i++)
len += scnprintf(buf + len, PAGE_SIZE - len,
"%u ", info->sample_freq_avail[i]);
/* replace trailing space by newline */
buf[len - 1] = '\n';
return len;
}
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(vf610_show_samp_freq_avail);
static struct attribute *vf610_attributes[] = { static struct attribute *vf610_attributes[] = {
&iio_const_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
NULL NULL
}; };
...@@ -502,7 +533,7 @@ static int vf610_read_raw(struct iio_dev *indio_dev, ...@@ -502,7 +533,7 @@ static int vf610_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_FRACTIONAL_LOG2; return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_SAMP_FREQ: case IIO_CHAN_INFO_SAMP_FREQ:
*val = vf610_sample_freq_avail[info->adc_feature.sample_rate]; *val = info->sample_freq_avail[info->adc_feature.sample_rate];
*val2 = 0; *val2 = 0;
return IIO_VAL_INT; return IIO_VAL_INT;
...@@ -525,9 +556,9 @@ static int vf610_write_raw(struct iio_dev *indio_dev, ...@@ -525,9 +556,9 @@ static int vf610_write_raw(struct iio_dev *indio_dev,
switch (mask) { switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ: case IIO_CHAN_INFO_SAMP_FREQ:
for (i = 0; for (i = 0;
i < ARRAY_SIZE(vf610_sample_freq_avail); i < ARRAY_SIZE(info->sample_freq_avail);
i++) i++)
if (val == vf610_sample_freq_avail[i]) { if (val == info->sample_freq_avail[i]) {
info->adc_feature.sample_rate = i; info->adc_feature.sample_rate = i;
vf610_adc_sample_set(info); vf610_adc_sample_set(info);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment