Merge "iio: adc: qcom-spmi-adc5-gen3: Fix calls to qcom_adc5_hw_scale()"

This commit is contained in:
qctecmdr 2022-06-07 13:57:47 -07:00 committed by Gerrit - the friendly Code Review server
commit fdf4d5a92f
9 changed files with 2875 additions and 25 deletions

View File

@ -872,6 +872,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

View File

@ -77,6 +77,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

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@ -90,6 +91,23 @@
#define ADC_APP_SID_MASK GENMASK(3, 0)
#define ADC7_CONV_TIMEOUT_MS 501
/* For ADC_PBS on PMIC7 with SW calibration */
#define ADC7_SW_CALIB_PBS_CALREF_FLAG 0x57
#define ADC7_SW_CALIB_PBS_CALREF_RDY BIT(7)
#define ADC7_SW_CALIB_PBS_GND_REF_D0 0x58
#define ADC7_SW_CALIB_PBS_GND_REF_D1 0x59
#define ADC7_SW_CALIB_PBS_VREF_VADC_DELTA_D0 0x5a
#define ADC7_SW_CALIB_PBS_VREF_VADC_DELTA_D1 0x5b
#define ADC7_SW_CALIB_PBS_VREF_MBG_DELTA_D0 0x5c
#define ADC7_SW_CALIB_PBS_VREF_MBG_DELTA_D1 0x5d
/* For ADC_CMN on PMIC7 with SW calibration */
#define ADC7_SW_CALIB_CMN_PBUS_WRITE_SYNC_CTL 0x4e
#define ADC7_PBUS_WRITE_SYNC_SW_CLK_REQ BIT(2)
#define ADC7_PBUS_WRITE_SYNC_SW_CLK_REQ_MODE BIT(1)
#define ADC7_PBUS_WRITE_SYNC_BYPASS BIT(0)
enum adc5_cal_method {
ADC5_NO_CAL = 0,
ADC5_RATIOMETRIC_CAL,
@ -135,6 +153,8 @@ struct adc5_channel_prop {
* @regmap: SPMI ADC5 peripheral register map field.
* @dev: SPMI ADC5 device.
* @base: base address for the ADC peripheral.
* @cmn_base: base address for the ADC_CMN peripheral, needed
* for SW calibrated ADC.
* @nchannels: number of ADC channels.
* @chan_props: array of ADC channel properties.
* @iio_chans: array of IIO channels specification.
@ -147,6 +167,7 @@ struct adc5_chip {
struct regmap *regmap;
struct device *dev;
u16 base;
u16 cmn_base;
unsigned int nchannels;
struct adc5_channel_prop *chan_props;
struct iio_chan_spec *iio_chans;
@ -154,21 +175,55 @@ struct adc5_chip {
struct completion complete;
struct mutex lock;
const struct adc5_data *data;
int irq_eoc;
};
static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len)
{
return regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
int ret;
ret = regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
if (ret)
pr_err("adc read to register %#x of length:%d failed, ret=%d\n",
offset, len, ret);
return ret;
}
static int adc5_write(struct adc5_chip *adc, u16 offset, u8 *data, int len)
{
return regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
int ret;
ret = regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
if (ret)
pr_err("adc write to register %#x of length:%d failed, ret=%d\n",
offset, len, ret);
return ret;
}
static int adc5_masked_write(struct adc5_chip *adc, u16 offset, u8 mask, u8 val)
{
return regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
int ret;
ret = regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
if (ret)
pr_err("adc masked write to register %#x with mask:0x%x failed, ret=%d\n",
offset, mask, ret);
return ret;
}
static int adc5_cmn_write(struct adc5_chip *adc, u16 offset, u8 *data, int len)
{
int ret;
ret = regmap_bulk_write(adc->regmap, adc->cmn_base + offset, data, len);
if (ret)
pr_err("adc_cmn write to register %#x of length:%d failed, ret=%d\n",
offset, len, ret);
return ret;
}
static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
@ -176,11 +231,11 @@ static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
int ret;
u8 rslt_lsb, rslt_msb;
ret = adc5_read(adc, ADC5_USR_DATA0, &rslt_lsb, sizeof(rslt_lsb));
ret = adc5_read(adc, ADC5_USR_DATA0, &rslt_lsb, 1);
if (ret)
return ret;
ret = adc5_read(adc, ADC5_USR_DATA1, &rslt_msb, sizeof(rslt_lsb));
ret = adc5_read(adc, ADC5_USR_DATA1, &rslt_msb, 1);
if (ret)
return ret;
@ -317,6 +372,52 @@ static int adc7_configure(struct adc5_chip *adc,
return adc5_write(adc, ADC5_USR_CONV_REQ, &conv_req, 1);
}
static int adc7_sw_calib_configure(struct adc5_chip *adc,
struct adc5_channel_prop *prop)
{
int ret;
u8 buf[5], val = 0;
/* Read registers 0x42 through 0x46 */
ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
if (ret < 0)
return ret;
/* Digital param selection */
adc5_update_dig_param(adc, prop, &buf[0]);
/* Update fast average sample value */
buf[1] &= (u8) ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK;
buf[1] |= prop->avg_samples | ADC5_USR_FAST_AVG_CTL_EN;
/* Select ADC channel */
buf[2] = prop->channel;
/* Select HW settle delay for channel */
buf[3] &= (u8) ~ADC5_USR_HW_SETTLE_DELAY_MASK;
buf[3] |= prop->hw_settle_time;
/* Select ADC enable */
buf[4] |= ADC5_USR_EN_CTL1_ADC_EN;
if (!adc->poll_eoc)
reinit_completion(&adc->complete);
ret = adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
if (ret < 0)
return ret;
val = ADC7_PBUS_WRITE_SYNC_SW_CLK_REQ | ADC7_PBUS_WRITE_SYNC_SW_CLK_REQ_MODE;
ret = adc5_cmn_write(adc, ADC7_SW_CALIB_CMN_PBUS_WRITE_SYNC_CTL, &val, 1);
if (ret < 0)
return ret;
/* Select CONV request */
val = ADC5_USR_CONV_REQ_REQ;
return adc5_write(adc, ADC5_USR_CONV_REQ, &val, 1);
}
static int adc5_do_conversion(struct adc5_chip *adc,
struct adc5_channel_prop *prop,
struct iio_chan_spec const *chan,
@ -427,6 +528,59 @@ static int adc7_do_conversion(struct adc5_chip *adc,
return ret;
}
#define ADC7_SW_CALIB_CONV_TIMEOUT_MS 150
static int adc7_sw_calib_do_conversion(struct adc5_chip *adc,
struct adc5_channel_prop *prop, u16 *adc_code_volt)
{
int ret;
unsigned long rc;
u8 status = 0, val;
mutex_lock(&adc->lock);
ret = adc7_sw_calib_configure(adc, prop);
if (ret) {
pr_err("ADC configure failed with %d\n", ret);
goto unlock;
}
/* No support for polling mode at present*/
rc = wait_for_completion_timeout(&adc->complete,
msecs_to_jiffies(ADC7_SW_CALIB_CONV_TIMEOUT_MS));
if (!rc) {
pr_err("Reading ADC channel %s timed out\n",
prop->datasheet_name);
ret = -ETIMEDOUT;
goto unlock;
}
ret = adc5_read(adc, ADC5_USR_STATUS1, &status, 1);
if (ret < 0)
goto unlock;
if (!(status & ADC5_USR_STATUS1_EOC)) {
pr_err("ADC channel %s EOC bit not set, status=%#x\n",
prop->datasheet_name, status);
ret = -EIO;
goto unlock;
}
ret = adc5_read_voltage_data(adc, adc_code_volt);
if (ret < 0)
goto unlock;
val = 0;
ret = adc5_write(adc, ADC5_USR_EN_CTL1, &val, 1);
if (ret < 0)
goto unlock;
ret = adc5_cmn_write(adc, ADC7_SW_CALIB_CMN_PBUS_WRITE_SYNC_CTL, &val, 1);
unlock:
mutex_unlock(&adc->lock);
return ret;
}
typedef int (*adc_do_conversion)(struct adc5_chip *adc,
struct adc5_channel_prop *prop,
struct iio_chan_spec const *chan,
@ -441,6 +595,21 @@ static irqreturn_t adc5_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
static struct adc5_channel_prop *adc7_get_channel(struct adc5_chip *adc,
unsigned int num)
{
unsigned int i;
for (i = 0; i < adc->nchannels; i++) {
if (adc->chan_props[i].channel == num)
return &adc->chan_props[i];
}
pr_err("Invalid channel %02x\n", num);
return NULL;
}
static int adc5_of_xlate(struct iio_dev *indio_dev,
const struct of_phandle_args *iiospec)
{
@ -517,6 +686,118 @@ static int adc7_read_raw(struct iio_dev *indio_dev,
mask, adc7_do_conversion);
}
static int adc7_calib(struct adc5_chip *adc)
{
int ret = 0;
u16 gnd, vref_1p25, vref_vdd;
u8 buf[2];
struct adc5_channel_prop *gnd_prop, *vref_1p25_prop, *vref_vdd_prop;
/* These channels are mandatory, they are used as reference points */
gnd_prop = adc7_get_channel(adc, ADC7_REF_GND);
if (!gnd_prop) {
dev_err(adc->dev, "GND channel not defined for SW calibration\n");
return -ENODEV;
}
vref_1p25_prop = adc7_get_channel(adc, ADC7_1P25VREF);
if (!vref_1p25_prop) {
dev_err(adc->dev, "1.25VREF channel not defined for SW calibration\n");
return -ENODEV;
}
vref_vdd_prop = adc7_get_channel(adc, ADC7_VREF_VADC);
if (!vref_vdd_prop) {
dev_err(adc->dev, "VDD channel not defined for SW calibration\n");
return -ENODEV;
}
ret = adc7_sw_calib_do_conversion(adc, gnd_prop, &gnd);
if (ret) {
dev_err(adc->dev, "Failed to read GND channel, ret = %d\n", ret);
return ret;
}
ret = adc7_sw_calib_do_conversion(adc, vref_1p25_prop, &vref_1p25);
if (ret) {
dev_err(adc->dev, "Failed to read 1.25VREF channel, ret = %d\n", ret);
return ret;
}
ret = adc7_sw_calib_do_conversion(adc, vref_vdd_prop, &vref_vdd);
if (ret) {
dev_err(adc->dev, "Failed to read VDD channel, ret = %d\n", ret);
return ret;
}
buf[0] = gnd & 0xff;
buf[1] = gnd >> 8;
ret = adc5_write(adc, ADC7_SW_CALIB_PBS_GND_REF_D0, buf, sizeof(buf));
if (ret)
return ret;
vref_vdd -= gnd;
buf[0] = vref_vdd & 0xff;
buf[1] = vref_vdd >> 8;
ret = adc5_write(adc, ADC7_SW_CALIB_PBS_VREF_VADC_DELTA_D0, buf, sizeof(buf));
if (ret)
return ret;
vref_1p25 -= gnd;
buf[0] = vref_1p25 & 0xff;
buf[1] = vref_1p25 >> 8;
ret = adc5_write(adc, ADC7_SW_CALIB_PBS_VREF_MBG_DELTA_D0, buf, sizeof(buf));
if (!ret)
dev_dbg(adc->dev, "SW calibration done, gnd:0x%x vref_vdd:0x%x vref_1p25:0x%x\n",
gnd, vref_vdd, vref_1p25);
return ret;
}
static int adc7_sw_calib_conv(struct adc5_chip *adc, struct adc5_channel_prop *prop, int *val)
{
int ret = 0;
u16 adc_code_volt;
ret = adc7_calib(adc);
if (ret)
return ret;
ret = adc7_sw_calib_do_conversion(adc, prop, &adc_code_volt);
if (ret)
return ret;
return qcom_adc5_hw_scale(prop->scale_fn_type,
prop->prescale,
adc->data,
adc_code_volt, val);
}
static int adc7_sw_calib_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2,
long mask)
{
struct adc5_chip *adc = iio_priv(indio_dev);
struct adc5_channel_prop *prop;
int ret;
prop = &adc->chan_props[chan->address];
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
ret = adc7_sw_calib_conv(adc, prop, val);
if (ret)
return ret;
return IIO_VAL_INT;
default:
return -EINVAL;
}
return 0;
}
static const struct iio_info adc5_info = {
.read_raw = adc5_read_raw,
.of_xlate = adc5_of_xlate,
@ -527,6 +808,11 @@ static const struct iio_info adc7_info = {
.of_xlate = adc7_of_xlate,
};
static const struct iio_info adc7_sw_calib_info = {
.read_raw = adc7_sw_calib_read_raw,
.of_xlate = adc5_of_xlate,
};
struct adc5_channels {
const char *datasheet_name;
unsigned int prescale_index;
@ -602,6 +888,8 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
SCALE_HW_CALIB_THERM_100K_PULLUP)
[ADC5_AMUX_THM2] = ADC5_CHAN_TEMP("amux_thm2", 0,
SCALE_HW_CALIB_PM5_SMB_TEMP)
[ADC5_PARALLEL_ISENSE] = ADC5_CHAN_VOLT("parallel_isense", 0,
SCALE_HW_CALIB_PM5_CUR)
[ADC5_GPIO1_100K_PU] = ADC5_CHAN_TEMP("gpio1_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
[ADC5_GPIO2_100K_PU] = ADC5_CHAN_TEMP("gpio2_100k_pu", 0,
@ -617,21 +905,25 @@ static const struct adc5_channels adc7_chans_pmic[ADC5_MAX_CHANNEL] = {
SCALE_HW_CALIB_DEFAULT)
[ADC7_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0,
SCALE_HW_CALIB_DEFAULT)
[ADC7_VREF_VADC] = ADC5_CHAN_VOLT("vref_vadc", 0,
SCALE_HW_CALIB_DEFAULT)
[ADC7_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1,
SCALE_HW_CALIB_DEFAULT)
[ADC7_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3,
SCALE_HW_CALIB_DEFAULT)
[ADC7_AMUX_THM3] = ADC5_CHAN_TEMP("smb_temp", 0,
[ADC7_USB_IN_V_16] = ADC5_CHAN_VOLT("usb_in_v_div_16", 8,
SCALE_HW_CALIB_DEFAULT)
[ADC7_AMUX_THM3] = ADC5_CHAN_TEMP("smb_temp", 9,
SCALE_HW_CALIB_PM7_SMB_TEMP)
[ADC7_CHG_TEMP] = ADC5_CHAN_TEMP("chg_temp", 0,
SCALE_HW_CALIB_PM7_CHG_TEMP)
[ADC7_IIN_FB] = ADC5_CHAN_CUR("iin_fb", 9,
[ADC7_IIN_FB] = ADC5_CHAN_CUR("iin_fb", 10,
SCALE_HW_CALIB_CUR)
[ADC7_ICHG_SMB] = ADC5_CHAN_CUR("ichg_smb", 10,
[ADC7_IIN_SMB] = ADC5_CHAN_CUR("iin_smb", 12,
SCALE_HW_CALIB_CUR)
[ADC7_IIN_SMB] = ADC5_CHAN_CUR("iin_smb", 11,
[ADC7_ICHG_SMB] = ADC5_CHAN_CUR("ichg_smb", 13,
SCALE_HW_CALIB_CUR)
[ADC7_ICHG_FB] = ADC5_CHAN_CUR("ichg_fb", 12,
[ADC7_ICHG_FB] = ADC5_CHAN_CUR("ichg_fb", 14,
SCALE_HW_CALIB_CUR_RAW)
[ADC7_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0,
SCALE_HW_CALIB_PMIC_THERM_PM7)
@ -682,6 +974,8 @@ static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = {
SCALE_HW_CALIB_THERM_100K_PULLUP)
[ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
[ADC5_GPIO2_100K_PU] = ADC5_CHAN_TEMP("gpio2_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
};
static int adc5_get_dt_channel_data(struct adc5_chip *adc,
@ -801,6 +1095,8 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
if (of_property_read_bool(node, "qcom,ratiometric"))
prop->cal_method = ADC5_RATIOMETRIC_CAL;
else if (of_property_read_bool(node, "qcom,no-cal"))
prop->cal_method = ADC5_NO_CAL;
else
prop->cal_method = ADC5_ABSOLUTE_CAL;
@ -816,6 +1112,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
}
static const struct adc5_data adc5_data_pmic = {
.name = "pm-adc5",
.full_scale_code_volt = 0x70e4,
.full_scale_code_cur = 0x2710,
.adc_chans = adc5_chans_pmic,
@ -831,6 +1128,7 @@ static const struct adc5_data adc5_data_pmic = {
};
static const struct adc5_data adc7_data_pmic = {
.name = "pm-adc7",
.full_scale_code_volt = 0x70e4,
.adc_chans = adc7_chans_pmic,
.info = &adc7_info,
@ -842,7 +1140,19 @@ static const struct adc5_data adc7_data_pmic = {
64000, 128000},
};
static const struct adc5_data adc5_data_pmic5_lite = {
.name = "pm-adc5-lite",
.full_scale_code_volt = 0x70e4,
/* On PMI632, IBAT LSB = 5A/32767 */
.full_scale_code_cur = 5000,
.adc_chans = adc5_chans_pmic,
.decimation = (unsigned int []) {250, 420, 840},
.hw_settle_1 = (unsigned int []) {15, 100, 200, 300, 400, 500, 600, 700,
800, 900, 1, 2, 4, 6, 8, 10},
};
static const struct adc5_data adc5_data_pmic_rev2 = {
.name = "pm-adc4-rev2",
.full_scale_code_volt = 0x4000,
.full_scale_code_cur = 0x1800,
.adc_chans = adc5_chans_rev2,
@ -866,10 +1176,18 @@ static const struct of_device_id adc5_match_table[] = {
.compatible = "qcom,spmi-adc7",
.data = &adc7_data_pmic,
},
{
.compatible = "qcom,spmi-adc7-sw-calib",
.data = &adc7_data_pmic,
},
{
.compatible = "qcom,spmi-adc-rev2",
.data = &adc5_data_pmic_rev2,
},
{
.compatible = "qcom,spmi-adc5-lite",
.data = &adc5_data_pmic5_lite,
},
{ }
};
MODULE_DEVICE_TABLE(of, adc5_match_table);
@ -936,8 +1254,11 @@ static int adc5_probe(struct platform_device *pdev)
struct iio_dev *indio_dev;
struct adc5_chip *adc;
struct regmap *regmap;
int ret, irq_eoc;
const char *irq_name;
const __be32 *prop_addr;
int ret;
u32 reg;
u8 val;
regmap = dev_get_regmap(dev->parent, NULL);
if (!regmap)
@ -954,7 +1275,27 @@ static int adc5_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 resource\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 cmn resource\n");
else
adc->cmn_base = be32_to_cpu(*prop_addr);
if (of_device_is_compatible(node, "qcom,spmi-adc7-sw-calib")) {
if (!adc->cmn_base) {
pr_err("ADC_CMN undefined\n");
return -ENODEV;
}
}
init_completion(&adc->complete);
mutex_init(&adc->lock);
@ -965,18 +1306,33 @@ static int adc5_probe(struct platform_device *pdev)
return ret;
}
irq_eoc = platform_get_irq(pdev, 0);
if (irq_eoc < 0) {
if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
return irq_eoc;
adc->irq_eoc = platform_get_irq(pdev, 0);
if (adc->irq_eoc < 0) {
if (adc->irq_eoc == -EPROBE_DEFER || adc->irq_eoc == -EINVAL)
return adc->irq_eoc;
adc->poll_eoc = true;
} else {
ret = devm_request_irq(dev, irq_eoc, adc5_isr, 0,
"pm-adc5", adc);
irq_name = "pm-adc5";
if (adc->data->name)
irq_name = adc->data->name;
ret = devm_request_irq(dev, adc->irq_eoc, adc5_isr, 0,
irq_name, adc);
if (ret)
return ret;
}
if (of_device_is_compatible(node, "qcom,spmi-adc7-sw-calib")) {
ret = adc7_calib(adc);
if (ret)
return ret;
val = ADC7_SW_CALIB_PBS_CALREF_RDY;
ret = adc5_write(adc, ADC7_SW_CALIB_PBS_CALREF_FLAG, &val, 1);
if (ret < 0)
return ret;
}
indio_dev->name = pdev->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = adc->data->info;
@ -986,10 +1342,39 @@ static int adc5_probe(struct platform_device *pdev)
return devm_iio_device_register(dev, indio_dev);
}
static int adc_restore(struct device *dev)
{
int ret = 0;
struct adc5_chip *adc = dev_get_drvdata(dev);
if (adc->irq_eoc > 0)
ret = devm_request_irq(dev, adc->irq_eoc, adc5_isr, 0,
"pm-adc5", adc);
return ret;
}
static int adc_freeze(struct device *dev)
{
struct adc5_chip *adc = dev_get_drvdata(dev);
if (adc->irq_eoc > 0)
devm_free_irq(dev, adc->irq_eoc, adc);
return 0;
}
static const struct dev_pm_ops adc_pm_ops = {
.freeze = adc_freeze,
.thaw = adc_restore,
.restore = adc_restore,
};
static struct platform_driver adc5_driver = {
.driver = {
.name = "qcom-spmi-adc5",
.of_match_table = adc5_match_table,
.pm = &adc_pm_ops,
},
.probe = adc5_probe,
};

View File

@ -520,6 +520,82 @@ static const struct vadc_map_pt adcmap7_100k[] = {
{ 2420, 130048 }
};
/*
* Resistance to temperature table for batt_therm.
*/
static const struct vadc_map_pt adcmap_gen3_batt_therm_100k[] = {
{ 5319890, -400 },
{ 4555860, -380 },
{ 3911780, -360 },
{ 3367320, -340 },
{ 2905860, -320 },
{ 2513730, -300 },
{ 2179660, -280 },
{ 1894360, -260 },
{ 1650110, -240 },
{ 1440520, -220 },
{ 1260250, -200 },
{ 1104850, -180 },
{ 970600, -160 },
{ 854370, -140 },
{ 753530, -120 },
{ 665860, -100 },
{ 589490, -80 },
{ 522830, -60 },
{ 464540, -40 },
{ 413470, -20 },
{ 368640, 0 },
{ 329220, 20 },
{ 294490, 40 },
{ 263850, 60 },
{ 236770, 80 },
{ 212790, 100 },
{ 191530, 120 },
{ 172640, 140 },
{ 155840, 160 },
{ 140880, 180 },
{ 127520, 200 },
{ 115590, 220 },
{ 104910, 240 },
{ 95350, 260 },
{ 86760, 280 },
{ 79050, 300 },
{ 72110, 320 },
{ 65860, 340 },
{ 60220, 360 },
{ 55130, 380 },
{ 50520, 400 },
{ 46350, 420 },
{ 42570, 440 },
{ 39140, 460 },
{ 36030, 480 },
{ 33190, 500 },
{ 30620, 520 },
{ 28260, 540 },
{ 26120, 560 },
{ 24160, 580 },
{ 22370, 600 },
{ 20730, 620 },
{ 19230, 640 },
{ 17850, 660 },
{ 16580, 680 },
{ 15420, 700 },
{ 14350, 720 },
{ 13370, 740 },
{ 12470, 760 },
{ 11630, 780 },
{ 10860, 800 },
{ 10150, 820 },
{ 9490, 840 },
{ 8880, 860 },
{ 8320, 880 },
{ 7800, 900 },
{ 7310, 920 },
{ 6860, 940 },
{ 6450, 960 },
{ 6060, 980 }
};
static const struct u32_fract adc5_prescale_ratios[] = {
{ .numerator = 1, .denominator = 1 },
{ .numerator = 1, .denominator = 3 },
@ -530,10 +606,12 @@ static const struct u32_fract adc5_prescale_ratios[] = {
{ .numerator = 10, .denominator = 81 },
{ .numerator = 1, .denominator = 10 },
{ .numerator = 1, .denominator = 16 },
{ .numerator = 40, .denominator = 41 }, /* PM7_SMB_TEMP */
/* Prescale ratios for current channels below */
{ .numerator = 32, .denominator = 100 }, /* IIN_FB */
{ .numerator = 14, .denominator = 100 }, /* ICHG_SMB */
{ .numerator = 28, .denominator = 100 }, /* IIN_SMB */
{ .numerator = 32, .denominator = 100 }, /* IIN_FB, IIN_SMB */
{ .numerator = 16, .denominator = 100 }, /* ICHG_SMB */
{ .numerator = 1280, .denominator = 4100 }, /* IIN_SMB_new */
{ .numerator = 640, .denominator = 4100 }, /* ICHG_SMB_new */
{ .numerator = 1000, .denominator = 305185 }, /* ICHG_FB */
{ .numerator = 1000, .denominator = 610370 }, /* ICHG_FB_2X */
};
@ -542,14 +620,21 @@ static int qcom_vadc_scale_hw_calib_volt(
const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_uv);
/* Current scaling for PMIC7 */
static int qcom_vadc_scale_hw_calib_current(
const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_ua);
/* Raw current for PMIC7 */
static int qcom_vadc_scale_hw_calib_current_raw(
const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_ua);
/* Current scaling for PMIC5 */
static int qcom_vadc5_scale_hw_calib_current(
const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_ua);
static int qcom_vadc_scale_hw_calib_therm(
const struct u32_fract *prescale,
const struct adc5_data *data,
@ -582,6 +667,22 @@ static int qcom_vadc_scale_hw_smb1398_temp(
const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec);
static int qcom_vadc_scale_hw_pm2250_s3_die_temp(
const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec);
static int qcom_adc5_gen3_scale_hw_calib_batt_therm_100(
const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec);
static int qcom_adc5_gen3_scale_hw_calib_batt_id_100(
const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec);
static int qcom_adc5_gen3_scale_hw_calib_usb_in_current(
const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec);
static int qcom_vadc_scale_hw_chg5_temp(
const struct u32_fract *prescale,
const struct adc5_data *data,
@ -603,6 +704,7 @@ static struct qcom_adc5_scale_type scale_adc5_fn[] = {
[SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt},
[SCALE_HW_CALIB_CUR] = {qcom_vadc_scale_hw_calib_current},
[SCALE_HW_CALIB_CUR_RAW] = {qcom_vadc_scale_hw_calib_current_raw},
[SCALE_HW_CALIB_PM5_CUR] = {qcom_vadc5_scale_hw_calib_current},
[SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm},
[SCALE_HW_CALIB_BATT_THERM_100K] = {
qcom_vadc_scale_hw_calib_batt_therm_100},
@ -619,6 +721,10 @@ static struct qcom_adc5_scale_type scale_adc5_fn[] = {
[SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp},
[SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp},
[SCALE_HW_CALIB_PM5_SMB1398_TEMP] = {qcom_vadc_scale_hw_smb1398_temp},
[SCALE_HW_CALIB_PM2250_S3_DIE_TEMP] = {qcom_vadc_scale_hw_pm2250_s3_die_temp},
[SCALE_HW_CALIB_PM5_GEN3_BATT_THERM_100K] = {qcom_adc5_gen3_scale_hw_calib_batt_therm_100},
[SCALE_HW_CALIB_PM5_GEN3_BATT_ID_100K] = {qcom_adc5_gen3_scale_hw_calib_batt_id_100},
[SCALE_HW_CALIB_PM5_GEN3_USB_IN_I] = {qcom_adc5_gen3_scale_hw_calib_usb_in_current},
[SCALE_HW_CALIB_PM7_SMB_TEMP] = {qcom_vadc_scale_hw_pm7_smb_temp},
[SCALE_HW_CALIB_PM7_CHG_TEMP] = {qcom_vadc_scale_hw_pm7_chg_temp},
};
@ -875,6 +981,30 @@ static int qcom_vadc_scale_hw_calib_current(
return 0;
}
static int qcom_vadc5_scale_hw_calib_current(
const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_ua)
{
s64 voltage = 0, result = 0;
bool positive = true;
if (adc_code & ADC5_USR_DATA_CHECK) {
adc_code = ~adc_code + 1;
positive = false;
}
voltage = (s64)(s16) adc_code * data->full_scale_code_cur * 1000;
voltage = div64_s64(voltage, VADC5_MAX_CODE);
result = div64_s64(voltage * prescale->denominator, prescale->numerator);
*result_ua = result;
if (!positive)
*result_ua = -result;
return 0;
}
static int qcom_vadc_scale_hw_calib_volt(
const struct u32_fract *prescale,
const struct adc5_data *data,
@ -1059,6 +1189,104 @@ static int qcom_vadc_scale_hw_smb1398_temp(
return 0;
}
static int qcom_vadc_scale_hw_pm2250_s3_die_temp(
const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec)
{
s64 voltage = 0, adc_vdd_ref_mv = 1875;
if (adc_code > VADC5_MAX_CODE)
adc_code = 0;
/* (ADC code * vref_vadc (1.875V)) / full_scale_code */
voltage = (s64) adc_code * adc_vdd_ref_mv * 1000;
voltage = div64_s64(voltage, data->full_scale_code_volt);
if (voltage > 0) {
voltage *= prescale->denominator;
voltage = div64_s64(voltage, prescale->numerator);
} else {
voltage = 0;
}
voltage = PMIC5_PM2250_S3_DIE_TEMP_CONSTANT - voltage;
voltage *= 100000;
voltage = div64_s64(voltage, PMIC5_PM2250_S3_DIE_TEMP_SCALE_FACTOR);
*result_mdec = voltage;
return 0;
}
static int qcom_adc5_gen3_scale_hw_calib_batt_therm_100(
const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec)
{
s64 resistance = 0;
int ret, result = 0;
if (adc_code >= RATIO_MAX_ADC7)
return -EINVAL;
/* (ADC code * R_PULLUP (100Kohm)) / (full_scale_code - ADC code)*/
resistance = (s64) adc_code * R_PU_100K;
resistance = div64_s64(resistance, (RATIO_MAX_ADC7 - adc_code));
ret = qcom_vadc_map_voltage_temp(adcmap_gen3_batt_therm_100k,
ARRAY_SIZE(adcmap_gen3_batt_therm_100k),
resistance, &result);
if (ret)
return ret;
*result_mdec = result;
return 0;
}
static int qcom_adc5_gen3_scale_hw_calib_batt_id_100(
const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_mdec)
{
s64 resistance = 0;
if (adc_code >= RATIO_MAX_ADC7)
return -EINVAL;
/* (ADC code * R_PULLUP (100Kohm)) / (full_scale_code - ADC code)*/
resistance = (s64) adc_code * R_PU_100K;
resistance = div64_s64(resistance, (RATIO_MAX_ADC7 - adc_code));
*result_mdec = (int)resistance;
return 0;
};
static int qcom_adc5_gen3_scale_hw_calib_usb_in_current(
const struct u32_fract *prescale,
const struct adc5_data *data,
u16 adc_code, int *result_ua)
{
s64 voltage = 0, result = 0;
bool positive = true;
if (adc_code & ADC5_USR_DATA_CHECK) {
adc_code = ~adc_code + 1;
positive = false;
}
voltage = (s64)(s16) adc_code * 1000000;
voltage = div64_s64(voltage, PMIC5_GEN3_USB_IN_I_SCALE_FACTOR);
result = div64_s64(voltage * prescale->denominator, prescale->numerator);
*result_ua = (int)result;
if (!positive)
*result_ua = -(int)result;
return 0;
};
static int qcom_vadc_scale_hw_chg5_temp(
const struct u32_fract *prescale,
const struct adc5_data *data,
@ -1071,6 +1299,95 @@ 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
*/
resistance = qcom_vadc_map_temp_voltage(adcmap7_100k,
ARRAY_SIZE(adcmap7_100k),
param->high_thr_temp);
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
*/
resistance = qcom_vadc_map_temp_voltage(adcmap7_100k,
ARRAY_SIZE(adcmap7_100k),
param->low_thr_temp);
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 lower 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 u32_fract *prescale,

View File

@ -0,0 +1,79 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021 The Linux Foundation. All rights reserved.
*/
#ifndef _DT_BINDINGS_QCOM_SPMI_VADC_PM5100_H
#define _DT_BINDINGS_QCOM_SPMI_VADC_PM5100_H
#ifndef PM5100_SID
#define PM5100_SID 0
#endif
/* ADC channels for PM5100_ADC for PMIC5 Gen3 */
#define PM5100_ADC5_GEN3_OFFSET_REF (PM5100_SID << 8 | 0x0)
#define PM5100_ADC5_GEN3_1P25VREF (PM5100_SID << 8 | 0x01)
#define PM5100_ADC5_GEN3_VREF_VADC (PM5100_SID << 8 | 0x02)
#define PM5100_ADC5_GEN3_DIE_TEMP (PM5100_SID << 8 | 0x03)
#define PM5100_ADC5_GEN3_AMUX1_THM (PM5100_SID << 8 | 0x04)
#define PM5100_ADC5_GEN3_BAT_ID (PM5100_SID << 8 | 0x05)
#define PM5100_ADC5_GEN3_BATT_THM (PM5100_SID << 8 | 0x06)
#define PM5100_ADC5_GEN3_AMUX4_THM (PM5100_SID << 8 | 0x07)
#define PM5100_ADC5_GEN3_AMUX5_THM (PM5100_SID << 8 | 0x08)
#define PM5100_ADC5_GEN3_AMUX6_THM (PM5100_SID << 8 | 0x09)
#define PM5100_ADC5_GEN3_AMUX1_GPIO10 (PM5100_SID << 8 | 0x0a)
#define PM5100_ADC5_GEN3_AMUX2_GPIO11 (PM5100_SID << 8 | 0x0b)
#define PM5100_ADC5_GEN3_AMUX3_GPIO (PM5100_SID << 8 | 0x0c)
#define PM5100_ADC5_GEN3_AMUX4_GPIO (PM5100_SID << 8 | 0x0d)
#define PM5100_ADC5_GEN3_CHG_TEMP (PM5100_SID << 8 | 0x10)
#define PM5100_ADC5_GEN3_USB_SNS_V_16 (PM5100_SID << 8 | 0x11)
#define PM5100_ADC5_GEN3_VIN_DIV16_MUX (PM5100_SID << 8 | 0x12)
#define PM5100_ADC5_GEN3_USB_IN_I (PM5100_SID << 8 | 0x17)
#define PM5100_ADC5_GEN3_ICHG_FB (PM5100_SID << 8 | 0xa1)
/* 30k pull-up1 */
#define PM5100_ADC5_GEN3_AMUX1_THM_30K_PU (PM5100_SID << 8 | 0x24)
#define PM5100_ADC5_GEN3_BAT_ID_30K_PU (PM5100_SID << 8 | 0x25)
#define PM5100_ADC5_GEN3_BATT_THM_30K_PU (PM5100_SID << 8 | 0x26)
#define PM5100_ADC5_GEN3_AMUX4_THM_30K_PU (PM5100_SID << 8 | 0x27)
#define PM5100_ADC5_GEN3_AMUX5_THM_30K_PU (PM5100_SID << 8 | 0x28)
#define PM5100_ADC5_GEN3_AMUX6_THM_30K_PU (PM5100_SID << 8 | 0x29)
#define PM5100_ADC5_GEN3_AMUX1_GPIO10_30K_PU (PM5100_SID << 8 | 0x2a)
#define PM5100_ADC5_GEN3_AMUX2_GPIO11_30K_PU (PM5100_SID << 8 | 0x2b)
#define PM5100_ADC5_GEN3_AMUX3_GPIO_30K_PU (PM5100_SID << 8 | 0x2c)
#define PM5100_ADC5_GEN3_AMUX4_GPIO_30K_PU (PM5100_SID << 8 | 0x2d)
/* 100k pull-up2 */
#define PM5100_ADC5_GEN3_AMUX1_THM_100K_PU (PM5100_SID << 8 | 0x44)
#define PM5100_ADC5_GEN3_BAT_ID_100K_PU (PM5100_SID << 8 | 0x45)
#define PM5100_ADC5_GEN3_BATT_THM_100K_PU (PM5100_SID << 8 | 0x46)
#define PM5100_ADC5_GEN3_AMUX4_THM_100K_PU (PM5100_SID << 8 | 0x47)
#define PM5100_ADC5_GEN3_AMUX5_THM_100K_PU (PM5100_SID << 8 | 0x48)
#define PM5100_ADC5_GEN3_AMUX6_THM_100K_PU (PM5100_SID << 8 | 0x49)
#define PM5100_ADC5_GEN3_AMUX1_GPIO10_100K_PU (PM5100_SID << 8 | 0x4a)
#define PM5100_ADC5_GEN3_AMUX2_GPIO11_100K_PU (PM5100_SID << 8 | 0x4b)
#define PM5100_ADC5_GEN3_AMUX3_GPIO_100K_PU (PM5100_SID << 8 | 0x4c)
#define PM5100_ADC5_GEN3_AMUX4_GPIO_100K_PU (PM5100_SID << 8 | 0x4d)
/* 400k pull-up3 */
#define PM5100_ADC5_GEN3_AMUX1_THM_400K_PU (PM5100_SID << 8 | 0x64)
#define PM5100_ADC5_GEN3_BAT_ID_400K_PU (PM5100_SID << 8 | 0x65)
#define PM5100_ADC5_GEN3_BATT_THM_400K_PU (PM5100_SID << 8 | 0x66)
#define PM5100_ADC5_GEN3_AMUX4_THM_400K_PU (PM5100_SID << 8 | 0x67)
#define PM5100_ADC5_GEN3_AMUX5_THM_400K_PU (PM5100_SID << 8 | 0x68)
#define PM5100_ADC5_GEN3_AMUX6_THM_400K_PU (PM5100_SID << 8 | 0x69)
#define PM5100_ADC5_GEN3_AMUX1_GPIO10_400K_PU (PM5100_SID << 8 | 0x6a)
#define PM5100_ADC5_GEN3_AMUX2_GPIO11_400K_PU (PM5100_SID << 8 | 0x6b)
#define PM5100_ADC5_GEN3_AMUX3_GPIO_400K_PU (PM5100_SID << 8 | 0x6c)
#define PM5100_ADC5_GEN3_AMUX4_GPIO_400K_PU (PM5100_SID << 8 | 0x6d)
/* 1/3 Divider */
#define PM5100_ADC5_GEN3_GPIO10_DIV3 (PM5100_SID << 8 | 0x8a)
#define PM5100_ADC5_GEN3_GPIO11_DIV3 (PM5100_SID << 8 | 0x8b)
#define PM5100_ADC5_GEN3_VPH_PWR (PM5100_SID << 8 | 0x8e)
#define PM5100_ADC5_GEN3_VBAT_SNS_QBG (PM5100_SID << 8 | 0x8f)
#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PM5100_H */

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2012-2014,2018-2020 The Linux Foundation. All rights reserved.
* Copyright (c) 2012-2014,2018-2021 The Linux Foundation. All rights reserved.
*/
#ifndef _DT_BINDINGS_QCOM_SPMI_VADC_H
@ -300,4 +300,109 @@
#define ADC7_SBUx 0x94
#define ADC7_VBAT_2S_MID 0x96
/* ADC channels for PMIC5 Gen3 */
#define ADC5_GEN3_OFFSET_REF 0x00
#define ADC5_GEN3_1P25VREF 0x01
#define ADC5_GEN3_VREF_VADC 0x02
#define ADC5_GEN3_DIE_TEMP 0x03
#define ADC5_GEN3_AMUX1_THM 0x04
#define ADC5_GEN3_AMUX2_THM 0x05
#define ADC5_GEN3_AMUX3_THM 0x06
#define ADC5_GEN3_AMUX4_THM 0x07
#define ADC5_GEN3_AMUX5_THM 0x08
#define ADC5_GEN3_AMUX6_THM 0x09
#define ADC5_GEN3_AMUX1_GPIO 0x0a
#define ADC5_GEN3_AMUX2_GPIO 0x0b
#define ADC5_GEN3_AMUX3_GPIO 0x0c
#define ADC5_GEN3_AMUX4_GPIO 0x0d
#define ADC5_GEN3_CHG_TEMP 0x10
#define ADC5_GEN3_USB_SNS_V_16 0x11
#define ADC5_GEN3_VIN_DIV16_MUX 0x12
#define ADC5_GEN3_VREF_BAT_THERM 0x15
#define ADC5_GEN3_IIN_FB 0x17
#define ADC5_GEN3_ICHG_SMB 0x18
#define ADC5_GEN3_IIN_SMB 0x19
#define ADC5_GEN3_ICHG_FB 0xa1
/* 30k pull-up1 */
#define ADC5_GEN3_AMUX1_THM_30K_PU 0x24
#define ADC5_GEN3_AMUX2_THM_30K_PU 0x25
#define ADC5_GEN3_AMUX3_THM_30K_PU 0x26
#define ADC5_GEN3_AMUX4_THM_30K_PU 0x27
#define ADC5_GEN3_AMUX5_THM_30K_PU 0x28
#define ADC5_GEN3_AMUX6_THM_30K_PU 0x29
#define ADC5_GEN3_AMUX1_GPIO_30K_PU 0x2a
#define ADC5_GEN3_AMUX2_GPIO_30K_PU 0x2b
#define ADC5_GEN3_AMUX3_GPIO_30K_PU 0x2c
#define ADC5_GEN3_AMUX4_GPIO_30K_PU 0x2d
/* 100k pull-up2 */
#define ADC5_GEN3_AMUX1_THM_100K_PU 0x44
#define ADC5_GEN3_AMUX2_THM_100K_PU 0x45
#define ADC5_GEN3_AMUX3_THM_100K_PU 0x46
#define ADC5_GEN3_AMUX4_THM_100K_PU 0x47
#define ADC5_GEN3_AMUX5_THM_100K_PU 0x48
#define ADC5_GEN3_AMUX6_THM_100K_PU 0x49
#define ADC5_GEN3_AMUX1_GPIO_100K_PU 0x4a
#define ADC5_GEN3_AMUX2_GPIO_100K_PU 0x4b
#define ADC5_GEN3_AMUX3_GPIO_100K_PU 0x4c
#define ADC5_GEN3_AMUX4_GPIO_100K_PU 0x4d
/* 400k pull-up3 */
#define ADC5_GEN3_AMUX1_THM_400K_PU 0x64
#define ADC5_GEN3_AMUX2_THM_400K_PU 0x65
#define ADC5_GEN3_AMUX3_THM_400K_PU 0x66
#define ADC5_GEN3_AMUX4_THM_400K_PU 0x67
#define ADC5_GEN3_AMUX5_THM_400K_PU 0x68
#define ADC5_GEN3_AMUX6_THM_400K_PU 0x69
#define ADC5_GEN3_AMUX1_GPIO_400K_PU 0x6a
#define ADC5_GEN3_AMUX2_GPIO_400K_PU 0x6b
#define ADC5_GEN3_AMUX3_GPIO_400K_PU 0x6c
#define ADC5_GEN3_AMUX4_GPIO_400K_PU 0x6d
/* 1/3 Divider */
#define ADC5_GEN3_AMUX1_GPIO_DIV3 0x8a
#define ADC5_GEN3_AMUX2_GPIO_DIV3 0x8b
#define ADC5_GEN3_AMUX3_GPIO_DIV3 0x8c
#define ADC5_GEN3_VPH_PWR 0x8e
#define ADC5_GEN3_VBAT_SNS_QBG 0x8f
#define ADC5_GEN3_VBAT_SNS_CHGR 0x94
#define ADC5_GEN3_VBAT_2S_MID_QBG 0x96
#define ADC5_GEN3_VBAT_2S_MID_CHGR 0x9d
#define ADC5_OFFSET_EXT2 0xf8
/* VADC scale function index */
#define ADC_SCALE_DEFAULT 0
#define ADC_SCALE_THERM_100K_PULLUP 1
#define ADC_SCALE_PMIC_THERM 2
#define ADC_SCALE_XOTHERM 3
#define ADC_SCALE_PMI_CHG_TEMP 4
#define ADC_SCALE_HW_CALIB_DEFAULT 5
#define ADC_SCALE_HW_CALIB_THERM_100K_PULLUP 6
#define ADC_SCALE_HW_CALIB_XOTHERM 7
#define ADC_SCALE_HW_CALIB_THERM_100K_PU_PM7 8
#define ADC_SCALE_HW_CALIB_PMIC_THERM 9
#define ADC_SCALE_HW_CALIB_PMIC_THERM_PM7 10
#define ADC_SCALE_HW_CALIB_PM5_CHG_TEMP 11
#define ADC_SCALE_HW_CALIB_PM5_SMB_TEMP 12
#define ADC_SCALE_HW_CALIB_BATT_THERM_100K 13
#define ADC_SCALE_HW_CALIB_BATT_THERM_30K 14
#define ADC_SCALE_HW_CALIB_BATT_THERM_400K 15
#define ADC_SCALE_HW_CALIB_PM5_SMB1398_TEMP 16
#define ADC_SCALE_HW_CALIB_PM7_SMB_TEMP 17
#define ADC_SCALE_HW_CALIB_PM7_CHG_TEMP 18
#define ADC_SCALE_HW_CALIB_CUR 19
#define ADC_SCALE_HW_CALIB_CUR_RAW 20
#define ADC_SCALE_HW_CALIB_PM2250_S3_DIE_TEMP 21
#define ADC_SCALE_HW_CALIB_PM5_CUR 22
#define ADC_SCALE_HW_CALIB_PM5_GEN3_BATT_THERM_100K 23
#define ADC_SCALE_HW_CALIB_PM5_GEN3_BATT_ID_100K 24
#define ADC_SCALE_HW_CALIB_PM5_GEN3_USB_IN_I 25
#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_H */

View File

@ -0,0 +1,102 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
*/
#ifndef __QCOM_ADC_TM_H_CLIENTS__
#define __QCOM_ADC_TM_H_CLIENTS__
#include <linux/err.h>
#include <linux/types.h>
struct adc_tm_chip;
struct adc5_chip;
/**
* enum adc_tm_state - This lets the client know whether the threshold
* that was crossed was high/low.
* %ADC_TM_HIGH_STATE: Client is notified of crossing the requested high
* voltage threshold.
* %ADC_TM_COOL_STATE: Client is notified of crossing the requested cool
* temperature threshold.
* %ADC_TM_LOW_STATE: Client is notified of crossing the requested low
* voltage threshold.
* %ADC_TM_WARM_STATE: Client is notified of crossing the requested high
* temperature threshold.
*/
enum adc_tm_state {
ADC_TM_HIGH_STATE = 0,
ADC_TM_COOL_STATE = ADC_TM_HIGH_STATE,
ADC_TM_LOW_STATE,
ADC_TM_WARM_STATE = ADC_TM_LOW_STATE,
ADC_TM_STATE_NUM,
};
/**
* enum adc_tm_state_request - Request to enable/disable the corresponding
* high/low voltage/temperature thresholds.
* %ADC_TM_HIGH_THR_ENABLE: Enable high voltage threshold.
* %ADC_TM_COOL_THR_ENABLE = Enables cool temperature threshold.
* %ADC_TM_LOW_THR_ENABLE: Enable low voltage/temperature threshold.
* %ADC_TM_WARM_THR_ENABLE = Enables warm temperature threshold.
* %ADC_TM_HIGH_LOW_THR_ENABLE: Enable high and low voltage/temperature
* threshold.
* %ADC_TM_HIGH_THR_DISABLE: Disable high voltage/temperature threshold.
* %ADC_TM_COOL_THR_ENABLE = Disables cool temperature threshold.
* %ADC_TM_LOW_THR_DISABLE: Disable low voltage/temperature threshold.
* %ADC_TM_WARM_THR_ENABLE = Disables warm temperature threshold.
* %ADC_TM_HIGH_THR_DISABLE: Disable high and low voltage/temperature
* threshold.
*/
enum adc_tm_state_request {
ADC_TM_HIGH_THR_ENABLE = 0,
ADC_TM_COOL_THR_ENABLE = ADC_TM_HIGH_THR_ENABLE,
ADC_TM_LOW_THR_ENABLE,
ADC_TM_WARM_THR_ENABLE = ADC_TM_LOW_THR_ENABLE,
ADC_TM_HIGH_LOW_THR_ENABLE,
ADC_TM_HIGH_THR_DISABLE,
ADC_TM_COOL_THR_DISABLE = ADC_TM_HIGH_THR_DISABLE,
ADC_TM_LOW_THR_DISABLE,
ADC_TM_WARM_THR_DISABLE = ADC_TM_LOW_THR_DISABLE,
ADC_TM_HIGH_LOW_THR_DISABLE,
ADC_TM_THR_NUM,
};
struct adc_tm_param {
unsigned long id;
int low_thr;
int high_thr;
uint32_t channel;
enum adc_tm_state_request state_request;
void *btm_ctx;
void (*threshold_notification)(enum adc_tm_state state,
void *ctx);
};
struct device;
/* Public API */
#if IS_ENABLED(CONFIG_QCOM_SPMI_ADC5_GEN3)
struct adc5_chip *get_adc_tm_gen3(struct device *dev, const char *name);
int32_t adc_tm_channel_measure_gen3(struct adc5_chip *chip,
struct adc_tm_param *param);
int32_t adc_tm_disable_chan_meas_gen3(struct adc5_chip *chip,
struct adc_tm_param *param);
#else
static inline struct adc5_chip *get_adc_tm_gen3(
struct device *dev, const char *name)
{ return ERR_PTR(-ENXIO); }
static inline int32_t adc_tm_channel_measure_gen3(
struct adc5_chip *chip,
struct adc_tm_param *param)
{ return -ENXIO; }
static inline int32_t adc_tm_disable_chan_meas_gen3(
struct adc5_chip *chip,
struct adc_tm_param *param)
{ return -ENXIO; }
#endif
#endif /* __QCOM_ADC_TM_H_CLIENTS__ */

View File

@ -6,6 +6,7 @@
#ifndef QCOM_VADC_COMMON_H
#define QCOM_VADC_COMMON_H
#include <linux/adc-tm-clients.h>
#include <linux/math.h>
#include <linux/types.h>
@ -47,6 +48,9 @@
#define PMIC5_SMB1398_TEMP_CONSTANT 268235
#define PMIC5_SMB1398_TEMP_SCALE_FACTOR 340
#define PMIC5_PM2250_S3_DIE_TEMP_SCALE_FACTOR 187263
#define PMIC5_PM2250_S3_DIE_TEMP_CONSTANT 720100
#define PMI_CHG_SCALE_1 -138890
#define PMI_CHG_SCALE_2 391750000000LL
@ -57,6 +61,10 @@
#define R_PU_100K 100000
#define RATIO_MAX_ADC7 BIT(14)
#define PMIC5_GEN3_USB_IN_I_SCALE_FACTOR 9248
#define ADC_VDD_REF 1875000
/*
* VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
* VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
@ -82,6 +90,54 @@ struct vadc_linear_graph {
s32 gnd;
};
/**
* 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.
@ -107,13 +163,13 @@ struct vadc_linear_graph {
* charger temperature.
* SCALE_HW_CALIB_PM5_SMB_TEMP: Returns result in millidegrees for PMIC5
* SMB1390 temperature.
* SCALE_HW_CALIB_BATT_THERM_100K: Returns battery thermistor voltage in
* SCALE_HW_CALIB_BATT_THERM_100K: Returns battery thermistor temperature in
* decidegC using 100k pullup. The hardware applies offset/slope to adc
* code.
* SCALE_HW_CALIB_BATT_THERM_30K: Returns battery thermistor voltage in
* SCALE_HW_CALIB_BATT_THERM_30K: Returns battery thermistor temperature in
* decidegC using 30k pullup. The hardware applies offset/slope to adc
* code.
* SCALE_HW_CALIB_BATT_THERM_400K: Returns battery thermistor voltage in
* SCALE_HW_CALIB_BATT_THERM_400K: Returns battery thermistor temperature in
* decidegC using 400k pullup. The hardware applies offset/slope to adc
* code.
* SCALE_HW_CALIB_PM5_SMB1398_TEMP: Returns result in millidegrees for PMIC5
@ -126,6 +182,17 @@ struct vadc_linear_graph {
* use voltage scaling.
* SCALE_HW_CALIB_CUR_RAW: Returns result in microamperes for PMIC7 channels
* that use raw ADC code.
* SCALE_HW_CALIB_PM2250_S3_DIE_TEMP: Returns result in millidegrees for
* S3 die temperature channel on PM2250.
* SCALE_HW_CALIB_PM5_CUR: Returns result in microamperes for PMIC5 channels
* that use voltage scaling.
* SCALE_HW_CALIB_PM5_GEN3_BATT_THERM_100K: Returns battery thermistor
* temperature in decidegC using 100k pullup. The hardware applies
* offset/slope to adc code.
* SCALE_HW_CALIB_PM5_GEN3_BATT_ID_100K: Returns battery ID resistance
* in ohms using 100k pullup. The hardware applies offset/slope to
* adc code.
* SCALE_HW_CALIB_PM5_GEN3_USB_IN_I: Returns USB input current in microamperes.
*/
enum vadc_scale_fn_type {
SCALE_DEFAULT = 0,
@ -149,10 +216,16 @@ enum vadc_scale_fn_type {
SCALE_HW_CALIB_PM7_CHG_TEMP,
SCALE_HW_CALIB_CUR,
SCALE_HW_CALIB_CUR_RAW,
SCALE_HW_CALIB_PM2250_S3_DIE_TEMP,
SCALE_HW_CALIB_PM5_CUR,
SCALE_HW_CALIB_PM5_GEN3_BATT_THERM_100K,
SCALE_HW_CALIB_PM5_GEN3_BATT_ID_100K,
SCALE_HW_CALIB_PM5_GEN3_USB_IN_I,
SCALE_HW_CALIB_INVALID,
};
struct adc5_data {
const char *name;
const u32 full_scale_code_volt;
const u32 full_scale_code_cur;
const struct adc5_channels *adc_chans;
@ -191,4 +264,8 @@ int qcom_adc5_decimation_from_dt(u32 value, const unsigned int *decimation);
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 */