hwmon: qpnp-adc: Add PMIC 2.0 ADC bringup fixes
VADC/IADC peripheral is supported from PMIC 2.0 onwards.
Add version check to allow VADC/IADC reads if the
version is supported. Add probe defereal api for clients
who need to know if the ADC driver is ready. There is a
bug where the completion is not initialized. Fix it
by initializing it. Add support to disable the peripheral
after the ADC is read and enable it before starting a
conversion.
Add scaling functions to support reading the die temperature,
XO_THERM, batt_therm and batt_id. Add the ratiometric
calibration routine that uses the vref/gnd for calibration.
The ratiometric calibration is used for calibrating xo_therm,
batt_therm, batt_id.
Update the gain value used for calibration on the IADC
peripheral. PMIC 2.0 IADC peripheral uses 17.857mV instead
of 25mV for its gain calibration.
Signed-off-by: Siddartha Mohanadoss <smohanad@codeaurora.org>
(cherry picked from commit 5ace110e2165fba66227e18154d58b0e6cbb24b2)
Change-Id: I37b02de53ea2bed913c30261624c31a1ae57131c
Signed-off-by: Sudhir Sharma <sudsha@codeaurora.org>
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index c122270..f908181 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -26,6 +26,7 @@
#include <linux/spmi.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
+#include <linux/completion.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/platform_device.h>
@@ -33,6 +34,491 @@
#define QPNP_VADC_MIN_ADC_CODE 0x6000
/* Max ADC code represents full-scale range of 1.8V */
#define QPNP_VADC_MAX_ADC_CODE 0xA800
+#define KELVINMIL_DEGMIL 273160
+
+/* Units for temperature below (on x axis) is in 0.1DegC as
+ required by the battery driver. Note the resolution used
+ here to compute the table was done for DegC to milli-volts.
+ In consideration to limit the size of the table for the given
+ temperature range below, the result is linearly interpolated
+ and provided to the battery driver in the units desired for
+ their framework which is 0.1DegC. True resolution of 0.1DegC
+ will result in the below table size to increase by 10 times */
+static const struct qpnp_vadc_map_pt adcmap_btm_threshold[] = {
+ {-300, 1642},
+ {-200, 1544},
+ {-100, 1414},
+ {0, 1260},
+ {10, 1244},
+ {20, 1228},
+ {30, 1212},
+ {40, 1195},
+ {50, 1179},
+ {60, 1162},
+ {70, 1146},
+ {80, 1129},
+ {90, 1113},
+ {100, 1097},
+ {110, 1080},
+ {120, 1064},
+ {130, 1048},
+ {140, 1032},
+ {150, 1016},
+ {160, 1000},
+ {170, 985},
+ {180, 969},
+ {190, 954},
+ {200, 939},
+ {210, 924},
+ {220, 909},
+ {230, 894},
+ {240, 880},
+ {250, 866},
+ {260, 852},
+ {270, 838},
+ {280, 824},
+ {290, 811},
+ {300, 798},
+ {310, 785},
+ {320, 773},
+ {330, 760},
+ {340, 748},
+ {350, 736},
+ {360, 725},
+ {370, 713},
+ {380, 702},
+ {390, 691},
+ {400, 681},
+ {410, 670},
+ {420, 660},
+ {430, 650},
+ {440, 640},
+ {450, 631},
+ {460, 622},
+ {470, 613},
+ {480, 604},
+ {490, 595},
+ {500, 587},
+ {510, 579},
+ {520, 571},
+ {530, 563},
+ {540, 556},
+ {550, 548},
+ {560, 541},
+ {570, 534},
+ {580, 527},
+ {590, 521},
+ {600, 514},
+ {610, 508},
+ {620, 502},
+ {630, 496},
+ {640, 490},
+ {650, 485},
+ {660, 281},
+ {670, 274},
+ {680, 267},
+ {690, 260},
+ {700, 254},
+ {710, 247},
+ {720, 241},
+ {730, 235},
+ {740, 229},
+ {750, 224},
+ {760, 218},
+ {770, 213},
+ {780, 208},
+ {790, 203}
+};
+
+static const struct qpnp_vadc_map_pt adcmap_ntcg_104ef_104fb[] = {
+ {696483, -40960},
+ {649148, -39936},
+ {605368, -38912},
+ {564809, -37888},
+ {527215, -36864},
+ {492322, -35840},
+ {460007, -34816},
+ {429982, -33792},
+ {402099, -32768},
+ {376192, -31744},
+ {352075, -30720},
+ {329714, -29696},
+ {308876, -28672},
+ {289480, -27648},
+ {271417, -26624},
+ {254574, -25600},
+ {238903, -24576},
+ {224276, -23552},
+ {210631, -22528},
+ {197896, -21504},
+ {186007, -20480},
+ {174899, -19456},
+ {164521, -18432},
+ {154818, -17408},
+ {145744, -16384},
+ {137265, -15360},
+ {129307, -14336},
+ {121866, -13312},
+ {114896, -12288},
+ {108365, -11264},
+ {102252, -10240},
+ {96499, -9216},
+ {91111, -8192},
+ {86055, -7168},
+ {81308, -6144},
+ {76857, -5120},
+ {72660, -4096},
+ {68722, -3072},
+ {65020, -2048},
+ {61538, -1024},
+ {58261, 0},
+ {55177, 1024},
+ {52274, 2048},
+ {49538, 3072},
+ {46962, 4096},
+ {44531, 5120},
+ {42243, 6144},
+ {40083, 7168},
+ {38045, 8192},
+ {36122, 9216},
+ {34308, 10240},
+ {32592, 11264},
+ {30972, 12288},
+ {29442, 13312},
+ {27995, 14336},
+ {26624, 15360},
+ {25333, 16384},
+ {24109, 17408},
+ {22951, 18432},
+ {21854, 19456},
+ {20807, 20480},
+ {19831, 21504},
+ {18899, 22528},
+ {18016, 23552},
+ {17178, 24576},
+ {16384, 25600},
+ {15631, 26624},
+ {14916, 27648},
+ {14237, 28672},
+ {13593, 29696},
+ {12976, 30720},
+ {12400, 31744},
+ {11848, 32768},
+ {11324, 33792},
+ {10825, 34816},
+ {10354, 35840},
+ {9900, 36864},
+ {9471, 37888},
+ {9062, 38912},
+ {8674, 39936},
+ {8306, 40960},
+ {7951, 41984},
+ {7616, 43008},
+ {7296, 44032},
+ {6991, 45056},
+ {6701, 46080},
+ {6424, 47104},
+ {6160, 48128},
+ {5908, 49152},
+ {5667, 50176},
+ {5439, 51200},
+ {5219, 52224},
+ {5010, 53248},
+ {4810, 54272},
+ {4619, 55296},
+ {4440, 56320},
+ {4263, 57344},
+ {4097, 58368},
+ {3938, 59392},
+ {3785, 60416},
+ {3637, 61440},
+ {3501, 62464},
+ {3368, 63488},
+ {3240, 64512},
+ {3118, 65536},
+ {2998, 66560},
+ {2889, 67584},
+ {2782, 68608},
+ {2680, 69632},
+ {2581, 70656},
+ {2490, 71680},
+ {2397, 72704},
+ {2310, 73728},
+ {2227, 74752},
+ {2147, 75776},
+ {2064, 76800},
+ {1998, 77824},
+ {1927, 78848},
+ {1860, 79872},
+ {1795, 80896},
+ {1736, 81920},
+ {1673, 82944},
+ {1615, 83968},
+ {1560, 84992},
+ {1507, 86016},
+ {1456, 87040},
+ {1407, 88064},
+ {1360, 89088},
+ {1314, 90112},
+ {1271, 91136},
+ {1228, 92160},
+ {1189, 93184},
+ {1150, 94208},
+ {1112, 95232},
+ {1076, 96256},
+ {1042, 97280},
+ {1008, 98304},
+ {976, 99328},
+ {945, 100352},
+ {915, 101376},
+ {886, 102400},
+ {859, 103424},
+ {832, 104448},
+ {807, 105472},
+ {782, 106496},
+ {756, 107520},
+ {735, 108544},
+ {712, 109568},
+ {691, 110592},
+ {670, 111616},
+ {650, 112640},
+ {631, 113664},
+ {612, 114688},
+ {594, 115712},
+ {577, 116736},
+ {560, 117760},
+ {544, 118784},
+ {528, 119808},
+ {513, 120832},
+ {498, 121856},
+ {483, 122880},
+ {470, 123904},
+ {457, 124928},
+ {444, 125952},
+ {431, 126976},
+ {419, 128000}
+};
+
+static int32_t qpnp_adc_map_linear(const struct qpnp_vadc_map_pt *pts,
+ uint32_t tablesize, int32_t input, int64_t *output)
+{
+ bool descending = 1;
+ uint32_t i = 0;
+
+ if ((pts == NULL) || (output == NULL))
+ return -EINVAL;
+
+ /* Check if table is descending or ascending */
+ if (tablesize > 1) {
+ if (pts[0].x < pts[1].x)
+ descending = 0;
+ }
+
+ while (i < tablesize) {
+ if ((descending == 1) && (pts[i].x < input)) {
+ /* table entry is less than measured
+ value and table is descending, stop */
+ break;
+ } else if ((descending == 0) &&
+ (pts[i].x > input)) {
+ /* table entry is greater than measured
+ value and table is ascending, stop */
+ break;
+ } else {
+ i++;
+ }
+ }
+
+ if (i == 0)
+ *output = pts[0].y;
+ else if (i == tablesize)
+ *output = pts[tablesize-1].y;
+ else {
+ /* result is between search_index and search_index-1 */
+ /* interpolate linearly */
+ *output = (((int32_t) ((pts[i].y - pts[i-1].y)*
+ (input - pts[i-1].x))/
+ (pts[i].x - pts[i-1].x))+
+ pts[i-1].y);
+ }
+
+ return 0;
+}
+
+static int32_t qpnp_adc_map_batt_therm(const struct qpnp_vadc_map_pt *pts,
+ uint32_t tablesize, int32_t input, int64_t *output)
+{
+ bool descending = 1;
+ uint32_t i = 0;
+
+ if ((pts == NULL) || (output == NULL))
+ return -EINVAL;
+
+ /* Check if table is descending or ascending */
+ if (tablesize > 1) {
+ if (pts[0].y < pts[1].y)
+ descending = 0;
+ }
+
+ while (i < tablesize) {
+ if ((descending == 1) && (pts[i].y < input)) {
+ /* table entry is less than measured
+ value and table is descending, stop */
+ break;
+ } else if ((descending == 0) && (pts[i].y > input)) {
+ /* table entry is greater than measured
+ value and table is ascending, stop */
+ break;
+ } else {
+ i++;
+ }
+ }
+
+ if (i == 0) {
+ *output = pts[0].x;
+ } else if (i == tablesize) {
+ *output = pts[tablesize-1].x;
+ } else {
+ /* result is between search_index and search_index-1 */
+ /* interpolate linearly */
+ *output = (((int32_t) ((pts[i].x - pts[i-1].x)*
+ (input - pts[i-1].y))/
+ (pts[i].y - pts[i-1].y))+
+ pts[i-1].x);
+ }
+
+ return 0;
+}
+
+static int64_t qpnp_adc_scale_ratiometric_calib(int32_t adc_code,
+ const struct qpnp_adc_properties *adc_properties,
+ const struct qpnp_vadc_chan_properties *chan_properties)
+{
+ int64_t adc_voltage = 0;
+ bool negative_offset = 0;
+
+ if (!chan_properties || !chan_properties->offset_gain_numerator ||
+ !chan_properties->offset_gain_denominator || !adc_properties)
+ return -EINVAL;
+
+ adc_voltage = (adc_code -
+ chan_properties->adc_graph[CALIB_RATIOMETRIC].adc_gnd)
+ * adc_properties->adc_vdd_reference;
+ if (adc_voltage < 0) {
+ negative_offset = 1;
+ adc_voltage = -adc_voltage;
+ }
+ do_div(adc_voltage,
+ chan_properties->adc_graph[CALIB_RATIOMETRIC].dy);
+ if (negative_offset)
+ adc_voltage = -adc_voltage;
+
+ return adc_voltage;
+}
+
+int32_t qpnp_adc_scale_pmic_therm(int32_t adc_code,
+ const struct qpnp_adc_properties *adc_properties,
+ const struct qpnp_vadc_chan_properties *chan_properties,
+ struct qpnp_vadc_result *adc_chan_result)
+{
+ int64_t pmic_voltage = 0;
+ bool negative_offset = 0;
+
+ if (!chan_properties || !chan_properties->offset_gain_numerator ||
+ !chan_properties->offset_gain_denominator || !adc_properties
+ || !adc_chan_result)
+ return -EINVAL;
+
+ pmic_voltage = (adc_code -
+ chan_properties->adc_graph[CALIB_ABSOLUTE].adc_gnd)
+ * chan_properties->adc_graph[CALIB_ABSOLUTE].dx;
+ if (pmic_voltage < 0) {
+ negative_offset = 1;
+ pmic_voltage = -pmic_voltage;
+ }
+ do_div(pmic_voltage,
+ chan_properties->adc_graph[CALIB_ABSOLUTE].dy);
+ if (negative_offset)
+ pmic_voltage = -pmic_voltage;
+ pmic_voltage += chan_properties->adc_graph[CALIB_ABSOLUTE].dx;
+
+ if (pmic_voltage > 0) {
+ /* 2mV/K */
+ adc_chan_result->measurement = pmic_voltage*
+ chan_properties->offset_gain_denominator;
+
+ do_div(adc_chan_result->measurement,
+ chan_properties->offset_gain_numerator * 2);
+ } else {
+ adc_chan_result->measurement = 0;
+ }
+ /* Change to .001 deg C */
+ adc_chan_result->measurement -= KELVINMIL_DEGMIL;
+ adc_chan_result->physical = (int32_t)adc_chan_result->measurement;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qpnp_adc_scale_pmic_therm);
+
+/* Scales the ADC code to 0.001 degrees C using the map
+ * table for the XO thermistor.
+ */
+int32_t qpnp_adc_tdkntcg_therm(int32_t adc_code,
+ const struct qpnp_adc_properties *adc_properties,
+ const struct qpnp_vadc_chan_properties *chan_properties,
+ struct qpnp_vadc_result *adc_chan_result)
+{
+ int64_t xo_thm = 0;
+
+ if (!chan_properties || !chan_properties->offset_gain_numerator ||
+ !chan_properties->offset_gain_denominator || !adc_properties
+ || !adc_chan_result)
+ return -EINVAL;
+
+ xo_thm = qpnp_adc_scale_ratiometric_calib(adc_code,
+ adc_properties, chan_properties);
+ xo_thm <<= 4;
+ qpnp_adc_map_linear(adcmap_ntcg_104ef_104fb,
+ ARRAY_SIZE(adcmap_ntcg_104ef_104fb),
+ xo_thm, &adc_chan_result->physical);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qpnp_adc_tdkntcg_therm);
+
+int32_t qpnp_adc_scale_batt_therm(int32_t adc_code,
+ const struct qpnp_adc_properties *adc_properties,
+ const struct qpnp_vadc_chan_properties *chan_properties,
+ struct qpnp_vadc_result *adc_chan_result)
+{
+ int64_t bat_voltage = 0;
+
+ bat_voltage = qpnp_adc_scale_ratiometric_calib(adc_code,
+ adc_properties, chan_properties);
+
+ return qpnp_adc_map_batt_therm(
+ adcmap_btm_threshold,
+ ARRAY_SIZE(adcmap_btm_threshold),
+ bat_voltage,
+ &adc_chan_result->physical);
+}
+EXPORT_SYMBOL_GPL(qpnp_adc_scale_batt_therm);
+
+int32_t qpnp_adc_scale_batt_id(int32_t adc_code,
+ const struct qpnp_adc_properties *adc_properties,
+ const struct qpnp_vadc_chan_properties *chan_properties,
+ struct qpnp_vadc_result *adc_chan_result)
+{
+ int64_t batt_id_voltage = 0;
+
+ batt_id_voltage = qpnp_adc_scale_ratiometric_calib(adc_code,
+ adc_properties, chan_properties);
+ adc_chan_result->physical = batt_id_voltage;
+ adc_chan_result->physical = adc_chan_result->measurement;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qpnp_adc_scale_batt_id);
int32_t qpnp_adc_scale_default(int32_t adc_code,
const struct qpnp_adc_properties *adc_properties,
@@ -203,7 +689,7 @@
"qcom,calibration-type", NULL);
if (!strncmp(calibration_param, "absolute", 8))
calib_type = CALIB_ABSOLUTE;
- else if (!strncmp(calibration_param, "historical", 9))
+ else if (!strncmp(calibration_param, "ratiometric", 11))
calib_type = CALIB_RATIOMETRIC;
else {
pr_err("%s: Invalid calibration property\n", __func__);
@@ -252,6 +738,8 @@
return -ENXIO;
}
+ init_completion(&adc_qpnp->adc_rslt_completion);
+
mutex_init(&adc_qpnp->adc_lock);
return 0;
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index b689255..aa375d7 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -33,6 +33,15 @@
#include <linux/platform_device.h>
/* QPNP IADC register definition */
+#define QPNP_IADC_REVISION1 0x0
+#define QPNP_IADC_REVISION2 0x1
+#define QPNP_IADC_REVISION3 0x2
+#define QPNP_IADC_REVISION4 0x3
+#define QPNP_IADC_PERPH_TYPE 0x4
+#define QPNP_IADC_PERH_SUBTYPE 0x5
+
+#define QPNP_IADC_SUPPORTED_REVISION2 1
+
#define QPNP_STATUS1 0x8
#define QPNP_STATUS1_OP_MODE 4
#define QPNP_STATUS1_MULTI_MEAS_EN BIT(3)
@@ -67,13 +76,14 @@
#define QPNP_INT_CLR_FIFO_NOT_EMPTY_INT_EN BIT(1)
#define QPNP_INT_CLR_EOC_INT_EN_CLR BIT(0)
#define QPNP_INT_CLR_MASK 0x1f
-#define QPNP_MODE_CTL 0x40
+#define QPNP_IADC_MODE_CTL 0x40
#define QPNP_OP_MODE_SHIFT 4
#define QPNP_USE_BMS_DATA BIT(4)
#define QPNP_VADC_SYNCH_EN BIT(2)
#define QPNP_OFFSET_RMV_EN BIT(1)
#define QPNP_ADC_TRIM_EN BIT(0)
-#define QPNP_EN_CTL1 0x46
+#define QPNP_IADC_EN_CTL1 0x46
+#define QPNP_IADC_ADC_EN BIT(7)
#define QPNP_ADC_CH_SEL_CTL 0x48
#define QPNP_ADC_DIG_PARAM 0x50
#define QPNP_ADC_CLK_SEL_MASK 0x3
@@ -101,13 +111,6 @@
#define QPNP_DATA1 0x61
#define QPNP_CONV_TIMEOUT_ERR 2
-#define QPNP_IADC_MODE_CTL 0x40
-#define QPNP_IADC_USE_BMS_DATA BIT(4)
-#define QPNP_IADC_RESERVED_BIT3 BIT(3)
-#define QPNP_IADC_VADC_SYNC_EN BIT(2)
-#define QPNP_IADC_OFFSET_RMV_EN BIT(1)
-#define QPNP_IADC_ADC_TRIM_EN BIT(0)
-
#define QPNP_IADC_ADC_CH_SEL_CTL 0x48
#define QPNP_IADC_ADC_CHX_SEL_SHIFT 3
@@ -121,16 +124,18 @@
#define QPNP_IADC_DATA0 0x60
#define QPNP_IADC_DATA1 0x61
-#define QPNP_ADC_CONV_TIME_MIN 2000
-#define QPNP_ADC_CONV_TIME_MAX 2200
+#define QPNP_ADC_CONV_TIME_MIN 8000
+#define QPNP_ADC_CONV_TIME_MAX 8200
-#define QPNP_ADC_GAIN_CALCULATION 2500
+#define QPNP_ADC_GAIN_CALCULATION_UV 17857
+#define QPNP_IADC_RSENSE_MILLI_FACTOR 1000
struct qpnp_iadc_drv {
struct qpnp_adc_drv *adc;
int32_t rsense;
struct device *iadc_hwmon;
bool iadc_init_calib;
+ bool iadc_initialized;
struct sensor_device_attribute sens_attr[0];
};
@@ -223,6 +228,31 @@
return IRQ_HANDLED;
}
+static int32_t qpnp_iadc_enable(bool state)
+{
+ int rc = 0;
+ u8 data = 0;
+
+ data = QPNP_IADC_ADC_EN;
+ if (state) {
+ rc = qpnp_iadc_write_reg(QPNP_IADC_EN_CTL1,
+ data);
+ if (rc < 0) {
+ pr_err("IADC enable failed\n");
+ return rc;
+ }
+ } else {
+ rc = qpnp_iadc_write_reg(QPNP_IADC_EN_CTL1,
+ (~data & QPNP_IADC_ADC_EN));
+ if (rc < 0) {
+ pr_err("IADC disable failed\n");
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
static int32_t qpnp_iadc_read_conversion_result(int32_t *data)
{
uint8_t rslt_lsb, rslt_msb;
@@ -242,12 +272,9 @@
*data = (rslt_msb << 8) | rslt_lsb;
- rc = qpnp_vadc_check_result(data);
- if (rc < 0) {
- pr_err("VADC data check failed\n");
+ rc = qpnp_iadc_enable(false);
+ if (rc)
return rc;
- }
-
return 0;
}
@@ -259,10 +286,7 @@
u8 qpnp_iadc_conv_req = 0, qpnp_iadc_dig_param_reg = 0;
int32_t rc = 0;
- qpnp_iadc_mode_reg |= (QPNP_IADC_USE_BMS_DATA | QPNP_IADC_USE_BMS_DATA
- | QPNP_IADC_OFFSET_RMV_EN | QPNP_IADC_ADC_TRIM_EN);
-
- qpnp_iadc_ch_sel_reg = channel << QPNP_IADC_ADC_CHX_SEL_SHIFT;
+ qpnp_iadc_ch_sel_reg = channel;
qpnp_iadc_dig_param_reg |= iadc->adc->amux_prop->decimation <<
QPNP_IADC_DEC_RATIO_SEL;
@@ -310,6 +334,10 @@
return rc;
}
+ rc = qpnp_iadc_enable(true);
+ if (rc)
+ return rc;
+
rc = qpnp_iadc_write_reg(QPNP_CONV_REQ, qpnp_iadc_conv_req);
if (rc) {
pr_err("qpnp adc read adc failed with %d\n", rc);
@@ -353,17 +381,53 @@
return rc;
}
+static int32_t qpnp_iadc_version_check(void)
+{
+ uint8_t revision;
+ int rc;
+
+ rc = qpnp_iadc_read_reg(QPNP_IADC_REVISION2, &revision);
+ if (rc < 0) {
+ pr_err("qpnp adc result read failed with %d\n", rc);
+ return rc;
+ }
+
+ if (revision < QPNP_IADC_SUPPORTED_REVISION2) {
+ pr_err("IADC Version not supported\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int32_t qpnp_iadc_is_ready(void)
+{
+ struct qpnp_iadc_drv *iadc = qpnp_iadc;
+
+ if (!iadc || !iadc->iadc_initialized)
+ return -EPROBE_DEFER;
+ else
+ return 0;
+}
+EXPORT_SYMBOL(qpnp_iadc_is_ready);
+
int32_t qpnp_iadc_read(enum qpnp_iadc_channels channel,
int32_t *result)
{
struct qpnp_iadc_drv *iadc = qpnp_iadc;
int32_t vsense_mv = 0, rc;
+ if (!iadc || !iadc->iadc_initialized)
+ return -EPROBE_DEFER;
+
mutex_lock(&iadc->adc->adc_lock);
if (!iadc->iadc_init_calib) {
+ rc = qpnp_iadc_version_check();
+ if (rc)
+ goto fail;
rc = qpnp_iadc_init_calib();
- if (!rc) {
+ if (rc) {
pr_err("Calibration failed\n");
goto fail;
} else
@@ -376,12 +440,11 @@
goto fail;
}
- vsense_mv = ((*result - iadc->adc->calib.offset)/
- (iadc->adc->calib.gain - iadc->adc->calib.offset))
- * QPNP_ADC_GAIN_CALCULATION;
+ *result = ((vsense_mv - iadc->adc->calib.offset) *
+ QPNP_ADC_GAIN_CALCULATION_UV)/
+ (iadc->adc->calib.gain - iadc->adc->calib.offset);
- *result = (vsense_mv/qpnp_iadc->rsense);
-
+ *result = (*result / (qpnp_iadc->rsense));
fail:
mutex_unlock(&iadc->adc->adc_lock);
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index 9e8a2e2..a0e7ab9 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -32,6 +32,15 @@
#include <linux/platform_device.h>
/* QPNP VADC register definition */
+#define QPNP_VADC_REVISION1 0x0
+#define QPNP_VADC_REVISION2 0x1
+#define QPNP_VADC_REVISION3 0x2
+#define QPNP_VADC_REVISION4 0x3
+#define QPNP_VADC_PERPH_TYPE 0x4
+#define QPNP_VADC_PERH_SUBTYPE 0x5
+
+#define QPNP_VADC_SUPPORTED_REVISION2 1
+
#define QPNP_VADC_STATUS1 0x8
#define QPNP_VADC_STATUS1_OP_MODE 4
#define QPNP_VADC_STATUS1_MEAS_INTERVAL_EN_STS BIT(2)
@@ -98,6 +107,8 @@
struct dentry *dent;
struct device *vadc_hwmon;
bool vadc_init_calib;
+ bool vadc_initialized;
+ int max_channels_available;
struct sensor_device_attribute sens_attr[0];
};
@@ -105,6 +116,9 @@
static struct qpnp_vadc_scale_fn vadc_scale_fn[] = {
[SCALE_DEFAULT] = {qpnp_adc_scale_default},
+ [SCALE_BATT_THERM] = {qpnp_adc_scale_batt_therm},
+ [SCALE_PMIC_THERM] = {qpnp_adc_scale_pmic_therm},
+ [SCALE_XOTHERM] = {qpnp_adc_tdkntcg_therm},
};
static int32_t qpnp_vadc_read_reg(int16_t reg, u8 *data)
@@ -206,32 +220,21 @@
u8 mode_ctrl = 0;
int rc = 0;
- if (vadc->vadc_init_calib) {
- /* Configure interrupt if calibration is complete */
- rc = qpnp_vadc_write_reg(QPNP_VADC_INT_EN_SET,
- QPNP_VADC_INT_EOC_BIT);
- if (rc < 0) {
- pr_err("Configure error for interrupt setup\n");
- return rc;
- }
+ rc = qpnp_vadc_write_reg(QPNP_VADC_INT_EN_SET,
+ QPNP_VADC_INT_EOC_BIT);
+ if (rc < 0) {
+ pr_err("Configure error for interrupt setup\n");
+ return rc;
}
/* Mode selection */
- rc = qpnp_vadc_read_reg(QPNP_VADC_MODE_CTL, &mode_ctrl);
- if (rc < 0) {
- pr_err("Mode configure read error\n");
- return rc;
- }
- mode_ctrl |= chan_prop->mode_sel << QPNP_VADC_OP_MODE_SHIFT;
+ mode_ctrl = chan_prop->mode_sel << QPNP_VADC_OP_MODE_SHIFT;
rc = qpnp_vadc_write_reg(QPNP_VADC_MODE_CTL, mode_ctrl);
if (rc < 0) {
pr_err("Mode configure write error\n");
return rc;
}
- rc = qpnp_vadc_enable(true);
- if (rc)
- return rc;
/* Channel selection */
rc = qpnp_vadc_write_reg(QPNP_VADC_ADC_CH_SEL_CTL,
@@ -242,12 +245,7 @@
}
/* Digital parameter setup */
- rc = qpnp_vadc_read_reg(QPNP_VADC_ADC_DIG_PARAM, &decimation);
- if (rc < 0) {
- pr_err("Digital parameter configure read error\n");
- return rc;
- }
- decimation |= chan_prop->decimation <<
+ decimation = chan_prop->decimation <<
QPNP_VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT;
rc = qpnp_vadc_write_reg(QPNP_VADC_ADC_DIG_PARAM, decimation);
if (rc < 0) {
@@ -296,6 +294,12 @@
}
}
+ INIT_COMPLETION(vadc->adc->adc_rslt_completion);
+
+ rc = qpnp_vadc_enable(true);
+ if (rc)
+ return rc;
+
/* Request conversion */
rc = qpnp_vadc_write_reg(QPNP_VADC_CONV_REQ, QPNP_VADC_CONV_REQ_SET);
if (rc < 0) {
@@ -402,6 +406,25 @@
return IRQ_HANDLED;
}
+static int32_t qpnp_vadc_version_check(void)
+{
+ uint8_t revision;
+ int rc;
+
+ rc = qpnp_vadc_read_reg(QPNP_VADC_REVISION2, &revision);
+ if (rc < 0) {
+ pr_err("qpnp adc result read failed with %d\n", rc);
+ return rc;
+ }
+
+ if (revision < QPNP_VADC_SUPPORTED_REVISION2) {
+ pr_err("VADC Version not supported\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static uint32_t qpnp_vadc_calib_device(void)
{
struct qpnp_vadc_drv *vadc = qpnp_vadc;
@@ -421,8 +444,7 @@
goto calib_fail;
}
- while (status1 != (~QPNP_VADC_STATUS1_REQ_STS |
- QPNP_VADC_STATUS1_EOC)) {
+ while (status1 != QPNP_VADC_STATUS1_EOC) {
rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS1, &status1);
if (rc < 0)
return rc;
@@ -449,8 +471,7 @@
}
status1 = 0;
- while (status1 != (~QPNP_VADC_STATUS1_REQ_STS |
- QPNP_VADC_STATUS1_EOC)) {
+ while (status1 != QPNP_VADC_STATUS1_EOC) {
rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS1, &status1);
if (rc < 0)
return rc;
@@ -459,7 +480,7 @@
QPNP_VADC_CONV_TIME_MAX);
}
- rc = qpnp_vadc_read_conversion_result(&calib_read_1);
+ rc = qpnp_vadc_read_conversion_result(&calib_read_2);
if (rc) {
pr_err("qpnp adc read adc failed with %d\n", rc);
goto calib_fail;
@@ -467,6 +488,7 @@
vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dy =
(calib_read_1 - calib_read_2);
+
vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dx
= QPNP_ADC_625_UV;
vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].adc_vref =
@@ -486,8 +508,7 @@
}
status1 = 0;
- while (status1 != (~QPNP_VADC_STATUS1_REQ_STS |
- QPNP_VADC_STATUS1_EOC)) {
+ while (status1 != QPNP_VADC_STATUS1_EOC) {
rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS1, &status1);
if (rc < 0)
return rc;
@@ -502,7 +523,7 @@
goto calib_fail;
}
- conv.amux_channel = VDD_VADC;
+ conv.amux_channel = GND_REF;
conv.decimation = DECIMATION_TYPE2;
conv.mode_sel = ADC_OP_NORMAL_MODE << QPNP_VADC_OP_MODE_SHIFT;
conv.hw_settle_time = ADC_CHANNEL_HW_SETTLE_DELAY_0US;
@@ -514,8 +535,7 @@
}
status1 = 0;
- while (status1 != (~QPNP_VADC_STATUS1_REQ_STS |
- QPNP_VADC_STATUS1_EOC)) {
+ while (status1 != QPNP_VADC_STATUS1_EOC) {
rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS1, &status1);
if (rc < 0)
return rc;
@@ -524,7 +544,7 @@
QPNP_VADC_CONV_TIME_MAX);
}
- rc = qpnp_vadc_read_conversion_result(&calib_read_1);
+ rc = qpnp_vadc_read_conversion_result(&calib_read_2);
if (rc) {
pr_err("qpnp adc read adc failed with %d\n", rc);
goto calib_fail;
@@ -543,14 +563,31 @@
return rc;
}
+int32_t qpnp_vadc_is_ready(void)
+{
+ struct qpnp_vadc_drv *vadc = qpnp_vadc;
+
+ if (!vadc || !vadc->vadc_initialized)
+ return -EPROBE_DEFER;
+ else
+ return 0;
+}
+EXPORT_SYMBOL(qpnp_vadc_is_ready);
+
int32_t qpnp_vadc_conv_seq_request(enum qpnp_vadc_trigger trigger_channel,
enum qpnp_vadc_channels channel,
struct qpnp_vadc_result *result)
{
struct qpnp_vadc_drv *vadc = qpnp_vadc;
- int rc = 0, scale_type, amux_prescaling;
+ int rc = 0, scale_type, amux_prescaling, dt_index = 0;
+
+ if (!vadc || !vadc->vadc_initialized)
+ return -EPROBE_DEFER;
if (!vadc->vadc_init_calib) {
+ rc = qpnp_vadc_version_check();
+ if (rc)
+ return rc;
rc = qpnp_vadc_calib_device();
if (rc) {
pr_err("Calibration failed\n");
@@ -562,12 +599,20 @@
mutex_lock(&vadc->adc->adc_lock);
vadc->adc->amux_prop->amux_channel = channel;
+
+ while (vadc->adc->adc_channels[dt_index].channel_num
+ != channel || dt_index > vadc->max_channels_available)
+ dt_index++;
+
+ if (dt_index > vadc->max_channels_available)
+ goto fail_unlock;
+
vadc->adc->amux_prop->decimation =
- vadc->adc->adc_channels[channel].adc_decimation;
+ vadc->adc->adc_channels[dt_index].adc_decimation;
vadc->adc->amux_prop->hw_settle_time =
- vadc->adc->adc_channels[channel].hw_settle_time;
+ vadc->adc->adc_channels[dt_index].hw_settle_time;
vadc->adc->amux_prop->fast_avg_setup =
- vadc->adc->adc_channels[channel].fast_avg_setup;
+ vadc->adc->adc_channels[dt_index].fast_avg_setup;
if (trigger_channel < ADC_SEQ_NONE)
vadc->adc->amux_prop->mode_sel = (ADC_OP_CONVERSION_SEQUENCER
@@ -602,14 +647,15 @@
goto fail_unlock;
}
- amux_prescaling = vadc->adc->adc_channels[channel].chan_path_prescaling;
+ amux_prescaling =
+ vadc->adc->adc_channels[dt_index].chan_path_prescaling;
vadc->adc->amux_prop->chan_prop->offset_gain_numerator =
qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
vadc->adc->amux_prop->chan_prop->offset_gain_denominator =
qpnp_vadc_amux_scaling_ratio[amux_prescaling].den;
- scale_type = vadc->adc->adc_channels[channel].adc_scale_fn;
+ scale_type = vadc->adc->adc_channels[dt_index].adc_scale_fn;
if (scale_type >= SCALE_NONE) {
rc = -EBADF;
goto fail_unlock;
@@ -642,8 +688,10 @@
rc = qpnp_vadc_read(attr->index, &result);
- if (rc)
+ if (rc) {
+ pr_err("VADC read error with %d\n", rc);
return 0;
+ }
return snprintf(buf, QPNP_ADC_HWMON_NAME_LENGTH,
"Result:%lld Raw:%d\n", result.physical, result.adc_code);
@@ -738,6 +786,8 @@
dev_err(&spmi->dev,
"failed to request adc irq with error %d\n", rc);
return rc;
+ } else {
+ enable_irq_wake(vadc->adc->adc_irq);
}
qpnp_vadc = vadc;
@@ -749,6 +799,8 @@
}
vadc->vadc_hwmon = hwmon_device_register(&vadc->adc->spmi->dev);
vadc->vadc_init_calib = false;
+ vadc->vadc_initialized = true;
+ vadc->max_channels_available = count_adc_channel_list;
rc = qpnp_vadc_configure_interrupt();
if (rc) {
@@ -777,6 +829,7 @@
i++;
}
free_irq(vadc->adc->adc_irq, vadc);
+ vadc->vadc_initialized = false;
dev_set_drvdata(&spmi->dev, NULL);
return 0;