struct palmas_gpadc_info { /* calibration codes and regs */ int x1; /* lower ideal code */ int x2; /* higher ideal code */ int v1; /* expected lower volt reading */ int v2; /* expected higher volt reading */
u8 trim1_reg; /* register number for lower trim */
u8 trim2_reg; /* register number for upper trim */ int gain; /* calculated from above (after reading trim regs) */ int offset; /* calculated from above (after reading trim regs) */ int gain_error; /* calculated from above (after reading trim regs) */ bool is_uncalibrated; /* if channel has calibration data */
};
struct palmas_gpadc_thresholds { int high; int low;
};
/* * struct palmas_gpadc - the palmas_gpadc structure * @ch0_current: channel 0 current source setting * 0: 0 uA * 1: 5 uA * 2: 15 uA * 3: 20 uA * @ch3_current: channel 0 current source setting * 0: 0 uA * 1: 10 uA * 2: 400 uA * 3: 800 uA * @extended_delay: enable the gpadc extended delay mode * @auto_conversion_period: define the auto_conversion_period * @lock: Lock to protect the device state during a potential concurrent * read access from userspace. Reading a raw value requires a sequence * of register writes, then a wait for a completion callback, * and finally a register read, during which userspace could issue * another read request. This lock protects a read access from * ocurring before another one has finished. * * This is the palmas_gpadc structure to store run-time information * and pointers for this driver instance.
*/ struct palmas_gpadc { struct device *dev; struct palmas *palmas;
u8 ch0_current;
u8 ch3_current; bool extended_delay; int irq; int irq_auto_0; int irq_auto_1; struct palmas_gpadc_info *adc_info; struct completion conv_completion; struct palmas_adc_event event0; struct palmas_adc_event event1; struct palmas_gpadc_thresholds thresholds[PALMAS_ADC_CH_MAX]; int auto_conversion_period; struct mutex lock;
};
staticstruct palmas_adc_event *palmas_gpadc_get_event(struct palmas_gpadc *adc, int adc_chan, enum iio_event_direction dir)
{ if (adc_chan == adc->event0.channel && dir == adc->event0.direction) return &adc->event0;
if (adc_chan == adc->event1.channel && dir == adc->event1.direction) return &adc->event1;
/* * GPADC lock issue in AUTO mode. * Impact: In AUTO mode, GPADC conversion can be locked after disabling AUTO * mode feature. * Details: * When the AUTO mode is the only conversion mode enabled, if the AUTO * mode feature is disabled with bit GPADC_AUTO_CTRL. AUTO_CONV1_EN = 0 * or bit GPADC_AUTO_CTRL. AUTO_CONV0_EN = 0 during a conversion, the * conversion mechanism can be seen as locked meaning that all following * conversion will give 0 as a result. Bit GPADC_STATUS.GPADC_AVAILABLE * will stay at 0 meaning that GPADC is busy. An RT conversion can unlock * the GPADC. * * Workaround(s): * To avoid the lock mechanism, the workaround to follow before any stop * conversion request is: * Force the GPADC state machine to be ON by using the GPADC_CTRL1. * GPADC_FORCE bit = 1 * Shutdown the GPADC AUTO conversion using * GPADC_AUTO_CTRL.SHUTDOWN_CONV[01] = 0. * After 100us, force the GPADC state machine to be OFF by using the * GPADC_CTRL1. GPADC_FORCE bit = 0
*/
staticint palmas_disable_auto_conversion(struct palmas_gpadc *adc)
{ int ret;
staticint palmas_gpadc_calibrate(struct palmas_gpadc *adc, int adc_chan)
{ int k; int d1; int d2; int ret; int gain; int x1 = adc->adc_info[adc_chan].x1; int x2 = adc->adc_info[adc_chan].x2; int v1 = adc->adc_info[adc_chan].v1; int v2 = adc->adc_info[adc_chan].v2;
ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE,
adc->adc_info[adc_chan].trim1_reg, &d1); if (ret < 0) {
dev_err(adc->dev, "TRIM read failed: %d\n", ret); goto scrub;
}
ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE,
adc->adc_info[adc_chan].trim2_reg, &d2); if (ret < 0) {
dev_err(adc->dev, "TRIM read failed: %d\n", ret); goto scrub;
}
/* gain error calculation */
k = (1000 + (1000 * (d2 - d1)) / (x2 - x1));
/* gain calculation */
gain = ((v2 - v1) * 1000) / (x2 - x1);
staticint palmas_gpadc_get_calibrated_code(struct palmas_gpadc *adc, int adc_chan, int val)
{ if (!adc->adc_info[adc_chan].is_uncalibrated)
val = (val*1000 - adc->adc_info[adc_chan].offset) /
adc->adc_info[adc_chan].gain_error;
if (val < 0) { if (val < -10)
dev_err(adc->dev, "Mismatch with calibration var = %d\n", val); return 0;
}
val = (val * adc->adc_info[adc_chan].gain) / 1000;
return val;
}
/* * The high and low threshold values are calculated based on the advice given * in TI Application Report SLIA087A, "Guide to Using the GPADC in PS65903x, * TPS65917-Q1, TPS65919-Q1, and TPS65916 Devices". This document recommend * taking ADC tolerances into account and is based on the device integral non- * linearity (INL), offset error and gain error: * * raw high threshold = (ideal threshold + INL) * gain error + offset error * * The gain error include both gain error, as specified in the datasheet, and * the gain error drift. These parameters vary depending on device and whether * the channel is calibrated (trimmed) or not.
*/ staticint palmas_gpadc_threshold_with_tolerance(int val, constint INL, constint gain_error, constint offset_error)
{
val = ((val + INL) * (1000 + gain_error)) / 1000 + offset_error;
return clamp(val, 0, 0xFFF);
}
/* * The values below are taken from the datasheet of TWL6035, TWL6037. * todo: get max INL, gain error, and offset error from OF.
*/ staticint palmas_gpadc_get_high_threshold_raw(struct palmas_gpadc *adc, struct palmas_adc_event *ev)
{ constint adc_chan = ev->channel; int val = adc->thresholds[adc_chan].high; /* integral nonlinearity, measured in LSB */ constint max_INL = 2; /* measured in LSB */ int max_offset_error; /* 0.2% when calibrated */ int max_gain_error = 2;
val = (val * 1000) / adc->adc_info[adc_chan].gain;
/* * The values below are taken from the datasheet of TWL6035, TWL6037. * todo: get min INL, gain error, and offset error from OF.
*/ staticint palmas_gpadc_get_low_threshold_raw(struct palmas_gpadc *adc, struct palmas_adc_event *ev)
{ constint adc_chan = ev->channel; int val = adc->thresholds[adc_chan].low; /* integral nonlinearity, measured in LSB */ constint min_INL = -2; /* measured in LSB */ int min_offset_error; /* -0.6% when calibrated */ int min_gain_error = -6;
val = (val * 1000) / adc->adc_info[adc_chan].gain;
staticint palmas_gpadc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask)
{ struct palmas_gpadc *adc = iio_priv(indio_dev); int adc_chan = chan->channel; int ret = 0;
if (adc_chan >= PALMAS_ADC_CH_MAX) return -EINVAL;
mutex_lock(&adc->lock);
switch (mask) { case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_PROCESSED:
ret = palmas_gpadc_read_prepare(adc, adc_chan); if (ret < 0) goto out;
ret = palmas_gpadc_start_conversion(adc, adc_chan); if (ret < 0) {
dev_err(adc->dev, "ADC start conversion failed\n"); goto out;
}
if (mask == IIO_CHAN_INFO_PROCESSED)
ret = palmas_gpadc_get_calibrated_code(
adc, adc_chan, ret);
if (palmas_gpadc_get_event(adc, adc_chan, dir)) /* already enabled */ return 0;
if (adc->event0.channel == -1) {
ev = &adc->event0;
} elseif (adc->event1.channel == -1) { /* event0 has to be the lowest channel */ if (adc_chan < adc->event0.channel) {
adc->event1 = adc->event0;
ev = &adc->event0;
} else {
ev = &adc->event1;
}
} else { /* both AUTO channels already in use */
dev_warn(adc->dev, "event0 - %d, event1 - %d\n",
adc->event0.channel, adc->event1.channel); return -EBUSY;
}
if (pdata && pdata->gpadc_pdata)
gpadc_pdata = pdata->gpadc_pdata;
if (!gpadc_pdata && pdev->dev.of_node) {
ret = palmas_gpadc_get_adc_dt_data(pdev, &gpadc_pdata); if (ret < 0) return ret;
} if (!gpadc_pdata) return -EINVAL;
ret = devm_iio_device_register(&pdev->dev, indio_dev); if (ret < 0) return dev_err_probe(adc->dev, ret, "iio_device_register() failed\n");
device_set_wakeup_capable(&pdev->dev, 1); for (i = 0; i < PALMAS_ADC_CH_MAX; i++) { if (!(adc->adc_info[i].is_uncalibrated))
palmas_gpadc_calibrate(adc, i);
}
ret = devm_add_action(&pdev->dev, palmas_gpadc_reset, adc); if (ret) return ret;
return 0;
}
staticint palmas_adc_configure_events(struct palmas_gpadc *adc)
{ int adc_period, conv; int i; int ch0 = 0, ch1 = 0; int thres; int ret;
adc_period = adc->auto_conversion_period; for (i = 0; i < 16; ++i) { if (((1000 * (1 << i)) / 32) >= adc_period) break;
} if (i > 0)
i--;
adc_period = i;
ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_AUTO_CTRL,
PALMAS_GPADC_AUTO_CTRL_COUNTER_CONV_MASK,
adc_period); if (ret < 0) {
dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret); return ret;
}
conv = 0; if (adc->event0.enabled) { struct palmas_adc_event *ev = &adc->event0; int polarity;
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.