summaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorSiddartha Mohanadoss <smohanad@codeaurora.org>2016-04-28 14:12:44 -0700
committerJeevan Shriram <jshriram@codeaurora.org>2016-05-10 13:20:13 -0700
commit81a5895080b012100d7e7f8cda695489734ee07a (patch)
tree0ad9c0958f42ca1cd01de89c3bb476c77b13dc83 /drivers/hwmon
parentbe6417fef9816bebe92db5d5d7288d255ccf6216 (diff)
thermal: qpnp-adc-tm: Support refreshed BTM driver
The BTM (Battery temperature module) peripheral driver on the PMIC (Power management IC) supports threshold monitoring and notifies clients when thresholds are crossed. PMCOBALT supports refreshed BTM peripheral register interface and the driver uses compatible property qpnp-adc-tm-hc to distinguish using the refreshed peripheral. The external client interface with the driver remains the same. Updates include handling the interrupt when the thresholds are crossed,programming the threholds and configuring the hardware based on the refreshed design. BTM peripheral needs the VADC_HC peripheral to compute the gain/offset that are used to reverse compute the threhold values to ADC code. Some of the reverse computation API's such as calculating thermistor thresholds require the gain and offset values before computing the ADC code to be programmed. This requires modification to the existing calibration API in the VADC_HC driver to calculate the reference calibration points and store these values for clients to use in the reverse computation Change-Id: I989cfa4f40e7f1671f04dfa9d4c3fe2ccbbc44ab Signed-off-by: Siddartha Mohanadoss <smohanad@codeaurora.org>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/qpnp-adc-common.c94
-rw-r--r--drivers/hwmon/qpnp-adc-voltage.c684
2 files changed, 460 insertions, 318 deletions
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index 24f4135045e4..aeca77c9390d 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -1074,6 +1074,7 @@ int32_t qpnp_adc_scale_therm_pu2(struct qpnp_vadc_chip *chip,
EXPORT_SYMBOL(qpnp_adc_scale_therm_pu2);
int32_t qpnp_adc_tm_scale_voltage_therm_pu2(struct qpnp_vadc_chip *chip,
+ const struct qpnp_adc_properties *adc_properties,
uint32_t reg, int64_t *result)
{
int64_t adc_voltage = 0;
@@ -1090,9 +1091,15 @@ int32_t qpnp_adc_tm_scale_voltage_therm_pu2(struct qpnp_vadc_chip *chip,
do_div(adc_voltage, param1.dy);
- qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
- ARRAY_SIZE(adcmap_100k_104ef_104fb),
- adc_voltage, result);
+ if (adc_properties->adc_hc)
+ qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
+ ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
+ adc_voltage, result);
+ else
+ qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
+ ARRAY_SIZE(adcmap_100k_104ef_104fb),
+ adc_voltage, result);
+
if (negative_offset)
adc_voltage = -adc_voltage;
@@ -1101,6 +1108,7 @@ int32_t qpnp_adc_tm_scale_voltage_therm_pu2(struct qpnp_vadc_chip *chip,
EXPORT_SYMBOL(qpnp_adc_tm_scale_voltage_therm_pu2);
int32_t qpnp_adc_tm_scale_therm_voltage_pu2(struct qpnp_vadc_chip *chip,
+ const struct qpnp_adc_properties *adc_properties,
struct qpnp_adc_tm_config *param)
{
struct qpnp_vadc_linear_graph param1;
@@ -1108,11 +1116,20 @@ int32_t qpnp_adc_tm_scale_therm_voltage_pu2(struct qpnp_vadc_chip *chip,
qpnp_get_vadc_gain_and_offset(chip, &param1, CALIB_RATIOMETRIC);
- rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb,
- ARRAY_SIZE(adcmap_100k_104ef_104fb),
- param->low_thr_temp, &param->low_thr_voltage);
- if (rc)
- return rc;
+ if (adc_properties->adc_hc) {
+ rc = qpnp_adc_map_temp_voltage(
+ adcmap_100k_104ef_104fb_1875_vref,
+ ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
+ param->low_thr_temp, &param->low_thr_voltage);
+ if (rc)
+ return rc;
+ } else {
+ rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb,
+ ARRAY_SIZE(adcmap_100k_104ef_104fb),
+ param->low_thr_temp, &param->low_thr_voltage);
+ if (rc)
+ return rc;
+ }
param->low_thr_voltage *= param1.dy;
do_div(param->low_thr_voltage, param1.adc_vref);
@@ -1772,6 +1789,7 @@ int32_t qpnp_adc_get_devicetree_data(struct platform_device *pdev,
struct qpnp_adc_properties *adc_prop;
struct qpnp_adc_amux_properties *amux_prop;
int count_adc_channel_list = 0, decimation, rc = 0, i = 0;
+ int decimation_tm_hc = 0, fast_avg_setup_tm_hc = 0;
bool adc_hc;
if (!node)
@@ -1812,6 +1830,27 @@ int32_t qpnp_adc_get_devicetree_data(struct platform_device *pdev,
adc_hc = adc_qpnp->adc_hc;
adc_prop->adc_hc = adc_hc;
+ if (of_device_is_compatible(node, "qcom,qpnp-adc-tm-hc")) {
+ rc = of_property_read_u32(node, "qcom,decimation",
+ &decimation_tm_hc);
+ if (rc) {
+ pr_err("Invalid decimation property\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node,
+ "qcom,fast-avg-setup", &fast_avg_setup_tm_hc);
+ if (rc) {
+ pr_err("Invalid fast average setup with %d\n", rc);
+ return -EINVAL;
+ }
+
+ if ((fast_avg_setup_tm_hc) > ADC_FAST_AVG_SAMPLE_16) {
+ pr_err("Max average support is 2^16\n");
+ return -EINVAL;
+ }
+ }
+
for_each_child_of_node(node, child) {
int channel_num, scaling, post_scaling, hw_settle_time;
int fast_avg_setup, calib_type = 0, rc;
@@ -1829,12 +1868,7 @@ int32_t qpnp_adc_get_devicetree_data(struct platform_device *pdev,
pr_err("Invalid channel num\n");
return -EINVAL;
}
- rc = of_property_read_u32(child, "qcom,decimation",
- &decimation);
- if (rc) {
- pr_err("Invalid channel decimation property\n");
- return -EINVAL;
- }
+
if (!of_device_is_compatible(node, "qcom,qpnp-iadc")) {
rc = of_property_read_u32(child,
"qcom,hw-settle-time", &hw_settle_time);
@@ -1860,6 +1894,23 @@ int32_t qpnp_adc_get_devicetree_data(struct platform_device *pdev,
pr_err("Invalid calibration type\n");
return -EINVAL;
}
+
+ /*
+ * ADC_TM_HC decimation setting is common across
+ * channels.
+ */
+ if (!of_device_is_compatible(node,
+ "qcom,qpnp-adc-tm-hc")) {
+ rc = of_property_read_u32(child,
+ "qcom,decimation", &decimation);
+ if (rc) {
+ pr_err("Invalid decimation\n");
+ return -EINVAL;
+ }
+ } else {
+ decimation = decimation_tm_hc;
+ }
+
if (!strcmp(calibration_param, "absolute")) {
if (adc_hc)
calib_type = ADC_HC_ABS_CAL;
@@ -1884,12 +1935,19 @@ int32_t qpnp_adc_get_devicetree_data(struct platform_device *pdev,
return -EINVAL;
}
}
- rc = of_property_read_u32(child,
+
+ /* ADC_TM_HC fast avg setting is common across channels */
+ if (!of_device_is_compatible(node, "qcom,qpnp-adc-tm-hc")) {
+ rc = of_property_read_u32(child,
"qcom,fast-avg-setup", &fast_avg_setup);
- if (rc) {
- pr_err("Invalid channel fast average setup\n");
- return -EINVAL;
+ if (rc) {
+ pr_err("Invalid channel fast average setup\n");
+ return -EINVAL;
+ }
+ } else {
+ fast_avg_setup = fast_avg_setup_tm_hc;
}
+
/* Individual channel properties */
adc_channel_list[i].name = (char *)channel_name;
adc_channel_list[i].channel_num = channel_num;
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index 2ce8d973866f..0d18c77cfdff 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -225,6 +225,8 @@ static struct qpnp_vadc_rscale_fn adc_vadc_rscale_fn[] = {
[SCALE_RVADC_ABSOLUTE] = {qpnp_vadc_absolute_rthr},
};
+static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc);
+
static int32_t qpnp_vadc_read_reg(struct qpnp_vadc_chip *vadc, int16_t reg,
u8 *data, int len)
{
@@ -255,6 +257,17 @@ static int32_t qpnp_vadc_write_reg(struct qpnp_vadc_chip *vadc, int16_t reg,
return 0;
}
+static int qpnp_vadc_is_valid(struct qpnp_vadc_chip *vadc)
+{
+ struct qpnp_vadc_chip *vadc_chip = NULL;
+
+ list_for_each_entry(vadc_chip, &qpnp_vadc_device_list, list)
+ if (vadc == vadc_chip)
+ return 0;
+
+ return -EINVAL;
+}
+
static int32_t qpnp_vadc_warm_rst_configure(struct qpnp_vadc_chip *vadc)
{
int rc = 0;
@@ -369,6 +382,267 @@ static int32_t qpnp_vadc_status_debug(struct qpnp_vadc_chip *vadc)
return 0;
}
+
+static int qpnp_vadc_hc_check_conversion_status(struct qpnp_vadc_chip *vadc)
+{
+ int rc = 0, count = 0;
+ u8 status1 = 0;
+
+ while (status1 != QPNP_VADC_STATUS1_EOC) {
+ rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1, 1);
+ if (rc < 0)
+ return rc;
+ status1 &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK;
+ if (status1 == QPNP_VADC_STATUS1_EOC)
+ break;
+ usleep_range(QPNP_VADC_HC1_CONV_TIME_MIN_US,
+ QPNP_VADC_HC1_CONV_TIME_MAX_US);
+ count++;
+ if (count > QPNP_VADC_HC1_ERR_COUNT) {
+ pr_err("retry error exceeded\n");
+ rc = qpnp_vadc_status_debug(vadc);
+ if (rc < 0)
+ pr_err("VADC disable failed with %d\n", rc);
+ return -EINVAL;
+ }
+ }
+
+ return rc;
+}
+
+static int qpnp_vadc_hc_read_data(struct qpnp_vadc_chip *vadc, int *data)
+{
+ int rc = 0;
+ u8 buf = 0, rslt_lsb = 0, rslt_msb = 0;
+
+ /* Set hold bit */
+ rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_DATA_HOLD_CTL, &buf, 1);
+ if (rc) {
+ pr_err("debug register dump failed\n");
+ return rc;
+ }
+ buf |= QPNP_VADC_HC1_DATA_HOLD_CTL_FIELD;
+ rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HC1_DATA_HOLD_CTL, &buf, 1);
+ if (rc) {
+ pr_err("debug register dump failed\n");
+ return rc;
+ }
+
+ rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_DATA0, &rslt_lsb, 1);
+ if (rc < 0) {
+ pr_err("qpnp adc result read failed for data0\n");
+ return rc;
+ }
+
+ rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_DATA1, &rslt_msb, 1);
+ if (rc < 0) {
+ pr_err("qpnp adc result read failed for data1\n");
+ return rc;
+ }
+
+ *data = (rslt_msb << 8) | rslt_lsb;
+
+ if (*data == QPNP_VADC_HC1_DATA_CHECK_USR) {
+ pr_err("Invalid data :0x%x\n", *data);
+ return -EINVAL;
+ }
+
+ rc = qpnp_vadc_enable(vadc, false);
+ if (rc) {
+ pr_err("VADC disable failed\n");
+ return rc;
+ }
+
+ /* De-assert hold bit */
+ buf &= ~QPNP_VADC_HC1_DATA_HOLD_CTL_FIELD;
+ rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HC1_DATA_HOLD_CTL, &buf, 1);
+ if (rc)
+ pr_err("de-asserting hold bit failed\n");
+
+ return rc;
+}
+
+static void qpnp_vadc_hc_update_adc_dig_param(struct qpnp_vadc_chip *vadc,
+ struct qpnp_adc_amux_properties *amux_prop, u8 *data)
+{
+ /* Update CAL value */
+ *data &= ~QPNP_VADC_HC1_CAL_VAL;
+ *data |= (amux_prop->cal_val << QPNP_VADC_HC1_CAL_VAL_SHIFT);
+
+ /* Update CAL select */
+ *data &= ~QPNP_VADC_HC1_CAL_SEL_MASK;
+ *data |= (amux_prop->calib_type << QPNP_VADC_HC1_CAL_SEL_SHIFT);
+
+ /* Update Decimation ratio select */
+ *data &= ~QPNP_VADC_HC1_DEC_RATIO_SEL;
+ *data |= (amux_prop->decimation << QPNP_VADC_HC1_DEC_RATIO_SHIFT);
+
+ pr_debug("VADC_DIG_PARAM value:0x%x\n", *data);
+}
+
+static int qpnp_vadc_hc_configure(struct qpnp_vadc_chip *vadc,
+ struct qpnp_adc_amux_properties *amux_prop)
+{
+ int rc = 0;
+ u8 buf[6];
+
+ /* Read registers 0x42 through 0x46 */
+ rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_ADC_DIG_PARAM, buf, 6);
+ if (rc < 0) {
+ pr_err("qpnp adc configure block read failed\n");
+ return rc;
+ }
+
+ /* ADC Digital param selection */
+ qpnp_vadc_hc_update_adc_dig_param(vadc, amux_prop, &buf[0]);
+
+ /* Update fast average sample value */
+ buf[1] &= (u8) ~QPNP_VADC_HC1_FAST_AVG_SAMPLES_MASK;
+ buf[1] |= amux_prop->fast_avg_setup;
+
+ /* Select ADC channel */
+ buf[2] = amux_prop->amux_channel;
+
+ /* Select hw settle delay for the channel */
+ buf[3] &= (u8) ~QPNP_VADC_HC1_DELAY_CTL_MASK;
+ buf[3] |= amux_prop->hw_settle_time;
+
+ /* Select ADC enable */
+ buf[4] |= QPNP_VADC_HC1_ADC_EN;
+
+ /* Select CONV request */
+ buf[5] |= QPNP_VADC_HC1_CONV_REQ_START;
+
+ if (!vadc->vadc_poll_eoc)
+ reinit_completion(&vadc->adc->adc_rslt_completion);
+
+ pr_debug("dig:0x%x, fast_avg:0x%x, channel:0x%x, hw_settle:0x%x\n",
+ buf[0], buf[1], buf[2], buf[3]);
+
+ /* Block register write from 0x42 through 0x46 */
+ rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HC1_ADC_DIG_PARAM, buf, 6);
+ if (rc < 0) {
+ pr_err("qpnp adc block register configure failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+int32_t qpnp_vadc_hc_read(struct qpnp_vadc_chip *vadc,
+ enum qpnp_vadc_channels channel,
+ struct qpnp_vadc_result *result)
+{
+ int rc = 0, scale_type, amux_prescaling, dt_index = 0, calib_type = 0;
+ struct qpnp_adc_amux_properties amux_prop;
+
+ if (qpnp_vadc_is_valid(vadc))
+ return -EPROBE_DEFER;
+
+ mutex_lock(&vadc->adc->adc_lock);
+
+ 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) {
+ pr_err("not a valid VADC channel:%d\n", channel);
+ rc = -EINVAL;
+ goto fail_unlock;
+ }
+
+ if (!vadc->vadc_init_calib) {
+ rc = qpnp_vadc_calib_device(vadc);
+ if (rc) {
+ pr_err("Calibration failed\n");
+ goto fail_unlock;
+ } else {
+ vadc->vadc_init_calib = true;
+ }
+ }
+
+ calib_type = vadc->adc->adc_channels[dt_index].calib_type;
+ if (calib_type >= ADC_HC_CAL_SEL_NONE) {
+ pr_err("not a valid calib_type\n");
+ rc = -EINVAL;
+ goto fail_unlock;
+ }
+
+ amux_prop.decimation =
+ vadc->adc->adc_channels[dt_index].adc_decimation;
+ amux_prop.calib_type = vadc->adc->adc_channels[dt_index].calib_type;
+ amux_prop.cal_val = vadc->adc->adc_channels[dt_index].cal_val;
+ amux_prop.fast_avg_setup =
+ vadc->adc->adc_channels[dt_index].fast_avg_setup;
+ amux_prop.amux_channel = channel;
+ amux_prop.hw_settle_time =
+ vadc->adc->adc_channels[dt_index].hw_settle_time;
+
+ rc = qpnp_vadc_hc_configure(vadc, &amux_prop);
+ if (rc < 0) {
+ pr_err("Configuring VADC channel failed with %d\n", rc);
+ goto fail_unlock;
+ }
+
+ if (vadc->vadc_poll_eoc) {
+ rc = qpnp_vadc_hc_check_conversion_status(vadc);
+ if (rc < 0) {
+ pr_err("polling mode conversion failed\n");
+ goto fail_unlock;
+ }
+ } else {
+ rc = wait_for_completion_timeout(
+ &vadc->adc->adc_rslt_completion,
+ QPNP_ADC_COMPLETION_TIMEOUT);
+ if (!rc) {
+ rc = qpnp_vadc_hc_check_conversion_status(vadc);
+ if (rc < 0) {
+ pr_err("interrupt mode conversion failed\n");
+ goto fail_unlock;
+ }
+ pr_debug("End of conversion status set\n");
+ }
+ }
+
+ rc = qpnp_vadc_hc_read_data(vadc, &result->adc_code);
+ if (rc) {
+ pr_err("qpnp vadc read adc code failed with %d\n", rc);
+ goto fail_unlock;
+ }
+
+ amux_prescaling =
+ vadc->adc->adc_channels[dt_index].chan_path_prescaling;
+
+ if (amux_prescaling >= PATH_SCALING_NONE) {
+ rc = -EINVAL;
+ goto fail_unlock;
+ }
+
+ 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[dt_index].adc_scale_fn;
+ if (scale_type >= SCALE_NONE) {
+ rc = -EBADF;
+ goto fail_unlock;
+ }
+
+ /* Note: Scaling functions for VADC_HC do not need offset/gain */
+ vadc_scale_fn[scale_type].chan(vadc, result->adc_code,
+ vadc->adc->adc_prop, vadc->adc->amux_prop->chan_prop, result);
+
+ pr_debug("channel=0x%x, adc_code=0x%x adc_result=%lld\n",
+ channel, result->adc_code, result->physical);
+
+fail_unlock:
+ mutex_unlock(&vadc->adc->adc_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(qpnp_vadc_hc_read);
+
static int32_t qpnp_vadc_configure(struct qpnp_vadc_chip *vadc,
struct qpnp_adc_amux_properties *chan_prop)
{
@@ -551,17 +825,6 @@ static int32_t qpnp_vadc_read_status(struct qpnp_vadc_chip *vadc, int mode_sel)
return 0;
}
-static int qpnp_vadc_is_valid(struct qpnp_vadc_chip *vadc)
-{
- struct qpnp_vadc_chip *vadc_chip = NULL;
-
- list_for_each_entry(vadc_chip, &qpnp_vadc_device_list, list)
- if (vadc == vadc_chip)
- return 0;
-
- return -EINVAL;
-}
-
static void qpnp_vadc_work(struct work_struct *work)
{
struct qpnp_vadc_chip *vadc = container_of(work,
@@ -1209,20 +1472,36 @@ int32_t qpnp_vadc_calib_vref(struct qpnp_vadc_chip *vadc,
int rc, count = 0, calib_read = 0;
u8 status1 = 0;
- if (calib_type == CALIB_ABSOLUTE)
- conv.amux_channel = REF_125V;
- else if (calib_type == CALIB_RATIOMETRIC)
- conv.amux_channel = VDD_VADC;
+ if (vadc->vadc_hc) {
+ if (calib_type == ADC_HC_ABS_CAL)
+ conv.amux_channel = VADC_CALIB_VREF_1P25;
+ else if (calib_type == CALIB_RATIOMETRIC)
+ conv.amux_channel = VADC_CALIB_VREF;
+ } else {
+ if (calib_type == CALIB_ABSOLUTE)
+ conv.amux_channel = REF_125V;
+ else if (calib_type == CALIB_RATIOMETRIC)
+ conv.amux_channel = VDD_VADC;
+ }
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;
conv.fast_avg_setup = ADC_FAST_AVG_SAMPLE_1;
+ conv.cal_val = calib_type;
- rc = qpnp_vadc_configure(vadc, &conv);
- if (rc) {
- pr_err("qpnp_vadc configure failed with %d\n", rc);
- goto calib_fail;
+ if (vadc->vadc_hc) {
+ rc = qpnp_vadc_hc_configure(vadc, &conv);
+ if (rc) {
+ pr_err("qpnp_vadc configure failed with %d\n", rc);
+ goto calib_fail;
+ }
+ } else {
+ rc = qpnp_vadc_configure(vadc, &conv);
+ if (rc) {
+ pr_err("qpnp_vadc configure failed with %d\n", rc);
+ goto calib_fail;
+ }
}
while (status1 != QPNP_VADC_STATUS1_EOC) {
@@ -1239,11 +1518,20 @@ int32_t qpnp_vadc_calib_vref(struct qpnp_vadc_chip *vadc,
}
}
- rc = qpnp_vadc_read_conversion_result(vadc, &calib_read);
- if (rc) {
- pr_err("qpnp adc read adc failed with %d\n", rc);
- goto calib_fail;
+ if (vadc->vadc_hc) {
+ rc = qpnp_vadc_hc_read_data(vadc, &calib_read);
+ if (rc) {
+ pr_err("qpnp vadc read adc code failed with %d\n", rc);
+ goto calib_fail;
+ }
+ } else {
+ rc = qpnp_vadc_read_conversion_result(vadc, &calib_read);
+ if (rc) {
+ pr_err("qpnp adc read adc failed with %d\n", rc);
+ goto calib_fail;
+ }
}
+
*calib_data = calib_read;
calib_fail:
return rc;
@@ -1259,21 +1547,34 @@ int32_t qpnp_vadc_calib_gnd(struct qpnp_vadc_chip *vadc,
u8 status1 = 0;
uint32_t ref_channel_sel = 0;
- if (calib_type == CALIB_ABSOLUTE) {
- qpnp_vadc_625mv_channel_sel(vadc, &ref_channel_sel);
- conv.amux_channel = ref_channel_sel;
- } else if (calib_type == CALIB_RATIOMETRIC)
- conv.amux_channel = GND_REF;
+ if (vadc->vadc_hc) {
+ conv.amux_channel = VADC_VREF_GND;
+ } else {
+ if (calib_type == CALIB_ABSOLUTE) {
+ qpnp_vadc_625mv_channel_sel(vadc, &ref_channel_sel);
+ conv.amux_channel = ref_channel_sel;
+ } else if (calib_type == CALIB_RATIOMETRIC)
+ 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;
conv.fast_avg_setup = ADC_FAST_AVG_SAMPLE_1;
+ conv.cal_val = calib_type;
- rc = qpnp_vadc_configure(vadc, &conv);
- if (rc) {
- pr_err("qpnp_vadc configure failed with %d\n", rc);
- goto calib_fail;
+ if (vadc->vadc_hc) {
+ rc = qpnp_vadc_hc_configure(vadc, &conv);
+ if (rc) {
+ pr_err("qpnp_vadc configure failed with %d\n", rc);
+ goto calib_fail;
+ }
+ } else {
+ rc = qpnp_vadc_configure(vadc, &conv);
+ if (rc) {
+ pr_err("qpnp_vadc configure failed with %d\n", rc);
+ goto calib_fail;
+ }
}
while (status1 != QPNP_VADC_STATUS1_EOC) {
@@ -1290,10 +1591,18 @@ int32_t qpnp_vadc_calib_gnd(struct qpnp_vadc_chip *vadc,
}
}
- rc = qpnp_vadc_read_conversion_result(vadc, &calib_read);
- if (rc) {
- pr_err("qpnp adc read adc failed with %d\n", rc);
- goto calib_fail;
+ if (vadc->vadc_hc) {
+ rc = qpnp_vadc_hc_read_data(vadc, &calib_read);
+ if (rc) {
+ pr_err("qpnp vadc read adc code failed with %d\n", rc);
+ goto calib_fail;
+ }
+ } else {
+ rc = qpnp_vadc_read_conversion_result(vadc, &calib_read);
+ if (rc) {
+ pr_err("qpnp adc read adc failed with %d\n", rc);
+ goto calib_fail;
+ }
}
*calib_data = calib_read;
calib_fail:
@@ -1303,35 +1612,41 @@ calib_fail:
static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc)
{
int rc, calib_read_1 = 0, calib_read_2 = 0;
+ enum qpnp_adc_calib_type calib_type;
- rc = qpnp_vadc_calib_vref(vadc, CALIB_ABSOLUTE, &calib_read_1);
+ if (vadc->vadc_hc)
+ calib_type = ADC_HC_ABS_CAL;
+ else
+ calib_type = CALIB_ABSOLUTE;
+
+ rc = qpnp_vadc_calib_vref(vadc, calib_type, &calib_read_1);
if (rc) {
pr_err("qpnp adc absolute vref calib failed with %d\n", rc);
goto calib_fail;
}
- rc = qpnp_vadc_calib_gnd(vadc, CALIB_ABSOLUTE, &calib_read_2);
+ rc = qpnp_vadc_calib_gnd(vadc, calib_type, &calib_read_2);
if (rc) {
pr_err("qpnp adc absolute gnd calib failed with %d\n", rc);
goto calib_fail;
}
- pr_debug("absolute reference raw: 625mV:0x%x 1.25V:0x%x\n",
- calib_read_2, calib_read_1);
+ pr_debug("absolute reference raw: 1.25V:0x%x, 625mV/GND:0x%x\n",
+ calib_read_1, calib_read_2);
if (calib_read_1 == calib_read_2) {
- pr_err("absolute reference raw: 625mV:0x%x 1.25V:0x%x\n",
+ pr_err("absolute reference raw: 1.25V:0x%x625mV:0x%x\n",
calib_read_2, calib_read_1);
rc = -EINVAL;
goto calib_fail;
}
- vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dy =
+ vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dy =
(calib_read_1 - calib_read_2);
- vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dx
+ vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx
= QPNP_ADC_625_UV;
- vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].adc_vref =
+ vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_vref =
calib_read_1;
- vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].adc_gnd =
+ vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_gnd =
calib_read_2;
calib_read_1 = 0;
@@ -1374,11 +1689,29 @@ int32_t qpnp_get_vadc_gain_and_offset(struct qpnp_vadc_chip *vadc,
enum qpnp_adc_calib_type calib_type)
{
int rc = 0;
+ struct qpnp_vadc_result result;
rc = qpnp_vadc_is_valid(vadc);
if (rc < 0)
return rc;
+ if (!vadc->vadc_init_calib) {
+ if (vadc->vadc_hc) {
+ rc = qpnp_vadc_hc_read(vadc, VADC_CALIB_VREF_1P25,
+ &result);
+ if (rc) {
+ pr_debug("vadc read failed with rc = %d\n", rc);
+ return rc;
+ }
+ } else {
+ rc = qpnp_vadc_read(vadc, REF_125V, &result);
+ if (rc) {
+ pr_debug("vadc read failed with rc = %d\n", rc);
+ return rc;
+ }
+ }
+ }
+
switch (calib_type) {
case CALIB_RATIOMETRIC:
param->dy =
@@ -1390,13 +1723,14 @@ int32_t qpnp_get_vadc_gain_and_offset(struct qpnp_vadc_chip *vadc,
vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].adc_gnd;
break;
case CALIB_ABSOLUTE:
+ case ADC_HC_ABS_CAL:
param->dy =
- vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dy;
+ vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dy;
param->dx =
- vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dx;
+ vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx;
param->adc_vref = vadc->adc->adc_prop->adc_vdd_reference;
param->adc_gnd =
- vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].adc_gnd;
+ vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_gnd;
break;
default:
rc = -EINVAL;
@@ -1429,7 +1763,8 @@ static int32_t qpnp_vadc_wait_for_req_sts_check(struct qpnp_vadc_chip *vadc)
while ((status1 & QPNP_VADC_STATUS1_REQ_STS) && (count < QPNP_RETRY)) {
/* Wait time is based on the optimum sampling rate
* and adding enough time buffer to account for ADC conversions
- * occuring on different peripheral banks */
+ * occurring on different peripheral banks
+ */
usleep_range(QPNP_MIN_TIME, QPNP_MAX_TIME);
rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1, 1);
if (rc < 0) {
@@ -2170,256 +2505,6 @@ int32_t qpnp_vadc_end_channel_monitor(struct qpnp_vadc_chip *chip)
}
EXPORT_SYMBOL(qpnp_vadc_end_channel_monitor);
-static int qpnp_vadc_hc_check_conversion_status(struct qpnp_vadc_chip *vadc)
-{
- int rc = 0, count = 0;
- u8 status1 = 0;
-
- while (status1 != QPNP_VADC_STATUS1_EOC) {
- rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1, 1);
- if (rc < 0)
- return rc;
- status1 &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK;
- if (status1 == QPNP_VADC_STATUS1_EOC)
- break;
- usleep_range(QPNP_VADC_HC1_CONV_TIME_MIN_US,
- QPNP_VADC_HC1_CONV_TIME_MAX_US);
- count++;
- if (count > QPNP_VADC_HC1_ERR_COUNT) {
- pr_err("retry error exceeded\n");
- rc = qpnp_vadc_status_debug(vadc);
- if (rc < 0)
- pr_err("VADC disable failed\n");
- return -EINVAL;
- }
- }
-
- return rc;
-}
-
-static int qpnp_vadc_hc_read_data(struct qpnp_vadc_chip *vadc, int *data)
-{
- int rc = 0;
- u8 buf = 0, rslt_lsb = 0, rslt_msb = 0;
-
- /* Set hold bit */
- rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_DATA_HOLD_CTL, &buf, 1);
- if (rc) {
- pr_err("debug register dump failed\n");
- return rc;
- }
- buf |= QPNP_VADC_HC1_DATA_HOLD_CTL_FIELD;
- rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HC1_DATA_HOLD_CTL, &buf, 1);
- if (rc) {
- pr_err("debug register dump failed\n");
- return rc;
- }
-
- rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_DATA0, &rslt_lsb, 1);
- if (rc < 0) {
- pr_err("qpnp adc result read failed for data0\n");
- return rc;
- }
-
- rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_DATA1, &rslt_msb, 1);
- if (rc < 0) {
- pr_err("qpnp adc result read failed for data1\n");
- return rc;
- }
-
- *data = (rslt_msb << 8) | rslt_lsb;
-
- if (*data == QPNP_VADC_HC1_DATA_CHECK_USR) {
- pr_err("Invalid data :0x%x\n", *data);
- return -EINVAL;
- }
-
- rc = qpnp_vadc_enable(vadc, false);
- if (rc) {
- pr_err("VADC disable failed\n");
- return rc;
- }
-
- /* De-assert hold bit */
- buf &= ~QPNP_VADC_HC1_DATA_HOLD_CTL_FIELD;
- rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HC1_DATA_HOLD_CTL, &buf, 1);
- if (rc)
- pr_err("de-asserting hold bit failed\n");
-
- return rc;
-}
-
-static void qpnp_vadc_hc_update_adc_dig_param(struct qpnp_vadc_chip *vadc,
- struct qpnp_adc_amux *amux_prop, u8 *data)
-{
- /* Update CAL value */
- *data &= ~QPNP_VADC_HC1_CAL_VAL;
- *data |= (amux_prop->cal_val << QPNP_VADC_HC1_CAL_VAL_SHIFT);
-
- /* Update CAL select */
- *data &= ~QPNP_VADC_HC1_CAL_SEL_MASK;
- *data |= (amux_prop->calib_type << QPNP_VADC_HC1_CAL_SEL_SHIFT);
-
- /* Update Decimation ratio select */
- *data &= ~QPNP_VADC_HC1_DEC_RATIO_SEL;
- *data |= (amux_prop->adc_decimation << QPNP_VADC_HC1_DEC_RATIO_SHIFT);
-
- pr_debug("VADC_DIG_PARAM value:0x%x\n", *data);
-}
-
-static int qpnp_vadc_hc_configure(struct qpnp_vadc_chip *vadc,
- struct qpnp_adc_amux *amux_prop)
-{
- int rc = 0;
- u8 buf[6];
-
- /* Read registers 0x42 through 0x46 */
- rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_ADC_DIG_PARAM, buf, 6);
- if (rc < 0) {
- pr_err("qpnp adc configure block read failed\n");
- return rc;
- }
-
- /* ADC Digital param selection */
- qpnp_vadc_hc_update_adc_dig_param(vadc, amux_prop, &buf[0]);
-
- /* Update fast average sample value */
- buf[1] &= (u8) ~QPNP_VADC_HC1_FAST_AVG_SAMPLES_MASK;
- buf[1] |= amux_prop->fast_avg_setup;
-
- /* Select ADC channel */
- buf[2] = amux_prop->channel_num;
-
- /* Select hw settle delay for the channel */
- buf[3] &= (u8) ~QPNP_VADC_HC1_DELAY_CTL_MASK;
- buf[3] |= amux_prop->hw_settle_time;
-
- /* Select ADC enable */
- buf[4] |= QPNP_VADC_HC1_ADC_EN;
-
- /* Select CONV request */
- buf[5] |= QPNP_VADC_HC1_CONV_REQ_START;
-
- if (!vadc->vadc_poll_eoc)
- reinit_completion(&vadc->adc->adc_rslt_completion);
-
- pr_debug("dig:0x%x, fast_avg:0x%x, channel:0x%x, hw_settle:0x%x\n",
- buf[0], buf[1], buf[2], buf[3]);
-
- /* Block register write from 0x42 through 0x46 */
- rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HC1_ADC_DIG_PARAM, buf, 6);
- if (rc < 0) {
- pr_err("qpnp adc block register configure failed\n");
- return rc;
- }
-
- return 0;
-}
-
-int32_t qpnp_vadc_hc_read(struct qpnp_vadc_chip *vadc,
- enum qpnp_vadc_channels channel,
- struct qpnp_vadc_result *result)
-{
- int rc = 0, scale_type, amux_prescaling, dt_index = 0, calib_type = 0;
- struct qpnp_adc_amux amux_prop;
-
- if (qpnp_vadc_is_valid(vadc))
- return -EPROBE_DEFER;
-
- mutex_lock(&vadc->adc->adc_lock);
-
- 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) {
- pr_err("not a valid VADC channel:%d\n", channel);
- rc = -EINVAL;
- goto fail_unlock;
- }
-
- calib_type = vadc->adc->adc_channels[dt_index].calib_type;
- if (calib_type >= ADC_HC_CAL_SEL_NONE) {
- pr_err("not a valid calib_type\n");
- rc = -EINVAL;
- goto fail_unlock;
- }
-
- amux_prop.adc_decimation =
- vadc->adc->adc_channels[dt_index].adc_decimation;
- amux_prop.calib_type = vadc->adc->adc_channels[dt_index].calib_type;
- amux_prop.cal_val = vadc->adc->adc_channels[dt_index].cal_val;
- amux_prop.fast_avg_setup =
- vadc->adc->adc_channels[dt_index].fast_avg_setup;
- amux_prop.channel_num = channel;
- amux_prop.hw_settle_time =
- vadc->adc->adc_channels[dt_index].hw_settle_time;
-
- rc = qpnp_vadc_hc_configure(vadc, &amux_prop);
- if (rc < 0) {
- pr_err("Configuring VADC channel failed with %d\n", rc);
- goto fail_unlock;
- }
-
- if (vadc->vadc_poll_eoc) {
- rc = qpnp_vadc_hc_check_conversion_status(vadc);
- if (rc < 0) {
- pr_err("polling mode conversion failed\n");
- goto fail_unlock;
- }
- } else {
- rc = wait_for_completion_timeout(
- &vadc->adc->adc_rslt_completion,
- QPNP_ADC_COMPLETION_TIMEOUT);
- if (!rc) {
- rc = qpnp_vadc_hc_check_conversion_status(vadc);
- if (rc < 0) {
- pr_err("interrupt mode conversion failed\n");
- goto fail_unlock;
- }
- pr_debug("End of conversion status set\n");
- }
- }
-
- rc = qpnp_vadc_hc_read_data(vadc, &result->adc_code);
- if (rc) {
- pr_err("qpnp vadc read adc code failed with %d\n", rc);
- goto fail_unlock;
- }
-
- amux_prescaling =
- vadc->adc->adc_channels[dt_index].chan_path_prescaling;
-
- if (amux_prescaling >= PATH_SCALING_NONE) {
- rc = -EINVAL;
- goto fail_unlock;
- }
-
- 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[dt_index].adc_scale_fn;
- if (scale_type >= SCALE_NONE) {
- rc = -EBADF;
- goto fail_unlock;
- }
-
- /* Note: Scaling functions for VADC_HC do not need offset/gain */
- vadc_scale_fn[scale_type].chan(vadc, result->adc_code,
- vadc->adc->adc_prop, vadc->adc->amux_prop->chan_prop, result);
-
- pr_debug("channel=0x%x, adc_code=0x%x adc_result=%lld\n",
- channel, result->adc_code, result->physical);
-
-fail_unlock:
- mutex_unlock(&vadc->adc->adc_lock);
-
- return rc;
-}
-EXPORT_SYMBOL(qpnp_vadc_hc_read);
-
static ssize_t qpnp_adc_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
@@ -2609,7 +2694,6 @@ static int qpnp_vadc_probe(struct platform_device *pdev)
}
vadc->vadc_therm_chan = adc_thermal;
-
if (!strcmp(id->compatible, "qcom,qpnp-vadc-hc")) {
vadc->vadc_hc = true;
vadc->adc->adc_hc = true;