Re: [PATCH v8 5/9] iio: adc: qcom-spmi-rradc: introduce round robin adc

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sat, 26 Feb 2022 17:35:35 +0000
Jonathan Cameron <jic23@xxxxxxxxxx> wrote:

> On Mon, 21 Feb 2022 22:07:39 +0000
> Caleb Connolly <caleb.connolly@xxxxxxxxxx> wrote:
> 
> > The Round Robin ADC is responsible for reading data about the rate of
> > charge from the USB or DC input ports, it can also read the battery
> > ID (resistence), skin temperature and the die temperature of the pmic.
> > It is found on the PMI8998 and PM660 Qualcomm PMICs.
> > 
> > Signed-off-by: Caleb Connolly <caleb.connolly@xxxxxxxxxx>  
> 
> Hi Calib,

Caleb that is. Sorry!

> 
> Unfortunately this fell for the normal rule that everytime someone
> rereads some code they'll find something new :(
> 
> All minor stuff though so fingers crossed for v9.
> The endian stuff isn't strictly necessary but it is always better to use explicit
> endian types where possible as it hardens the code against forgetting to convert
> them etc.
> 
> Jonathan
> 
> > ---  
> 
> 
> > diff --git a/drivers/iio/adc/qcom-spmi-rradc.c b/drivers/iio/adc/qcom-spmi-rradc.c
> > new file mode 100644
> > index 000000000000..f69d95103c82
> > --- /dev/null
> > +++ b/drivers/iio/adc/qcom-spmi-rradc.c
> > @@ -0,0 +1,1011 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Copyright (c) 2022 Linaro Limited.
> > + *  Author: Caleb Connolly <caleb.connolly@xxxxxxxxxx>
> > + *
> > + * This driver is for the Round Robin ADC found in the pmi8998 and pm660 PMICs.
> > + */  
> 
> ...
> 
> > +static const int batt_id_delays[] = { 0, 1, 4, 12, 20, 40, 60, 80 };
> > +static const struct rradc_channel rradc_chans[RR_ADC_CHAN_MAX];
> > +static const struct iio_chan_spec rradc_iio_chans[RR_ADC_CHAN_MAX];
> > +
> > +static int rradc_read(struct rradc_chip *chip, u16 addr, u8 *data, int len)  
> 
> This function is only ever called in paths which then convert *data to le16.
> As such, pass in __le16 *buf
> regmap_bulk_read() takes a void * so that will work fine without casting.
> The size should still be bytes to reflect that we are reading multiple 8 bit
> registers.
> 
> > +{
> > +	int ret, retry_cnt = 0;
> > +	u8 data_check[RR_ADC_CHAN_MAX_CONTINUOUS_BUFFER_LEN];  
> elegance would make this __le16 as well but that probably doesn't matter.
> 
> Possibly you'll have to do something a bit clever at the memcmp to force
> dropping of the endian markings - build with C=1, W=1 and see if it complains.
> 
> > +
> > +	if (len > RR_ADC_CHAN_MAX_CONTINUOUS_BUFFER_LEN) {
> > +		dev_err(chip->dev,
> > +			"Can't read more than %d bytes, but asked to read %d bytes.\n",
> > +			RR_ADC_CHAN_MAX_CONTINUOUS_BUFFER_LEN, len);
> > +		return -EINVAL;
> > +	}
> > +
> > +	while (retry_cnt < RR_ADC_COHERENT_CHECK_RETRY) {
> > +		ret = regmap_bulk_read(chip->regmap, chip->base + addr, data,
> > +				       len);
> > +		if (ret < 0) {
> > +			dev_err(chip->dev, "rr_adc reg 0x%x failed :%d\n", addr,
> > +				ret);
> > +			return ret;
> > +		}
> > +
> > +		ret = regmap_bulk_read(chip->regmap, chip->base + addr,
> > +				       data_check, len);
> > +		if (ret < 0) {
> > +			dev_err(chip->dev, "rr_adc reg 0x%x failed :%d\n", addr,
> > +				ret);
> > +			return ret;
> > +		}
> > +
> > +		if (memcmp(data, data_check, len) != 0) {
> > +			retry_cnt++;
> > +			dev_dbg(chip->dev,
> > +				"coherent read error, retry_cnt:%d\n",
> > +				retry_cnt);
> > +			continue;
> > +		}
> > +
> > +		break;
> > +	}
> > +
> > +	if (retry_cnt == RR_ADC_COHERENT_CHECK_RETRY)
> > +		dev_err(chip->dev, "Retry exceeded for coherrency check\n");
> > +
> > +	return ret;
> > +}
> > +  
> 
> > +static int rradc_do_conversion(struct rradc_chip *chip,
> > +			       enum rradc_channel_id chan_address, u16 *data)
> > +{
> > +	const struct rradc_channel *chan = &rradc_chans[chan_address];
> > +	const struct iio_chan_spec *iio_chan = &rradc_iio_chans[chan_address];
> > +	int ret;
> > +	u8 buf[6];  
> 
> I missed this until now, but buf is only ever used as __le16 buf[3]
> so give it that type and you can use
> le16_to_cpu() as it will be aligned.
> 
>  
> > +
> > +	mutex_lock(&chip->conversion_lock);
> > +
> > +	switch (chan_address) {
> > +	case RR_ADC_BATT_ID:
> > +		ret = rradc_prepare_batt_id_conversion(chip, chan_address, data);
> > +		if (ret < 0) {
> > +			dev_err(chip->dev, "Battery ID conversion failed:%d\n",
> > +				ret);
> > +			goto unlock_out;
> > +		}
> > +		break;
> > +
> > +	case RR_ADC_USBIN_V:
> > +	case RR_ADC_DIE_TEMP:
> > +		ret = rradc_read_status_in_cont_mode(chip, chan_address);
> > +		if (ret < 0) {
> > +			dev_err(chip->dev,
> > +				"Error reading in continuous mode:%d\n", ret);
> > +			goto unlock_out;
> > +		}
> > +		break;
> > +	default:
> > +		if (!rradc_is_ready(chip, chan_address)) {
> > +			/*
> > +			 * Usually this means the channel isn't attached, for example
> > +			 * the in_voltage_usbin_v_input channel will not be ready if
> > +			 * no USB cable is attached
> > +			 */
> > +			dev_dbg(chip->dev, "channel '%s' is not ready\n",
> > +				iio_chan->extend_name);
> > +			ret = -ENODATA;
> > +			goto unlock_out;
> > +		}
> > +		break;
> > +	}
> > +
> > +	ret = rradc_read(chip, chan->lsb, buf, chan->size);
> > +	if (ret) {
> > +		dev_err(chip->dev, "read data failed\n");
> > +		goto unlock_out;
> > +	}
> > +
> > +	/*
> > +	 * For the battery ID we read the register for every ID ADC and then
> > +	 * see which one is actually connected.
> > +	 */
> > +	if (chan_address == RR_ADC_BATT_ID) {
> > +		u16 batt_id_150 = get_unaligned_le16(buf + 4);
> > +		u16 batt_id_15 = get_unaligned_le16(buf + 2);
> > +		u16 batt_id_5 = get_unaligned_le16(buf);
> > +
> > +		if (!batt_id_150 && !batt_id_15 && !batt_id_5) {
> > +			dev_err(chip->dev,
> > +				"Invalid batt_id values with all zeros\n");
> > +			ret = -EINVAL;
> > +			goto unlock_out;
> > +		}
> > +
> > +		if (batt_id_150 <= RR_ADC_BATT_ID_RANGE) {
> > +			*data = batt_id_150;
> > +			chip->batt_id_data = 150;
> > +		} else if (batt_id_15 <= RR_ADC_BATT_ID_RANGE) {
> > +			*data = batt_id_15;
> > +			chip->batt_id_data = 15;
> > +		} else {
> > +			*data = batt_id_5;
> > +			chip->batt_id_data = 5;
> > +		}
> > +	} else {
> > +		/*
> > +		 * All of the other channels are either 1 or 2 bytes.
> > +		 * We can rely on the second byte being 0 for 1-byte channels.
> > +		 */
> > +		*data = get_unaligned_le16(buf);
> > +	}
> > +
> > +unlock_out:
> > +	mutex_unlock(&chip->conversion_lock);
> > +
> > +	return ret;
> > +}
> > +  
> 
> ...
> 
> 
> > +
> > +static int rradc_read_raw(struct iio_dev *indio_dev,
> > +			  struct iio_chan_spec const *chan_spec, int *val,
> > +			  int *val2, long mask)
> > +{
> > +	struct rradc_chip *chip = iio_priv(indio_dev);
> > +	const struct rradc_channel *chan;
> > +	int ret;
> > +	u16 adc_code;
> > +
> > +	if (chan_spec->address >= RR_ADC_CHAN_MAX) {
> > +		dev_err(chip->dev, "Invalid channel index:%lu\n",
> > +			chan_spec->address);
> > +		return -EINVAL;
> > +	}
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_SCALE:
> > +		return rradc_read_scale(chip, chan_spec->address, val, val2);
> > +	case IIO_CHAN_INFO_OFFSET:
> > +		return rradc_read_offset(chip, chan_spec->address, val);
> > +	case IIO_CHAN_INFO_RAW:
> > +		chan = &rradc_chans[chan_spec->address];  
> 
> chan unused in this case statement.
> 
> > +		ret = rradc_do_conversion(chip, chan_spec->address, &adc_code);
> > +		if (ret < 0)
> > +			return ret;
> > +
> > +		*val = adc_code;
> > +		return IIO_VAL_INT;
> > +	case IIO_CHAN_INFO_PROCESSED:
> > +		chan = &rradc_chans[chan_spec->address];
> > +		if (!chan->scale_fn)
> > +			return -EINVAL;
> > +		ret = rradc_do_conversion(chip, chan_spec->address, &adc_code);
> > +		if (ret < 0)
> > +			return ret;
> > +
> > +		*val = chan->scale_fn(chip, adc_code, val);
> > +		return IIO_VAL_INT;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +  
> 
> ...
> 
> > +static const struct iio_chan_spec rradc_iio_chans[RR_ADC_CHAN_MAX] = {
> > +	{
> > +		.type = IIO_RESISTANCE,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> > +		.address = RR_ADC_BATT_ID,
> > +		.channel = 0,
> > +		.indexed = 1,
> > +	}, {
> > +		.type = IIO_TEMP,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> > +		.address = RR_ADC_BATT_THERM,
> > +		.channel = 0,
> > +		.indexed = 1,
> > +	}, {
> > +		.type = IIO_TEMP,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > +				      BIT(IIO_CHAN_INFO_SCALE) |
> > +				      BIT(IIO_CHAN_INFO_OFFSET),
> > +		.address = RR_ADC_SKIN_TEMP,
> > +		.channel = 1,
> > +		.indexed = 1,
> > +	}, {
> > +		.type = IIO_CURRENT,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > +				      BIT(IIO_CHAN_INFO_SCALE),
> > +		.address = RR_ADC_USBIN_I,
> > +		.channel = 0,
> > +		.indexed = 1,
> > +	}, {
> > +		.type = IIO_VOLTAGE,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > +			BIT(IIO_CHAN_INFO_SCALE),  
> 
> Inconsistent indenting vs the other similar cases.
> 
> > +		.address = RR_ADC_USBIN_V,
> > +		.channel = 0,
> > +		.indexed = 1,
> > +	}, {
> > +		.type = IIO_CURRENT,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > +				      BIT(IIO_CHAN_INFO_SCALE),
> > +		.address = RR_ADC_DCIN_I,
> > +		.channel = 1,
> > +		.indexed = 1,
> > +	}, {
> > +		.type = IIO_VOLTAGE,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > +				      BIT(IIO_CHAN_INFO_SCALE),
> > +		.address = RR_ADC_DCIN_V,
> > +		.channel = 1,
> > +		.indexed = 1,
> > +	}, {
> > +		.type = IIO_TEMP,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > +				      BIT(IIO_CHAN_INFO_SCALE) |
> > +				      BIT(IIO_CHAN_INFO_OFFSET),
> > +		.address = RR_ADC_DIE_TEMP,
> > +		.channel = 2,
> > +		.indexed = 1,
> > +	}, {
> > +		.type = IIO_TEMP,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > +				      BIT(IIO_CHAN_INFO_OFFSET) |
> > +				      BIT(IIO_CHAN_INFO_SCALE),
> > +		.address = RR_ADC_CHG_TEMP,
> > +		.channel = 3,
> > +		.indexed = 1,
> > +	}, {
> > +		.type = IIO_VOLTAGE,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > +				      BIT(IIO_CHAN_INFO_SCALE),
> > +		.address = RR_ADC_GPIO,
> > +		.channel = 2,
> > +		.indexed = 1,
> > +	},
> > +};
> > +
> > +static int rradc_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct iio_dev *indio_dev;
> > +	struct rradc_chip *chip;
> > +	int ret, i, batt_id_delay;
> > +
> > +	indio_dev = devm_iio_device_alloc(dev, sizeof(*chip));
> > +	if (!indio_dev)
> > +		return -ENOMEM;
> > +
> > +	chip = iio_priv(indio_dev);
> > +	chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> > +	if (!chip->regmap) {
> > +		dev_err(dev, "Couldn't get parent's regmap\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	chip->dev = dev;
> > +	mutex_init(&chip->conversion_lock);
> > +
> > +	ret = device_property_read_u32(dev, "reg", &chip->base);
> > +	if (ret < 0) {
> > +		dev_err(chip->dev, "Couldn't find reg address, ret = %d\n",
> > +			ret);
> > +		return ret;
> > +	}
> > +
> > +	batt_id_delay = -1;
> > +	ret = device_property_read_u32(dev, "qcom,batt-id-delay-ms",
> > +				       &batt_id_delay);
> > +	if (!ret) {
> > +		for (i = 0; i < RRADC_BATT_ID_DELAY_MAX; i++) {
> > +			if (batt_id_delay == batt_id_delays[i])
> > +				break;
> > +		}
> > +		if (i == RRADC_BATT_ID_DELAY_MAX)
> > +			batt_id_delay = -1;
> > +	}
> > +
> > +	if (batt_id_delay >= 0) {
> > +		batt_id_delay = FIELD_PREP(BATT_ID_SETTLE_MASK, batt_id_delay);
> > +		ret = regmap_update_bits(chip->regmap,
> > +					 chip->base + RR_ADC_BATT_ID_CFG,
> > +					 batt_id_delay, batt_id_delay);
> > +		if (ret < 0) {
> > +			dev_err(chip->dev,
> > +				"BATT_ID settling time config failed:%d\n",
> > +				ret);
> > +		}
> > +	}
> > +
> > +	/* Get the PMIC revision ID, we need to handle some varying coefficients */
> > +	chip->pmic = qcom_pmic_get(chip->dev);
> > +	if (IS_ERR(chip->pmic)) {
> > +		dev_err(chip->dev, "Unable to get reference to PMIC device\n");
> > +		return PTR_ERR(chip->pmic);
> > +	}
> > +
> > +	indio_dev->name = DRIVER_NAME;  
> 
> I missed this in earlier versions, but this should be the specific
> part number if possible, so probably
> pm660-rradc / pmi8998-rradc as appropriate.
> You can set it based on chip->pmic->sub_type I think
> 
> 
> > +	indio_dev->modes = INDIO_DIRECT_MODE;
> > +	indio_dev->info = &rradc_info;
> > +	indio_dev->channels = rradc_iio_chans;
> > +	indio_dev->num_channels = ARRAY_SIZE(rradc_iio_chans);
> > +
> > +	return devm_iio_device_register(dev, indio_dev);
> > +}
> > +
> > +static const struct of_device_id rradc_match_table[] = {
> > +	{ .compatible = "qcom,pm660-rradc" },
> > +	{ .compatible = "qcom,pmi8998-rradc" },
> > +	{}
> > +};
> > +MODULE_DEVICE_TABLE(of, rradc_match_table);
> > +
> > +static struct platform_driver rradc_driver = {
> > +	.driver		= {
> > +		.name		= DRIVER_NAME,
> > +		.of_match_table	= rradc_match_table,
> > +	},
> > +	.probe = rradc_probe,
> > +};
> > +module_platform_driver(rradc_driver);
> > +
> > +MODULE_DESCRIPTION("QCOM SPMI PMIC RR ADC driver");
> > +MODULE_AUTHOR("Caleb Connolly <caleb.connolly@xxxxxxxxxx>");
> > +MODULE_LICENSE("GPL v2");  
> 




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [Linux for Sparc]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux