Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

iio: adc: tsc2046: add vref support

If VREF pin is attached, we should use external VREF source instead of
the internal. Otherwise we will get wrong measurements on some of the channel
types.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20220904100203.3614502-2-o.rempel@pengutronix.de
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Oleksij Rempel and committed by
Jonathan Cameron
a616a6a1 6cda6aa8

+55 -2
+55 -2
drivers/iio/adc/ti-tsc2046.c
··· 8 8 #include <linux/bitfield.h> 9 9 #include <linux/delay.h> 10 10 #include <linux/module.h> 11 + #include <linux/regulator/consumer.h> 11 12 #include <linux/spi/spi.h> 13 + #include <linux/units.h> 12 14 13 15 #include <asm/unaligned.h> 14 16 ··· 141 139 struct tsc2046_adc_priv { 142 140 struct spi_device *spi; 143 141 const struct tsc2046_adc_dcfg *dcfg; 142 + struct regulator *vref_reg; 144 143 145 144 struct iio_trigger *trig; 146 145 struct hrtimer trig_timer; ··· 176 173 u32 scan_interval_us; 177 174 u32 time_per_scan_us; 178 175 u32 time_per_bit_ns; 176 + unsigned int vref_mv; 179 177 180 178 struct tsc2046_adc_ch_cfg ch_cfg[TI_TSC2046_MAX_CHAN]; 181 179 }; ··· 256 252 case TI_TSC2046_ADDR_AUX: 257 253 case TI_TSC2046_ADDR_VBAT: 258 254 case TI_TSC2046_ADDR_TEMP0: 259 - pd |= TI_TSC2046_SER | TI_TSC2046_PD1_VREF_ON; 255 + pd |= TI_TSC2046_SER; 256 + if (!priv->vref_reg) 257 + pd |= TI_TSC2046_PD1_VREF_ON; 260 258 } 261 259 262 260 return TI_TSC2046_START | FIELD_PREP(TI_TSC2046_ADDR, ch_idx) | pd; ··· 474 468 * So, it is better to use external voltage-divider driver 475 469 * instead, which is calculating complete chain. 476 470 */ 477 - *val = TI_TSC2046_INT_VREF; 471 + *val = priv->vref_mv; 478 472 *val2 = chan->scan_type.realbits; 479 473 return IIO_VAL_FRACTIONAL_LOG2; 480 474 } ··· 746 740 } 747 741 } 748 742 743 + static void tsc2046_adc_regulator_disable(void *data) 744 + { 745 + struct tsc2046_adc_priv *priv = data; 746 + 747 + regulator_disable(priv->vref_reg); 748 + } 749 + 750 + static int tsc2046_adc_configure_regulator(struct tsc2046_adc_priv *priv) 751 + { 752 + struct device *dev = &priv->spi->dev; 753 + int ret; 754 + 755 + priv->vref_reg = devm_regulator_get_optional(dev, "vref"); 756 + if (IS_ERR(priv->vref_reg)) { 757 + /* If regulator exists but can't be get, return an error */ 758 + if (PTR_ERR(priv->vref_reg) != -ENODEV) 759 + return PTR_ERR(priv->vref_reg); 760 + priv->vref_reg = NULL; 761 + } 762 + if (!priv->vref_reg) { 763 + /* Use internal reference */ 764 + priv->vref_mv = TI_TSC2046_INT_VREF; 765 + return 0; 766 + } 767 + 768 + ret = regulator_enable(priv->vref_reg); 769 + if (ret) 770 + return ret; 771 + 772 + ret = devm_add_action_or_reset(dev, tsc2046_adc_regulator_disable, 773 + priv); 774 + if (ret) 775 + return ret; 776 + 777 + ret = regulator_get_voltage(priv->vref_reg); 778 + if (ret < 0) 779 + return ret; 780 + 781 + priv->vref_mv = ret / MILLI; 782 + 783 + return 0; 784 + } 785 + 749 786 static int tsc2046_adc_probe(struct spi_device *spi) 750 787 { 751 788 const struct tsc2046_adc_dcfg *dcfg; ··· 829 780 indio_dev->channels = dcfg->channels; 830 781 indio_dev->num_channels = dcfg->num_channels; 831 782 indio_dev->info = &tsc2046_adc_info; 783 + 784 + ret = tsc2046_adc_configure_regulator(priv); 785 + if (ret) 786 + return ret; 832 787 833 788 tsc2046_adc_parse_fwnode(priv); 834 789