/* * Make this runtime configurable if necessary. Currently, if the buffered mode * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000) * seconds. The result is that the samples arrive every 500mS.
*/ #define LRADC_DELAY_TIMER_PER 200 #define LRADC_DELAY_TIMER_LOOP 5
/* Raw I/O operations */ staticint mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan, int *val)
{ struct mxs_lradc_adc *adc = iio_priv(iio_dev); struct mxs_lradc *lradc = adc->lradc; int ret;
/* * See if there is no buffered operation in progress. If there is simply * bail out. This can be improved to support both buffered and raw IO at * the same time, yet the code becomes horribly complicated. Therefore I * applied KISS principle here.
*/ if (!iio_device_claim_direct(iio_dev)) return -EBUSY;
reinit_completion(&adc->completion);
/* * No buffered operation in progress, map the channel and trigger it. * Virtual channel 0 is always used here as the others are always not * used if doing raw sampling.
*/ if (lradc->soc == IMX28_LRADC)
writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
writel(0x1, adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
/* Enable / disable the divider per requirement */ if (test_bit(chan, &adc->is_divided))
writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_SET); else
writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_CLR);
/* Clean the slot's previous content, then set new one. */
writel(LRADC_CTRL4_LRADCSELECT_MASK(0),
adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
writel(chan, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
writel(0, adc->base + LRADC_CH(0));
/* Enable the IRQ and start sampling the channel. */
writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
writel(BIT(0), adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
/* Wait for completion on the channel, 1 second max. */
ret = wait_for_completion_killable_timeout(&adc->completion, HZ); if (!ret)
ret = -ETIMEDOUT; if (ret < 0) goto err;
/* Read the data. */
*val = readl(adc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
ret = IIO_VAL_INT;
staticint mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val)
{ int ret, min, max;
ret = mxs_lradc_adc_read_single(iio_dev, 8, &min); if (ret != IIO_VAL_INT) return ret;
ret = mxs_lradc_adc_read_single(iio_dev, 9, &max); if (ret != IIO_VAL_INT) return ret;
*val = max - min;
return IIO_VAL_INT;
}
staticint mxs_lradc_adc_read_raw(struct iio_dev *iio_dev, conststruct iio_chan_spec *chan, int *val, int *val2, long m)
{ struct mxs_lradc_adc *adc = iio_priv(iio_dev);
switch (m) { case IIO_CHAN_INFO_RAW: if (chan->type == IIO_TEMP) return mxs_lradc_adc_read_temp(iio_dev, val);
case IIO_CHAN_INFO_SCALE: if (chan->type == IIO_TEMP) { /* * From the datasheet, we have to multiply by 1.012 and * divide by 4
*/
*val = 0;
*val2 = 253000; return IIO_VAL_INT_PLUS_MICRO;
}
case IIO_CHAN_INFO_OFFSET: if (chan->type == IIO_TEMP) { /* * The calculated value from the ADC is in Kelvin, we * want Celsius for hwmon so the offset is -273.15 * The offset is applied before scaling so it is * actually -213.15 * 4 / 1.012 = -1079.644268
*/
*val = -1079;
*val2 = 644268;
return IIO_VAL_INT_PLUS_MICRO;
}
return -EINVAL;
default: break;
}
return -EINVAL;
}
staticint mxs_lradc_adc_write_raw(struct iio_dev *iio_dev, conststruct iio_chan_spec *chan, int val, int val2, long m)
{ struct mxs_lradc_adc *adc = iio_priv(iio_dev); struct mxs_lradc_scale *scale_avail =
adc->scale_avail[chan->channel]; int ret;
if (!iio_device_claim_direct(iio_dev)) return -EBUSY;
switch (m) { case IIO_CHAN_INFO_SCALE:
ret = -EINVAL; if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) { /* divider by two disabled */
clear_bit(chan->channel, &adc->is_divided);
ret = 0;
} elseif (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) { /* divider by two enabled */
set_bit(chan->channel, &adc->is_divided);
ret = 0;
}
break; default:
ret = -EINVAL; break;
}
iio_device_release_direct(iio_dev);
return ret;
}
staticint mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev, conststruct iio_chan_spec *chan, long m)
{ return IIO_VAL_INT_PLUS_NANO;
}
ch = iio_attr->address; for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++)
len += sprintf(buf + len, "%u.%09u ",
adc->scale_avail[ch][i].integer,
adc->scale_avail[ch][i].nano);
/* * Start internal temperature sensing by clearing bit * HW_LRADC_CTRL2_TEMPSENSE_PWD. This bit can be left cleared * after power up.
*/
writel(0, adc->base + LRADC_CTRL2);
}
ret = devm_request_irq(dev, virq, mxs_lradc_adc_handle_irq,
0, irq_name[i], iio); if (ret) return ret;
}
ret = mxs_lradc_adc_trigger_init(iio); if (ret) return ret;
ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
&mxs_lradc_adc_trigger_handler,
&mxs_lradc_adc_buffer_ops); if (ret) goto err_trig;
adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc];
/* Populate available ADC input ranges */ for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) { for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) { /* * [s=0] = optional divider by two disabled (default) * [s=1] = optional divider by two enabled * * The scale is calculated by doing: * Vref >> (realbits - s) * which multiplies by two on the second component * of the array.
*/
scale_uv = ((u64)adc->vref_mv[i] * 100000000) >>
(LRADC_RESOLUTION - s);
adc->scale_avail[i][s].nano =
do_div(scale_uv, 100000000) * 10;
adc->scale_avail[i][s].integer = scale_uv;
}
}
/* Configure the hardware. */
mxs_lradc_adc_hw_init(adc);
/* Register IIO device. */
ret = iio_device_register(iio); if (ret) {
dev_err(dev, "Failed to register IIO device\n"); goto err_dev;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.