diff mbox series

[6/7] iio: adc: sc27xx: add support for PMIC ump9620

Message ID 20220106125947.139523-7-gengcixi@gmail.com (mailing list archive)
State Changes Requested
Headers show
Series iio: adc: sc27xx: adjust structure and add PMIC's support | expand

Commit Message

Cixi Geng Jan. 6, 2022, 12:59 p.m. UTC
From: Cixi Geng <cixi.geng1@unisoc.com>

The ump9620 is variant from sc27xx chip, add it in here.

Signed-off-by: Yuming Zhu <yuming.zhu1@unisoc.com>
Signed-off-by: Cixi Geng <cixi.geng1@unisoc.com>
---
 drivers/iio/adc/sc27xx_adc.c | 263 +++++++++++++++++++++++++++++++++--
 1 file changed, 254 insertions(+), 9 deletions(-)

Comments

Baolin Wang Jan. 7, 2022, 7:23 a.m. UTC | #1
On Thu, Jan 6, 2022 at 9:00 PM Cixi Geng <gengcixi@gmail.com> wrote:
>
> From: Cixi Geng <cixi.geng1@unisoc.com>
>
> The ump9620 is variant from sc27xx chip, add it in here.
>
> Signed-off-by: Yuming Zhu <yuming.zhu1@unisoc.com>
> Signed-off-by: Cixi Geng <cixi.geng1@unisoc.com>
> ---
>  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
>
Dan Carpenter Jan. 10, 2022, 6:30 a.m. UTC | #2
Hi Cixi,

url:    https://github.com/0day-ci/linux/commits/Cixi-Geng/iio-adc-sc27xx-adjust-structure-and-add-PMIC-s-support/20220106-210151
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
config: openrisc-randconfig-m031-20220106 (https://download.01.org/0day-ci/archive/20220108/202201080313.70ZrhufR-lkp@intel.com/config)
compiler: or1k-linux-gcc (GCC) 11.2.0

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

New smatch warnings:
drivers/iio/adc/sc27xx_adc.c:196 adc_nvmem_cell_calib_data() warn: passing zero to 'PTR_ERR'

vim +/PTR_ERR +196 drivers/iio/adc/sc27xx_adc.c

87da8dcc76b4aa Cixi Geng   2022-01-06  184  static int adc_nvmem_cell_calib_data(struct sc27xx_adc_data *data, const char *cell_name)
87da8dcc76b4aa Cixi Geng   2022-01-06  185  {
87da8dcc76b4aa Cixi Geng   2022-01-06  186  	struct nvmem_cell *cell;
87da8dcc76b4aa Cixi Geng   2022-01-06  187  	void *buf;
87da8dcc76b4aa Cixi Geng   2022-01-06  188  	u32 calib_data = 0;
87da8dcc76b4aa Cixi Geng   2022-01-06  189  	size_t len = 0;
87da8dcc76b4aa Cixi Geng   2022-01-06  190  
87da8dcc76b4aa Cixi Geng   2022-01-06  191  	if (!data)
87da8dcc76b4aa Cixi Geng   2022-01-06  192  		return -EINVAL;
87da8dcc76b4aa Cixi Geng   2022-01-06  193  
87da8dcc76b4aa Cixi Geng   2022-01-06  194  	cell = nvmem_cell_get(data->dev, cell_name);
87da8dcc76b4aa Cixi Geng   2022-01-06  195  	if (IS_ERR_OR_NULL(cell))
87da8dcc76b4aa Cixi Geng   2022-01-06 @196  		return PTR_ERR(cell);

When functions return a both error pointers and NULL, then the NULL
return means that the feature has been deliberately disabled by the
admin.

nvmem_cell_get() cannot be disabled and never returns NULL so this code
should just be:

	if (IS_ERR(cell))
		return PTR_ERR(cell);

Otherwise we have to create the whole infrastructure to disable it.  We
can't just create random sprinklings of dead code to half way support
disabling the feature.

87da8dcc76b4aa Cixi Geng   2022-01-06  197  
87da8dcc76b4aa Cixi Geng   2022-01-06  198  	buf = nvmem_cell_read(cell, &len);
87da8dcc76b4aa Cixi Geng   2022-01-06  199  	if (IS_ERR_OR_NULL(buf)) {
87da8dcc76b4aa Cixi Geng   2022-01-06  200  		nvmem_cell_put(cell);
87da8dcc76b4aa Cixi Geng   2022-01-06  201  		return PTR_ERR(buf);

Same.

87da8dcc76b4aa Cixi Geng   2022-01-06  202  	}
87da8dcc76b4aa Cixi Geng   2022-01-06  203  
87da8dcc76b4aa Cixi Geng   2022-01-06  204  	memcpy(&calib_data, buf, min(len, sizeof(u32)));
87da8dcc76b4aa Cixi Geng   2022-01-06  205  
87da8dcc76b4aa Cixi Geng   2022-01-06  206  	kfree(buf);
87da8dcc76b4aa Cixi Geng   2022-01-06  207  	nvmem_cell_put(cell);
87da8dcc76b4aa Cixi Geng   2022-01-06  208  	return calib_data;
87da8dcc76b4aa Cixi Geng   2022-01-06  209  }

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

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);