xref: /linux/drivers/regulator/da9052-regulator.c (revision 5c99a7b1f0dd460140f229b9c5a89e6d5880aa81)
108bf1c0aSAshish Jangam /*
208bf1c0aSAshish Jangam * da9052-regulator.c: Regulator driver for DA9052
308bf1c0aSAshish Jangam *
408bf1c0aSAshish Jangam * Copyright(c) 2011 Dialog Semiconductor Ltd.
508bf1c0aSAshish Jangam *
608bf1c0aSAshish Jangam * Author: David Dajun Chen <dchen@diasemi.com>
708bf1c0aSAshish Jangam *
808bf1c0aSAshish Jangam * This program is free software; you can redistribute it and/or modify
908bf1c0aSAshish Jangam * it under the terms of the GNU General Public License as published by
1008bf1c0aSAshish Jangam * the Free Software Foundation; either version 2 of the License, or
1108bf1c0aSAshish Jangam * (at your option) any later version.
1208bf1c0aSAshish Jangam *
1308bf1c0aSAshish Jangam */
1408bf1c0aSAshish Jangam 
1508bf1c0aSAshish Jangam #include <linux/module.h>
1608bf1c0aSAshish Jangam #include <linux/moduleparam.h>
1708bf1c0aSAshish Jangam #include <linux/init.h>
1808bf1c0aSAshish Jangam #include <linux/err.h>
1908bf1c0aSAshish Jangam #include <linux/platform_device.h>
2008bf1c0aSAshish Jangam #include <linux/regulator/driver.h>
2108bf1c0aSAshish Jangam #include <linux/regulator/machine.h>
2288c84c14SYing-Chun Liu (PaulLiu) #ifdef CONFIG_OF
23cd22a965SMark Brown #include <linux/of.h>
2488c84c14SYing-Chun Liu (PaulLiu) #include <linux/regulator/of_regulator.h>
2588c84c14SYing-Chun Liu (PaulLiu) #endif
2608bf1c0aSAshish Jangam 
2708bf1c0aSAshish Jangam #include <linux/mfd/da9052/da9052.h>
2808bf1c0aSAshish Jangam #include <linux/mfd/da9052/reg.h>
2908bf1c0aSAshish Jangam #include <linux/mfd/da9052/pdata.h>
3008bf1c0aSAshish Jangam 
3108bf1c0aSAshish Jangam /* Buck step size */
3208bf1c0aSAshish Jangam #define DA9052_BUCK_PERI_3uV_STEP		100000
3308bf1c0aSAshish Jangam #define DA9052_BUCK_PERI_REG_MAP_UPTO_3uV	24
3408bf1c0aSAshish Jangam #define DA9052_CONST_3uV			3000000
3508bf1c0aSAshish Jangam 
3608bf1c0aSAshish Jangam #define DA9052_MIN_UA		0
3708bf1c0aSAshish Jangam #define DA9052_MAX_UA		3
3808bf1c0aSAshish Jangam #define DA9052_CURRENT_RANGE	4
3908bf1c0aSAshish Jangam 
4008bf1c0aSAshish Jangam /* Bit masks */
4108bf1c0aSAshish Jangam #define DA9052_BUCK_ILIM_MASK_EVEN	0x0c
4208bf1c0aSAshish Jangam #define DA9052_BUCK_ILIM_MASK_ODD	0xc0
4308bf1c0aSAshish Jangam 
449210f05bSAxel Lin /* DA9052 REGULATOR IDs */
459210f05bSAxel Lin #define DA9052_ID_BUCK1		0
469210f05bSAxel Lin #define DA9052_ID_BUCK2		1
479210f05bSAxel Lin #define DA9052_ID_BUCK3		2
489210f05bSAxel Lin #define DA9052_ID_BUCK4		3
499210f05bSAxel Lin #define DA9052_ID_LDO1		4
509210f05bSAxel Lin #define DA9052_ID_LDO2		5
519210f05bSAxel Lin #define DA9052_ID_LDO3		6
529210f05bSAxel Lin #define DA9052_ID_LDO4		7
539210f05bSAxel Lin #define DA9052_ID_LDO5		8
549210f05bSAxel Lin #define DA9052_ID_LDO6		9
559210f05bSAxel Lin #define DA9052_ID_LDO7		10
569210f05bSAxel Lin #define DA9052_ID_LDO8		11
579210f05bSAxel Lin #define DA9052_ID_LDO9		12
589210f05bSAxel Lin #define DA9052_ID_LDO10		13
599210f05bSAxel Lin 
6008bf1c0aSAshish Jangam static const u32 da9052_current_limits[3][4] = {
6108bf1c0aSAshish Jangam 	{700000, 800000, 1000000, 1200000},	/* DA9052-BC BUCKs */
6208bf1c0aSAshish Jangam 	{1600000, 2000000, 2400000, 3000000},	/* DA9053-AA/Bx BUCK-CORE */
6308bf1c0aSAshish Jangam 	{800000, 1000000, 1200000, 1500000},	/* DA9053-AA/Bx BUCK-PRO,
6408bf1c0aSAshish Jangam 						 * BUCK-MEM and BUCK-PERI
6508bf1c0aSAshish Jangam 						*/
6608bf1c0aSAshish Jangam };
6708bf1c0aSAshish Jangam 
6808bf1c0aSAshish Jangam struct da9052_regulator_info {
6908bf1c0aSAshish Jangam 	struct regulator_desc reg_desc;
7008bf1c0aSAshish Jangam 	int step_uV;
7108bf1c0aSAshish Jangam 	int min_uV;
7208bf1c0aSAshish Jangam 	int max_uV;
73d706b1e4SAxel Lin 	unsigned char activate_bit;
7408bf1c0aSAshish Jangam };
7508bf1c0aSAshish Jangam 
7608bf1c0aSAshish Jangam struct da9052_regulator {
7708bf1c0aSAshish Jangam 	struct da9052 *da9052;
7808bf1c0aSAshish Jangam 	struct da9052_regulator_info *info;
7908bf1c0aSAshish Jangam 	struct regulator_dev *rdev;
8008bf1c0aSAshish Jangam };
8108bf1c0aSAshish Jangam 
8208bf1c0aSAshish Jangam static int verify_range(struct da9052_regulator_info *info,
8308bf1c0aSAshish Jangam 			 int min_uV, int max_uV)
8408bf1c0aSAshish Jangam {
8508bf1c0aSAshish Jangam 	if (min_uV > info->max_uV || max_uV < info->min_uV)
8608bf1c0aSAshish Jangam 		return -EINVAL;
8708bf1c0aSAshish Jangam 
8808bf1c0aSAshish Jangam 	return 0;
8908bf1c0aSAshish Jangam }
9008bf1c0aSAshish Jangam 
9108bf1c0aSAshish Jangam static int da9052_dcdc_get_current_limit(struct regulator_dev *rdev)
9208bf1c0aSAshish Jangam {
9308bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
9408bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
9508bf1c0aSAshish Jangam 	int ret, row = 2;
9608bf1c0aSAshish Jangam 
9708bf1c0aSAshish Jangam 	ret = da9052_reg_read(regulator->da9052, DA9052_BUCKA_REG + offset/2);
9808bf1c0aSAshish Jangam 	if (ret < 0)
9908bf1c0aSAshish Jangam 		return ret;
10008bf1c0aSAshish Jangam 
10108bf1c0aSAshish Jangam 	/* Determine the even or odd position of the buck current limit
10208bf1c0aSAshish Jangam 	 * register field
10308bf1c0aSAshish Jangam 	*/
10408bf1c0aSAshish Jangam 	if (offset % 2 == 0)
10508bf1c0aSAshish Jangam 		ret = (ret & DA9052_BUCK_ILIM_MASK_EVEN) >> 2;
10608bf1c0aSAshish Jangam 	else
10708bf1c0aSAshish Jangam 		ret = (ret & DA9052_BUCK_ILIM_MASK_ODD) >> 6;
10808bf1c0aSAshish Jangam 
10908bf1c0aSAshish Jangam 	/* Select the appropriate current limit range */
11008bf1c0aSAshish Jangam 	if (regulator->da9052->chip_id == DA9052)
11108bf1c0aSAshish Jangam 		row = 0;
11208bf1c0aSAshish Jangam 	else if (offset == 0)
11308bf1c0aSAshish Jangam 		row = 1;
11408bf1c0aSAshish Jangam 
11508bf1c0aSAshish Jangam 	return da9052_current_limits[row][ret];
11608bf1c0aSAshish Jangam }
11708bf1c0aSAshish Jangam 
11808bf1c0aSAshish Jangam static int da9052_dcdc_set_current_limit(struct regulator_dev *rdev, int min_uA,
11908bf1c0aSAshish Jangam 					  int max_uA)
12008bf1c0aSAshish Jangam {
12108bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
12208bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
12308bf1c0aSAshish Jangam 	int reg_val = 0;
12408bf1c0aSAshish Jangam 	int i, row = 2;
12508bf1c0aSAshish Jangam 
12608bf1c0aSAshish Jangam 	/* Select the appropriate current limit range */
12708bf1c0aSAshish Jangam 	if (regulator->da9052->chip_id == DA9052)
12808bf1c0aSAshish Jangam 		row = 0;
12908bf1c0aSAshish Jangam 	else if (offset == 0)
13008bf1c0aSAshish Jangam 		row = 1;
13108bf1c0aSAshish Jangam 
13219d23c21SAxel Lin 	for (i = DA9052_CURRENT_RANGE - 1; i >= 0; i--) {
1331e369bcdSAxel Lin 		if ((min_uA <= da9052_current_limits[row][i]) &&
1341e369bcdSAxel Lin 		    (da9052_current_limits[row][i] <= max_uA)) {
13508bf1c0aSAshish Jangam 			reg_val = i;
13608bf1c0aSAshish Jangam 			break;
13708bf1c0aSAshish Jangam 		}
13808bf1c0aSAshish Jangam 	}
13908bf1c0aSAshish Jangam 
1401e369bcdSAxel Lin 	if (i < 0)
1411e369bcdSAxel Lin 		return -EINVAL;
1421e369bcdSAxel Lin 
14308bf1c0aSAshish Jangam 	/* Determine the even or odd position of the buck current limit
14408bf1c0aSAshish Jangam 	 * register field
14508bf1c0aSAshish Jangam 	*/
14608bf1c0aSAshish Jangam 	if (offset % 2 == 0)
14708bf1c0aSAshish Jangam 		return da9052_reg_update(regulator->da9052,
14808bf1c0aSAshish Jangam 					 DA9052_BUCKA_REG + offset/2,
14908bf1c0aSAshish Jangam 					 DA9052_BUCK_ILIM_MASK_EVEN,
15008bf1c0aSAshish Jangam 					 reg_val << 2);
15108bf1c0aSAshish Jangam 	else
15208bf1c0aSAshish Jangam 		return da9052_reg_update(regulator->da9052,
15308bf1c0aSAshish Jangam 					 DA9052_BUCKA_REG + offset/2,
15408bf1c0aSAshish Jangam 					 DA9052_BUCK_ILIM_MASK_ODD,
15508bf1c0aSAshish Jangam 					 reg_val << 6);
15608bf1c0aSAshish Jangam }
15708bf1c0aSAshish Jangam 
15808bf1c0aSAshish Jangam static int da9052_list_voltage(struct regulator_dev *rdev,
15908bf1c0aSAshish Jangam 				unsigned int selector)
16008bf1c0aSAshish Jangam {
16108bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
16208bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
1630ec446eaSAxel Lin 	int id = rdev_get_id(rdev);
16408bf1c0aSAshish Jangam 	int volt_uV;
16508bf1c0aSAshish Jangam 
1660ec446eaSAxel Lin 	if ((id == DA9052_ID_BUCK4) && (regulator->da9052->chip_id == DA9052)
1670ec446eaSAxel Lin 		&& (selector >= DA9052_BUCK_PERI_REG_MAP_UPTO_3uV)) {
1680ec446eaSAxel Lin 		volt_uV = ((DA9052_BUCK_PERI_REG_MAP_UPTO_3uV * info->step_uV)
1690ec446eaSAxel Lin 			  + info->min_uV);
1700ec446eaSAxel Lin 		volt_uV += (selector - DA9052_BUCK_PERI_REG_MAP_UPTO_3uV)
1710ec446eaSAxel Lin 				    * (DA9052_BUCK_PERI_3uV_STEP);
1720ec446eaSAxel Lin 	} else {
1730ec446eaSAxel Lin 		volt_uV = (selector * info->step_uV) + info->min_uV;
1740ec446eaSAxel Lin 	}
17508bf1c0aSAshish Jangam 
17608bf1c0aSAshish Jangam 	if (volt_uV > info->max_uV)
17708bf1c0aSAshish Jangam 		return -EINVAL;
17808bf1c0aSAshish Jangam 
17908bf1c0aSAshish Jangam 	return volt_uV;
18008bf1c0aSAshish Jangam }
18108bf1c0aSAshish Jangam 
1824923b48bSAxel Lin static int da9052_map_voltage(struct regulator_dev *rdev,
1834923b48bSAxel Lin 			      int min_uV, int max_uV)
18408bf1c0aSAshish Jangam {
18508bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
18608bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
1870ec446eaSAxel Lin 	int id = rdev_get_id(rdev);
1884923b48bSAxel Lin 	int ret, sel;
18908bf1c0aSAshish Jangam 
19008bf1c0aSAshish Jangam 	ret = verify_range(info, min_uV, max_uV);
19108bf1c0aSAshish Jangam 	if (ret < 0)
19208bf1c0aSAshish Jangam 		return ret;
19308bf1c0aSAshish Jangam 
19408bf1c0aSAshish Jangam 	if (min_uV < info->min_uV)
19508bf1c0aSAshish Jangam 		min_uV = info->min_uV;
19608bf1c0aSAshish Jangam 
1970ec446eaSAxel Lin 	if ((id == DA9052_ID_BUCK4) && (regulator->da9052->chip_id == DA9052)
1980ec446eaSAxel Lin 		&& (min_uV >= DA9052_CONST_3uV)) {
1994923b48bSAxel Lin 			sel = DA9052_BUCK_PERI_REG_MAP_UPTO_3uV +
2000ec446eaSAxel Lin 			      DIV_ROUND_UP(min_uV - DA9052_CONST_3uV,
2010ec446eaSAxel Lin 					   DA9052_BUCK_PERI_3uV_STEP);
2020ec446eaSAxel Lin 	} else {
2034923b48bSAxel Lin 		sel = DIV_ROUND_UP(min_uV - info->min_uV, info->step_uV);
2040ec446eaSAxel Lin 	}
20508bf1c0aSAshish Jangam 
2064923b48bSAxel Lin 	ret = da9052_list_voltage(rdev, sel);
20708bf1c0aSAshish Jangam 	if (ret < 0)
20808bf1c0aSAshish Jangam 		return ret;
20908bf1c0aSAshish Jangam 
2104923b48bSAxel Lin 	return sel;
2114923b48bSAxel Lin }
2124923b48bSAxel Lin 
213d706b1e4SAxel Lin static int da9052_regulator_set_voltage_sel(struct regulator_dev *rdev,
214d706b1e4SAxel Lin 					    unsigned int selector)
215d706b1e4SAxel Lin {
216d706b1e4SAxel Lin 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
217d706b1e4SAxel Lin 	struct da9052_regulator_info *info = regulator->info;
218d706b1e4SAxel Lin 	int id = rdev_get_id(rdev);
219d706b1e4SAxel Lin 	int ret;
220d706b1e4SAxel Lin 
221d706b1e4SAxel Lin 	ret = da9052_reg_update(regulator->da9052, rdev->desc->vsel_reg,
222d706b1e4SAxel Lin 				rdev->desc->vsel_mask, selector);
223d706b1e4SAxel Lin 	if (ret < 0)
224d706b1e4SAxel Lin 		return ret;
225d706b1e4SAxel Lin 
226d706b1e4SAxel Lin 	/* Some LDOs and DCDCs are DVC controlled which requires enabling of
227d706b1e4SAxel Lin 	 * the activate bit to implment the changes on the output.
228d706b1e4SAxel Lin 	 */
229d706b1e4SAxel Lin 	switch (id) {
230d706b1e4SAxel Lin 	case DA9052_ID_BUCK1:
231d706b1e4SAxel Lin 	case DA9052_ID_BUCK2:
232d706b1e4SAxel Lin 	case DA9052_ID_BUCK3:
233d706b1e4SAxel Lin 	case DA9052_ID_LDO2:
234d706b1e4SAxel Lin 	case DA9052_ID_LDO3:
235d706b1e4SAxel Lin 		ret = da9052_reg_update(regulator->da9052, DA9052_SUPPLY_REG,
236d706b1e4SAxel Lin 					info->activate_bit, info->activate_bit);
237d706b1e4SAxel Lin 		break;
238d706b1e4SAxel Lin 	}
239d706b1e4SAxel Lin 
240d706b1e4SAxel Lin 	return ret;
241d706b1e4SAxel Lin }
242d706b1e4SAxel Lin 
243*5c99a7b1SPhilipp Zabel static int da9052_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
244*5c99a7b1SPhilipp Zabel 						 unsigned int old_sel,
245*5c99a7b1SPhilipp Zabel 						 unsigned int new_sel)
246*5c99a7b1SPhilipp Zabel {
247*5c99a7b1SPhilipp Zabel 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
248*5c99a7b1SPhilipp Zabel 	struct da9052_regulator_info *info = regulator->info;
249*5c99a7b1SPhilipp Zabel 	int id = rdev_get_id(rdev);
250*5c99a7b1SPhilipp Zabel 	int ret = 0;
251*5c99a7b1SPhilipp Zabel 
252*5c99a7b1SPhilipp Zabel 	/* The DVC controlled LDOs and DCDCs ramp with 6.25mV/µs after enabling
253*5c99a7b1SPhilipp Zabel 	 * the activate bit.
254*5c99a7b1SPhilipp Zabel 	 */
255*5c99a7b1SPhilipp Zabel 	switch (id) {
256*5c99a7b1SPhilipp Zabel 	case DA9052_ID_BUCK1:
257*5c99a7b1SPhilipp Zabel 	case DA9052_ID_BUCK2:
258*5c99a7b1SPhilipp Zabel 	case DA9052_ID_BUCK3:
259*5c99a7b1SPhilipp Zabel 	case DA9052_ID_LDO2:
260*5c99a7b1SPhilipp Zabel 	case DA9052_ID_LDO3:
261*5c99a7b1SPhilipp Zabel 		ret = (new_sel - old_sel) * info->step_uV / 6250;
262*5c99a7b1SPhilipp Zabel 		break;
263*5c99a7b1SPhilipp Zabel 	}
264*5c99a7b1SPhilipp Zabel 
265*5c99a7b1SPhilipp Zabel 	return ret;
266*5c99a7b1SPhilipp Zabel }
267*5c99a7b1SPhilipp Zabel 
26808bf1c0aSAshish Jangam static struct regulator_ops da9052_dcdc_ops = {
26908bf1c0aSAshish Jangam 	.get_current_limit = da9052_dcdc_get_current_limit,
27008bf1c0aSAshish Jangam 	.set_current_limit = da9052_dcdc_set_current_limit,
27108bf1c0aSAshish Jangam 
27208bf1c0aSAshish Jangam 	.list_voltage = da9052_list_voltage,
2734923b48bSAxel Lin 	.map_voltage = da9052_map_voltage,
27409812bc4SAxel Lin 	.get_voltage_sel = regulator_get_voltage_sel_regmap,
275d706b1e4SAxel Lin 	.set_voltage_sel = da9052_regulator_set_voltage_sel,
276*5c99a7b1SPhilipp Zabel 	.set_voltage_time_sel = da9052_regulator_set_voltage_time_sel,
2770d481f74SAxel Lin 	.is_enabled = regulator_is_enabled_regmap,
2780d481f74SAxel Lin 	.enable = regulator_enable_regmap,
2790d481f74SAxel Lin 	.disable = regulator_disable_regmap,
28008bf1c0aSAshish Jangam };
28108bf1c0aSAshish Jangam 
28208bf1c0aSAshish Jangam static struct regulator_ops da9052_ldo_ops = {
28308bf1c0aSAshish Jangam 	.list_voltage = da9052_list_voltage,
2844923b48bSAxel Lin 	.map_voltage = da9052_map_voltage,
28509812bc4SAxel Lin 	.get_voltage_sel = regulator_get_voltage_sel_regmap,
286d706b1e4SAxel Lin 	.set_voltage_sel = da9052_regulator_set_voltage_sel,
287*5c99a7b1SPhilipp Zabel 	.set_voltage_time_sel = da9052_regulator_set_voltage_time_sel,
2880d481f74SAxel Lin 	.is_enabled = regulator_is_enabled_regmap,
2890d481f74SAxel Lin 	.enable = regulator_enable_regmap,
2900d481f74SAxel Lin 	.disable = regulator_disable_regmap,
29108bf1c0aSAshish Jangam };
29208bf1c0aSAshish Jangam 
29308bf1c0aSAshish Jangam #define DA9052_LDO(_id, step, min, max, sbits, ebits, abits) \
29408bf1c0aSAshish Jangam {\
29508bf1c0aSAshish Jangam 	.reg_desc = {\
2969210f05bSAxel Lin 		.name = #_id,\
29708bf1c0aSAshish Jangam 		.ops = &da9052_ldo_ops,\
29808bf1c0aSAshish Jangam 		.type = REGULATOR_VOLTAGE,\
2999210f05bSAxel Lin 		.id = DA9052_ID_##_id,\
3007b957654SAxel Lin 		.n_voltages = (max - min) / step + 1, \
30108bf1c0aSAshish Jangam 		.owner = THIS_MODULE,\
30209812bc4SAxel Lin 		.vsel_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \
30309812bc4SAxel Lin 		.vsel_mask = (1 << (sbits)) - 1,\
3040d481f74SAxel Lin 		.enable_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \
3050d481f74SAxel Lin 		.enable_mask = 1 << (ebits),\
30608bf1c0aSAshish Jangam 	},\
30708bf1c0aSAshish Jangam 	.min_uV = (min) * 1000,\
30808bf1c0aSAshish Jangam 	.max_uV = (max) * 1000,\
30908bf1c0aSAshish Jangam 	.step_uV = (step) * 1000,\
310d706b1e4SAxel Lin 	.activate_bit = (abits),\
31108bf1c0aSAshish Jangam }
31208bf1c0aSAshish Jangam 
31308bf1c0aSAshish Jangam #define DA9052_DCDC(_id, step, min, max, sbits, ebits, abits) \
31408bf1c0aSAshish Jangam {\
31508bf1c0aSAshish Jangam 	.reg_desc = {\
3169210f05bSAxel Lin 		.name = #_id,\
31708bf1c0aSAshish Jangam 		.ops = &da9052_dcdc_ops,\
31808bf1c0aSAshish Jangam 		.type = REGULATOR_VOLTAGE,\
3199210f05bSAxel Lin 		.id = DA9052_ID_##_id,\
3207b957654SAxel Lin 		.n_voltages = (max - min) / step + 1, \
32108bf1c0aSAshish Jangam 		.owner = THIS_MODULE,\
32209812bc4SAxel Lin 		.vsel_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \
32309812bc4SAxel Lin 		.vsel_mask = (1 << (sbits)) - 1,\
3240d481f74SAxel Lin 		.enable_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \
3250d481f74SAxel Lin 		.enable_mask = 1 << (ebits),\
32608bf1c0aSAshish Jangam 	},\
32708bf1c0aSAshish Jangam 	.min_uV = (min) * 1000,\
32808bf1c0aSAshish Jangam 	.max_uV = (max) * 1000,\
32908bf1c0aSAshish Jangam 	.step_uV = (step) * 1000,\
330d706b1e4SAxel Lin 	.activate_bit = (abits),\
33108bf1c0aSAshish Jangam }
33208bf1c0aSAshish Jangam 
3336242eae9SAxel Lin static struct da9052_regulator_info da9052_regulator_info[] = {
3349210f05bSAxel Lin 	DA9052_DCDC(BUCK1, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBCOREGO),
3359210f05bSAxel Lin 	DA9052_DCDC(BUCK2, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBPROGO),
3369210f05bSAxel Lin 	DA9052_DCDC(BUCK3, 25, 925, 2500, 6, 6, DA9052_SUPPLY_VBMEMGO),
3370ec446eaSAxel Lin 	DA9052_DCDC(BUCK4, 50, 1800, 3600, 5, 6, 0),
3389210f05bSAxel Lin 	DA9052_LDO(LDO1, 50, 600, 1800, 5, 6, 0),
3390ec446eaSAxel Lin 	DA9052_LDO(LDO2, 25, 600, 1800, 6, 6, DA9052_SUPPLY_VLDO2GO),
3400ec446eaSAxel Lin 	DA9052_LDO(LDO3, 25, 1725, 3300, 6, 6, DA9052_SUPPLY_VLDO3GO),
3419210f05bSAxel Lin 	DA9052_LDO(LDO4, 25, 1725, 3300, 6, 6, 0),
3429210f05bSAxel Lin 	DA9052_LDO(LDO5, 50, 1200, 3600, 6, 6, 0),
3439210f05bSAxel Lin 	DA9052_LDO(LDO6, 50, 1200, 3600, 6, 6, 0),
3449210f05bSAxel Lin 	DA9052_LDO(LDO7, 50, 1200, 3600, 6, 6, 0),
3459210f05bSAxel Lin 	DA9052_LDO(LDO8, 50, 1200, 3600, 6, 6, 0),
3469210f05bSAxel Lin 	DA9052_LDO(LDO9, 50, 1250, 3650, 6, 6, 0),
3479210f05bSAxel Lin 	DA9052_LDO(LDO10, 50, 1200, 3600, 6, 6, 0),
34808bf1c0aSAshish Jangam };
34908bf1c0aSAshish Jangam 
3506242eae9SAxel Lin static struct da9052_regulator_info da9053_regulator_info[] = {
3519210f05bSAxel Lin 	DA9052_DCDC(BUCK1, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBCOREGO),
3529210f05bSAxel Lin 	DA9052_DCDC(BUCK2, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBPROGO),
3539210f05bSAxel Lin 	DA9052_DCDC(BUCK3, 25, 925, 2500, 6, 6, DA9052_SUPPLY_VBMEMGO),
3540ec446eaSAxel Lin 	DA9052_DCDC(BUCK4, 25, 925, 2500, 6, 6, 0),
3559210f05bSAxel Lin 	DA9052_LDO(LDO1, 50, 600, 1800, 5, 6, 0),
3560ec446eaSAxel Lin 	DA9052_LDO(LDO2, 25, 600, 1800, 6, 6, DA9052_SUPPLY_VLDO2GO),
3570ec446eaSAxel Lin 	DA9052_LDO(LDO3, 25, 1725, 3300, 6, 6, DA9052_SUPPLY_VLDO3GO),
3589210f05bSAxel Lin 	DA9052_LDO(LDO4, 25, 1725, 3300, 6, 6, 0),
3599210f05bSAxel Lin 	DA9052_LDO(LDO5, 50, 1200, 3600, 6, 6, 0),
3609210f05bSAxel Lin 	DA9052_LDO(LDO6, 50, 1200, 3600, 6, 6, 0),
3619210f05bSAxel Lin 	DA9052_LDO(LDO7, 50, 1200, 3600, 6, 6, 0),
3629210f05bSAxel Lin 	DA9052_LDO(LDO8, 50, 1200, 3600, 6, 6, 0),
3639210f05bSAxel Lin 	DA9052_LDO(LDO9, 50, 1250, 3650, 6, 6, 0),
3649210f05bSAxel Lin 	DA9052_LDO(LDO10, 50, 1200, 3600, 6, 6, 0),
36508bf1c0aSAshish Jangam };
36608bf1c0aSAshish Jangam 
36708bf1c0aSAshish Jangam static inline struct da9052_regulator_info *find_regulator_info(u8 chip_id,
36808bf1c0aSAshish Jangam 								 int id)
36908bf1c0aSAshish Jangam {
37008bf1c0aSAshish Jangam 	struct da9052_regulator_info *info;
37108bf1c0aSAshish Jangam 	int i;
37208bf1c0aSAshish Jangam 
373984b5a6bSAshish Jangam 	switch (chip_id) {
374984b5a6bSAshish Jangam 	case DA9052:
37508bf1c0aSAshish Jangam 		for (i = 0; i < ARRAY_SIZE(da9052_regulator_info); i++) {
37608bf1c0aSAshish Jangam 			info = &da9052_regulator_info[i];
37708bf1c0aSAshish Jangam 			if (info->reg_desc.id == id)
37808bf1c0aSAshish Jangam 				return info;
37908bf1c0aSAshish Jangam 		}
380984b5a6bSAshish Jangam 		break;
381984b5a6bSAshish Jangam 	case DA9053_AA:
382984b5a6bSAshish Jangam 	case DA9053_BA:
383984b5a6bSAshish Jangam 	case DA9053_BB:
38408bf1c0aSAshish Jangam 		for (i = 0; i < ARRAY_SIZE(da9053_regulator_info); i++) {
38508bf1c0aSAshish Jangam 			info = &da9053_regulator_info[i];
38608bf1c0aSAshish Jangam 			if (info->reg_desc.id == id)
38708bf1c0aSAshish Jangam 				return info;
38808bf1c0aSAshish Jangam 		}
389984b5a6bSAshish Jangam 		break;
39008bf1c0aSAshish Jangam 	}
39108bf1c0aSAshish Jangam 
39208bf1c0aSAshish Jangam 	return NULL;
39308bf1c0aSAshish Jangam }
39408bf1c0aSAshish Jangam 
395a5023574SBill Pemberton static int da9052_regulator_probe(struct platform_device *pdev)
39608bf1c0aSAshish Jangam {
397c172708dSMark Brown 	struct regulator_config config = { };
39808bf1c0aSAshish Jangam 	struct da9052_regulator *regulator;
39908bf1c0aSAshish Jangam 	struct da9052 *da9052;
40008bf1c0aSAshish Jangam 	struct da9052_pdata *pdata;
40108bf1c0aSAshish Jangam 
402984b5a6bSAshish Jangam 	regulator = devm_kzalloc(&pdev->dev, sizeof(struct da9052_regulator),
403984b5a6bSAshish Jangam 				 GFP_KERNEL);
40408bf1c0aSAshish Jangam 	if (!regulator)
40508bf1c0aSAshish Jangam 		return -ENOMEM;
40608bf1c0aSAshish Jangam 
40708bf1c0aSAshish Jangam 	da9052 = dev_get_drvdata(pdev->dev.parent);
408dff91d0bSJingoo Han 	pdata = dev_get_platdata(da9052->dev);
40908bf1c0aSAshish Jangam 	regulator->da9052 = da9052;
41008bf1c0aSAshish Jangam 
41108bf1c0aSAshish Jangam 	regulator->info = find_regulator_info(regulator->da9052->chip_id,
41208bf1c0aSAshish Jangam 					      pdev->id);
41308bf1c0aSAshish Jangam 	if (regulator->info == NULL) {
41408bf1c0aSAshish Jangam 		dev_err(&pdev->dev, "invalid regulator ID specified\n");
4157eb6444fSAxel Lin 		return -EINVAL;
41608bf1c0aSAshish Jangam 	}
417c172708dSMark Brown 
418c172708dSMark Brown 	config.dev = &pdev->dev;
419c172708dSMark Brown 	config.driver_data = regulator;
4200d481f74SAxel Lin 	config.regmap = da9052->regmap;
42188c84c14SYing-Chun Liu (PaulLiu) 	if (pdata && pdata->regulators) {
42288c84c14SYing-Chun Liu (PaulLiu) 		config.init_data = pdata->regulators[pdev->id];
42388c84c14SYing-Chun Liu (PaulLiu) 	} else {
42488c84c14SYing-Chun Liu (PaulLiu) #ifdef CONFIG_OF
425c92f5dd2SAxel Lin 		struct device_node *nproot, *np;
42688c84c14SYing-Chun Liu (PaulLiu) 
427c92f5dd2SAxel Lin 		nproot = of_node_get(da9052->dev->of_node);
42888c84c14SYing-Chun Liu (PaulLiu) 		if (!nproot)
42988c84c14SYing-Chun Liu (PaulLiu) 			return -ENODEV;
43088c84c14SYing-Chun Liu (PaulLiu) 
431b327bcc0SSachin Kamat 		nproot = of_get_child_by_name(nproot, "regulators");
43288c84c14SYing-Chun Liu (PaulLiu) 		if (!nproot)
43388c84c14SYing-Chun Liu (PaulLiu) 			return -ENODEV;
43488c84c14SYing-Chun Liu (PaulLiu) 
435b86bbdddSAxel Lin 		for_each_child_of_node(nproot, np) {
43688c84c14SYing-Chun Liu (PaulLiu) 			if (!of_node_cmp(np->name,
43788c84c14SYing-Chun Liu (PaulLiu) 					 regulator->info->reg_desc.name)) {
43888c84c14SYing-Chun Liu (PaulLiu) 				config.init_data = of_get_regulator_init_data(
43988c84c14SYing-Chun Liu (PaulLiu) 					&pdev->dev, np);
440e76b9cc7SAxel Lin 				config.of_node = np;
44188c84c14SYing-Chun Liu (PaulLiu) 				break;
44288c84c14SYing-Chun Liu (PaulLiu) 			}
44388c84c14SYing-Chun Liu (PaulLiu) 		}
444c92f5dd2SAxel Lin 		of_node_put(nproot);
44588c84c14SYing-Chun Liu (PaulLiu) #endif
44688c84c14SYing-Chun Liu (PaulLiu) 	}
447c172708dSMark Brown 
448ea49a5ebSAxel Lin 	regulator->rdev = devm_regulator_register(&pdev->dev,
449ea49a5ebSAxel Lin 						  &regulator->info->reg_desc,
450c172708dSMark Brown 						  &config);
45108bf1c0aSAshish Jangam 	if (IS_ERR(regulator->rdev)) {
45208bf1c0aSAshish Jangam 		dev_err(&pdev->dev, "failed to register regulator %s\n",
45308bf1c0aSAshish Jangam 			regulator->info->reg_desc.name);
4547eb6444fSAxel Lin 		return PTR_ERR(regulator->rdev);
45508bf1c0aSAshish Jangam 	}
45608bf1c0aSAshish Jangam 
45708bf1c0aSAshish Jangam 	platform_set_drvdata(pdev, regulator);
45808bf1c0aSAshish Jangam 
45908bf1c0aSAshish Jangam 	return 0;
46008bf1c0aSAshish Jangam }
46108bf1c0aSAshish Jangam 
46208bf1c0aSAshish Jangam static struct platform_driver da9052_regulator_driver = {
46308bf1c0aSAshish Jangam 	.probe = da9052_regulator_probe,
46408bf1c0aSAshish Jangam 	.driver = {
46508bf1c0aSAshish Jangam 		.name = "da9052-regulator",
46608bf1c0aSAshish Jangam 		.owner = THIS_MODULE,
46708bf1c0aSAshish Jangam 	},
46808bf1c0aSAshish Jangam };
46908bf1c0aSAshish Jangam 
47008bf1c0aSAshish Jangam static int __init da9052_regulator_init(void)
47108bf1c0aSAshish Jangam {
47208bf1c0aSAshish Jangam 	return platform_driver_register(&da9052_regulator_driver);
47308bf1c0aSAshish Jangam }
47408bf1c0aSAshish Jangam subsys_initcall(da9052_regulator_init);
47508bf1c0aSAshish Jangam 
47608bf1c0aSAshish Jangam static void __exit da9052_regulator_exit(void)
47708bf1c0aSAshish Jangam {
47808bf1c0aSAshish Jangam 	platform_driver_unregister(&da9052_regulator_driver);
47908bf1c0aSAshish Jangam }
48008bf1c0aSAshish Jangam module_exit(da9052_regulator_exit);
48108bf1c0aSAshish Jangam 
48208bf1c0aSAshish Jangam MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
48308bf1c0aSAshish Jangam MODULE_DESCRIPTION("Power Regulator driver for Dialog DA9052 PMIC");
48408bf1c0aSAshish Jangam MODULE_LICENSE("GPL");
48508bf1c0aSAshish Jangam MODULE_ALIAS("platform:da9052-regulator");
486