diff --git a/drivers/thermal/qcom/bcl_pmic5.c b/drivers/thermal/qcom/bcl_pmic5.c index b4730211eec7..04352b2ec974 100644 --- a/drivers/thermal/qcom/bcl_pmic5.c +++ b/drivers/thermal/qcom/bcl_pmic5.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include "../thermal_core.h" @@ -91,6 +93,19 @@ static char bcl_int_names[BCL_TYPE_MAX][25] = { "bcl-lvl2", }; +enum bcl_ibat_ext_range_type { + BCL_IBAT_RANGE_LVL0, + BCL_IBAT_RANGE_LVL1, + BCL_IBAT_RANGE_LVL2, + BCL_IBAT_RANGE_MAX, +}; + +static uint32_t bcl_ibat_ext_ranges[BCL_IBAT_RANGE_MAX] = { + 10, /* default range factor */ + 20, + 25 +}; + struct bcl_device; struct bcl_peripheral_data { @@ -112,6 +127,7 @@ struct bcl_device { uint16_t fg_bcl_addr; void *ipc_log; bool ibat_ccm_enabled; + uint32_t ibat_ext_range_factor; struct bcl_peripheral_data param[BCL_TYPE_MAX]; }; @@ -192,9 +208,11 @@ static void convert_ibat_to_adc_val(int *val, int scaling_factor) if (ibat_use_qg_adc) *val = (int)div_s64(*val * 2000 * 2, scaling_factor); else if (no_bit_shift) - *val = (int)div_s64(*val * 1000, scaling_factor); + *val = (int)div_s64(*val * 1000 * bcl_ibat_ext_ranges[BCL_IBAT_RANGE_LVL0], + scaling_factor); else - *val = (int)div_s64(*val * 2000, scaling_factor); + *val = (int)div_s64(*val * 2000 * bcl_ibat_ext_ranges[BCL_IBAT_RANGE_LVL0], + scaling_factor); } @@ -204,7 +222,8 @@ static void convert_adc_to_ibat_val(int *val, int scaling_factor) if (ibat_use_qg_adc) *val = (int)div_s64(*val * scaling_factor, 2 * 1000); else - *val = (int)div_s64(*val * scaling_factor, 1000); + *val = (int)div_s64(*val * scaling_factor, + 1000 * bcl_ibat_ext_ranges[BCL_IBAT_RANGE_LVL0]); } static int8_t convert_ibat_to_ccm_val(int ibat) @@ -247,9 +266,11 @@ static int bcl_set_ibat(void *data, int low, int high) ibat_ua = thresh_value; if (bat_data->dev->ibat_ccm_enabled) - convert_ibat_to_adc_val(&thresh_value, BCL_IBAT_CCM_SCALING_UA); + convert_ibat_to_adc_val(&thresh_value, + BCL_IBAT_CCM_SCALING_UA * bat_data->dev->ibat_ext_range_factor); else - convert_ibat_to_adc_val(&thresh_value, BCL_IBAT_SCALING_UA); + convert_ibat_to_adc_val(&thresh_value, + BCL_IBAT_SCALING_UA * bat_data->dev->ibat_ext_range_factor); val = (int8_t)thresh_value; switch (bat_data->type) { case BCL_IBAT_LVL0: @@ -304,9 +325,11 @@ static int bcl_read_ibat(void *data, int *adc_value) *adc_value = bat_data->last_val; } else { if (bat_data->dev->ibat_ccm_enabled) - convert_adc_to_ibat_val(adc_value, BCL_IBAT_CCM_SCALING_UA); + convert_adc_to_ibat_val(adc_value, + BCL_IBAT_CCM_SCALING_UA * bat_data->dev->ibat_ext_range_factor); else - convert_adc_to_ibat_val(adc_value, BCL_IBAT_SCALING_UA); + convert_adc_to_ibat_val(adc_value, + BCL_IBAT_SCALING_UA * bat_data->dev->ibat_ext_range_factor); bat_data->last_val = *adc_value; } pr_debug("ibat:%d mA ADC:0x%02x\n", bat_data->last_val, val); @@ -518,6 +541,56 @@ static irqreturn_t bcl_handle_irq(int irq, void *data) return IRQ_HANDLED; } +static int bcl_get_ibat_ext_range_factor(struct platform_device *pdev, + uint32_t *ibat_range_factor) +{ + int ret = 0; + const char *name; + struct nvmem_cell *cell; + size_t len; + char *buf; + uint32_t ext_range_index = 0; + + ret = of_property_read_string(pdev->dev.of_node, "nvmem-cell-names", &name); + if (ret) { + *ibat_range_factor = bcl_ibat_ext_ranges[BCL_IBAT_RANGE_LVL0]; + pr_debug("Default ibat range factor enabled %u\n", *ibat_range_factor); + return 0; + } + + cell = nvmem_cell_get(&pdev->dev, name); + if (IS_ERR(cell)) { + dev_err(&pdev->dev, "failed to get nvmem cell %s\n", name); + return PTR_ERR(cell); + } + + buf = nvmem_cell_read(cell, &len); + nvmem_cell_put(cell); + if (IS_ERR_OR_NULL(buf)) { + dev_err(&pdev->dev, "failed to read nvmem cell %s\n", name); + return PTR_ERR(buf); + } + + if (len <= 0 || len > sizeof(uint32_t)) { + dev_err(&pdev->dev, "nvmem cell length out of range %d\n", len); + kfree(buf); + return -EINVAL; + } + memcpy(&ext_range_index, buf, min(len, sizeof(ext_range_index))); + kfree(buf); + + if (ext_range_index >= BCL_IBAT_RANGE_MAX) { + dev_err(&pdev->dev, "invalid BCL ibat scaling factor %d\n", ext_range_index); + return -EINVAL; + } + + *ibat_range_factor = bcl_ibat_ext_ranges[ext_range_index]; + pr_debug("ext_range_index %u, ibat range factor %u\n", + ext_range_index, *ibat_range_factor); + + return 0; +} + static int bcl_get_devicetree_data(struct platform_device *pdev, struct bcl_device *bcl_perph) { @@ -541,6 +614,9 @@ static int bcl_get_devicetree_data(struct platform_device *pdev, bcl_perph->ibat_ccm_enabled = of_property_read_bool(dev_node, "qcom,ibat-ccm-hw-support"); + ret = bcl_get_ibat_ext_range_factor(pdev, + &bcl_perph->ibat_ext_range_factor); + return ret; } @@ -701,6 +777,7 @@ static int bcl_probe(struct platform_device *pdev) { struct bcl_device *bcl_perph = NULL; char bcl_name[40]; + int err = 0; if (bcl_device_ct >= MAX_PERPH_COUNT) { dev_err(&pdev->dev, "Max bcl peripheral supported already.\n"); @@ -720,7 +797,11 @@ static int bcl_probe(struct platform_device *pdev) } bcl_device_ct++; - bcl_get_devicetree_data(pdev, bcl_perph); + err = bcl_get_devicetree_data(pdev, bcl_perph); + if (err) { + bcl_device_ct--; + return err; + } bcl_probe_vbat(pdev, bcl_perph); bcl_probe_ibat(pdev, bcl_perph); bcl_probe_lvls(pdev, bcl_perph);