summaryrefslogtreecommitdiff
path: root/drivers/iio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/adc/Kconfig27
-rw-r--r--drivers/iio/adc/Makefile2
-rw-r--r--drivers/iio/adc/qcom-rradc.c1109
-rw-r--r--drivers/iio/adc/qcom-tadc.c1324
-rw-r--r--drivers/iio/inkern.c18
5 files changed, 2480 insertions, 0 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index bda6bbe4479c..51cb6d8cffda 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -303,6 +303,33 @@ config QCOM_SPMI_VADC
To compile this driver as a module, choose M here: the module will
be called qcom-spmi-vadc.
+config QCOM_RRADC
+ tristate "Qualcomm Technologies Inc. PMIC Round robin ADC"
+ depends on SPMI
+ select REGMAP_SPMI
+ help
+ This is the PMIC Round Robin ADC driver.
+
+ The driver supports multiple channels read used for telemetry
+ and supports clients to read batt_id, batt_therm, PMIC die
+ temperature, USB_IN and DC_IN voltage and current.
+ The RRADC is a 10-bit ADC.
+
+ To compile this driver as a module, choose M here: the module will
+ be called qcom-rradc.
+
+config QCOM_TADC
+ tristate "Qualcomm Technologies Inc. TADC driver"
+ depends on MFD_I2C_PMIC
+ help
+ Say yes here to support the Qualcomm Technologies Inc. telemetry ADC.
+ The TADC provides battery temperature, skin temperature,
+ die temperature, battery voltage, battery current, input voltage,
+ input current, and OTG current.
+
+ The driver can also be built as a module. If so, the module will be
+ called qcom-tadc.
+
config ROCKCHIP_SARADC
tristate "Rockchip SARADC driver"
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 99b37a963a1e..9124e49a5f65 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -29,6 +29,8 @@ obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
+obj-$(CONFIG_QCOM_RRADC) += qcom-rradc.o
+obj-$(CONFIG_QCOM_TADC) += qcom-tadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c
new file mode 100644
index 000000000000..537cca877f66
--- /dev/null
+++ b/drivers/iio/adc/qcom-rradc.c
@@ -0,0 +1,1109 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "RRADC: %s: " fmt, __func__
+
+#include <linux/iio/iio.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include <linux/qpnp/qpnp-revid.h>
+
+#define FG_ADC_RR_EN_CTL 0x46
+#define FG_ADC_RR_SKIN_TEMP_LSB 0x50
+#define FG_ADC_RR_SKIN_TEMP_MSB 0x51
+#define FG_ADC_RR_RR_ADC_CTL 0x52
+#define FG_ADC_RR_ADC_CTL_CONTINUOUS_SEL_MASK 0x8
+#define FG_ADC_RR_ADC_CTL_CONTINUOUS_SEL BIT(3)
+#define FG_ADC_RR_ADC_LOG 0x53
+#define FG_ADC_RR_ADC_LOG_CLR_CTRL BIT(0)
+
+#define FG_ADC_RR_FAKE_BATT_LOW_LSB 0x58
+#define FG_ADC_RR_FAKE_BATT_LOW_MSB 0x59
+#define FG_ADC_RR_FAKE_BATT_HIGH_LSB 0x5A
+#define FG_ADC_RR_FAKE_BATT_HIGH_MSB 0x5B
+
+#define FG_ADC_RR_BATT_ID_CTRL 0x60
+#define FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV BIT(0)
+#define FG_ADC_RR_BATT_ID_TRIGGER 0x61
+#define FG_ADC_RR_BATT_ID_TRIGGER_CTL BIT(0)
+#define FG_ADC_RR_BATT_ID_STS 0x62
+#define FG_ADC_RR_BATT_ID_CFG 0x63
+#define FG_ADC_RR_BATT_ID_5_LSB 0x66
+#define FG_ADC_RR_BATT_ID_5_MSB 0x67
+#define FG_ADC_RR_BATT_ID_15_LSB 0x68
+#define FG_ADC_RR_BATT_ID_15_MSB 0x69
+#define FG_ADC_RR_BATT_ID_150_LSB 0x6A
+#define FG_ADC_RR_BATT_ID_150_MSB 0x6B
+
+#define FG_ADC_RR_BATT_THERM_CTRL 0x70
+#define FG_ADC_RR_BATT_THERM_TRIGGER 0x71
+#define FG_ADC_RR_BATT_THERM_STS 0x72
+#define FG_ADC_RR_BATT_THERM_CFG 0x73
+#define FG_ADC_RR_BATT_THERM_LSB 0x74
+#define FG_ADC_RR_BATT_THERM_MSB 0x75
+#define FG_ADC_RR_BATT_THERM_FREQ 0x76
+
+#define FG_ADC_RR_AUX_THERM_CTRL 0x80
+#define FG_ADC_RR_AUX_THERM_TRIGGER 0x81
+#define FG_ADC_RR_AUX_THERM_STS 0x82
+#define FG_ADC_RR_AUX_THERM_CFG 0x83
+#define FG_ADC_RR_AUX_THERM_LSB 0x84
+#define FG_ADC_RR_AUX_THERM_MSB 0x85
+
+#define FG_ADC_RR_SKIN_HOT 0x86
+#define FG_ADC_RR_SKIN_TOO_HOT 0x87
+
+#define FG_ADC_RR_AUX_THERM_C1 0x88
+#define FG_ADC_RR_AUX_THERM_C2 0x89
+#define FG_ADC_RR_AUX_THERM_C3 0x8A
+#define FG_ADC_RR_AUX_THERM_HALF_RANGE 0x8B
+
+#define FG_ADC_RR_USB_IN_V_CTRL 0x90
+#define FG_ADC_RR_USB_IN_V_TRIGGER 0x91
+#define FG_ADC_RR_USB_IN_V_EVERY_CYCLE_MASK 0x80
+#define FG_ADC_RR_USB_IN_V_EVERY_CYCLE BIT(7)
+#define FG_ADC_RR_USB_IN_V_STS 0x92
+#define FG_ADC_RR_USB_IN_V_LSB 0x94
+#define FG_ADC_RR_USB_IN_V_MSB 0x95
+#define FG_ADC_RR_USB_IN_I_CTRL 0x98
+#define FG_ADC_RR_USB_IN_I_TRIGGER 0x99
+#define FG_ADC_RR_USB_IN_I_STS 0x9A
+#define FG_ADC_RR_USB_IN_I_LSB 0x9C
+#define FG_ADC_RR_USB_IN_I_MSB 0x9D
+
+#define FG_ADC_RR_DC_IN_V_CTRL 0xA0
+#define FG_ADC_RR_DC_IN_V_TRIGGER 0xA1
+#define FG_ADC_RR_DC_IN_V_STS 0xA2
+#define FG_ADC_RR_DC_IN_V_LSB 0xA4
+#define FG_ADC_RR_DC_IN_V_MSB 0xA5
+#define FG_ADC_RR_DC_IN_I_CTRL 0xA8
+#define FG_ADC_RR_DC_IN_I_TRIGGER 0xA9
+#define FG_ADC_RR_DC_IN_I_STS 0xAA
+#define FG_ADC_RR_DC_IN_I_LSB 0xAC
+#define FG_ADC_RR_DC_IN_I_MSB 0xAD
+
+#define FG_ADC_RR_PMI_DIE_TEMP_CTRL 0xB0
+#define FG_ADC_RR_PMI_DIE_TEMP_TRIGGER 0xB1
+#define FG_ADC_RR_PMI_DIE_TEMP_STS 0xB2
+#define FG_ADC_RR_PMI_DIE_TEMP_CFG 0xB3
+#define FG_ADC_RR_PMI_DIE_TEMP_LSB 0xB4
+#define FG_ADC_RR_PMI_DIE_TEMP_MSB 0xB5
+
+#define FG_ADC_RR_CHARGER_TEMP_CTRL 0xB8
+#define FG_ADC_RR_CHARGER_TEMP_TRIGGER 0xB9
+#define FG_ADC_RR_CHARGER_TEMP_STS 0xBA
+#define FG_ADC_RR_CHARGER_TEMP_CFG 0xBB
+#define FG_ADC_RR_CHARGER_TEMP_LSB 0xBC
+#define FG_ADC_RR_CHARGER_TEMP_MSB 0xBD
+#define FG_ADC_RR_CHARGER_HOT 0xBE
+#define FG_ADC_RR_CHARGER_TOO_HOT 0xBF
+
+#define FG_ADC_RR_GPIO_CTRL 0xC0
+#define FG_ADC_RR_GPIO_TRIGGER 0xC1
+#define FG_ADC_RR_GPIO_STS 0xC2
+#define FG_ADC_RR_GPIO_LSB 0xC4
+#define FG_ADC_RR_GPIO_MSB 0xC5
+
+#define FG_ADC_RR_ATEST_CTRL 0xC8
+#define FG_ADC_RR_ATEST_TRIGGER 0xC9
+#define FG_ADC_RR_ATEST_STS 0xCA
+#define FG_ADC_RR_ATEST_LSB 0xCC
+#define FG_ADC_RR_ATEST_MSB 0xCD
+#define FG_ADC_RR_SEC_ACCESS 0xD0
+
+#define FG_ADC_RR_PERPH_RESET_CTL2 0xD9
+#define FG_ADC_RR_PERPH_RESET_CTL3 0xDA
+#define FG_ADC_RR_PERPH_RESET_CTL4 0xDB
+#define FG_ADC_RR_INT_TEST1 0xE0
+#define FG_ADC_RR_INT_TEST_VAL 0xE1
+
+#define FG_ADC_RR_TM_TRIGGER_CTRLS 0xE2
+#define FG_ADC_RR_TM_ADC_CTRLS 0xE3
+#define FG_ADC_RR_TM_CNL_CTRL 0xE4
+#define FG_ADC_RR_TM_BATT_ID_CTRL 0xE5
+#define FG_ADC_RR_TM_THERM_CTRL 0xE6
+#define FG_ADC_RR_TM_CONV_STS 0xE7
+#define FG_ADC_RR_TM_ADC_READ_LSB 0xE8
+#define FG_ADC_RR_TM_ADC_READ_MSB 0xE9
+#define FG_ADC_RR_TM_ATEST_MUX_1 0xEA
+#define FG_ADC_RR_TM_ATEST_MUX_2 0xEB
+#define FG_ADC_RR_TM_REFERENCES 0xED
+#define FG_ADC_RR_TM_MISC_CTL 0xEE
+#define FG_ADC_RR_TM_RR_CTRL 0xEF
+
+#define FG_ADC_RR_BATT_ID_5_MA 5
+#define FG_ADC_RR_BATT_ID_15_MA 15
+#define FG_ADC_RR_BATT_ID_150_MA 150
+#define FG_ADC_RR_BATT_ID_RANGE 820
+
+#define FG_ADC_BITS 10
+#define FG_MAX_ADC_READINGS (1 << FG_ADC_BITS)
+#define FG_ADC_RR_FS_VOLTAGE_MV 2500
+
+/* BATT_THERM 0.25K/LSB */
+#define FG_ADC_RR_BATT_THERM_LSB_K 4
+
+#define FG_ADC_RR_TEMP_FS_VOLTAGE_NUM 5000000
+#define FG_ADC_RR_TEMP_FS_VOLTAGE_DEN 3
+#define FG_ADC_RR_DIE_TEMP_OFFSET 601400
+#define FG_ADC_RR_DIE_TEMP_SLOPE 2
+#define FG_ADC_RR_DIE_TEMP_OFFSET_MILLI_DEGC 25000
+
+#define FG_ADC_RR_CHG_TEMP_GF_OFFSET_UV 1303168
+#define FG_ADC_RR_CHG_TEMP_GF_SLOPE_UV_PER_C 3784
+#define FG_ADC_RR_CHG_TEMP_SMIC_OFFSET_UV 1338433
+#define FG_ADC_RR_CHG_TEMP_SMIC_SLOPE_UV_PER_C 3655
+#define FG_ADC_RR_CHG_TEMP_660_GF_OFFSET_UV 1309001
+#define FG_RR_CHG_TEMP_660_GF_SLOPE_UV_PER_C 3403
+#define FG_ADC_RR_CHG_TEMP_660_SMIC_OFFSET_UV 1295898
+#define FG_RR_CHG_TEMP_660_SMIC_SLOPE_UV_PER_C 3596
+#define FG_ADC_RR_CHG_TEMP_660_MGNA_OFFSET_UV 1314779
+#define FG_RR_CHG_TEMP_660_MGNA_SLOPE_UV_PER_C 3496
+#define FG_ADC_RR_CHG_TEMP_OFFSET_MILLI_DEGC 25000
+#define FG_ADC_RR_CHG_THRESHOLD_SCALE 4
+
+#define FG_ADC_RR_VOLT_INPUT_FACTOR 8
+#define FG_ADC_RR_CURR_INPUT_FACTOR 2000
+#define FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL 1886
+#define FG_ADC_SCALE_MILLI_FACTOR 1000
+#define FG_ADC_KELVINMIL_CELSIUSMIL 273150
+
+#define FG_ADC_RR_GPIO_FS_RANGE 5000
+#define FG_RR_ADC_COHERENT_CHECK_RETRY 5
+#define FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN 16
+#define FG_RR_ADC_STS_CHANNEL_READING_MASK 0x3
+#define FG_RR_ADC_STS_CHANNEL_STS 0x2
+
+#define FG_RR_CONV_CONTINUOUS_TIME_MIN_US 50000
+#define FG_RR_CONV_CONTINUOUS_TIME_MAX_US 51000
+#define FG_RR_CONV_MAX_RETRY_CNT 50
+
+/*
+ * The channel number is not a physical index in hardware,
+ * rather it's a list of supported channels and an index to
+ * select the respective channel properties such as scaling
+ * the result. Add any new additional channels supported by
+ * the RR ADC before RR_ADC_MAX.
+ */
+enum rradc_channel_id {
+ RR_ADC_BATT_ID = 0,
+ RR_ADC_BATT_THERM,
+ RR_ADC_SKIN_TEMP,
+ RR_ADC_USBIN_I,
+ RR_ADC_USBIN_V,
+ RR_ADC_DCIN_I,
+ RR_ADC_DCIN_V,
+ RR_ADC_DIE_TEMP,
+ RR_ADC_CHG_TEMP,
+ RR_ADC_GPIO,
+ RR_ADC_CHG_HOT_TEMP,
+ RR_ADC_CHG_TOO_HOT_TEMP,
+ RR_ADC_SKIN_HOT_TEMP,
+ RR_ADC_SKIN_TOO_HOT_TEMP,
+ RR_ADC_MAX
+};
+
+struct rradc_chip {
+ struct device *dev;
+ struct mutex lock;
+ struct regmap *regmap;
+ u16 base;
+ struct iio_chan_spec *iio_chans;
+ unsigned int nchannels;
+ struct rradc_chan_prop *chan_props;
+ struct device_node *revid_dev_node;
+ struct pmic_revid_data *pmic_fab_id;
+};
+
+struct rradc_channels {
+ const char *datasheet_name;
+ enum iio_chan_type type;
+ long info_mask;
+ u8 lsb;
+ u8 msb;
+ u8 sts;
+ int (*scale)(struct rradc_chip *chip, struct rradc_chan_prop *prop,
+ u16 adc_code, int *result);
+};
+
+struct rradc_chan_prop {
+ enum rradc_channel_id channel;
+ uint32_t channel_data;
+ int (*scale)(struct rradc_chip *chip, struct rradc_chan_prop *prop,
+ u16 adc_code, int *result);
+};
+
+static int rradc_masked_write(struct rradc_chip *rr_adc, u16 offset, u8 mask,
+ u8 val)
+{
+ int rc;
+
+ rc = regmap_update_bits(rr_adc->regmap, rr_adc->base + offset,
+ mask, val);
+ if (rc) {
+ pr_err("spmi write failed: addr=%03X, rc=%d\n", offset, rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int rradc_read(struct rradc_chip *rr_adc, u16 offset, u8 *data, int len)
+{
+ int rc = 0, retry_cnt = 0, i = 0;
+ u8 data_check[FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN];
+ bool coherent_err = false;
+
+ if (len > FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN) {
+ pr_err("Increase the buffer length\n");
+ return -EINVAL;
+ }
+
+ while (retry_cnt < FG_RR_ADC_COHERENT_CHECK_RETRY) {
+ rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset,
+ data, len);
+ if (rc < 0) {
+ pr_err("rr_adc reg 0x%x failed :%d\n", offset, rc);
+ return rc;
+ }
+
+ rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset,
+ data_check, len);
+ if (rc < 0) {
+ pr_err("rr_adc reg 0x%x failed :%d\n", offset, rc);
+ return rc;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (data[i] != data_check[i])
+ coherent_err = true;
+ }
+
+ if (coherent_err) {
+ retry_cnt++;
+ coherent_err = false;
+ pr_debug("retry_cnt:%d\n", retry_cnt);
+ } else {
+ break;
+ }
+ }
+
+ if (retry_cnt == FG_RR_ADC_COHERENT_CHECK_RETRY)
+ pr_err("Retry exceeded for coherrency check\n");
+
+ return rc;
+}
+
+static int rradc_post_process_batt_id(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 adc_code,
+ int *result_ohms)
+{
+ uint32_t current_value;
+ int64_t r_id;
+
+ current_value = prop->channel_data;
+ r_id = ((int64_t)adc_code * FG_ADC_RR_FS_VOLTAGE_MV);
+ r_id = div64_s64(r_id, (FG_MAX_ADC_READINGS * current_value));
+ *result_ohms = (r_id * FG_ADC_SCALE_MILLI_FACTOR);
+
+ return 0;
+}
+
+static int rradc_post_process_therm(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 adc_code,
+ int *result_millidegc)
+{
+ int64_t temp;
+
+ /* K = code/4 */
+ temp = div64_s64(adc_code, FG_ADC_RR_BATT_THERM_LSB_K);
+ temp *= FG_ADC_SCALE_MILLI_FACTOR;
+ *result_millidegc = temp - FG_ADC_KELVINMIL_CELSIUSMIL;
+
+ return 0;
+}
+
+static int rradc_post_process_volt(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 adc_code,
+ int *result_uv)
+{
+ int64_t uv = 0;
+
+ /* 8x input attenuation; 2.5V ADC full scale */
+ uv = ((int64_t)adc_code * FG_ADC_RR_VOLT_INPUT_FACTOR);
+ uv *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR);
+ uv = div64_s64(uv, FG_MAX_ADC_READINGS);
+ *result_uv = uv;
+
+ return 0;
+}
+
+static int rradc_post_process_curr(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 adc_code,
+ int *result_ua)
+{
+ int64_t ua = 0, scale = 0;
+
+ if (!prop)
+ return -EINVAL;
+
+ if (prop->channel == RR_ADC_USBIN_I)
+ scale = FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL;
+ else
+ scale = FG_ADC_RR_CURR_INPUT_FACTOR;
+
+ /* scale * V/A; 2.5V ADC full scale */
+ ua = ((int64_t)adc_code * scale);
+ ua *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR);
+ ua = div64_s64(ua, (FG_MAX_ADC_READINGS * 1000));
+ *result_ua = ua;
+
+ return 0;
+}
+
+static int rradc_post_process_die_temp(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 adc_code,
+ int *result_millidegc)
+{
+ int64_t temp = 0;
+
+ temp = ((int64_t)adc_code * FG_ADC_RR_TEMP_FS_VOLTAGE_NUM);
+ temp = div64_s64(temp, (FG_ADC_RR_TEMP_FS_VOLTAGE_DEN *
+ FG_MAX_ADC_READINGS));
+ temp -= FG_ADC_RR_DIE_TEMP_OFFSET;
+ temp = div64_s64(temp, FG_ADC_RR_DIE_TEMP_SLOPE);
+ temp += FG_ADC_RR_DIE_TEMP_OFFSET_MILLI_DEGC;
+ *result_millidegc = temp;
+
+ return 0;
+}
+
+static int rradc_get_660_fab_coeff(struct rradc_chip *chip,
+ int64_t *offset, int64_t *slope)
+{
+ switch (chip->pmic_fab_id->fab_id) {
+ case PM660_FAB_ID_GF:
+ *offset = FG_ADC_RR_CHG_TEMP_660_GF_OFFSET_UV;
+ *slope = FG_RR_CHG_TEMP_660_GF_SLOPE_UV_PER_C;
+ break;
+ case PM660_FAB_ID_TSMC:
+ *offset = FG_ADC_RR_CHG_TEMP_660_SMIC_OFFSET_UV;
+ *slope = FG_RR_CHG_TEMP_660_SMIC_SLOPE_UV_PER_C;
+ break;
+ default:
+ *offset = FG_ADC_RR_CHG_TEMP_660_MGNA_OFFSET_UV;
+ *slope = FG_RR_CHG_TEMP_660_MGNA_SLOPE_UV_PER_C;
+ }
+
+ return 0;
+}
+
+static int rradc_get_8998_fab_coeff(struct rradc_chip *chip,
+ int64_t *offset, int64_t *slope)
+{
+ switch (chip->pmic_fab_id->fab_id) {
+ case PMI8998_FAB_ID_GF:
+ *offset = FG_ADC_RR_CHG_TEMP_GF_OFFSET_UV;
+ *slope = FG_ADC_RR_CHG_TEMP_GF_SLOPE_UV_PER_C;
+ break;
+ case PMI8998_FAB_ID_SMIC:
+ *offset = FG_ADC_RR_CHG_TEMP_SMIC_OFFSET_UV;
+ *slope = FG_ADC_RR_CHG_TEMP_SMIC_SLOPE_UV_PER_C;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rradc_post_process_chg_temp_hot(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 adc_code,
+ int *result_millidegc)
+{
+ int64_t uv = 0, offset = 0, slope = 0;
+ int rc = 0;
+
+ if (chip->revid_dev_node) {
+ switch (chip->pmic_fab_id->pmic_subtype) {
+ case PM660_SUBTYPE:
+ rc = rradc_get_660_fab_coeff(chip, &offset, &slope);
+ if (rc < 0) {
+ pr_err("Unable to get fab id coefficients\n");
+ return -EINVAL;
+ }
+ break;
+ case PMI8998_SUBTYPE:
+ rc = rradc_get_8998_fab_coeff(chip, &offset, &slope);
+ if (rc < 0) {
+ pr_err("Unable to get fab id coefficients\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ pr_err("No PMIC subtype found\n");
+ return -EINVAL;
+ }
+ } else {
+ pr_err("No temperature scaling coefficients\n");
+ return -EINVAL;
+ }
+
+ uv = (int64_t) adc_code * FG_ADC_RR_CHG_THRESHOLD_SCALE;
+ uv = uv * FG_ADC_RR_TEMP_FS_VOLTAGE_NUM;
+ uv = div64_s64(uv, (FG_ADC_RR_TEMP_FS_VOLTAGE_DEN *
+ FG_MAX_ADC_READINGS));
+ uv = offset - uv;
+ uv = div64_s64((uv * FG_ADC_SCALE_MILLI_FACTOR), slope);
+ uv = uv + FG_ADC_RR_CHG_TEMP_OFFSET_MILLI_DEGC;
+ *result_millidegc = uv;
+
+ return 0;
+}
+
+static int rradc_post_process_skin_temp_hot(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 adc_code,
+ int *result_millidegc)
+{
+ int64_t temp = 0;
+
+ temp = (int64_t) adc_code;
+ temp = div64_s64(temp, 2);
+ temp = temp - 30;
+ temp *= FG_ADC_SCALE_MILLI_FACTOR;
+ *result_millidegc = temp;
+
+ return 0;
+}
+
+static int rradc_post_process_chg_temp(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 adc_code,
+ int *result_millidegc)
+{
+ int64_t uv = 0, offset = 0, slope = 0;
+ int rc = 0;
+
+ if (chip->revid_dev_node) {
+ switch (chip->pmic_fab_id->pmic_subtype) {
+ case PM660_SUBTYPE:
+ rc = rradc_get_660_fab_coeff(chip, &offset, &slope);
+ if (rc < 0) {
+ pr_err("Unable to get fab id coefficients\n");
+ return -EINVAL;
+ }
+ break;
+ case PMI8998_SUBTYPE:
+ rc = rradc_get_8998_fab_coeff(chip, &offset, &slope);
+ if (rc < 0) {
+ pr_err("Unable to get fab id coefficients\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ pr_err("No PMIC subtype found\n");
+ return -EINVAL;
+ }
+ } else {
+ pr_err("No temperature scaling coefficients\n");
+ return -EINVAL;
+ }
+
+ uv = ((int64_t) adc_code * FG_ADC_RR_TEMP_FS_VOLTAGE_NUM);
+ uv = div64_s64(uv, (FG_ADC_RR_TEMP_FS_VOLTAGE_DEN *
+ FG_MAX_ADC_READINGS));
+ uv = offset - uv;
+ uv = div64_s64((uv * FG_ADC_SCALE_MILLI_FACTOR), slope);
+ uv += FG_ADC_RR_CHG_TEMP_OFFSET_MILLI_DEGC;
+ *result_millidegc = uv;
+
+ return 0;
+}
+
+static int rradc_post_process_gpio(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 adc_code,
+ int *result_mv)
+{
+ int64_t mv = 0;
+
+ /* 5V ADC full scale, 10 bit */
+ mv = ((int64_t)adc_code * FG_ADC_RR_GPIO_FS_RANGE);
+ mv = div64_s64(mv, FG_MAX_ADC_READINGS);
+ *result_mv = mv;
+
+ return 0;
+}
+
+#define RR_ADC_CHAN(_dname, _type, _mask, _scale, _lsb, _msb, _sts) \
+ { \
+ .datasheet_name = (_dname), \
+ .type = _type, \
+ .info_mask = _mask, \
+ .scale = _scale, \
+ .lsb = _lsb, \
+ .msb = _msb, \
+ .sts = _sts, \
+ }, \
+
+#define RR_ADC_CHAN_TEMP(_dname, _scale, mask, _lsb, _msb, _sts) \
+ RR_ADC_CHAN(_dname, IIO_TEMP, \
+ mask, \
+ _scale, _lsb, _msb, _sts) \
+
+#define RR_ADC_CHAN_VOLT(_dname, _scale, _lsb, _msb, _sts) \
+ RR_ADC_CHAN(_dname, IIO_VOLTAGE, \
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\
+ _scale, _lsb, _msb, _sts) \
+
+#define RR_ADC_CHAN_CURRENT(_dname, _scale, _lsb, _msb, _sts) \
+ RR_ADC_CHAN(_dname, IIO_CURRENT, \
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\
+ _scale, _lsb, _msb, _sts) \
+
+#define RR_ADC_CHAN_RESISTANCE(_dname, _scale, _lsb, _msb, _sts) \
+ RR_ADC_CHAN(_dname, IIO_RESISTANCE, \
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\
+ _scale, _lsb, _msb, _sts) \
+
+static const struct rradc_channels rradc_chans[] = {
+ RR_ADC_CHAN_RESISTANCE("batt_id", rradc_post_process_batt_id,
+ FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB,
+ FG_ADC_RR_BATT_ID_STS)
+ RR_ADC_CHAN_TEMP("batt_therm", &rradc_post_process_therm,
+ BIT(IIO_CHAN_INFO_RAW),
+ FG_ADC_RR_BATT_THERM_LSB, FG_ADC_RR_BATT_THERM_MSB,
+ FG_ADC_RR_BATT_THERM_STS)
+ RR_ADC_CHAN_TEMP("skin_temp", &rradc_post_process_therm,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
+ FG_ADC_RR_SKIN_TEMP_LSB, FG_ADC_RR_SKIN_TEMP_MSB,
+ FG_ADC_RR_AUX_THERM_STS)
+ RR_ADC_CHAN_CURRENT("usbin_i", &rradc_post_process_curr,
+ FG_ADC_RR_USB_IN_I_LSB, FG_ADC_RR_USB_IN_I_MSB,
+ FG_ADC_RR_USB_IN_I_STS)
+ RR_ADC_CHAN_VOLT("usbin_v", &rradc_post_process_volt,
+ FG_ADC_RR_USB_IN_V_LSB, FG_ADC_RR_USB_IN_V_MSB,
+ FG_ADC_RR_USB_IN_V_STS)
+ RR_ADC_CHAN_CURRENT("dcin_i", &rradc_post_process_curr,
+ FG_ADC_RR_DC_IN_I_LSB, FG_ADC_RR_DC_IN_I_MSB,
+ FG_ADC_RR_DC_IN_I_STS)
+ RR_ADC_CHAN_VOLT("dcin_v", &rradc_post_process_volt,
+ FG_ADC_RR_DC_IN_V_LSB, FG_ADC_RR_DC_IN_V_MSB,
+ FG_ADC_RR_DC_IN_V_STS)
+ RR_ADC_CHAN_TEMP("die_temp", &rradc_post_process_die_temp,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
+ FG_ADC_RR_PMI_DIE_TEMP_LSB, FG_ADC_RR_PMI_DIE_TEMP_MSB,
+ FG_ADC_RR_PMI_DIE_TEMP_STS)
+ RR_ADC_CHAN_TEMP("chg_temp", &rradc_post_process_chg_temp,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
+ FG_ADC_RR_CHARGER_TEMP_LSB, FG_ADC_RR_CHARGER_TEMP_MSB,
+ FG_ADC_RR_CHARGER_TEMP_STS)
+ RR_ADC_CHAN_VOLT("gpio", &rradc_post_process_gpio,
+ FG_ADC_RR_GPIO_LSB, FG_ADC_RR_GPIO_MSB,
+ FG_ADC_RR_GPIO_STS)
+ RR_ADC_CHAN_TEMP("chg_temp_hot", &rradc_post_process_chg_temp_hot,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
+ FG_ADC_RR_CHARGER_HOT, FG_ADC_RR_CHARGER_HOT,
+ FG_ADC_RR_CHARGER_TEMP_STS)
+ RR_ADC_CHAN_TEMP("chg_temp_too_hot", &rradc_post_process_chg_temp_hot,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
+ FG_ADC_RR_CHARGER_TOO_HOT, FG_ADC_RR_CHARGER_TOO_HOT,
+ FG_ADC_RR_CHARGER_TEMP_STS)
+ RR_ADC_CHAN_TEMP("skin_temp_hot", &rradc_post_process_skin_temp_hot,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
+ FG_ADC_RR_SKIN_HOT, FG_ADC_RR_SKIN_HOT,
+ FG_ADC_RR_AUX_THERM_STS)
+ RR_ADC_CHAN_TEMP("skin_temp_too_hot", &rradc_post_process_skin_temp_hot,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
+ FG_ADC_RR_SKIN_TOO_HOT, FG_ADC_RR_SKIN_TOO_HOT,
+ FG_ADC_RR_AUX_THERM_STS)
+};
+
+static int rradc_enable_continuous_mode(struct rradc_chip *chip)
+{
+ int rc = 0;
+
+ /* Clear channel log */
+ rc = rradc_masked_write(chip, FG_ADC_RR_ADC_LOG,
+ FG_ADC_RR_ADC_LOG_CLR_CTRL,
+ FG_ADC_RR_ADC_LOG_CLR_CTRL);
+ if (rc < 0) {
+ pr_err("log ctrl update to clear failed:%d\n", rc);
+ return rc;
+ }
+
+ rc = rradc_masked_write(chip, FG_ADC_RR_ADC_LOG,
+ FG_ADC_RR_ADC_LOG_CLR_CTRL, 0);
+ if (rc < 0) {
+ pr_err("log ctrl update to not clear failed:%d\n", rc);
+ return rc;
+ }
+
+ /* Switch to continuous mode */
+ rc = rradc_masked_write(chip, FG_ADC_RR_RR_ADC_CTL,
+ FG_ADC_RR_ADC_CTL_CONTINUOUS_SEL_MASK,
+ FG_ADC_RR_ADC_CTL_CONTINUOUS_SEL);
+ if (rc < 0) {
+ pr_err("Update to continuous mode failed:%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int rradc_disable_continuous_mode(struct rradc_chip *chip)
+{
+ int rc = 0;
+
+ /* Switch to non continuous mode */
+ rc = rradc_masked_write(chip, FG_ADC_RR_RR_ADC_CTL,
+ FG_ADC_RR_ADC_CTL_CONTINUOUS_SEL_MASK, 0);
+ if (rc < 0) {
+ pr_err("Update to non-continuous mode failed:%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int rradc_check_status_ready_with_retry(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u8 *buf, u16 status)
+{
+ int rc = 0, retry_cnt = 0, mask = 0;
+
+ switch (prop->channel) {
+ case RR_ADC_BATT_ID:
+ /* BATT_ID STS bit does not get set initially */
+ mask = FG_RR_ADC_STS_CHANNEL_STS;
+ break;
+ default:
+ mask = FG_RR_ADC_STS_CHANNEL_READING_MASK;
+ break;
+ }
+
+ while (((buf[0] & mask) != mask) &&
+ (retry_cnt < FG_RR_CONV_MAX_RETRY_CNT)) {
+ pr_debug("%s is not ready; nothing to read:0x%x\n",
+ rradc_chans[prop->channel].datasheet_name, buf[0]);
+ usleep_range(FG_RR_CONV_CONTINUOUS_TIME_MIN_US,
+ FG_RR_CONV_CONTINUOUS_TIME_MAX_US);
+ retry_cnt++;
+ rc = rradc_read(chip, status, buf, 1);
+ if (rc < 0) {
+ pr_err("status read failed:%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (retry_cnt >= FG_RR_CONV_MAX_RETRY_CNT)
+ rc = -ENODATA;
+
+ return rc;
+}
+
+static int rradc_read_channel_with_continuous_mode(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u8 *buf)
+{
+ int rc = 0;
+ u16 status = 0;
+
+ rc = rradc_enable_continuous_mode(chip);
+ if (rc < 0) {
+ pr_err("Failed to switch to continuous mode\n");
+ return rc;
+ }
+
+ status = rradc_chans[prop->channel].sts;
+ rc = rradc_read(chip, status, buf, 1);
+ if (rc < 0) {
+ pr_err("status read failed:%d\n", rc);
+ return rc;
+ }
+
+ rc = rradc_check_status_ready_with_retry(chip, prop,
+ buf, status);
+ if (rc < 0) {
+ pr_err("Status read failed:%d\n", rc);
+ return rc;
+ }
+
+ rc = rradc_disable_continuous_mode(chip);
+ if (rc < 0) {
+ pr_err("Failed to switch to non continuous mode\n");
+ return rc;
+ }
+
+ return rc;
+}
+
+static int rradc_enable_batt_id_channel(struct rradc_chip *chip, bool enable)
+{
+ int rc = 0;
+
+ if (enable) {
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_CTRL,
+ FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV,
+ FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV);
+ if (rc < 0) {
+ pr_err("Enabling BATT ID channel failed:%d\n", rc);
+ return rc;
+ }
+ } else {
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_CTRL,
+ FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV, 0);
+ if (rc < 0) {
+ pr_err("Disabling BATT ID channel failed:%d\n", rc);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int rradc_do_batt_id_conversion(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 *data, u8 *buf)
+{
+ int rc = 0, ret = 0;
+
+ rc = rradc_enable_batt_id_channel(chip, true);
+ if (rc < 0) {
+ pr_err("Enabling BATT ID channel failed:%d\n", rc);
+ return rc;
+ }
+
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
+ FG_ADC_RR_BATT_ID_TRIGGER_CTL,
+ FG_ADC_RR_BATT_ID_TRIGGER_CTL);
+ if (rc < 0) {
+ pr_err("BATT_ID trigger set failed:%d\n", rc);
+ ret = rc;
+ rc = rradc_enable_batt_id_channel(chip, false);
+ if (rc < 0)
+ pr_err("Disabling BATT ID channel failed:%d\n", rc);
+ return ret;
+ }
+
+ rc = rradc_read_channel_with_continuous_mode(chip, prop, buf);
+ if (rc < 0) {
+ pr_err("Error reading in continuous mode:%d\n", rc);
+ ret = rc;
+ }
+
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
+ FG_ADC_RR_BATT_ID_TRIGGER_CTL, 0);
+ if (rc < 0) {
+ pr_err("BATT_ID trigger re-set failed:%d\n", rc);
+ ret = rc;
+ }
+
+ rc = rradc_enable_batt_id_channel(chip, false);
+ if (rc < 0) {
+ pr_err("Disabling BATT ID channel failed:%d\n", rc);
+ ret = rc;
+ }
+
+ return ret;
+}
+
+static int rradc_do_conversion(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 *data)
+{
+ int rc = 0, bytes_to_read = 0;
+ u8 buf[6];
+ u16 offset = 0, batt_id_5 = 0, batt_id_15 = 0, batt_id_150 = 0;
+ u16 status = 0;
+
+ mutex_lock(&chip->lock);
+
+ switch (prop->channel) {
+ case RR_ADC_BATT_ID:
+ rc = rradc_do_batt_id_conversion(chip, prop, data, buf);
+ if (rc < 0) {
+ pr_err("Battery ID conversion failed:%d\n", rc);
+ goto fail;
+ }
+ break;
+ case RR_ADC_USBIN_V:
+ /* Force conversion every cycle */
+ rc = rradc_masked_write(chip, FG_ADC_RR_USB_IN_V_TRIGGER,
+ FG_ADC_RR_USB_IN_V_EVERY_CYCLE_MASK,
+ FG_ADC_RR_USB_IN_V_EVERY_CYCLE);
+ if (rc < 0) {
+ pr_err("Force every cycle update failed:%d\n", rc);
+ goto fail;
+ }
+
+ rc = rradc_read_channel_with_continuous_mode(chip, prop, buf);
+ if (rc < 0) {
+ pr_err("Error reading in continuous mode:%d\n", rc);
+ goto fail;
+ }
+
+ /* Restore usb_in trigger */
+ rc = rradc_masked_write(chip, FG_ADC_RR_USB_IN_V_TRIGGER,
+ FG_ADC_RR_USB_IN_V_EVERY_CYCLE_MASK, 0);
+ if (rc < 0) {
+ pr_err("Restore every cycle update failed:%d\n", rc);
+ goto fail;
+ }
+ break;
+ case RR_ADC_CHG_HOT_TEMP:
+ case RR_ADC_CHG_TOO_HOT_TEMP:
+ case RR_ADC_SKIN_HOT_TEMP:
+ case RR_ADC_SKIN_TOO_HOT_TEMP:
+ pr_debug("Read only the data registers\n");
+ break;
+ default:
+ status = rradc_chans[prop->channel].sts;
+ rc = rradc_read(chip, status, buf, 1);
+ if (rc < 0) {
+ pr_err("status read failed:%d\n", rc);
+ goto fail;
+ }
+
+ rc = rradc_check_status_ready_with_retry(chip, prop,
+ buf, status);
+ if (rc < 0) {
+ pr_debug("Status read failed:%d\n", rc);
+ rc = -ENODATA;
+ goto fail;
+ }
+ break;
+ }
+
+ offset = rradc_chans[prop->channel].lsb;
+ if (prop->channel == RR_ADC_BATT_ID)
+ bytes_to_read = 6;
+ else if ((prop->channel == RR_ADC_CHG_HOT_TEMP) ||
+ (prop->channel == RR_ADC_CHG_TOO_HOT_TEMP) ||
+ (prop->channel == RR_ADC_SKIN_HOT_TEMP) ||
+ (prop->channel == RR_ADC_SKIN_TOO_HOT_TEMP))
+ bytes_to_read = 1;
+ else
+ bytes_to_read = 2;
+
+ buf[0] = 0;
+ rc = rradc_read(chip, offset, buf, bytes_to_read);
+ if (rc) {
+ pr_err("read data failed\n");
+ goto fail;
+ }
+
+ if (prop->channel == RR_ADC_BATT_ID) {
+ batt_id_150 = (buf[5] << 8) | buf[4];
+ batt_id_15 = (buf[3] << 8) | buf[2];
+ batt_id_5 = (buf[1] << 8) | buf[0];
+ if ((!batt_id_150) && (!batt_id_15) && (!batt_id_5)) {
+ pr_err("Invalid batt_id values with all zeros\n");
+ rc = -EINVAL;
+ goto fail;
+ }
+
+ if (batt_id_150 <= FG_ADC_RR_BATT_ID_RANGE) {
+ pr_debug("Batt_id_150 is chosen\n");
+ *data = batt_id_150;
+ prop->channel_data = FG_ADC_RR_BATT_ID_150_MA;
+ } else if (batt_id_15 <= FG_ADC_RR_BATT_ID_RANGE) {
+ pr_debug("Batt_id_15 is chosen\n");
+ *data = batt_id_15;
+ prop->channel_data = FG_ADC_RR_BATT_ID_15_MA;
+ } else {
+ pr_debug("Batt_id_5 is chosen\n");
+ *data = batt_id_5;
+ prop->channel_data = FG_ADC_RR_BATT_ID_5_MA;
+ }
+ } else if ((prop->channel == RR_ADC_CHG_HOT_TEMP) ||
+ (prop->channel == RR_ADC_CHG_TOO_HOT_TEMP) ||
+ (prop->channel == RR_ADC_SKIN_HOT_TEMP) ||
+ (prop->channel == RR_ADC_SKIN_TOO_HOT_TEMP)) {
+ *data = buf[0];
+ } else {
+ *data = (buf[1] << 8) | buf[0];
+ }
+fail:
+ mutex_unlock(&chip->lock);
+
+ return rc;
+}
+
+static int rradc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct rradc_chip *chip = iio_priv(indio_dev);
+ struct rradc_chan_prop *prop;
+ u16 adc_code;
+ int rc = 0;
+
+ if (chan->address >= RR_ADC_MAX) {
+ pr_err("Invalid channel index:%ld\n", chan->address);
+ return -EINVAL;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ prop = &chip->chan_props[chan->address];
+ rc = rradc_do_conversion(chip, prop, &adc_code);
+ if (rc)
+ break;
+
+ prop->scale(chip, prop, adc_code, val);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_RAW:
+ prop = &chip->chan_props[chan->address];
+ rc = rradc_do_conversion(chip, prop, &adc_code);
+ if (rc)
+ break;
+
+ *val = (int) adc_code;
+
+ return IIO_VAL_INT;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static const struct iio_info rradc_info = {
+ .read_raw = &rradc_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int rradc_get_dt_data(struct rradc_chip *chip, struct device_node *node)
+{
+ const struct rradc_channels *rradc_chan;
+ struct iio_chan_spec *iio_chan;
+ unsigned int i = 0, base;
+ int rc = 0;
+ struct rradc_chan_prop prop;
+
+ chip->nchannels = RR_ADC_MAX;
+ chip->iio_chans = devm_kcalloc(chip->dev, chip->nchannels,
+ sizeof(*chip->iio_chans), GFP_KERNEL);
+ if (!chip->iio_chans)
+ return -ENOMEM;
+
+ chip->chan_props = devm_kcalloc(chip->dev, chip->nchannels,
+ sizeof(*chip->chan_props), GFP_KERNEL);
+ if (!chip->chan_props)
+ return -ENOMEM;
+
+ /* Get the peripheral address */
+ rc = of_property_read_u32(node, "reg", &base);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't find reg in node = %s rc = %d\n",
+ node->name, rc);
+ return rc;
+ }
+
+ chip->base = base;
+ chip->revid_dev_node = of_parse_phandle(node, "qcom,pmic-revid", 0);
+ if (chip->revid_dev_node) {
+ chip->pmic_fab_id = get_revid_data(chip->revid_dev_node);
+ if (IS_ERR(chip->pmic_fab_id)) {
+ rc = PTR_ERR(chip->pmic_fab_id);
+ if (rc != -EPROBE_DEFER)
+ pr_err("Unable to get pmic_revid rc=%d\n", rc);
+ return rc;
+ }
+
+ if (chip->pmic_fab_id->fab_id == -EINVAL) {
+ rc = chip->pmic_fab_id->fab_id;
+ pr_debug("Unable to read fabid rc=%d\n", rc);
+ }
+ }
+
+ iio_chan = chip->iio_chans;
+
+ for (i = 0; i < RR_ADC_MAX; i++) {
+ prop.channel = i;
+ prop.scale = rradc_chans[i].scale;
+ /* Private channel data used for selecting batt_id */
+ prop.channel_data = 0;
+ chip->chan_props[i] = prop;
+
+ rradc_chan = &rradc_chans[i];
+
+ iio_chan->channel = prop.channel;
+ iio_chan->datasheet_name = rradc_chan->datasheet_name;
+ iio_chan->extend_name = rradc_chan->datasheet_name;
+ iio_chan->info_mask_separate = rradc_chan->info_mask;
+ iio_chan->type = rradc_chan->type;
+ iio_chan->address = i;
+ iio_chan++;
+ }
+
+ return 0;
+}
+
+static int rradc_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct rradc_chip *chip;
+ int rc = 0;
+
+ 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(&pdev->dev, "Couldn't get parent's regmap\n");
+ return -EINVAL;
+ }
+
+ chip->dev = dev;
+ mutex_init(&chip->lock);
+
+ rc = rradc_get_dt_data(chip, node);
+ if (rc)
+ return rc;
+
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = node;
+ indio_dev->name = pdev->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &rradc_info;
+ indio_dev->channels = chip->iio_chans;
+ indio_dev->num_channels = chip->nchannels;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id rradc_match_table[] = {
+ { .compatible = "qcom,rradc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rradc_match_table);
+
+static struct platform_driver rradc_driver = {
+ .driver = {
+ .name = "qcom-rradc",
+ .of_match_table = rradc_match_table,
+ },
+ .probe = rradc_probe,
+};
+module_platform_driver(rradc_driver);
+
+MODULE_DESCRIPTION("QPNP PMIC RR ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/qcom-tadc.c b/drivers/iio/adc/qcom-tadc.c
new file mode 100644
index 000000000000..05b1985ba378
--- /dev/null
+++ b/drivers/iio/adc/qcom-tadc.c
@@ -0,0 +1,1324 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "TADC: %s: " fmt, __func__
+
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/power_supply.h>
+#include <linux/pmic-voter.h>
+
+#define USB_PRESENT_VOTER "USB_PRESENT_VOTER"
+#define SLEEP_VOTER "SLEEP_VOTER"
+#define SHUTDOWN_VOTER "SHUTDOWN_VOTER"
+#define TADC_REVISION1_REG 0x00
+#define TADC_REVISION2_REG 0x01
+#define TADC_REVISION3_REG 0x02
+#define TADC_REVISION4_REG 0x03
+#define TADC_PERPH_TYPE_REG 0x04
+#define TADC_PERPH_SUBTYPE_REG 0x05
+
+/* TADC register definitions */
+#define TADC_SW_CH_CONV_REG(chip) (chip->tadc_base + 0x06)
+#define TADC_MBG_ERR_REG(chip) (chip->tadc_base + 0x07)
+#define TADC_EN_CTL_REG(chip) (chip->tadc_base + 0x46)
+#define TADC_CONV_REQ_REG(chip) (chip->tadc_base + 0x51)
+#define TADC_HWTRIG_CONV_CH_EN_REG(chip) (chip->tadc_base + 0x52)
+#define TADC_HW_SETTLE_DELAY_REG(chip) (chip->tadc_base + 0x53)
+#define TADC_LONG_HW_SETTLE_DLY_EN_REG(chip) (chip->tadc_base + 0x54)
+#define TADC_LONG_HW_SETTLE_DLY_REG(chip) (chip->tadc_base + 0x55)
+#define TADC_ADC_BUF_CH_REG(chip) (chip->tadc_base + 0x56)
+#define TADC_ADC_AAF_CH_REG(chip) (chip->tadc_base + 0x57)
+#define TADC_ADC_DATA_RDBK_REG(chip) (chip->tadc_base + 0x58)
+#define TADC_CH1_ADC_LO_REG(chip) (chip->tadc_base + 0x60)
+#define TADC_CH1_ADC_HI_REG(chip) (chip->tadc_base + 0x61)
+#define TADC_CH2_ADC_LO_REG(chip) (chip->tadc_base + 0x62)
+#define TADC_CH2_ADC_HI_REG(chip) (chip->tadc_base + 0x63)
+#define TADC_CH3_ADC_LO_REG(chip) (chip->tadc_base + 0x64)
+#define TADC_CH3_ADC_HI_REG(chip) (chip->tadc_base + 0x65)
+#define TADC_CH4_ADC_LO_REG(chip) (chip->tadc_base + 0x66)
+#define TADC_CH4_ADC_HI_REG(chip) (chip->tadc_base + 0x67)
+#define TADC_CH5_ADC_LO_REG(chip) (chip->tadc_base + 0x68)
+#define TADC_CH5_ADC_HI_REG(chip) (chip->tadc_base + 0x69)
+#define TADC_CH6_ADC_LO_REG(chip) (chip->tadc_base + 0x70)
+#define TADC_CH6_ADC_HI_REG(chip) (chip->tadc_base + 0x71)
+#define TADC_CH7_ADC_LO_REG(chip) (chip->tadc_base + 0x72)
+#define TADC_CH7_ADC_HI_REG(chip) (chip->tadc_base + 0x73)
+#define TADC_CH8_ADC_LO_REG(chip) (chip->tadc_base + 0x74)
+#define TADC_CH8_ADC_HI_REG(chip) (chip->tadc_base + 0x75)
+#define TADC_ADC_DIRECT_TST(chip) (chip->tadc_base + 0xE7)
+
+/* TADC_CMP register definitions */
+#define TADC_CMP_THR1_CMP_REG(chip) (chip->tadc_cmp_base + 0x51)
+#define TADC_CMP_THR1_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x52)
+#define TADC_CMP_THR1_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x53)
+#define TADC_CMP_THR1_CH2_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x54)
+#define TADC_CMP_THR1_CH2_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x55)
+#define TADC_CMP_THR1_CH3_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x56)
+#define TADC_CMP_THR1_CH3_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x57)
+#define TADC_CMP_THR2_CMP_REG(chip) (chip->tadc_cmp_base + 0x67)
+#define TADC_CMP_THR2_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x68)
+#define TADC_CMP_THR2_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x69)
+#define TADC_CMP_THR2_CH2_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x6A)
+#define TADC_CMP_THR2_CH2_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x6B)
+#define TADC_CMP_THR2_CH3_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x6C)
+#define TADC_CMP_THR2_CH3_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x6D)
+#define TADC_CMP_THR3_CMP_REG(chip) (chip->tadc_cmp_base + 0x7D)
+#define TADC_CMP_THR3_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x7E)
+#define TADC_CMP_THR3_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x7F)
+#define TADC_CMP_THR3_CH2_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x80)
+#define TADC_CMP_THR3_CH2_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x81)
+#define TADC_CMP_THR3_CH3_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x82)
+#define TADC_CMP_THR3_CH3_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x83)
+#define TADC_CMP_THR4_CMP_REG(chip) (chip->tadc_cmp_base + 0x93)
+#define TADC_CMP_THR4_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x94)
+#define TADC_CMP_THR4_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x95)
+#define TADC_CMP_THR1_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB0)
+#define TADC_CMP_THR2_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB1)
+#define TADC_CMP_THR3_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB2)
+#define TADC_CMP_THR4_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB3)
+
+/* 10 bits of resolution */
+#define TADC_RESOLUTION 1024
+/* number of hardware channels */
+#define TADC_NUM_CH 8
+
+enum tadc_chan_id {
+ TADC_THERM1 = 0,
+ TADC_THERM2,
+ TADC_DIE_TEMP,
+ TADC_BATT_I,
+ TADC_BATT_V,
+ TADC_INPUT_I,
+ TADC_INPUT_V,
+ TADC_OTG_I,
+ /* virtual channels */
+ TADC_BATT_P,
+ TADC_INPUT_P,
+ TADC_THERM1_THR1,
+ TADC_THERM1_THR2,
+ TADC_THERM1_THR3,
+ TADC_THERM1_THR4,
+ TADC_THERM2_THR1,
+ TADC_THERM2_THR2,
+ TADC_THERM2_THR3,
+ TADC_DIE_TEMP_THR1,
+ TADC_DIE_TEMP_THR2,
+ TADC_DIE_TEMP_THR3,
+ TADC_CHAN_ID_MAX,
+};
+
+#define TADC_CHAN(_name, _type, _channel, _info_mask) \
+{ \
+ .type = _type, \
+ .channel = _channel, \
+ .info_mask_separate = _info_mask, \
+ .extend_name = _name, \
+}
+
+#define TADC_THERM_CHAN(_name, _channel) \
+TADC_CHAN(_name, IIO_TEMP, _channel, \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_PROCESSED))
+
+#define TADC_TEMP_CHAN(_name, _channel) \
+TADC_CHAN(_name, IIO_TEMP, _channel, \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_PROCESSED) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET))
+
+#define TADC_CURRENT_CHAN(_name, _channel) \
+TADC_CHAN(_name, IIO_CURRENT, _channel, \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_PROCESSED) | \
+ BIT(IIO_CHAN_INFO_SCALE))
+
+
+#define TADC_VOLTAGE_CHAN(_name, _channel) \
+TADC_CHAN(_name, IIO_VOLTAGE, _channel, \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_PROCESSED) | \
+ BIT(IIO_CHAN_INFO_SCALE))
+
+#define TADC_POWER_CHAN(_name, _channel) \
+TADC_CHAN(_name, IIO_POWER, _channel, \
+ BIT(IIO_CHAN_INFO_PROCESSED))
+
+static const struct iio_chan_spec tadc_iio_chans[] = {
+ [TADC_THERM1] = TADC_THERM_CHAN(
+ "batt", TADC_THERM1),
+ [TADC_THERM2] = TADC_THERM_CHAN(
+ "skin", TADC_THERM2),
+ [TADC_DIE_TEMP] = TADC_TEMP_CHAN(
+ "die", TADC_DIE_TEMP),
+ [TADC_BATT_I] = TADC_CURRENT_CHAN(
+ "batt", TADC_BATT_I),
+ [TADC_BATT_V] = TADC_VOLTAGE_CHAN(
+ "batt", TADC_BATT_V),
+ [TADC_INPUT_I] = TADC_CURRENT_CHAN(
+ "input", TADC_INPUT_I),
+ [TADC_INPUT_V] = TADC_VOLTAGE_CHAN(
+ "input", TADC_INPUT_V),
+ [TADC_OTG_I] = TADC_CURRENT_CHAN(
+ "otg", TADC_OTG_I),
+ [TADC_BATT_P] = TADC_POWER_CHAN(
+ "batt", TADC_BATT_P),
+ [TADC_INPUT_P] = TADC_POWER_CHAN(
+ "input", TADC_INPUT_P),
+ [TADC_THERM1_THR1] = TADC_THERM_CHAN(
+ "batt_warm", TADC_THERM1_THR1),
+ [TADC_THERM1_THR2] = TADC_THERM_CHAN(
+ "batt_cool", TADC_THERM1_THR2),
+ [TADC_THERM1_THR3] = TADC_THERM_CHAN(
+ "batt_cold", TADC_THERM1_THR3),
+ [TADC_THERM1_THR4] = TADC_THERM_CHAN(
+ "batt_hot", TADC_THERM1_THR4),
+ [TADC_THERM2_THR1] = TADC_THERM_CHAN(
+ "skin_lb", TADC_THERM2_THR1),
+ [TADC_THERM2_THR2] = TADC_THERM_CHAN(
+ "skin_ub", TADC_THERM2_THR2),
+ [TADC_THERM2_THR3] = TADC_THERM_CHAN(
+ "skin_rst", TADC_THERM2_THR3),
+ [TADC_DIE_TEMP_THR1] = TADC_THERM_CHAN(
+ "die_lb", TADC_DIE_TEMP_THR1),
+ [TADC_DIE_TEMP_THR2] = TADC_THERM_CHAN(
+ "die_ub", TADC_DIE_TEMP_THR2),
+ [TADC_DIE_TEMP_THR3] = TADC_THERM_CHAN(
+ "die_rst", TADC_DIE_TEMP_THR3),
+};
+
+struct tadc_therm_thr {
+ int addr_lo;
+ int addr_hi;
+};
+
+struct tadc_chan_data {
+ s32 scale;
+ s32 offset;
+ u32 rbias;
+ const struct tadc_pt *table;
+ size_t tablesize;
+ struct tadc_therm_thr thr[4];
+};
+
+struct tadc_chip {
+ struct device *dev;
+ struct regmap *regmap;
+ u32 tadc_base;
+ u32 tadc_cmp_base;
+ struct tadc_chan_data chans[TADC_NUM_CH];
+ struct completion eoc_complete;
+ struct mutex write_lock;
+ struct mutex conv_lock;
+ struct power_supply *usb_psy;
+ struct votable *tadc_disable_votable;
+ struct work_struct status_change_work;
+ struct notifier_block nb;
+ u8 hwtrig_conv;
+};
+
+struct tadc_pt {
+ s32 x;
+ s32 y;
+};
+
+/*
+ * Thermistor tables are generated by the B-parameter equation which is a
+ * simplifed version of the Steinhart-Hart equation.
+ *
+ * (1 / T) = (1 / T0) + (1 / B) * ln(R / R0)
+ *
+ * Where R0 is the resistance at temperature T0, and T0 is typically room
+ * temperature (25C).
+ */
+static const struct tadc_pt tadc_therm_3450b_68k[] = {
+ { 4151, 120000 },
+ { 4648, 115000 },
+ { 5220, 110000 },
+ { 5880, 105000 },
+ { 6644, 100000 },
+ { 7533, 95000 },
+ { 8571, 90000 },
+ { 9786, 85000 },
+ { 11216, 80000 },
+ { 12906, 75000 },
+ { 14910, 70000 },
+ { 17300, 65000 },
+ { 20163, 60000 },
+ { 23609, 55000 },
+ { 27780, 50000 },
+ { 32855, 45000 },
+ { 39065, 40000 },
+ { 46712, 35000 },
+ { 56185, 30000 },
+ { 68000, 25000 },
+ { 82837, 20000 },
+ { 101604, 15000 },
+ { 125525, 10000 },
+ { 156261, 5000 },
+ { 196090, 0 },
+ { 248163, -5000 },
+ { 316887, -10000 },
+ { 408493, -15000 },
+ { 531889, -20000 },
+ { 699966, -25000 },
+ { 931618, -30000 },
+ { 1254910, -35000 },
+ { 1712127, -40000 },
+};
+
+static bool tadc_is_reg_locked(struct tadc_chip *chip, u16 reg)
+{
+ if ((reg & 0xFF00) == chip->tadc_cmp_base)
+ return true;
+
+ if (reg >= TADC_HWTRIG_CONV_CH_EN_REG(chip))
+ return true;
+
+ return false;
+}
+
+static int tadc_read(struct tadc_chip *chip, u16 reg, u8 *val, size_t count)
+{
+ int rc = 0;
+
+ rc = regmap_bulk_read(chip->regmap, reg, val, count);
+ if (rc < 0)
+ pr_err("Couldn't read 0x%04x rc=%d\n", reg, rc);
+
+ return rc;
+}
+
+static int tadc_write(struct tadc_chip *chip, u16 reg, u8 data)
+{
+ int rc = 0;
+
+ mutex_lock(&chip->write_lock);
+ if (tadc_is_reg_locked(chip, reg)) {
+ rc = regmap_write(chip->regmap, (reg & 0xFF00) | 0xD0, 0xA5);
+ if (rc < 0) {
+ pr_err("Couldn't unlock secure register rc=%d\n", rc);
+ goto unlock;
+ }
+ }
+
+ rc = regmap_write(chip->regmap, reg, data);
+ if (rc < 0) {
+ pr_err("Couldn't write 0x%02x to 0x%04x rc=%d\n",
+ data, reg, rc);
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&chip->write_lock);
+ return rc;
+}
+static int tadc_bulk_write(struct tadc_chip *chip, u16 reg, u8 *data,
+ size_t count)
+{
+ int rc = 0, i;
+
+ mutex_lock(&chip->write_lock);
+ for (i = 0; i < count; ++i, ++reg) {
+ if (tadc_is_reg_locked(chip, reg)) {
+ rc = regmap_write(chip->regmap,
+ (reg & 0xFF00) | 0xD0, 0xA5);
+ if (rc < 0) {
+ pr_err("Couldn't unlock secure register rc=%d\n",
+ rc);
+ goto unlock;
+ }
+ }
+
+ rc = regmap_write(chip->regmap, reg, data[i]);
+ if (rc < 0) {
+ pr_err("Couldn't write 0x%02x to 0x%04x rc=%d\n",
+ data[i], reg, rc);
+ goto unlock;
+ }
+ }
+
+unlock:
+ mutex_unlock(&chip->write_lock);
+ return rc;
+}
+
+static int tadc_masked_write(struct tadc_chip *chip, u16 reg, u8 mask, u8 data)
+{
+ int rc = 0;
+
+ mutex_lock(&chip->write_lock);
+ if (tadc_is_reg_locked(chip, reg)) {
+ rc = regmap_write(chip->regmap, (reg & 0xFF00) | 0xD0, 0xA5);
+ if (rc < 0) {
+ pr_err("Couldn't unlock secure register rc=%d\n", rc);
+ goto unlock;
+ }
+ }
+
+ rc = regmap_update_bits(chip->regmap, reg, mask, data);
+
+unlock:
+ mutex_unlock(&chip->write_lock);
+ return rc;
+}
+
+static int tadc_lerp(const struct tadc_pt *pts, size_t size, bool inv,
+ s32 input, s32 *output)
+{
+ int i;
+ s64 temp;
+ bool ascending;
+
+ if (pts == NULL) {
+ pr_err("Table is NULL\n");
+ return -EINVAL;
+ }
+
+ if (size < 1) {
+ pr_err("Table has no entries\n");
+ return -ENOENT;
+ }
+
+ if (size == 1) {
+ *output = inv ? pts[0].x : pts[0].y;
+ return 0;
+ }
+
+ ascending = inv ? (pts[0].y < pts[1].y) : (pts[0].x < pts[1].x);
+ if (ascending ? (input <= (inv ? pts[0].y : pts[0].x)) :
+ (input >= (inv ? pts[0].y : pts[0].x))) {
+ *output = inv ? pts[0].x : pts[0].y;
+ return 0;
+ }
+
+ if (ascending ? (input >= (inv ? pts[size - 1].y : pts[size - 1].x)) :
+ (input <= (inv ? pts[size - 1].y : pts[size - 1].x))) {
+ *output = inv ? pts[size - 1].x : pts[size - 1].y;
+ return 0;
+ }
+
+ for (i = 1; i < size; i++)
+ if (ascending ? (input <= (inv ? pts[i].y : pts[i].x)) :
+ (input >= (inv ? pts[i].y : pts[i].x)))
+ break;
+
+ if (inv) {
+ temp = (s64)(pts[i].x - pts[i - 1].x) *
+ (s64)(input - pts[i - 1].y);
+ temp = div_s64(temp, pts[i].y - pts[i - 1].y);
+ *output = temp + pts[i - 1].x;
+ } else {
+ temp = (s64)(pts[i].y - pts[i - 1].y) *
+ (s64)(input - pts[i - 1].x);
+ temp = div_s64(temp, pts[i].x - pts[i - 1].x);
+ *output = temp + pts[i - 1].y;
+ }
+
+ return 0;
+}
+
+/*
+ * Process the result of a thermistor reading.
+ *
+ * The voltage input to the ADC is a result of a voltage divider circuit.
+ * Vout = (Rtherm / (Rbias + Rtherm)) * Vbias
+ *
+ * The ADC value is based on the output voltage of the voltage divider, and the
+ * bias voltage.
+ * ADC = (Vin * 1024) / Vbias
+ *
+ * Combine these equations and solve for Rtherm
+ * Rtherm = (ADC * Rbias) / (1024 - ADC)
+ */
+static int tadc_get_processed_therm(const struct tadc_chan_data *chan_data,
+ s16 adc, s32 *result)
+{
+ s32 rtherm;
+
+ rtherm = div_s64((s64)adc * chan_data->rbias, TADC_RESOLUTION - adc);
+ return tadc_lerp(chan_data->table, chan_data->tablesize, false, rtherm,
+ result);
+}
+
+static int tadc_get_raw_therm(const struct tadc_chan_data *chan_data,
+ int mdegc, int *result)
+{
+ int rc;
+ s32 rtherm;
+
+ rc = tadc_lerp(chan_data->table, chan_data->tablesize, true, mdegc,
+ &rtherm);
+ if (rc < 0) {
+ pr_err("Couldn't interpolate %d\n rc=%d", mdegc, rc);
+ return rc;
+ }
+
+ *result = div64_s64((s64)rtherm * TADC_RESOLUTION,
+ (s64)chan_data->rbias + rtherm);
+ return 0;
+}
+
+static int tadc_read_channel(struct tadc_chip *chip, u16 address, int *adc)
+{
+ u8 val[2];
+ int rc;
+
+ rc = tadc_read(chip, address, val, ARRAY_SIZE(val));
+ if (rc < 0) {
+ pr_err("Couldn't read channel rc=%d\n", rc);
+ return rc;
+ }
+
+ /* the 10th bit is the sign bit for all channels */
+ *adc = sign_extend32(val[0] | val[1] << BITS_PER_BYTE, 10);
+ return rc;
+}
+
+static int tadc_write_channel(struct tadc_chip *chip, u16 address, int adc)
+{
+ u8 val[2];
+ int rc;
+
+ /* the 10th bit is the sign bit for all channels */
+ adc = sign_extend32(adc, 10);
+ val[0] = (u8)adc;
+ val[1] = (u8)(adc >> BITS_PER_BYTE);
+ rc = tadc_bulk_write(chip, address, val, 2);
+ if (rc < 0) {
+ pr_err("Couldn't write to channel rc=%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+#define CONVERSION_TIMEOUT_MS 100
+static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc)
+{
+ unsigned long timeout, timeleft;
+ u8 val[TADC_NUM_CH * 2];
+ int rc = 0, i;
+
+ mutex_lock(&chip->conv_lock);
+ rc = tadc_read(chip, TADC_MBG_ERR_REG(chip), val, 1);
+ if (rc < 0) {
+ pr_err("Couldn't read mbg error status rc=%d\n", rc);
+ goto unlock;
+ }
+
+ reinit_completion(&chip->eoc_complete);
+
+ if (get_effective_result(chip->tadc_disable_votable)) {
+ /* leave it back in completed state */
+ complete_all(&chip->eoc_complete);
+ rc = -ENODATA;
+ goto unlock;
+ }
+
+ if (val[0] != 0) {
+ tadc_write(chip, TADC_EN_CTL_REG(chip), 0);
+ tadc_write(chip, TADC_EN_CTL_REG(chip), 0x80);
+ }
+
+ rc = tadc_write(chip, TADC_CONV_REQ_REG(chip), channels);
+ if (rc < 0) {
+ pr_err("Couldn't write conversion request rc=%d\n", rc);
+ goto unlock;
+ }
+
+ timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS);
+ timeleft = wait_for_completion_timeout(&chip->eoc_complete, timeout);
+
+ if (timeleft == 0) {
+ rc = tadc_read(chip, TADC_SW_CH_CONV_REG(chip), val, 1);
+ if (rc < 0) {
+ pr_err("Couldn't read conversion status rc=%d\n", rc);
+ goto unlock;
+ }
+
+ /*
+ * check one last time if the channel we are requesting
+ * has completed conversion
+ */
+ if (val[0] != channels) {
+ rc = -ETIMEDOUT;
+ goto unlock;
+ }
+ }
+
+ rc = tadc_read(chip, TADC_CH1_ADC_LO_REG(chip), val, ARRAY_SIZE(val));
+ if (rc < 0) {
+ pr_err("Couldn't read adc channels rc=%d\n", rc);
+ goto unlock;
+ }
+
+ for (i = 0; i < TADC_NUM_CH; i++)
+ adc[i] = (s16)(val[i * 2] | (u16)val[i * 2 + 1] << 8);
+
+ pr_debug("Conversion time for channels 0x%x = %dms\n", channels,
+ jiffies_to_msecs(timeout - timeleft));
+
+unlock:
+ mutex_unlock(&chip->conv_lock);
+ return rc;
+}
+
+static int tadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct tadc_chip *chip = iio_priv(indio_dev);
+ struct tadc_chan_data *chan_data = NULL;
+ int rc, offset = 0, scale, scale2, scale_type;
+ s16 adc[TADC_NUM_CH];
+
+ switch (chan->channel) {
+ case TADC_THERM1_THR1:
+ case TADC_THERM1_THR2:
+ case TADC_THERM1_THR3:
+ case TADC_THERM1_THR4:
+ chan_data = &chip->chans[TADC_THERM1];
+ break;
+ case TADC_THERM2_THR1:
+ case TADC_THERM2_THR2:
+ case TADC_THERM2_THR3:
+ chan_data = &chip->chans[TADC_THERM2];
+ break;
+ case TADC_DIE_TEMP_THR1:
+ case TADC_DIE_TEMP_THR2:
+ case TADC_DIE_TEMP_THR3:
+ chan_data = &chip->chans[TADC_DIE_TEMP];
+ break;
+ default:
+ if (chan->channel >= ARRAY_SIZE(chip->chans)) {
+ pr_err("Channel %d is out of bounds\n", chan->channel);
+ return -EINVAL;
+ }
+
+ chan_data = &chip->chans[chan->channel];
+ break;
+ }
+
+ if (!chan_data)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->channel) {
+ case TADC_THERM1_THR1:
+ case TADC_THERM2_THR1:
+ case TADC_DIE_TEMP_THR1:
+ rc = tadc_read_channel(chip,
+ chan_data->thr[0].addr_lo, val);
+ break;
+ case TADC_THERM1_THR2:
+ case TADC_THERM2_THR2:
+ case TADC_DIE_TEMP_THR2:
+ rc = tadc_read_channel(chip,
+ chan_data->thr[1].addr_lo, val);
+ break;
+ case TADC_THERM1_THR3:
+ case TADC_THERM2_THR3:
+ case TADC_DIE_TEMP_THR3:
+ rc = tadc_read_channel(chip,
+ chan_data->thr[2].addr_lo, val);
+ break;
+ case TADC_THERM1_THR4:
+ rc = tadc_read_channel(chip,
+ chan_data->thr[3].addr_lo, val);
+ break;
+ default:
+ rc = tadc_do_conversion(chip, BIT(chan->channel), adc);
+ if (rc < 0) {
+ if (rc != -ENODATA)
+ pr_err("Couldn't read battery current and voltage channels rc=%d\n",
+ rc);
+ return rc;
+ }
+ *val = adc[chan->channel];
+ break;
+ }
+
+ if (rc < 0 && rc != -ENODATA) {
+ pr_err("Couldn't read channel %d\n", chan->channel);
+ return rc;
+ }
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->channel) {
+ case TADC_THERM1:
+ case TADC_THERM2:
+ case TADC_THERM1_THR1:
+ case TADC_THERM1_THR2:
+ case TADC_THERM1_THR3:
+ case TADC_THERM1_THR4:
+ case TADC_THERM2_THR1:
+ case TADC_THERM2_THR2:
+ case TADC_THERM2_THR3:
+ rc = tadc_read_raw(indio_dev, chan, val, NULL,
+ IIO_CHAN_INFO_RAW);
+ if (rc < 0)
+ return rc;
+
+ rc = tadc_get_processed_therm(chan_data, *val, val);
+ if (rc < 0) {
+ pr_err("Couldn't process 0x%04x from channel %d rc=%d\n",
+ *val, chan->channel, rc);
+ return rc;
+ }
+ break;
+ case TADC_BATT_P:
+ rc = tadc_do_conversion(chip,
+ BIT(TADC_BATT_I) | BIT(TADC_BATT_V), adc);
+ if (rc < 0 && rc != -ENODATA) {
+ pr_err("Couldn't read battery current and voltage channels rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ *val = adc[TADC_BATT_I] * adc[TADC_BATT_V];
+ break;
+ case TADC_INPUT_P:
+ rc = tadc_do_conversion(chip,
+ BIT(TADC_INPUT_I) | BIT(TADC_INPUT_V), adc);
+ if (rc < 0 && rc != -ENODATA) {
+ pr_err("Couldn't read input current and voltage channels rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ *val = adc[TADC_INPUT_I] * adc[TADC_INPUT_V];
+ break;
+ default:
+ rc = tadc_read_raw(indio_dev, chan, val, NULL,
+ IIO_CHAN_INFO_RAW);
+ if (rc < 0)
+ return rc;
+
+ /* offset is optional */
+ rc = tadc_read_raw(indio_dev, chan, &offset, NULL,
+ IIO_CHAN_INFO_OFFSET);
+ if (rc < 0)
+ return rc;
+
+ scale_type = tadc_read_raw(indio_dev, chan,
+ &scale, &scale2, IIO_CHAN_INFO_SCALE);
+ switch (scale_type) {
+ case IIO_VAL_INT:
+ *val = *val * scale + offset;
+ break;
+ case IIO_VAL_FRACTIONAL:
+ *val = div_s64((s64)*val * scale + offset,
+ scale2);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ }
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->channel) {
+ case TADC_DIE_TEMP:
+ case TADC_DIE_TEMP_THR1:
+ case TADC_DIE_TEMP_THR2:
+ case TADC_DIE_TEMP_THR3:
+ *val = chan_data->scale;
+ return IIO_VAL_INT;
+ case TADC_BATT_I:
+ case TADC_BATT_V:
+ case TADC_INPUT_I:
+ case TADC_INPUT_V:
+ case TADC_OTG_I:
+ *val = chan_data->scale;
+ *val2 = TADC_RESOLUTION;
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ return -EINVAL;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = chan_data->offset;
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int tadc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2,
+ long mask)
+{
+ struct tadc_chip *chip = iio_priv(indio_dev);
+ const struct tadc_chan_data *chan_data;
+ int rc, raw;
+ s32 rem;
+
+ switch (chan->channel) {
+ case TADC_THERM1_THR1:
+ case TADC_THERM1_THR2:
+ case TADC_THERM1_THR3:
+ case TADC_THERM1_THR4:
+ chan_data = &chip->chans[TADC_THERM1];
+ break;
+ case TADC_THERM2_THR1:
+ case TADC_THERM2_THR2:
+ case TADC_THERM2_THR3:
+ chan_data = &chip->chans[TADC_THERM2];
+ break;
+ case TADC_DIE_TEMP_THR1:
+ case TADC_DIE_TEMP_THR2:
+ case TADC_DIE_TEMP_THR3:
+ chan_data = &chip->chans[TADC_DIE_TEMP];
+ break;
+ default:
+ if (chan->channel >= ARRAY_SIZE(chip->chans)) {
+ pr_err("Channel %d is out of bounds\n", chan->channel);
+ return -EINVAL;
+ }
+
+ chan_data = &chip->chans[chan->channel];
+ break;
+ }
+
+ if (!chan_data)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->channel) {
+ case TADC_THERM1_THR1:
+ case TADC_THERM1_THR2:
+ case TADC_THERM1_THR3:
+ case TADC_THERM1_THR4:
+ case TADC_THERM2_THR1:
+ case TADC_THERM2_THR2:
+ case TADC_THERM2_THR3:
+ rc = tadc_get_raw_therm(chan_data, val, &raw);
+ if (rc < 0) {
+ pr_err("Couldn't get raw value rc=%d\n", rc);
+ return rc;
+ }
+ break;
+ case TADC_DIE_TEMP_THR1:
+ case TADC_DIE_TEMP_THR2:
+ case TADC_DIE_TEMP_THR3:
+ /* DIV_ROUND_CLOSEST does not like negative numbers */
+ raw = div_s64_rem(val - chan_data->offset,
+ chan_data->scale, &rem);
+ if (abs(rem) >= abs(chan_data->scale / 2))
+ raw++;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rc = tadc_write_raw(indio_dev, chan, raw, 0,
+ IIO_CHAN_INFO_RAW);
+ if (rc < 0) {
+ pr_err("Couldn't write raw rc=%d\n", rc);
+ return rc;
+ }
+
+ break;
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->channel) {
+ case TADC_THERM1_THR1:
+ case TADC_THERM2_THR1:
+ case TADC_DIE_TEMP_THR1:
+ rc = tadc_write_channel(chip,
+ chan_data->thr[0].addr_lo, val);
+ break;
+ case TADC_THERM1_THR2:
+ case TADC_THERM2_THR2:
+ case TADC_DIE_TEMP_THR2:
+ rc = tadc_write_channel(chip,
+ chan_data->thr[1].addr_lo, val);
+ break;
+ case TADC_THERM1_THR3:
+ case TADC_THERM2_THR3:
+ case TADC_DIE_TEMP_THR3:
+ rc = tadc_write_channel(chip,
+ chan_data->thr[2].addr_lo, val);
+ break;
+ case TADC_THERM1_THR4:
+ rc = tadc_write_channel(chip,
+ chan_data->thr[3].addr_lo, val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rc < 0) {
+ pr_err("Couldn't write channel %d\n", chan->channel);
+ return rc;
+ }
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static irqreturn_t handle_eoc(int irq, void *dev_id)
+{
+ struct tadc_chip *chip = dev_id;
+
+ complete_all(&chip->eoc_complete);
+ return IRQ_HANDLED;
+}
+
+static int tadc_disable_vote_callback(struct votable *votable,
+ void *data, int disable, const char *client)
+{
+ struct tadc_chip *chip = data;
+ int rc;
+ int timeout;
+ unsigned long timeleft;
+
+ if (disable) {
+ timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS);
+ timeleft = wait_for_completion_timeout(&chip->eoc_complete,
+ timeout);
+ if (timeleft == 0)
+ pr_err("Timed out waiting for eoc, disabling hw conversions regardless\n");
+
+ rc = tadc_read(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip),
+ &chip->hwtrig_conv, 1);
+ if (rc < 0) {
+ pr_err("Couldn't save hw conversions rc=%d\n", rc);
+ return rc;
+ }
+ rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip), 0x00);
+ if (rc < 0) {
+ pr_err("Couldn't disable hw conversions rc=%d\n", rc);
+ return rc;
+ }
+ rc = tadc_write(chip, TADC_ADC_DIRECT_TST(chip), 0x80);
+ if (rc < 0) {
+ pr_err("Couldn't enable direct test mode rc=%d\n", rc);
+ return rc;
+ }
+ } else {
+ rc = tadc_write(chip, TADC_ADC_DIRECT_TST(chip), 0x00);
+ if (rc < 0) {
+ pr_err("Couldn't disable direct test mode rc=%d\n", rc);
+ return rc;
+ }
+ rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip),
+ chip->hwtrig_conv);
+ if (rc < 0) {
+ pr_err("Couldn't restore hw conversions rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ pr_debug("client: %s disable: %d\n", client, disable);
+ return 0;
+}
+
+static void status_change_work(struct work_struct *work)
+{
+ struct tadc_chip *chip = container_of(work,
+ struct tadc_chip, status_change_work);
+ union power_supply_propval pval = {0, };
+ int rc;
+
+ if (!chip->usb_psy)
+ chip->usb_psy = power_supply_get_by_name("usb");
+
+ if (!chip->usb_psy) {
+ /* treat usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0);
+ return;
+ }
+
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't get present status rc=%d\n", rc);
+ /* treat usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0);
+ return;
+ }
+
+ /* disable if usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, !pval.intval, 0);
+}
+
+static int tadc_notifier_call(struct notifier_block *nb,
+ unsigned long ev, void *v)
+{
+ struct power_supply *psy = v;
+ struct tadc_chip *chip = container_of(nb, struct tadc_chip, nb);
+
+ if (ev != PSY_EVENT_PROP_CHANGED)
+ return NOTIFY_OK;
+
+ if ((strcmp(psy->desc->name, "usb") == 0))
+ schedule_work(&chip->status_change_work);
+
+ return NOTIFY_OK;
+}
+
+static int tadc_register_notifier(struct tadc_chip *chip)
+{
+ int rc;
+
+ chip->nb.notifier_call = tadc_notifier_call;
+ rc = power_supply_reg_notifier(&chip->nb);
+ if (rc < 0) {
+ pr_err("Couldn't register psy notifier rc = %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int tadc_suspend(struct device *dev)
+{
+ struct tadc_chip *chip = dev_get_drvdata(dev);
+
+ vote(chip->tadc_disable_votable, SLEEP_VOTER, true, 0);
+ return 0;
+}
+
+static int tadc_resume(struct device *dev)
+{
+ struct tadc_chip *chip = dev_get_drvdata(dev);
+
+ vote(chip->tadc_disable_votable, SLEEP_VOTER, false, 0);
+ return 0;
+}
+
+static int tadc_set_therm_table(struct tadc_chan_data *chan_data, u32 beta,
+ u32 rtherm)
+{
+ if (beta == 3450 && rtherm == 68000) {
+ chan_data->table = tadc_therm_3450b_68k;
+ chan_data->tablesize = ARRAY_SIZE(tadc_therm_3450b_68k);
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static int tadc_parse_dt(struct tadc_chip *chip)
+{
+ struct device_node *child, *node;
+ struct tadc_chan_data *chan_data;
+ u32 chan_id, rtherm, beta;
+ int rc = 0;
+
+ node = chip->dev->of_node;
+ for_each_available_child_of_node(node, child) {
+ rc = of_property_read_u32(child, "reg", &chan_id);
+ if (rc < 0) {
+ pr_err("Couldn't find channel for %s rc=%d",
+ child->name, rc);
+ return rc;
+ }
+
+ if (chan_id > TADC_NUM_CH - 1) {
+ pr_err("Channel %d is out of range [0, %d]\n",
+ chan_id, TADC_NUM_CH - 1);
+ return -EINVAL;
+ }
+
+ chan_data = &chip->chans[chan_id];
+ if (chan_id == TADC_THERM1 || chan_id == TADC_THERM2) {
+ rc = of_property_read_u32(child,
+ "qcom,rbias", &chan_data->rbias);
+ if (rc < 0) {
+ pr_err("Couldn't read qcom,rbias rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(child,
+ "qcom,beta-coefficient", &beta);
+ if (rc < 0) {
+ pr_err("Couldn't read qcom,beta-coefficient rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(child,
+ "qcom,rtherm-at-25degc", &rtherm);
+ if (rc < 0) {
+ pr_err("Couldn't read qcom,rtherm-at-25degc rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = tadc_set_therm_table(chan_data, beta, rtherm);
+ if (rc < 0) {
+ pr_err("Couldn't set therm table rc=%d\n", rc);
+ return rc;
+ }
+ } else {
+ rc = of_property_read_s32(child, "qcom,scale",
+ &chan_data->scale);
+ if (rc < 0) {
+ pr_err("Couldn't read scale rc=%d\n", rc);
+ return rc;
+ }
+
+ of_property_read_s32(child, "qcom,offset",
+ &chan_data->offset);
+ }
+ }
+
+ return rc;
+}
+
+static int tadc_init_hw(struct tadc_chip *chip)
+{
+ int rc;
+
+ chip->chans[TADC_THERM1].thr[0].addr_lo =
+ TADC_CMP_THR1_CH1_CMP_LO_REG(chip);
+ chip->chans[TADC_THERM1].thr[0].addr_hi =
+ TADC_CMP_THR1_CH1_CMP_HI_REG(chip);
+ chip->chans[TADC_THERM1].thr[1].addr_lo =
+ TADC_CMP_THR2_CH1_CMP_LO_REG(chip);
+ chip->chans[TADC_THERM1].thr[1].addr_hi =
+ TADC_CMP_THR2_CH1_CMP_HI_REG(chip);
+ chip->chans[TADC_THERM1].thr[2].addr_lo =
+ TADC_CMP_THR3_CH1_CMP_LO_REG(chip);
+ chip->chans[TADC_THERM1].thr[2].addr_hi =
+ TADC_CMP_THR3_CH1_CMP_HI_REG(chip);
+ chip->chans[TADC_THERM1].thr[3].addr_lo =
+ TADC_CMP_THR4_CH1_CMP_LO_REG(chip);
+ chip->chans[TADC_THERM1].thr[3].addr_hi =
+ TADC_CMP_THR4_CH1_CMP_HI_REG(chip);
+
+ chip->chans[TADC_THERM2].thr[0].addr_lo =
+ TADC_CMP_THR1_CH2_CMP_LO_REG(chip);
+ chip->chans[TADC_THERM2].thr[0].addr_hi =
+ TADC_CMP_THR1_CH2_CMP_HI_REG(chip);
+ chip->chans[TADC_THERM2].thr[1].addr_lo =
+ TADC_CMP_THR2_CH2_CMP_LO_REG(chip);
+ chip->chans[TADC_THERM2].thr[1].addr_hi =
+ TADC_CMP_THR2_CH2_CMP_HI_REG(chip);
+ chip->chans[TADC_THERM2].thr[2].addr_lo =
+ TADC_CMP_THR3_CH2_CMP_LO_REG(chip);
+ chip->chans[TADC_THERM2].thr[2].addr_hi =
+ TADC_CMP_THR3_CH2_CMP_HI_REG(chip);
+
+ chip->chans[TADC_DIE_TEMP].thr[0].addr_lo =
+ TADC_CMP_THR1_CH3_CMP_LO_REG(chip);
+ chip->chans[TADC_DIE_TEMP].thr[0].addr_hi =
+ TADC_CMP_THR1_CH3_CMP_HI_REG(chip);
+ chip->chans[TADC_DIE_TEMP].thr[1].addr_lo =
+ TADC_CMP_THR2_CH3_CMP_LO_REG(chip);
+ chip->chans[TADC_DIE_TEMP].thr[1].addr_hi =
+ TADC_CMP_THR2_CH3_CMP_HI_REG(chip);
+ chip->chans[TADC_DIE_TEMP].thr[2].addr_lo =
+ TADC_CMP_THR3_CH3_CMP_LO_REG(chip);
+ chip->chans[TADC_DIE_TEMP].thr[2].addr_hi =
+ TADC_CMP_THR3_CH3_CMP_HI_REG(chip);
+
+ rc = tadc_write(chip, TADC_CMP_THR1_CMP_REG(chip), 0);
+ if (rc < 0) {
+ pr_err("Couldn't enable hardware triggers rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = tadc_write(chip, TADC_CMP_THR2_CMP_REG(chip), 0);
+ if (rc < 0) {
+ pr_err("Couldn't enable hardware triggers rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = tadc_write(chip, TADC_CMP_THR3_CMP_REG(chip), 0);
+ if (rc < 0) {
+ pr_err("Couldn't enable hardware triggers rc=%d\n", rc);
+ return rc;
+ }
+
+ /* enable connector and die temp hardware triggers */
+ rc = tadc_masked_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip),
+ BIT(TADC_THERM2) | BIT(TADC_DIE_TEMP),
+ BIT(TADC_THERM2) | BIT(TADC_DIE_TEMP));
+ if (rc < 0) {
+ pr_err("Couldn't enable hardware triggers rc=%d\n", rc);
+ return rc;
+ }
+
+ /* save hw triggered conversion configuration */
+ rc = tadc_read(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip),
+ &chip->hwtrig_conv, 1);
+ if (rc < 0) {
+ pr_err("Couldn't save hw conversions rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static const struct iio_info tadc_info = {
+ .read_raw = &tadc_read_raw,
+ .write_raw = &tadc_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int tadc_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct iio_dev *indio_dev;
+ struct tadc_chip *chip;
+ int rc, irq;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ chip = iio_priv(indio_dev);
+ chip->dev = &pdev->dev;
+ init_completion(&chip->eoc_complete);
+
+ /*
+ * set the completion in "completed" state so disable of the tadc
+ * can progress
+ */
+ complete_all(&chip->eoc_complete);
+
+ rc = of_property_read_u32(node, "reg", &chip->tadc_base);
+ if (rc < 0) {
+ pr_err("Couldn't read base address rc=%d\n", rc);
+ return rc;
+ }
+ chip->tadc_cmp_base = chip->tadc_base + 0x100;
+
+ mutex_init(&chip->write_lock);
+ mutex_init(&chip->conv_lock);
+ INIT_WORK(&chip->status_change_work, status_change_work);
+ chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+ if (!chip->regmap) {
+ pr_err("Couldn't get regmap\n");
+ return -ENODEV;
+ }
+
+ rc = tadc_parse_dt(chip);
+ if (rc < 0) {
+ pr_err("Couldn't parse device tree rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = tadc_init_hw(chip);
+ if (rc < 0) {
+ pr_err("Couldn't initialize hardware rc=%d\n", rc);
+ return rc;
+ }
+
+ chip->tadc_disable_votable = create_votable("SMB_TADC_DISABLE",
+ VOTE_SET_ANY,
+ tadc_disable_vote_callback,
+ chip);
+ if (IS_ERR(chip->tadc_disable_votable)) {
+ rc = PTR_ERR(chip->tadc_disable_votable);
+ return rc;
+ }
+ /* assume usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0);
+ vote(chip->tadc_disable_votable, SHUTDOWN_VOTER, false, 0);
+ vote(chip->tadc_disable_votable, SLEEP_VOTER, false, 0);
+
+ rc = tadc_register_notifier(chip);
+ if (rc < 0) {
+ pr_err("Couldn't register notifier=%d\n", rc);
+ goto destroy_votable;
+ }
+
+ irq = of_irq_get_byname(node, "eoc");
+ if (irq < 0) {
+ pr_err("Couldn't get eoc irq rc=%d\n", irq);
+ goto destroy_votable;
+ }
+
+ rc = devm_request_threaded_irq(chip->dev, irq, NULL, handle_eoc,
+ IRQF_ONESHOT, "eoc", chip);
+ if (rc < 0) {
+ pr_err("Couldn't request irq %d rc=%d\n", irq, rc);
+ goto destroy_votable;
+ }
+
+ indio_dev->dev.parent = chip->dev;
+ indio_dev->name = pdev->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &tadc_info;
+ indio_dev->channels = tadc_iio_chans;
+ indio_dev->num_channels = ARRAY_SIZE(tadc_iio_chans);
+
+ rc = devm_iio_device_register(chip->dev, indio_dev);
+ if (rc < 0) {
+ pr_err("Couldn't register IIO device rc=%d\n", rc);
+ goto destroy_votable;
+ }
+
+ platform_set_drvdata(pdev, chip);
+ return 0;
+
+destroy_votable:
+ destroy_votable(chip->tadc_disable_votable);
+ return rc;
+}
+
+static int tadc_remove(struct platform_device *pdev)
+{
+ struct tadc_chip *chip = platform_get_drvdata(pdev);
+
+ destroy_votable(chip->tadc_disable_votable);
+ return 0;
+}
+
+static void tadc_shutdown(struct platform_device *pdev)
+{
+ struct tadc_chip *chip = platform_get_drvdata(pdev);
+
+ vote(chip->tadc_disable_votable, SHUTDOWN_VOTER, true, 0);
+}
+
+static const struct dev_pm_ops tadc_pm_ops = {
+ .resume = tadc_resume,
+ .suspend = tadc_suspend,
+};
+
+static const struct of_device_id tadc_match_table[] = {
+ { .compatible = "qcom,tadc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tadc_match_table);
+
+static struct platform_driver tadc_driver = {
+ .driver = {
+ .name = "qcom-tadc",
+ .of_match_table = tadc_match_table,
+ .pm = &tadc_pm_ops,
+ },
+ .probe = tadc_probe,
+ .remove = tadc_remove,
+ .shutdown = tadc_shutdown,
+};
+module_platform_driver(tadc_driver);
+
+MODULE_DESCRIPTION("Qualcomm Technologies Inc. TADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index 217e9306aa0f..407b2ef4d2e9 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -664,3 +664,21 @@ err_unlock:
return ret;
}
EXPORT_SYMBOL_GPL(iio_write_channel_raw);
+
+int iio_write_channel_processed(struct iio_channel *chan, int val)
+{
+ int ret;
+
+ mutex_lock(&chan->indio_dev->info_exist_lock);
+ if (chan->indio_dev->info == NULL) {
+ ret = -ENODEV;
+ goto err_unlock;
+ }
+
+ ret = iio_channel_write(chan, val, 0, IIO_CHAN_INFO_PROCESSED);
+err_unlock:
+ mutex_unlock(&chan->indio_dev->info_exist_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_write_channel_processed);