[PATCH v3 6/9] iio: adc: ti-ads1262: support excitation currents
Kurt Borja <[email protected]>
| Newsgroups | org.kernel.vger.linux-gpio,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-iio,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Support the two IDAC excitation current sources. Each channel can route its IDAC1/IDAC2 outputs to a pin via the "excitation-channels" property and select a magnitude via "excitation-current-nanoamp". Signed-off-by: Kurt Borja <[email protected]> --- drivers/iio/adc/ti-ads1262.c | 101 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ti-ads1262.c b/drivers/iio/adc/ti-ads1262.c index e2659941e1af..d5464b4f2bfb 100644 --- a/drivers/iio/adc/ti-ads1262.c +++ b/drivers/iio/adc/ti-ads1262.c @@ -98,6 +98,8 @@ #define ADS1262_IDACMUX_NO_CONN 0xB #define ADS1262_IDACMAG_REG 0x0E +#define ADS1262_IDACMAG_MAG2_MASK GENMASK(7, 4) +#define ADS1262_IDACMAG_MAG1_MASK GENMASK(3, 0) #define ADS1262_REFMUX_REG 0x0F #define ADS1262_REFMUX_RMUXP_MASK GENMASK(5, 3) @@ -205,12 +207,15 @@ struct ads1262_chip_info { struct ads1262_channel { u8 data_rate; + u8 idac_mux[2]; + u8 idac_mag[2]; u8 gain; u8 ref_p; u8 ref_n; bool ref_reversal; bool is_resistance; bool input_chop; + bool idac_chop; int offset; int scales[6][2]; size_t num_scales; @@ -277,6 +282,25 @@ static const char * const ads1262_ref_sources_neg[] = { NULL }; +static const u32 ads1262_idac_mags_nA[] = { + 0, 50000, 100000, 250000, 500000, 750000, 1000000, 1500000, 2000000, + 2500000, 3000000 +}; + +static int ads1262_find_one(const int *array, size_t num_elements, int val) +{ + int i; + + for (i = 0; i < num_elements; i++) { + if (val == array[i]) + break; + } + if (i == num_elements) + return -EINVAL; + + return i; +} + static int ads1262_find_two(const int (*array)[2], size_t num_elements, int val, int val2) { @@ -433,9 +457,11 @@ static int ads1262_channel_enable(struct ads1262 *st, guard(mutex)(&st->chan_lock); val = FIELD_PREP(ADS1262_MODE0_INPUT_CHOP_MASK, chan->input_chop) | + FIELD_PREP(ADS1262_MODE0_IDAC_CHOP_MASK, chan->idac_chop) | FIELD_PREP(ADS1262_MODE0_REFREV_MASK, chan->ref_reversal); ret = regmap_update_bits(st->regmap, ADS1262_MODE0_REG, ADS1262_MODE0_INPUT_CHOP_MASK | + ADS1262_MODE0_IDAC_CHOP_MASK | ADS1262_MODE0_REFREV_MASK, val); if (ret) return ret; @@ -456,6 +482,22 @@ static int ads1262_channel_enable(struct ads1262 *st, if (ret) return ret; + val = FIELD_PREP(ADS1262_IDACMUX_MUX1_MASK, chan->idac_mux[0]) | + FIELD_PREP(ADS1262_IDACMUX_MUX2_MASK, chan->idac_mux[1]); + ret = regmap_update_bits(st->regmap, ADS1262_IDACMUX_REG, + ADS1262_IDACMUX_MUX1_MASK | + ADS1262_IDACMUX_MUX2_MASK, val); + if (ret) + return ret; + + val = FIELD_PREP(ADS1262_IDACMAG_MAG1_MASK, chan->idac_mag[0]) | + FIELD_PREP(ADS1262_IDACMAG_MAG2_MASK, chan->idac_mag[1]); + ret = regmap_update_bits(st->regmap, ADS1262_IDACMAG_REG, + ADS1262_IDACMAG_MAG1_MASK | + ADS1262_IDACMAG_MAG2_MASK, val); + if (ret) + return ret; + val = FIELD_PREP(ADS1262_REFMUX_RMUXN_MASK, chan->ref_n) | FIELD_PREP(ADS1262_REFMUX_RMUXP_MASK, chan->ref_p); return regmap_update_bits(st->regmap, ADS1262_REFMUX_REG, @@ -490,7 +532,7 @@ static int ads1262_channel_read(struct iio_dev *indio_dev, * CONTINUOUS mode and briefly starting and stopping conversions to * achieve the same effect (Section 9.4.1.2). */ - if (chan->input_chop) + if (chan->input_chop || chan->idac_chop) runmode = ADS1262_RUNMODE_CONTINUOUS; else runmode = ADS1262_RUNMODE_PULSE; @@ -1070,8 +1112,8 @@ static int ads1262_parse_channel_node(struct ads1262 *st, struct device *dev = &st->spi->dev; const char *sources[2]; char name[sizeof("ti,refpN-refnM-resistor-ohms")]; - u32 pins[2]; - int ret; + u32 pins[2], mags[2]; + int count, ret; if (fwnode_property_present(node, "single-channel")) { ret = fwnode_property_read_u32(node, "single-channel", &pins[0]); @@ -1150,7 +1192,58 @@ static int ads1262_parse_channel_node(struct ads1262 *st, } } + if (fwnode_property_present(node, "excitation-channels")) { + count = fwnode_property_count_u32(node, "excitation-channels"); + if (count < 0) + return dev_err_probe(dev, count, + "%s: failed to count excitation-channels\n", + fwnode_get_name(node)); + + pins[0] = ADS1262_IDACMUX_NO_CONN; + pins[1] = ADS1262_IDACMUX_NO_CONN; + ret = fwnode_property_read_u32_array(node, "excitation-channels", + pins, min(count, ARRAY_SIZE(pins))); + if (ret) + return dev_err_probe(dev, ret, "%s: failed to read excitation-channels\n", + fwnode_get_name(node)); + if (pins[0] > ADS1262_IDACMUX_NO_CONN || pins[1] > ADS1262_IDACMUX_NO_CONN) + return dev_err_probe(dev, -EINVAL, "%s: excitation-channels not in range\n", + fwnode_get_name(node)); + chan->idac_mux[0] = pins[0]; + chan->idac_mux[1] = pins[1]; + + mags[0] = 0; + mags[1] = 0; + ret = fwnode_property_read_u32_array(node, "excitation-current-nanoamp", + mags, min(count, ARRAY_SIZE(mags))); + if (ret == -EOVERFLOW) + return dev_err_probe(dev, ret, + "%s: excitation-current-nanoamp size mismatch\n", + fwnode_get_name(node)); + if (ret) + return dev_err_probe(dev, ret, + "%s: failed to read excitation-current-nanoamp\n", + fwnode_get_name(node)); + + ret = ads1262_find_one(ads1262_idac_mags_nA, + ARRAY_SIZE(ads1262_idac_mags_nA), mags[0]); + if (ret < 0) + return dev_err_probe(dev, ret, + "%s: invalid excitation-current-nanoamp\n", + fwnode_get_name(node)); + chan->idac_mag[0] = ret; + + ret = ads1262_find_one(ads1262_idac_mags_nA, + ARRAY_SIZE(ads1262_idac_mags_nA), mags[1]); + if (ret < 0) + return dev_err_probe(dev, ret, + "%s: invalid excitation-current-nanoamp\n", + fwnode_get_name(node)); + chan->idac_mag[1] = ret; + } + chan->input_chop = fwnode_property_read_bool(node, "input-chopping"); + chan->idac_chop = fwnode_property_read_bool(node, "ti,idac-rotation"); chan->ref_reversal = fwnode_property_read_bool(node, "ti,reference-reversal"); return 0; @@ -1198,6 +1291,8 @@ static int ads1262_parse_channels(struct iio_dev *indio_dev) fwnode_get_name(node)); st->channels[reg].data_rate = ADS1262_DR_20_SPS; + st->channels[reg].idac_mux[0] = ADS1262_IDACMUX_NO_CONN; + st->channels[reg].idac_mux[1] = ADS1262_IDACMUX_NO_CONN; specs[reg].scan_index = reg; specs[reg].scan_type = (struct iio_scan_type) { -- 2.55.0