On Thu, Jan 6, 2022 at 9:00 PM Cixi Geng <gengcixi@xxxxxxxxx> wrote: > > 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; > +} These are some duplicated code in sc27xx_adc_scale_calibration(), please factor out the sc27xx_adc_scale_calibration() firstly. > + > 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; These are also copy-paste from sc27xx_adc_to_volt(), please avoid duplicate code. > + > + 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 > -- Baolin Wang