Commit 8cf5f032 authored by Marcus Folkesson's avatar Marcus Folkesson Committed by Jonathan Cameron
Browse files

iio: adc: mcp3911: add support to set PGA



Add support for setting the Programmable Gain Amplifiers by adjust the
scale value.

Signed-off-by: default avatarMarcus Folkesson <marcus.folkesson@gmail.com>
Reviewed-by: default avatarAndy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20220922194639.1118971-1-marcus.folkesson@gmail.com


Signed-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 4e615140
Loading
Loading
Loading
Loading
+80 −24
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@
#define MCP3911_REG_MOD			0x06
#define MCP3911_REG_PHASE		0x07
#define MCP3911_REG_GAIN		0x09
#define MCP3911_GAIN_MASK(ch)		(GENMASK(2, 0) << 3 * ch)
#define MCP3911_GAIN_VAL(ch, val)      ((val << 3 * ch) & MCP3911_GAIN_MASK(ch))

#define MCP3911_REG_STATUSCOM		0x0a
#define MCP3911_STATUSCOM_DRHIZ         BIT(12)
@@ -60,8 +62,10 @@
#define MCP3911_REG_MASK		GENMASK(4, 1)

#define MCP3911_NUM_CHANNELS		2
#define MCP3911_NUM_SCALES		6

static const int mcp3911_osr_table[] = { 32, 64, 128, 256, 512, 1024, 2048, 4096 };
static u32 mcp3911_scale_table[MCP3911_NUM_SCALES][2];

struct mcp3911 {
	struct spi_device *spi;
@@ -70,6 +74,7 @@ struct mcp3911 {
	struct clk *clki;
	u32 dev_addr;
	struct iio_trigger *trig;
	u32 gain[MCP3911_NUM_CHANNELS];
	struct {
		u32 channels[MCP3911_NUM_CHANNELS];
		s64 ts __aligned(8);
@@ -146,6 +151,11 @@ static int mcp3911_read_avail(struct iio_dev *indio_dev,
		*vals = mcp3911_osr_table;
		*length = ARRAY_SIZE(mcp3911_osr_table);
		return IIO_AVAIL_LIST;
	case IIO_CHAN_INFO_SCALE:
		*type = IIO_VAL_INT_PLUS_NANO;
		*vals = (int *)mcp3911_scale_table;
		*length = ARRAY_SIZE(mcp3911_scale_table) * 2;
		return IIO_AVAIL_LIST;
	default:
		return -EINVAL;
	}
@@ -190,29 +200,9 @@ static int mcp3911_read_raw(struct iio_dev *indio_dev,
		break;

	case IIO_CHAN_INFO_SCALE:
		if (adc->vref) {
			ret = regulator_get_voltage(adc->vref);
			if (ret < 0) {
				dev_err(indio_dev->dev.parent,
					"failed to get vref voltage: %d\n",
				       ret);
				goto out;
			}

			*val = ret / 1000;
		} else {
			*val = MCP3911_INT_VREF_MV;
		}

		/*
		 * For 24bit Conversion
		 * Raw = ((Voltage)/(Vref) * 2^23 * Gain * 1.5
		 * Voltage = Raw * (Vref)/(2^23 * Gain * 1.5)
		 */

		/* val2 = (2^23 * 1.5) */
		*val2 = 12582912;
		ret = IIO_VAL_FRACTIONAL;
		*val = mcp3911_scale_table[ilog2(adc->gain[channel->channel])][0];
		*val2 = mcp3911_scale_table[ilog2(adc->gain[channel->channel])][1];
		ret = IIO_VAL_INT_PLUS_NANO;
		break;
	}

@@ -230,6 +220,18 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev,

	mutex_lock(&adc->lock);
	switch (mask) {
	case IIO_CHAN_INFO_SCALE:
		for (int i = 0; i < MCP3911_NUM_SCALES; i++) {
			if (val == mcp3911_scale_table[i][0] &&
				val2 == mcp3911_scale_table[i][1]) {

				adc->gain[channel->channel] = BIT(i);
				ret = mcp3911_update(adc, MCP3911_REG_GAIN,
						MCP3911_GAIN_MASK(channel->channel),
						MCP3911_GAIN_VAL(channel->channel, i), 1);
			}
		}
		break;
	case IIO_CHAN_INFO_OFFSET:
		if (val2 != 0) {
			ret = -EINVAL;
@@ -265,6 +267,44 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev,
	return ret;
}

static int mcp3911_calc_scale_table(struct mcp3911 *adc)
{
	u32 ref = MCP3911_INT_VREF_MV;
	u32 div;
	int ret;
	u64 tmp;

	if (adc->vref) {
		ret = regulator_get_voltage(adc->vref);
		if (ret < 0) {
			dev_err(&adc->spi->dev,
				"failed to get vref voltage: %d\n",
			       ret);
			return ret;
		}

		ref = ret / 1000;
	}

	/*
	 * For 24-bit Conversion
	 * Raw = ((Voltage)/(Vref) * 2^23 * Gain * 1.5
	 * Voltage = Raw * (Vref)/(2^23 * Gain * 1.5)
	 *
	 * ref = Reference voltage
	 * div = (2^23 * 1.5 * gain) = 12582912 * gain
	 */
	for (int i = 0; i < MCP3911_NUM_SCALES; i++) {
		div = 12582912 * BIT(i);
		tmp = div_s64((s64)ref * 1000000000LL, div);

		mcp3911_scale_table[i][0] = 0;
		mcp3911_scale_table[i][1] = tmp;
	}

	return 0;
}

#define MCP3911_CHAN(idx) {					\
		.type = IIO_VOLTAGE,				\
		.indexed = 1,					\
@@ -276,6 +316,8 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev,
			BIT(IIO_CHAN_INFO_SCALE),		\
		.info_mask_shared_by_type_available =           \
			BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),	\
		.info_mask_separate_available =			\
			BIT(IIO_CHAN_INFO_SCALE),		\
		.scan_type = {					\
			.sign = 's',				\
			.realbits = 24,				\
@@ -482,6 +524,20 @@ static int mcp3911_probe(struct spi_device *spi)
	if (ret)
		return ret;

	ret = mcp3911_calc_scale_table(adc);
	if (ret)
		return ret;

       /* Set gain to 1 for all channels */
	for (int i = 0; i < MCP3911_NUM_CHANNELS; i++) {
		adc->gain[i] = 1;
		ret = mcp3911_update(adc, MCP3911_REG_GAIN,
				MCP3911_GAIN_MASK(i),
				MCP3911_GAIN_VAL(i, 0), 1);
		if (ret)
			return ret;
	}

	indio_dev->name = spi_get_device_id(spi)->name;
	indio_dev->modes = INDIO_DIRECT_MODE;
	indio_dev->info = &mcp3911_info;