Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c18689d5 authored by Siddartha Mohanadoss's avatar Siddartha Mohanadoss
Browse files

iio: adc: Update reading USB_IN_V channel



Update USB_IN channel read sequence. This channel read
sequence is done to prevent over voltage in charger.
As part of the read sequence update the calibration
measurement needs to be updated and requires accessing
the CAL peripheral. The read sequence is followed
if the optional property to read the CAL peripheral
is available for the PMIC that supports reading
the USB_IN channel.

Change-Id: I31fa5b3340fdd744e192e3ec09061d1915d12682
Signed-off-by: default avatarSiddartha Mohanadoss <smohanad@codeaurora.org>
parent b9c29f3b
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -12,9 +12,18 @@ ADC node:
		Should contain "qcom,spmi-adc-rev2" for PMIC refresh ADC driver.

- reg:
    Usage: required
    Usage: required for VADC base address
    Value type: <prop-encoded-array>
    Definition: VADC base address and length in the SPMI PMIC register map.
                ADC_CAL base address and length in SPMI PMIC register map.
                ADC_CAL base is optional and is dependent on USB_IN_V channel
                read sequence for the PMIC.

- reg-names
    Usage: required
    Value type: <string>
    Definition: Names associated with base addresses. should be
                "adc5-usr-base", "adc5-cal-base".

- #address-cells:
    Usage: required
+2 −1
Original line number Diff line number Diff line
@@ -81,7 +81,8 @@

		pm8150b_vadc: vadc@3100 {
			compatible = "qcom,spmi-adc5";
			reg = <0x3100 0x100>;
			reg = <0x3100 0x100>, <0x3700 0x100>;
			reg-names = "adc5-usr-base", "adc5-cal-base";
			#address-cells = <1>;
			#size-cells = <0>;
			interrupts = <0x2 0x31 0x0 IRQ_TYPE_EDGE_RISING>;
+138 −20
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -50,6 +51,8 @@
#define ADC_USR_DIG_PARAM_DEC_RATIO_SEL		0xc
#define ADC_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT	2

#define ADC_USR_DIG_PARAM_ABS_CAL_VAL		0x28

#define ADC_USR_FAST_AVG_CTL			0x43
#define ADC_USR_FAST_AVG_CTL_EN			BIT(7)
#define ADC_USR_FAST_AVG_CTL_SAMPLES_MASK	0x7
@@ -86,6 +89,11 @@
#define ADC_CONV_TIME_RETRY			400
#define ADC_CONV_TIMEOUT			msecs_to_jiffies(100)

/* CAL peripheral */
#define ADC_CAL_DELAY_CTL			0x44
#define ADC_CAL_DELAY_CTL_VAL_256S		0x73
#define ADC_CAL_DELAY_CTL_VAL_125MS		0x3

enum adc_cal_method {
	ADC_NO_CAL = 0,
	ADC_RATIOMETRIC_CAL,
@@ -128,6 +136,7 @@ struct adc_channel_prop {
 * @regmap: pointer to struct regmap.
 * @dev: pointer to struct device.
 * @base: base address for the ADC peripheral.
 * @cal_addr: base address for the CAL peripheral.
 * @nchannels: number of ADC channels.
 * @chan_props: array of ADC channel properties.
 * @iio_chans: array of IIO channels specification.
@@ -140,6 +149,7 @@ struct adc_chip {
	struct regmap		*regmap;
	struct device		*dev;
	u16			base;
	u16			cal_addr;
	unsigned int		nchannels;
	struct adc_channel_prop	*chan_props;
	struct iio_chan_spec	*iio_chans;
@@ -275,6 +285,32 @@ static int adc_poll_wait_eoc(struct adc_chip *adc)
	return -ETIMEDOUT;
}

static int adc_wait_eoc(struct adc_chip *adc)
{
	int ret;

	if (adc->poll_eoc) {
		ret = adc_poll_wait_eoc(adc);
		if (ret < 0) {
			pr_err("EOC bit not set\n");
			return ret;
		}
	} else {
		ret = wait_for_completion_timeout(&adc->complete,
							ADC_CONV_TIMEOUT);
		if (!ret) {
			pr_debug("Did not get completion timeout.\n");
			ret = adc_poll_wait_eoc(adc);
			if (ret < 0) {
				pr_err("EOC bit not set\n");
				return ret;
			}
		}
	}

	return ret;
}

static void adc_update_dig_param(struct adc_chip *adc,
			struct adc_channel_prop *prop, u8 *data)
{
@@ -291,6 +327,79 @@ static void adc_update_dig_param(struct adc_chip *adc,
	*data |= (prop->decimation << ADC_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT);
}

static int adc_post_configure_usb_in_read(struct adc_chip *adc,
					struct adc_channel_prop *prop)
{
	u8 data;

	if ((prop->channel == ADC_USB_IN_V_16) && adc->cal_addr) {
		data = ADC_CAL_DELAY_CTL_VAL_125MS;
		/* Set calibration measurement interval to 125ms */
		return regmap_bulk_write(adc->regmap,
					adc->cal_addr + ADC_CAL_DELAY_CTL,
					&data, 1);
	}

	return 0;
}

static int adc_pre_configure_usb_in_read(struct adc_chip *adc)
{
	int ret;
	u8 data = ADC_CAL_DELAY_CTL_VAL_256S;

	/* Increase calibration measurement interval to 256s */
	ret = regmap_bulk_write(adc->regmap,
				adc->cal_addr + ADC_CAL_DELAY_CTL, &data, 1);
	if (ret)
		return ret;

	/* Add delay of 20ms to allow completion of pending conversions */
	msleep(20);

	/* Select REF_GND and start a conversion */
	data = ADC_REF_GND;
	ret = adc_write(adc, ADC_USR_CH_SEL_CTL, &data, 1);
	if (ret)
		return ret;

	data = ADC_USR_EN_CTL1_ADC_EN;
	ret = adc_write(adc, ADC_USR_EN_CTL1, &data, 1);
	if (ret)
		return ret;

	if (!adc->poll_eoc)
		reinit_completion(&adc->complete);

	data = ADC_USR_CONV_REQ_REQ;
	ret = adc_write(adc, ADC_USR_CONV_REQ, &data, 1);
	if (ret)
		return ret;

	/* Select DIG PARAM and CH_SEL for USBIN */
	data = ADC_USR_DIG_PARAM_ABS_CAL_VAL;
	ret = adc_write(adc, ADC_USR_DIG_PARAM, &data, 1);
	if (ret)
		return ret;

	data = ADC_USB_IN_V_16;
	ret = adc_write(adc, ADC_USR_CH_SEL_CTL, &data, 1);
	if (ret)
		return ret;

	/* Check EOC for GND conversion */
	ret = adc_wait_eoc(adc);
	if (ret < 0)
		return ret;

	if (!adc->poll_eoc)
		reinit_completion(&adc->complete);

	/* Conversion request for USB_IN */
	data = ADC_USR_CONV_REQ_REQ;
	return adc_write(adc, ADC_USR_CONV_REQ, &data, 1);
}

static int adc_configure(struct adc_chip *adc,
			struct adc_channel_prop *prop)
{
@@ -339,30 +448,23 @@ static int adc_do_conversion(struct adc_chip *adc,

	mutex_lock(&adc->lock);

	ret = adc_configure(adc, prop);
	if ((prop->channel == ADC_USB_IN_V_16) && adc->cal_addr) {
		ret = adc_pre_configure_usb_in_read(adc);
		if (ret) {
			pr_err("ADC configure failed with %d\n", ret);
			goto unlock;
		}

	if (adc->poll_eoc) {
		ret = adc_poll_wait_eoc(adc);
		if (ret < 0) {
			pr_err("EOC bit not set\n");
			goto unlock;
		}
	} else {
		ret = wait_for_completion_timeout(&adc->complete,
							ADC_CONV_TIMEOUT);
		if (!ret) {
			pr_debug("Did not get completion timeout.\n");
			ret = adc_poll_wait_eoc(adc);
			if (ret < 0) {
				pr_err("EOC bit not set\n");
		ret = adc_configure(adc, prop);
		if (ret) {
			pr_err("ADC configure failed with %d\n", ret);
			goto unlock;
		}
	}
	}

	ret = adc_wait_eoc(adc);
	if (ret < 0)
		goto unlock;

	if ((chan->type == IIO_VOLTAGE) || (chan->type == IIO_TEMP))
		ret = adc_read_voltage_data(adc, data_volt);
@@ -373,6 +475,8 @@ static int adc_do_conversion(struct adc_chip *adc,

		ret = adc_read_current_data(adc, data_cur);
	}

	ret = adc_post_configure_usb_in_read(adc, prop);
unlock:
	mutex_unlock(&adc->lock);

@@ -762,6 +866,7 @@ static int adc_probe(struct platform_device *pdev)
	struct iio_dev *indio_dev;
	struct adc_chip *adc;
	struct regmap *regmap;
	const __be32 *prop_addr;
	int ret, irq_eoc;
	u32 reg;

@@ -780,7 +885,20 @@ static int adc_probe(struct platform_device *pdev)
	adc = iio_priv(indio_dev);
	adc->regmap = regmap;
	adc->dev = dev;
	adc->base = reg;

	prop_addr = of_get_address(dev->of_node, 0, NULL, NULL);
	if (!prop_addr) {
		pr_err("invalid IO resources\n");
		return -EINVAL;
	}
	adc->base = be32_to_cpu(*prop_addr);

	prop_addr = of_get_address(dev->of_node, 1, NULL, NULL);
	if (!prop_addr)
		pr_debug("invalid cal IO resources\n");
	else
		adc->cal_addr = be32_to_cpu(*prop_addr);

	init_completion(&adc->complete);
	mutex_init(&adc->lock);