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

Commit ba0a19a6 authored by Jishnu Prakash's avatar Jishnu Prakash
Browse files

iio: adc: Add QCOM SPMI PMIC5 GEN3 ADC driver



Add support for QCOM SPMI PMIC5 GEN3 ADC driver that supports
hardware based offset and gain compensation.
The ADC peripheral can measure both voltage and current
channels whose input signal is connected to the PMIC ADC AMUX.

The PMIC5 GEN3 ADC peripheral uses registers defined in SDAM.
The register set and configuration have been refreshed compared
to the prior QCOM PMIC7 ADC family.

Change-Id: I3512c3c067d9102406aa92c389d66283f0b12103
Signed-off-by: default avatarJishnu Prakash <jprakash@codeaurora.org>
parent 9833d437
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -722,6 +722,26 @@ config QCOM_SPMI_ADC5
	  To compile this driver as a module, choose M here: the module will
	  be called qcom-spmi-adc5.

config QCOM_SPMI_ADC5_GEN3
	tristate "Qualcomm Technologies Inc. SPMI PMIC5 GEN3 ADC"
	depends on SPMI
	select REGMAP_SPMI
	select QCOM_VADC_COMMON
	help
	  This is the IIO Voltage PMIC5 Gen3 ADC driver for Qualcomm Technologies Inc.

	  The driver supports multiple channels read. The ADC is a 16-bit
	  sigma-delta ADC. The hardware supports calibrated results for
	  conversion requests and clients include reading voltage phone
	  power, on board system thermistors connected to the PMIC ADC,
	  PMIC die temperature, charger temperature, battery current, USB voltage
	  input, voltage signals connected to supported PMIC GPIO inputs. The
	  hardware supports internal pull-up for thermistors and can choose between
	  a 100k, 30k and 400k pull up using the ADC channels.

	  To compile this driver as a module, choose M here: the module will
	  be called qcom-spmi-adc5-gen3.

config RCAR_GYRO_ADC
	tristate "Renesas R-Car GyroADC driver"
	depends on ARCH_RCAR_GEN2 || COMPILE_TEST
+1 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
obj-$(CONFIG_QCOM_SPMI_ADC5_GEN3) += qcom-spmi-adc5-gen3.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
+1736 −0

File added.

Preview size limit exceeded, changes collapsed.

+140 −0
Original line number Diff line number Diff line
@@ -651,6 +651,55 @@ static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
	return 0;
}

static int qcom_vadc_map_temp_voltage(const struct vadc_map_pt *pts,
		size_t tablesize, int input, int64_t *output)
{
	unsigned int i = 0, descending = 1;

	if (!pts)
		return -EINVAL;

	/* Check if table is descending or ascending */
	if (tablesize > 1) {
		if (pts[0].y < pts[1].y)
			descending = 0;
	}

	while (i < tablesize) {
		if (descending && (pts[i].y < input)) {
			/*
			 * Table entry is less than measured value.
			 * Table is descending, stop.
			 */
			break;
		} else if (!descending && (pts[i].y > input)) {
			/*
			 * Table entry is greater than measured value.
			 * Table is ascending, stop.
			 */
			break;
		}
		i++;
	}

	if (i == 0) {
		*output = pts[0].x;
	} else if (i == tablesize) {
		*output = pts[tablesize-1].x;
	} else {
		/*
		 * Result is between search_index and search_index-1.
		 * Interpolate linearly.
		 */
		*output = (((int32_t) ((pts[i].x - pts[i-1].x) *
			(input - pts[i-1].y)) /
			(pts[i].y - pts[i-1].y)) +
			pts[i-1].x);
	}

	return 0;
}

static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph,
				  u16 adc_code,
				  bool absolute,
@@ -1110,6 +1159,97 @@ static int qcom_vadc_scale_hw_chg5_temp(
	return 0;
}

void adc_tm_scale_therm_voltage_100k_gen3(struct adc_tm_config *param)
{
	int temp, ret;
	int64_t resistance = 0;

	/*
	 * High temperature maps to lower threshold voltage.
	 * Same API can be used for resistance-temperature table
	 */
	qcom_vadc_map_temp_voltage(
		adcmap7_100k,
		ARRAY_SIZE(adcmap7_100k),
		param->high_thr_temp, &resistance);

	param->low_thr_voltage = resistance * RATIO_MAX_ADC7;
	param->low_thr_voltage = div64_s64(param->low_thr_voltage,
						(resistance + R_PU_100K));

	/*
	 * low_thr_voltage is ADC raw code corresponding to upper temperature
	 * threshold.
	 * Instead of returning the ADC raw code obtained at this point,we first
	 * do a forward conversion on the (low voltage / high temperature) threshold code,
	 * to temperature, to check if that code, when read by TM, would translate to
	 * a temperature greater than or equal to the upper temperature limit (which is
	 * expected). If it is instead lower than the upper limit (not expected for correct
	 * TM functionality), we lower the raw code of the threshold written by 1
	 * to ensure TM does see a violation when it reads raw code corresponding
	 * to the upper limit temperature specified.
	 */
	ret = qcom_vadc7_scale_hw_calib_therm(NULL, NULL, param->low_thr_voltage, &temp);
	if (ret < 0)
		return;

	if (temp < param->high_thr_temp)
		param->low_thr_voltage--;

	/*
	 * Low temperature maps to higher threshold voltage
	 * Same API can be used for resistance-temperature table
	 */
	qcom_vadc_map_temp_voltage(
		adcmap7_100k,
		ARRAY_SIZE(adcmap7_100k),
		param->low_thr_temp, &resistance);

	param->high_thr_voltage = resistance * RATIO_MAX_ADC7;
	param->high_thr_voltage = div64_s64(param->high_thr_voltage,
						(resistance + R_PU_100K));

	/*
	 * high_thr_voltage is ADC raw code corresponding to upper temperature
	 * threshold.
	 * Similar to what is done above for low_thr voltage, we first
	 * do a forward conversion on the (high voltage / low temperature)threshold code,
	 * to temperature, to check if that code, when read by TM, would translate to a
	 * temperature less than or equal to the lower temperature limit (which is expected).
	 * If it is instead greater than the lower limit (not expected for correct
	 * TM functionality), we increase the raw code of the threshold written by 1
	 * to ensure TM does see a violation when it reads raw code corresponding
	 * to the lower limit temperature specified.
	 */
	ret = qcom_vadc7_scale_hw_calib_therm(NULL, NULL, param->high_thr_voltage, &temp);
	if (ret < 0)
		return;

	if (temp > param->low_thr_temp)
		param->high_thr_voltage++;
}
EXPORT_SYMBOL(adc_tm_scale_therm_voltage_100k_gen3);

int32_t adc_tm_absolute_rthr_gen3(struct adc_tm_config *tm_config)
{
	int64_t low_thr = 0, high_thr = 0;

	low_thr =  tm_config->low_thr_voltage;
	low_thr *= ADC5_FULL_SCALE_CODE;

	low_thr = div64_s64(low_thr, ADC_VDD_REF);
	tm_config->low_thr_voltage = low_thr;

	high_thr =  tm_config->high_thr_voltage;
	high_thr *= ADC5_FULL_SCALE_CODE;

	high_thr = div64_s64(high_thr, ADC_VDD_REF);
	tm_config->high_thr_voltage = high_thr;

	return 0;
}
EXPORT_SYMBOL(adc_tm_absolute_rthr_gen3);

int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
		    const struct vadc_linear_graph *calib_graph,
		    const struct vadc_prescale_ratio *prescale,
+56 −0
Original line number Diff line number Diff line
@@ -6,6 +6,8 @@
#ifndef QCOM_VADC_COMMON_H
#define QCOM_VADC_COMMON_H

#include <linux/adc-tm-clients.h>

#define VADC_CONV_TIME_MIN_US			2000
#define VADC_CONV_TIME_MAX_US			2100

@@ -63,6 +65,8 @@
#define DIE_TEMP_ADC7_SCALE_FACTOR			1000
#define DIE_TEMP_ADC7_MAX				160000

#define ADC_VDD_REF			1875000

/**
 * struct vadc_map_pt - Map the graph representation for ADC channel
 * @x: Represent the ADC digitized code.
@@ -109,6 +113,54 @@ struct vadc_prescale_ratio {
	u32 den;
};

/**
 * enum adc_tm_rscale_fn_type - Scaling function used to convert the
 *	channels input voltage/temperature to corresponding ADC code that is
 *	applied for thresholds. Check the corresponding channels scaling to
 *	determine the appropriate temperature/voltage units that are passed
 *	to the scaling function. Example battery follows the power supply
 *	framework that needs its units to be in decidegreesC so it passes
 *	deci-degreesC. PA_THERM clients pass the temperature in degrees.
 *	The order below should match the one in the driver for
 *	adc_tm_rscale_fn[].
 */
enum adc_tm_rscale_fn_type {
	SCALE_R_ABSOLUTE = 0,
	SCALE_RSCALE_NONE,
};

/**
 * struct adc_tm_config - Represent ADC Thermal Monitor configuration.
 * @high_thr_temp: Temperature at which high threshold notification is required.
 * @low_thr_temp: Temperature at which low threshold notification is required.
 * @low_thr_voltage : Low threshold voltage ADC code used for reverse
 *			calibration.
 * @high_thr_voltage: High threshold voltage ADC code used for reverse
 *			calibration.
 */
struct adc_tm_config {
	int	high_thr_temp;
	int	low_thr_temp;
	int64_t	high_thr_voltage;
	int64_t	low_thr_voltage;
};

struct adc_tm_reverse_scale_fn {
	int32_t (*chan)(struct adc_tm_config *tm_config);
};

struct adc_tm_client_info {
	struct list_head			list;
	struct adc_tm_param			*param;
	int32_t						low_thr_requested;
	int32_t						high_thr_requested;
	bool						notify_low_thr;
	bool						notify_high_thr;
	bool						high_thr_set;
	bool						low_thr_set;
	enum adc_tm_state_request	state_request;
};

/**
 * enum vadc_scale_fn_type - Scaling function to convert ADC code to
 *				physical scaled units for the channel.
@@ -211,4 +263,8 @@ int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,

int qcom_vadc_decimation_from_dt(u32 value);

void adc_tm_scale_therm_voltage_100k_gen3(struct adc_tm_config *param);

int32_t adc_tm_absolute_rthr_gen3(struct adc_tm_config *tm_config);

#endif /* QCOM_VADC_COMMON_H */
Loading