From: Cixi Geng <cixi.geng1@xxxxxxxxxx> The ump9620 is variant from sc27xx chip, add it in here. Signed-off-by: Yuming Zhu <yuming.zhu1@xxxxxxxxxx> Signed-off-by: Cixi Geng <cixi.geng1@xxxxxxxxxx> --- drivers/iio/adc/sc27xx_adc.c | 263 +++++++++++++++++++++++++++++++++-- 1 file changed, 254 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c index 195f44cf61e1..68b967f32498 100644 --- a/drivers/iio/adc/sc27xx_adc.c +++ b/drivers/iio/adc/sc27xx_adc.c @@ -15,12 +15,16 @@ /* PMIC global registers definition */ #define SC2730_MODULE_EN 0x1808 #define SC2731_MODULE_EN 0xc08 +#define UMP9620_MODULE_EN 0x2008 #define SC27XX_MODULE_ADC_EN BIT(5) #define SC2721_ARM_CLK_EN 0xc0c #define SC2730_ARM_CLK_EN 0x180c #define SC2731_ARM_CLK_EN 0xc10 +#define UMP9620_ARM_CLK_EN 0x200c +#define UMP9620_XTL_WAIT_CTRL0 0x2378 #define SC27XX_CLK_ADC_EN BIT(5) #define SC27XX_CLK_ADC_CLK_EN BIT(6) +#define UMP9620_XTL_WAIT_CTRL0_EN BIT(8) /* ADC controller registers definition */ #define SC27XX_ADC_CTL 0x0 @@ -82,6 +86,13 @@ enum sc27xx_pmic_type { SC27XX_ADC, SC2721_ADC, + UMP9620_ADC, +}; + +enum ump96xx_scale_cal { + UMP96XX_VBAT_SENSES_CAL, + UMP96XX_VBAT_DET_CAL, + UMP96XX_CH1_CAL, }; struct sc27xx_adc_data { @@ -140,6 +151,11 @@ static struct sc27xx_adc_linear_graph small_scale_graph = { 100, 341, }; +static struct sc27xx_adc_linear_graph ump9620_bat_det_graph = { + 1400, 3482, + 200, 476, +}; + static const struct sc27xx_adc_linear_graph sc2731_big_scale_graph_calib = { 4200, 850, 3600, 728, @@ -165,6 +181,33 @@ static int sc27xx_adc_get_calib_data(u32 calib_data, int calib_adc) return ((calib_data & 0xff) + calib_adc - 128) * 4; } +static int adc_nvmem_cell_calib_data(struct sc27xx_adc_data *data, const char *cell_name) +{ + struct nvmem_cell *cell; + void *buf; + u32 calib_data = 0; + size_t len = 0; + + if (!data) + return -EINVAL; + + cell = nvmem_cell_get(data->dev, cell_name); + if (IS_ERR_OR_NULL(cell)) + return PTR_ERR(cell); + + buf = nvmem_cell_read(cell, &len); + if (IS_ERR_OR_NULL(buf)) { + nvmem_cell_put(cell); + return PTR_ERR(buf); + } + + memcpy(&calib_data, buf, min(len, sizeof(u32))); + + kfree(buf); + nvmem_cell_put(cell); + return calib_data; +} + static int sc27xx_adc_scale_calibration(struct sc27xx_adc_data *data, bool big_scale) { @@ -207,6 +250,56 @@ static int sc27xx_adc_scale_calibration(struct sc27xx_adc_data *data, return 0; } +static int ump96xx_adc_scale_cal(struct sc27xx_adc_data *data, + enum ump96xx_scale_cal cal_type) +{ + struct sc27xx_adc_linear_graph *graph = NULL; + const char *cell_name1 = NULL, *cell_name2 = NULL; + int adc_calib_data1 = 0, adc_calib_data2 = 0; + + if (!data) + return -EINVAL; + + if (cal_type == UMP96XX_VBAT_DET_CAL) { + graph = &ump9620_bat_det_graph; + cell_name1 = "vbat_det_cal1"; + cell_name2 = "vbat_det_cal2"; + } else if (cal_type == UMP96XX_VBAT_SENSES_CAL) { + graph = &big_scale_graph; + cell_name1 = "big_scale_calib1"; + cell_name2 = "big_scale_calib2"; + } else if (cal_type == UMP96XX_CH1_CAL) { + graph = &small_scale_graph; + cell_name1 = "small_scale_calib1"; + cell_name2 = "small_scale_calib2"; + } else { + graph = &small_scale_graph; + cell_name1 = "small_scale_calib1"; + cell_name2 = "small_scale_calib2"; + } + + adc_calib_data1 = adc_nvmem_cell_calib_data(data, cell_name1); + if (adc_calib_data1 < 0) { + dev_err(data->dev, "err! %s:%d\n", cell_name1, adc_calib_data1); + return adc_calib_data1; + } + + adc_calib_data2 = adc_nvmem_cell_calib_data(data, cell_name2); + if (adc_calib_data2 < 0) { + dev_err(data->dev, "err! %s:%d\n", cell_name2, adc_calib_data2); + return adc_calib_data2; + } + + /* + *Read the data in the two blocks of efuse and convert them into the + *calibration value in the ump9620 adc linear graph. + */ + graph->adc0 = (adc_calib_data1 & 0xfff0) >> 4; + graph->adc1 = (adc_calib_data2 & 0xfff0) >> 4; + + return 0; +} + static int sc2720_adc_get_ratio(int channel, int scale) { switch (channel) { @@ -394,6 +487,50 @@ static int sc2731_adc_get_ratio(int channel, int scale) return SC27XX_VOLT_RATIO(1, 1); } +static int ump9620_adc_get_ratio(int channel, int scale) +{ + switch (channel) { + case 11: + return SC27XX_VOLT_RATIO(1, 1); + case 14: + switch (scale) { + case 0: + return SC27XX_VOLT_RATIO(68, 900); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + case 15: + switch (scale) { + case 0: + return SC27XX_VOLT_RATIO(1, 3); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + case 21: + case 22: + case 23: + switch (scale) { + case 0: + return SC27XX_VOLT_RATIO(3, 8); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + default: + switch (scale) { + case 0: + return SC27XX_VOLT_RATIO(1, 1); + case 1: + return SC27XX_VOLT_RATIO(1000, 1955); + case 2: + return SC27XX_VOLT_RATIO(1000, 2600); + case 3: + return SC27XX_VOLT_RATIO(1000, 4060); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + } +} + /* * According to the datasheet set specific value on some channel. */ @@ -453,6 +590,22 @@ static void sc2731_adc_scale_init(struct sc27xx_adc_data *data) } } +static void ump9620_adc_scale_init(struct sc27xx_adc_data *data) +{ + int i; + + for (i = 0; i < SC27XX_ADC_CHANNEL_MAX; i++) { + if (i == 10 || i == 19 || i == 30 || i == 31) + data->channel_scale[i] = 3; + else if (i == 7 || i == 9) + data->channel_scale[i] = 2; + else if (i == 0 || i == 13) + data->channel_scale[i] = 1; + else + data->channel_scale[i] = 0; + } +} + static int sc27xx_adc_read(struct sc27xx_adc_data *data, int channel, int scale, int *val) { @@ -578,6 +731,23 @@ static int sc27xx_adc_to_volt(struct sc27xx_adc_linear_graph *graph, return tmp < 0 ? 0 : tmp; } +static int ump96xx_adc_to_volt(struct sc27xx_adc_linear_graph *graph, int scale, + int raw_adc) +{ + int tmp; + + tmp = (graph->volt0 - graph->volt1) * (raw_adc - graph->adc1); + tmp /= (graph->adc0 - graph->adc1); + tmp += graph->volt1; + + if (scale == 2) + tmp = tmp * 2600 / 1000; + else if (scale == 3) + tmp = tmp * 4060 / 1000; + + return tmp < 0 ? 0 : tmp; +} + static int sc27xx_adc_convert_volt(struct sc27xx_adc_data *data, int channel, int scale, int raw_adc) { @@ -608,6 +778,39 @@ static int sc27xx_adc_convert_volt(struct sc27xx_adc_data *data, int channel, return DIV_ROUND_CLOSEST(volt * denominator, numerator); } +static int ump96xx_adc_convert_volt(struct sc27xx_adc_data *data, int channel, + int scale, int raw_adc) +{ + u32 numerator, denominator; + u32 volt; + + switch (channel) { + case 0: + if (scale == 1) + volt = sc27xx_adc_to_volt(&ump9620_bat_det_graph, raw_adc); + else + volt = ump96xx_adc_to_volt(&small_scale_graph, scale, raw_adc); + break; + case 11: + volt = sc27xx_adc_to_volt(&big_scale_graph, raw_adc); + break; + default: + if (scale == 1) + volt = sc27xx_adc_to_volt(&ump9620_bat_det_graph, raw_adc); + else + volt = ump96xx_adc_to_volt(&small_scale_graph, scale, raw_adc); + break; + } + + if (channel == 0 && scale == 1) + return volt; + + sc27xx_adc_volt_ratio(data, channel, scale, &numerator, &denominator); + + return DIV_ROUND_CLOSEST(volt * denominator, numerator); +} + + static int sc27xx_adc_read_processed(struct sc27xx_adc_data *data, int channel, int scale, int *val) { @@ -617,7 +820,11 @@ static int sc27xx_adc_read_processed(struct sc27xx_adc_data *data, if (ret) return ret; - *val = sc27xx_adc_convert_volt(data, channel, scale, raw_adc); + if (data->var_data->pmic_type == UMP9620_ADC) + *val = ump96xx_adc_convert_volt(data, channel, scale, raw_adc); + else + *val = sc27xx_adc_convert_volt(data, channel, scale, raw_adc); + return 0; } @@ -735,21 +942,42 @@ static int sc27xx_adc_enable(struct sc27xx_adc_data *data) if (ret) return ret; - /* Enable ADC work clock and controller clock */ + /* Enable 26MHz crvstal oscillator wait cycles for UMP9620 ADC */ + if (data->var_data->pmic_type == UMP9620_ADC) { + ret = regmap_update_bits(data->regmap, UMP9620_XTL_WAIT_CTRL0, + UMP9620_XTL_WAIT_CTRL0_EN, + UMP9620_XTL_WAIT_CTRL0_EN); + } + + /* Enable ADC work clock */ ret = regmap_update_bits(data->regmap, data->var_data->clk_en, SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN); if (ret) goto disable_adc; - /* ADC channel scales' calibration from nvmem device */ - ret = sc27xx_adc_scale_calibration(data, true); - if (ret) - goto disable_clk; + /* ADC channel scales calibration from nvmem device */ + if (data->var_data->pmic_type == UMP9620_ADC) { + ret = ump96xx_adc_scale_cal(data, UMP96XX_VBAT_SENSES_CAL); + if (ret) + goto disable_clk; - ret = sc27xx_adc_scale_calibration(data, false); - if (ret) - goto disable_clk; + ret = ump96xx_adc_scale_cal(data, UMP96XX_VBAT_DET_CAL); + if (ret) + goto disable_clk; + + ret = ump96xx_adc_scale_cal(data, UMP96XX_CH1_CAL); + if (ret) + goto disable_clk; + } else { + ret = sc27xx_adc_scale_calibration(data, true); + if (ret) + goto disable_clk; + + ret = sc27xx_adc_scale_calibration(data, false); + if (ret) + goto disable_clk; + } return 0; @@ -773,6 +1001,10 @@ static void sc27xx_adc_disable(void *_data) regmap_update_bits(data->regmap, data->var_data->module_en, SC27XX_MODULE_ADC_EN, 0); + + if (data->var_data->pmic_type == UMP9620_ADC) + regmap_update_bits(data->regmap, UMP9620_XTL_WAIT_CTRL0, + UMP9620_XTL_WAIT_CTRL0_EN, 0); } static const struct sc27xx_adc_variant_data sc2731_data = { @@ -823,6 +1055,18 @@ static const struct sc27xx_adc_variant_data sc2720_data = { .get_ratio = sc2720_adc_get_ratio, }; +static const struct sc27xx_adc_variant_data ump9620_data = { + .pmic_type = UMP9620_ADC, + .module_en = UMP9620_MODULE_EN, + .clk_en = UMP9620_ARM_CLK_EN, + .scale_shift = SC27XX_ADC_SCALE_SHIFT, + .scale_mask = SC27XX_ADC_SCALE_MASK, + .bscale_cal = &big_scale_graph, + .sscale_cal = &small_scale_graph, + .init_scale = ump9620_adc_scale_init, + .get_ratio = ump9620_adc_get_ratio, +}; + static int sc27xx_adc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -914,6 +1158,7 @@ static const struct of_device_id sc27xx_adc_of_match[] = { { .compatible = "sprd,sc2730-adc", .data = &sc2730_data}, { .compatible = "sprd,sc2721-adc", .data = &sc2721_data}, { .compatible = "sprd,sc2720-adc", .data = &sc2720_data}, + { .compatible = "sprd,ump9620-adc", .data = &ump9620_data}, { } }; MODULE_DEVICE_TABLE(of, sc27xx_adc_of_match); -- 2.25.1