162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * IIO driver for Texas Instruments ADS7924 ADC, 12-bit, 4-Channels, I2C
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Hugo Villeneuve <hvilleneuve@dimonoff.com>
662306a36Sopenharmony_ci * Copyright 2022 DimOnOff
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * based on iio/adc/ti-ads1015.c
962306a36Sopenharmony_ci * Copyright (c) 2016, Intel Corporation.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Datasheet: https://www.ti.com/lit/gpn/ads7924
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/bitfield.h>
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1762306a36Sopenharmony_ci#include <linux/init.h>
1862306a36Sopenharmony_ci#include <linux/irq.h>
1962306a36Sopenharmony_ci#include <linux/i2c.h>
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/mutex.h>
2262306a36Sopenharmony_ci#include <linux/regmap.h>
2362306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/iio/iio.h>
2662306a36Sopenharmony_ci#include <linux/iio/types.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define ADS7924_CHANNELS	 4
2962306a36Sopenharmony_ci#define ADS7924_BITS		12
3062306a36Sopenharmony_ci#define ADS7924_DATA_SHIFT	 4
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* Registers. */
3362306a36Sopenharmony_ci#define ADS7924_MODECNTRL_REG	0x00
3462306a36Sopenharmony_ci#define ADS7924_INTCNTRL_REG	0x01
3562306a36Sopenharmony_ci#define ADS7924_DATA0_U_REG	0x02
3662306a36Sopenharmony_ci#define ADS7924_DATA0_L_REG	0x03
3762306a36Sopenharmony_ci#define ADS7924_DATA1_U_REG	0x04
3862306a36Sopenharmony_ci#define ADS7924_DATA1_L_REG	0x05
3962306a36Sopenharmony_ci#define ADS7924_DATA2_U_REG	0x06
4062306a36Sopenharmony_ci#define ADS7924_DATA2_L_REG	0x07
4162306a36Sopenharmony_ci#define ADS7924_DATA3_U_REG	0x08
4262306a36Sopenharmony_ci#define ADS7924_DATA3_L_REG	0x09
4362306a36Sopenharmony_ci#define ADS7924_ULR0_REG	0x0A
4462306a36Sopenharmony_ci#define ADS7924_LLR0_REG	0x0B
4562306a36Sopenharmony_ci#define ADS7924_ULR1_REG	0x0C
4662306a36Sopenharmony_ci#define ADS7924_LLR1_REG	0x0D
4762306a36Sopenharmony_ci#define ADS7924_ULR2_REG	0x0E
4862306a36Sopenharmony_ci#define ADS7924_LLR2_REG	0x0F
4962306a36Sopenharmony_ci#define ADS7924_ULR3_REG	0x10
5062306a36Sopenharmony_ci#define ADS7924_LLR3_REG	0x11
5162306a36Sopenharmony_ci#define ADS7924_INTCONFIG_REG	0x12
5262306a36Sopenharmony_ci#define ADS7924_SLPCONFIG_REG	0x13
5362306a36Sopenharmony_ci#define ADS7924_ACQCONFIG_REG	0x14
5462306a36Sopenharmony_ci#define ADS7924_PWRCONFIG_REG	0x15
5562306a36Sopenharmony_ci#define ADS7924_RESET_REG	0x16
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/*
5862306a36Sopenharmony_ci * Register address INC bit: when set to '1', the register address is
5962306a36Sopenharmony_ci * automatically incremented after every register read which allows convenient
6062306a36Sopenharmony_ci * reading of multiple registers. Set INC to '0' when reading a single register.
6162306a36Sopenharmony_ci */
6262306a36Sopenharmony_ci#define ADS7924_AUTO_INCREMENT_BIT	BIT(7)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define ADS7924_MODECNTRL_MODE_MASK	GENMASK(7, 2)
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define ADS7924_MODECNTRL_SEL_MASK	GENMASK(1, 0)
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define ADS7924_CFG_INTPOL_BIT		1
6962306a36Sopenharmony_ci#define ADS7924_CFG_INTTRIG_BIT		0
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#define ADS7924_CFG_INTPOL_MASK		BIT(ADS7924_CFG_INTPOL_BIT)
7262306a36Sopenharmony_ci#define ADS7924_CFG_INTTRIG_MASK	BIT(ADS7924_CFG_INTTRIG_BIT)
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* Interrupt pin polarity */
7562306a36Sopenharmony_ci#define ADS7924_CFG_INTPOL_LOW		0
7662306a36Sopenharmony_ci#define ADS7924_CFG_INTPOL_HIGH		1
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/* Interrupt pin signaling */
7962306a36Sopenharmony_ci#define ADS7924_CFG_INTTRIG_LEVEL	0
8062306a36Sopenharmony_ci#define ADS7924_CFG_INTTRIG_EDGE	1
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/* Mode control values */
8362306a36Sopenharmony_ci#define ADS7924_MODECNTRL_IDLE			0x00
8462306a36Sopenharmony_ci#define ADS7924_MODECNTRL_AWAKE			0x20
8562306a36Sopenharmony_ci#define ADS7924_MODECNTRL_MANUAL_SINGLE		0x30
8662306a36Sopenharmony_ci#define ADS7924_MODECNTRL_MANUAL_SCAN		0x32
8762306a36Sopenharmony_ci#define ADS7924_MODECNTRL_AUTO_SINGLE		0x31
8862306a36Sopenharmony_ci#define ADS7924_MODECNTRL_AUTO_SCAN		0x33
8962306a36Sopenharmony_ci#define ADS7924_MODECNTRL_AUTO_SINGLE_SLEEP	0x39
9062306a36Sopenharmony_ci#define ADS7924_MODECNTRL_AUTO_SCAN_SLEEP	0x3B
9162306a36Sopenharmony_ci#define ADS7924_MODECNTRL_AUTO_BURST_SLEEP	0x3F
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define ADS7924_ACQTIME_MASK	GENMASK(4, 0)
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci#define ADS7924_PWRUPTIME_MASK	GENMASK(4, 0)
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/*
9862306a36Sopenharmony_ci * The power-up time is allowed to elapse whenever the device has been shutdown
9962306a36Sopenharmony_ci * in idle mode. Power-up time can allow external circuits, such as an
10062306a36Sopenharmony_ci * operational amplifier, between the MUXOUT and ADCIN pins to turn on.
10162306a36Sopenharmony_ci * The nominal time programmed by the PUTIME[4:0] register bits is given by:
10262306a36Sopenharmony_ci *     t PU = PWRUPTIME[4:0] × 2 μs
10362306a36Sopenharmony_ci * If a power-up time is not required, set the bits to '0' to effectively bypass.
10462306a36Sopenharmony_ci */
10562306a36Sopenharmony_ci#define ADS7924_PWRUPTIME_US 0 /* Bypass (0us). */
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/*
10862306a36Sopenharmony_ci * Acquisition Time according to ACQTIME[4:0] register bits.
10962306a36Sopenharmony_ci * The Acquisition Time is given by:
11062306a36Sopenharmony_ci *     t ACQ = (ACQTIME[4:0] × 2 μs) + 6 μs
11162306a36Sopenharmony_ci * Using default value of 0 for ACQTIME[4:0] results in a minimum acquisition
11262306a36Sopenharmony_ci * time of 6us.
11362306a36Sopenharmony_ci */
11462306a36Sopenharmony_ci#define ADS7924_ACQTIME_US 6
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/* The conversion time is always 4μs and cannot be programmed by the user. */
11762306a36Sopenharmony_ci#define ADS7924_CONVTIME_US 4
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci#define ADS7924_TOTAL_CONVTIME_US (ADS7924_PWRUPTIME_US + ADS7924_ACQTIME_US + \
12062306a36Sopenharmony_ci				   ADS7924_CONVTIME_US)
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci#define ADS7924_V_CHAN(_chan, _addr) {				\
12362306a36Sopenharmony_ci	.type = IIO_VOLTAGE,					\
12462306a36Sopenharmony_ci	.indexed = 1,						\
12562306a36Sopenharmony_ci	.channel = _chan,					\
12662306a36Sopenharmony_ci	.address = _addr,					\
12762306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 		\
12862306a36Sopenharmony_ci	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
12962306a36Sopenharmony_ci	.datasheet_name = "AIN"#_chan,				\
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistruct ads7924_data {
13362306a36Sopenharmony_ci	struct device *dev;
13462306a36Sopenharmony_ci	struct regmap *regmap;
13562306a36Sopenharmony_ci	struct regulator *vref_reg;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* GPIO descriptor for device hard-reset pin. */
13862306a36Sopenharmony_ci	struct gpio_desc *reset_gpio;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/*
14162306a36Sopenharmony_ci	 * Protects ADC ops, e.g: concurrent sysfs/buffered
14262306a36Sopenharmony_ci	 * data reads, configuration updates
14362306a36Sopenharmony_ci	 */
14462306a36Sopenharmony_ci	struct mutex lock;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/*
14762306a36Sopenharmony_ci	 * Set to true when the ADC is switched to the continuous-conversion
14862306a36Sopenharmony_ci	 * mode and exits from a power-down state. This flag is used to avoid
14962306a36Sopenharmony_ci	 * getting the stale result from the conversion register.
15062306a36Sopenharmony_ci	 */
15162306a36Sopenharmony_ci	bool conv_invalid;
15262306a36Sopenharmony_ci};
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic bool ads7924_is_writeable_reg(struct device *dev, unsigned int reg)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	switch (reg) {
15762306a36Sopenharmony_ci	case ADS7924_MODECNTRL_REG:
15862306a36Sopenharmony_ci	case ADS7924_INTCNTRL_REG:
15962306a36Sopenharmony_ci	case ADS7924_ULR0_REG:
16062306a36Sopenharmony_ci	case ADS7924_LLR0_REG:
16162306a36Sopenharmony_ci	case ADS7924_ULR1_REG:
16262306a36Sopenharmony_ci	case ADS7924_LLR1_REG:
16362306a36Sopenharmony_ci	case ADS7924_ULR2_REG:
16462306a36Sopenharmony_ci	case ADS7924_LLR2_REG:
16562306a36Sopenharmony_ci	case ADS7924_ULR3_REG:
16662306a36Sopenharmony_ci	case ADS7924_LLR3_REG:
16762306a36Sopenharmony_ci	case ADS7924_INTCONFIG_REG:
16862306a36Sopenharmony_ci	case ADS7924_SLPCONFIG_REG:
16962306a36Sopenharmony_ci	case ADS7924_ACQCONFIG_REG:
17062306a36Sopenharmony_ci	case ADS7924_PWRCONFIG_REG:
17162306a36Sopenharmony_ci	case ADS7924_RESET_REG:
17262306a36Sopenharmony_ci		return true;
17362306a36Sopenharmony_ci	default:
17462306a36Sopenharmony_ci		return false;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic const struct regmap_config ads7924_regmap_config = {
17962306a36Sopenharmony_ci	.reg_bits = 8,
18062306a36Sopenharmony_ci	.val_bits = 8,
18162306a36Sopenharmony_ci	.max_register = ADS7924_RESET_REG,
18262306a36Sopenharmony_ci	.writeable_reg = ads7924_is_writeable_reg,
18362306a36Sopenharmony_ci};
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic const struct iio_chan_spec ads7924_channels[] = {
18662306a36Sopenharmony_ci	ADS7924_V_CHAN(0, ADS7924_DATA0_U_REG),
18762306a36Sopenharmony_ci	ADS7924_V_CHAN(1, ADS7924_DATA1_U_REG),
18862306a36Sopenharmony_ci	ADS7924_V_CHAN(2, ADS7924_DATA2_U_REG),
18962306a36Sopenharmony_ci	ADS7924_V_CHAN(3, ADS7924_DATA3_U_REG),
19062306a36Sopenharmony_ci};
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int ads7924_get_adc_result(struct ads7924_data *data,
19362306a36Sopenharmony_ci				  struct iio_chan_spec const *chan, int *val)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	int ret;
19662306a36Sopenharmony_ci	__be16 be_val;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (chan->channel < 0 || chan->channel >= ADS7924_CHANNELS)
19962306a36Sopenharmony_ci		return -EINVAL;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (data->conv_invalid) {
20262306a36Sopenharmony_ci		int conv_time;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci		conv_time = ADS7924_TOTAL_CONVTIME_US;
20562306a36Sopenharmony_ci		/* Allow 10% for internal clock inaccuracy. */
20662306a36Sopenharmony_ci		conv_time += conv_time / 10;
20762306a36Sopenharmony_ci		usleep_range(conv_time, conv_time + 1);
20862306a36Sopenharmony_ci		data->conv_invalid = false;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ret = regmap_raw_read(data->regmap, ADS7924_AUTO_INCREMENT_BIT |
21262306a36Sopenharmony_ci			      chan->address, &be_val, sizeof(be_val));
21362306a36Sopenharmony_ci	if (ret)
21462306a36Sopenharmony_ci		return ret;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	*val = be16_to_cpu(be_val) >> ADS7924_DATA_SHIFT;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	return 0;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic int ads7924_read_raw(struct iio_dev *indio_dev,
22262306a36Sopenharmony_ci			    struct iio_chan_spec const *chan, int *val,
22362306a36Sopenharmony_ci			    int *val2, long mask)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	int ret, vref_uv;
22662306a36Sopenharmony_ci	struct ads7924_data *data = iio_priv(indio_dev);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	switch (mask) {
22962306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
23062306a36Sopenharmony_ci		mutex_lock(&data->lock);
23162306a36Sopenharmony_ci		ret = ads7924_get_adc_result(data, chan, val);
23262306a36Sopenharmony_ci		mutex_unlock(&data->lock);
23362306a36Sopenharmony_ci		if (ret < 0)
23462306a36Sopenharmony_ci			return ret;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		return IIO_VAL_INT;
23762306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
23862306a36Sopenharmony_ci		vref_uv = regulator_get_voltage(data->vref_reg);
23962306a36Sopenharmony_ci		if (vref_uv < 0)
24062306a36Sopenharmony_ci			return vref_uv;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		*val =  vref_uv / 1000; /* Convert reg voltage to mV */
24362306a36Sopenharmony_ci		*val2 = ADS7924_BITS;
24462306a36Sopenharmony_ci		return IIO_VAL_FRACTIONAL_LOG2;
24562306a36Sopenharmony_ci	default:
24662306a36Sopenharmony_ci		return -EINVAL;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic const struct iio_info ads7924_info = {
25162306a36Sopenharmony_ci	.read_raw = ads7924_read_raw,
25262306a36Sopenharmony_ci};
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic int ads7924_get_channels_config(struct i2c_client *client,
25562306a36Sopenharmony_ci				       struct iio_dev *indio_dev)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct ads7924_data *priv = iio_priv(indio_dev);
25862306a36Sopenharmony_ci	struct device *dev = priv->dev;
25962306a36Sopenharmony_ci	struct fwnode_handle *node;
26062306a36Sopenharmony_ci	int num_channels = 0;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	device_for_each_child_node(dev, node) {
26362306a36Sopenharmony_ci		u32 pval;
26462306a36Sopenharmony_ci		unsigned int channel;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		if (fwnode_property_read_u32(node, "reg", &pval)) {
26762306a36Sopenharmony_ci			dev_err(dev, "invalid reg on %pfw\n", node);
26862306a36Sopenharmony_ci			continue;
26962306a36Sopenharmony_ci		}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		channel = pval;
27262306a36Sopenharmony_ci		if (channel >= ADS7924_CHANNELS) {
27362306a36Sopenharmony_ci			dev_err(dev, "invalid channel index %d on %pfw\n",
27462306a36Sopenharmony_ci				channel, node);
27562306a36Sopenharmony_ci			continue;
27662306a36Sopenharmony_ci		}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		num_channels++;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (!num_channels)
28262306a36Sopenharmony_ci		return -EINVAL;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	return 0;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic int ads7924_set_conv_mode(struct ads7924_data *data, int mode)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	int ret;
29062306a36Sopenharmony_ci	unsigned int mode_field;
29162306a36Sopenharmony_ci	struct device *dev = data->dev;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/*
29462306a36Sopenharmony_ci	 * When switching between modes, be sure to first select the Awake mode
29562306a36Sopenharmony_ci	 * and then switch to the desired mode. This procedure ensures the
29662306a36Sopenharmony_ci	 * internal control logic is properly synchronized.
29762306a36Sopenharmony_ci	 */
29862306a36Sopenharmony_ci	if (mode != ADS7924_MODECNTRL_IDLE) {
29962306a36Sopenharmony_ci		mode_field = FIELD_PREP(ADS7924_MODECNTRL_MODE_MASK,
30062306a36Sopenharmony_ci					ADS7924_MODECNTRL_AWAKE);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		ret = regmap_update_bits(data->regmap, ADS7924_MODECNTRL_REG,
30362306a36Sopenharmony_ci					 ADS7924_MODECNTRL_MODE_MASK,
30462306a36Sopenharmony_ci					 mode_field);
30562306a36Sopenharmony_ci		if (ret) {
30662306a36Sopenharmony_ci			dev_err(dev, "failed to set awake mode (%pe)\n",
30762306a36Sopenharmony_ci				ERR_PTR(ret));
30862306a36Sopenharmony_ci			return ret;
30962306a36Sopenharmony_ci		}
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	mode_field = FIELD_PREP(ADS7924_MODECNTRL_MODE_MASK, mode);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap, ADS7924_MODECNTRL_REG,
31562306a36Sopenharmony_ci				 ADS7924_MODECNTRL_MODE_MASK, mode_field);
31662306a36Sopenharmony_ci	if (ret)
31762306a36Sopenharmony_ci		dev_err(dev, "failed to set mode %d (%pe)\n", mode,
31862306a36Sopenharmony_ci			ERR_PTR(ret));
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	return ret;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic int ads7924_reset(struct iio_dev *indio_dev)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct ads7924_data *data = iio_priv(indio_dev);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (data->reset_gpio) {
32862306a36Sopenharmony_ci		gpiod_set_value(data->reset_gpio, 1); /* Assert. */
32962306a36Sopenharmony_ci		/* Educated guess: assert time not specified in datasheet... */
33062306a36Sopenharmony_ci		mdelay(100);
33162306a36Sopenharmony_ci		gpiod_set_value(data->reset_gpio, 0); /* Deassert. */
33262306a36Sopenharmony_ci		return 0;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	/*
33662306a36Sopenharmony_ci	 * A write of 10101010 to this register will generate a
33762306a36Sopenharmony_ci	 * software reset of the ADS7924.
33862306a36Sopenharmony_ci	 */
33962306a36Sopenharmony_ci	return regmap_write(data->regmap, ADS7924_RESET_REG, 0b10101010);
34062306a36Sopenharmony_ci};
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic void ads7924_reg_disable(void *data)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	regulator_disable(data);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic void ads7924_set_idle_mode(void *data)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	ads7924_set_conv_mode(data, ADS7924_MODECNTRL_IDLE);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic int ads7924_probe(struct i2c_client *client)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct iio_dev *indio_dev;
35562306a36Sopenharmony_ci	struct ads7924_data *data;
35662306a36Sopenharmony_ci	struct device *dev = &client->dev;
35762306a36Sopenharmony_ci	int ret;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
36062306a36Sopenharmony_ci	if (!indio_dev)
36162306a36Sopenharmony_ci		return dev_err_probe(dev, -ENOMEM,
36262306a36Sopenharmony_ci				     "failed to allocate iio device\n");
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	data = iio_priv(indio_dev);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	data->dev = dev;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/* Initialize the reset GPIO as output with an initial value of 0. */
36962306a36Sopenharmony_ci	data->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
37062306a36Sopenharmony_ci	if (IS_ERR(data->reset_gpio))
37162306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(data->reset_gpio),
37262306a36Sopenharmony_ci				     "failed to get request reset GPIO\n");
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	mutex_init(&data->lock);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	indio_dev->name = "ads7924";
37762306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	indio_dev->channels = ads7924_channels;
38062306a36Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(ads7924_channels);
38162306a36Sopenharmony_ci	indio_dev->info = &ads7924_info;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	ret = ads7924_get_channels_config(client, indio_dev);
38462306a36Sopenharmony_ci	if (ret < 0)
38562306a36Sopenharmony_ci		return dev_err_probe(dev, ret,
38662306a36Sopenharmony_ci				     "failed to get channels configuration\n");
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	data->regmap = devm_regmap_init_i2c(client, &ads7924_regmap_config);
38962306a36Sopenharmony_ci	if (IS_ERR(data->regmap))
39062306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(data->regmap),
39162306a36Sopenharmony_ci				     "failed to init regmap\n");
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	data->vref_reg = devm_regulator_get(dev, "vref");
39462306a36Sopenharmony_ci	if (IS_ERR(data->vref_reg))
39562306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(data->vref_reg),
39662306a36Sopenharmony_ci				     "failed to get vref regulator\n");
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	ret = regulator_enable(data->vref_reg);
39962306a36Sopenharmony_ci	if (ret)
40062306a36Sopenharmony_ci		return dev_err_probe(dev, ret,
40162306a36Sopenharmony_ci				     "failed to enable regulator\n");
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	ret = devm_add_action_or_reset(dev, ads7924_reg_disable, data->vref_reg);
40462306a36Sopenharmony_ci	if (ret)
40562306a36Sopenharmony_ci		return dev_err_probe(dev, ret,
40662306a36Sopenharmony_ci				     "failed to add regulator disable action\n");
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	ret = ads7924_reset(indio_dev);
40962306a36Sopenharmony_ci	if (ret < 0)
41062306a36Sopenharmony_ci		return dev_err_probe(dev, ret,
41162306a36Sopenharmony_ci				     "failed to reset device\n");
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	ret = ads7924_set_conv_mode(data, ADS7924_MODECNTRL_AUTO_SCAN);
41462306a36Sopenharmony_ci	if (ret)
41562306a36Sopenharmony_ci		return dev_err_probe(dev, ret,
41662306a36Sopenharmony_ci				     "failed to set conversion mode\n");
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	ret = devm_add_action_or_reset(dev, ads7924_set_idle_mode, data);
41962306a36Sopenharmony_ci	if (ret)
42062306a36Sopenharmony_ci		return dev_err_probe(dev, ret,
42162306a36Sopenharmony_ci				     "failed to add idle mode action\n");
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	/* Use minimum signal acquire time. */
42462306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap, ADS7924_ACQCONFIG_REG,
42562306a36Sopenharmony_ci				 ADS7924_ACQTIME_MASK,
42662306a36Sopenharmony_ci				 FIELD_PREP(ADS7924_ACQTIME_MASK, 0));
42762306a36Sopenharmony_ci	if (ret < 0)
42862306a36Sopenharmony_ci		return dev_err_probe(dev, ret,
42962306a36Sopenharmony_ci				     "failed to configure signal acquire time\n");
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	/* Disable power-up time. */
43262306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap, ADS7924_PWRCONFIG_REG,
43362306a36Sopenharmony_ci				 ADS7924_PWRUPTIME_MASK,
43462306a36Sopenharmony_ci				 FIELD_PREP(ADS7924_PWRUPTIME_MASK, 0));
43562306a36Sopenharmony_ci	if (ret < 0)
43662306a36Sopenharmony_ci		return dev_err_probe(dev, ret,
43762306a36Sopenharmony_ci				     "failed to configure power-up time\n");
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	data->conv_invalid = true;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	ret = devm_iio_device_register(dev, indio_dev);
44262306a36Sopenharmony_ci	if (ret < 0)
44362306a36Sopenharmony_ci		return dev_err_probe(dev, ret,
44462306a36Sopenharmony_ci				     "failed to register IIO device\n");
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	return 0;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic const struct i2c_device_id ads7924_id[] = {
45062306a36Sopenharmony_ci	{ "ads7924", 0 },
45162306a36Sopenharmony_ci	{}
45262306a36Sopenharmony_ci};
45362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ads7924_id);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic const struct of_device_id ads7924_of_match[] = {
45662306a36Sopenharmony_ci	{ .compatible = "ti,ads7924", },
45762306a36Sopenharmony_ci	{}
45862306a36Sopenharmony_ci};
45962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ads7924_of_match);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic struct i2c_driver ads7924_driver = {
46262306a36Sopenharmony_ci	.driver = {
46362306a36Sopenharmony_ci		.name = "ads7924",
46462306a36Sopenharmony_ci		.of_match_table = ads7924_of_match,
46562306a36Sopenharmony_ci	},
46662306a36Sopenharmony_ci	.probe		= ads7924_probe,
46762306a36Sopenharmony_ci	.id_table	= ads7924_id,
46862306a36Sopenharmony_ci};
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cimodule_i2c_driver(ads7924_driver);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ciMODULE_AUTHOR("Hugo Villeneuve <hvilleneuve@dimonoff.com>");
47362306a36Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments ADS7924 ADC I2C driver");
47462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
475