xref: /openbmc/linux/drivers/regulator/da9052-regulator.c (revision 88c84c14cca44d9409f1733dfdecc1f473463f20)
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>
22*88c84c14SYing-Chun Liu (PaulLiu) #ifdef CONFIG_OF
23*88c84c14SYing-Chun Liu (PaulLiu) #include <linux/regulator/of_regulator.h>
24*88c84c14SYing-Chun Liu (PaulLiu) #endif
2508bf1c0aSAshish Jangam 
2608bf1c0aSAshish Jangam #include <linux/mfd/da9052/da9052.h>
2708bf1c0aSAshish Jangam #include <linux/mfd/da9052/reg.h>
2808bf1c0aSAshish Jangam #include <linux/mfd/da9052/pdata.h>
2908bf1c0aSAshish Jangam 
3008bf1c0aSAshish Jangam /* Buck step size */
3108bf1c0aSAshish Jangam #define DA9052_BUCK_PERI_3uV_STEP		100000
3208bf1c0aSAshish Jangam #define DA9052_BUCK_PERI_REG_MAP_UPTO_3uV	24
3308bf1c0aSAshish Jangam #define DA9052_CONST_3uV			3000000
3408bf1c0aSAshish Jangam 
3508bf1c0aSAshish Jangam #define DA9052_MIN_UA		0
3608bf1c0aSAshish Jangam #define DA9052_MAX_UA		3
3708bf1c0aSAshish Jangam #define DA9052_CURRENT_RANGE	4
3808bf1c0aSAshish Jangam 
3908bf1c0aSAshish Jangam /* Bit masks */
4008bf1c0aSAshish Jangam #define DA9052_BUCK_ILIM_MASK_EVEN	0x0c
4108bf1c0aSAshish Jangam #define DA9052_BUCK_ILIM_MASK_ODD	0xc0
4208bf1c0aSAshish Jangam 
439210f05bSAxel Lin /* DA9052 REGULATOR IDs */
449210f05bSAxel Lin #define DA9052_ID_BUCK1		0
459210f05bSAxel Lin #define DA9052_ID_BUCK2		1
469210f05bSAxel Lin #define DA9052_ID_BUCK3		2
479210f05bSAxel Lin #define DA9052_ID_BUCK4		3
489210f05bSAxel Lin #define DA9052_ID_LDO1		4
499210f05bSAxel Lin #define DA9052_ID_LDO2		5
509210f05bSAxel Lin #define DA9052_ID_LDO3		6
519210f05bSAxel Lin #define DA9052_ID_LDO4		7
529210f05bSAxel Lin #define DA9052_ID_LDO5		8
539210f05bSAxel Lin #define DA9052_ID_LDO6		9
549210f05bSAxel Lin #define DA9052_ID_LDO7		10
559210f05bSAxel Lin #define DA9052_ID_LDO8		11
569210f05bSAxel Lin #define DA9052_ID_LDO9		12
579210f05bSAxel Lin #define DA9052_ID_LDO10		13
589210f05bSAxel Lin 
5908bf1c0aSAshish Jangam static const u32 da9052_current_limits[3][4] = {
6008bf1c0aSAshish Jangam 	{700000, 800000, 1000000, 1200000},	/* DA9052-BC BUCKs */
6108bf1c0aSAshish Jangam 	{1600000, 2000000, 2400000, 3000000},	/* DA9053-AA/Bx BUCK-CORE */
6208bf1c0aSAshish Jangam 	{800000, 1000000, 1200000, 1500000},	/* DA9053-AA/Bx BUCK-PRO,
6308bf1c0aSAshish Jangam 						 * BUCK-MEM and BUCK-PERI
6408bf1c0aSAshish Jangam 						*/
6508bf1c0aSAshish Jangam };
6608bf1c0aSAshish Jangam 
6708bf1c0aSAshish Jangam struct da9052_regulator_info {
6808bf1c0aSAshish Jangam 	struct regulator_desc reg_desc;
6908bf1c0aSAshish Jangam 	int step_uV;
7008bf1c0aSAshish Jangam 	int min_uV;
7108bf1c0aSAshish Jangam 	int max_uV;
7208bf1c0aSAshish Jangam 	unsigned char volt_shift;
7308bf1c0aSAshish Jangam 	unsigned char en_bit;
7408bf1c0aSAshish Jangam 	unsigned char activate_bit;
7508bf1c0aSAshish Jangam };
7608bf1c0aSAshish Jangam 
7708bf1c0aSAshish Jangam struct da9052_regulator {
7808bf1c0aSAshish Jangam 	struct da9052 *da9052;
7908bf1c0aSAshish Jangam 	struct da9052_regulator_info *info;
8008bf1c0aSAshish Jangam 	struct regulator_dev *rdev;
8108bf1c0aSAshish Jangam };
8208bf1c0aSAshish Jangam 
8308bf1c0aSAshish Jangam static int verify_range(struct da9052_regulator_info *info,
8408bf1c0aSAshish Jangam 			 int min_uV, int max_uV)
8508bf1c0aSAshish Jangam {
8608bf1c0aSAshish Jangam 	if (min_uV > info->max_uV || max_uV < info->min_uV)
8708bf1c0aSAshish Jangam 		return -EINVAL;
8808bf1c0aSAshish Jangam 
8908bf1c0aSAshish Jangam 	return 0;
9008bf1c0aSAshish Jangam }
9108bf1c0aSAshish Jangam 
9208bf1c0aSAshish Jangam static int da9052_regulator_enable(struct regulator_dev *rdev)
9308bf1c0aSAshish Jangam {
9408bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
9508bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
9608bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
9708bf1c0aSAshish Jangam 
9808bf1c0aSAshish Jangam 	return da9052_reg_update(regulator->da9052,
9908bf1c0aSAshish Jangam 				 DA9052_BUCKCORE_REG + offset,
10008bf1c0aSAshish Jangam 				 1 << info->en_bit, 1 << info->en_bit);
10108bf1c0aSAshish Jangam }
10208bf1c0aSAshish Jangam 
10308bf1c0aSAshish Jangam static int da9052_regulator_disable(struct regulator_dev *rdev)
10408bf1c0aSAshish Jangam {
10508bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
10608bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
10708bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
10808bf1c0aSAshish Jangam 
10908bf1c0aSAshish Jangam 	return da9052_reg_update(regulator->da9052,
11008bf1c0aSAshish Jangam 				 DA9052_BUCKCORE_REG + offset,
11108bf1c0aSAshish Jangam 				 1 << info->en_bit, 0);
11208bf1c0aSAshish Jangam }
11308bf1c0aSAshish Jangam 
11408bf1c0aSAshish Jangam static int da9052_regulator_is_enabled(struct regulator_dev *rdev)
11508bf1c0aSAshish Jangam {
11608bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
11708bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
11808bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
11908bf1c0aSAshish Jangam 	int ret;
12008bf1c0aSAshish Jangam 
12108bf1c0aSAshish Jangam 	ret = da9052_reg_read(regulator->da9052, DA9052_BUCKCORE_REG + offset);
12208bf1c0aSAshish Jangam 	if (ret < 0)
12308bf1c0aSAshish Jangam 		return ret;
12408bf1c0aSAshish Jangam 
12508bf1c0aSAshish Jangam 	return ret & (1 << info->en_bit);
12608bf1c0aSAshish Jangam }
12708bf1c0aSAshish Jangam 
12808bf1c0aSAshish Jangam static int da9052_dcdc_get_current_limit(struct regulator_dev *rdev)
12908bf1c0aSAshish Jangam {
13008bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
13108bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
13208bf1c0aSAshish Jangam 	int ret, row = 2;
13308bf1c0aSAshish Jangam 
13408bf1c0aSAshish Jangam 	ret = da9052_reg_read(regulator->da9052, DA9052_BUCKA_REG + offset/2);
13508bf1c0aSAshish Jangam 	if (ret < 0)
13608bf1c0aSAshish Jangam 		return ret;
13708bf1c0aSAshish Jangam 
13808bf1c0aSAshish Jangam 	/* Determine the even or odd position of the buck current limit
13908bf1c0aSAshish Jangam 	 * register field
14008bf1c0aSAshish Jangam 	*/
14108bf1c0aSAshish Jangam 	if (offset % 2 == 0)
14208bf1c0aSAshish Jangam 		ret = (ret & DA9052_BUCK_ILIM_MASK_EVEN) >> 2;
14308bf1c0aSAshish Jangam 	else
14408bf1c0aSAshish Jangam 		ret = (ret & DA9052_BUCK_ILIM_MASK_ODD) >> 6;
14508bf1c0aSAshish Jangam 
14608bf1c0aSAshish Jangam 	/* Select the appropriate current limit range */
14708bf1c0aSAshish Jangam 	if (regulator->da9052->chip_id == DA9052)
14808bf1c0aSAshish Jangam 		row = 0;
14908bf1c0aSAshish Jangam 	else if (offset == 0)
15008bf1c0aSAshish Jangam 		row = 1;
15108bf1c0aSAshish Jangam 
15208bf1c0aSAshish Jangam 	return da9052_current_limits[row][ret];
15308bf1c0aSAshish Jangam }
15408bf1c0aSAshish Jangam 
15508bf1c0aSAshish Jangam static int da9052_dcdc_set_current_limit(struct regulator_dev *rdev, int min_uA,
15608bf1c0aSAshish Jangam 					  int max_uA)
15708bf1c0aSAshish Jangam {
15808bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
15908bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
16008bf1c0aSAshish Jangam 	int reg_val = 0;
16108bf1c0aSAshish Jangam 	int i, row = 2;
16208bf1c0aSAshish Jangam 
16308bf1c0aSAshish Jangam 	/* Select the appropriate current limit range */
16408bf1c0aSAshish Jangam 	if (regulator->da9052->chip_id == DA9052)
16508bf1c0aSAshish Jangam 		row = 0;
16608bf1c0aSAshish Jangam 	else if (offset == 0)
16708bf1c0aSAshish Jangam 		row = 1;
16808bf1c0aSAshish Jangam 
16908bf1c0aSAshish Jangam 	if (min_uA > da9052_current_limits[row][DA9052_MAX_UA] ||
17008bf1c0aSAshish Jangam 	    max_uA < da9052_current_limits[row][DA9052_MIN_UA])
17108bf1c0aSAshish Jangam 		return -EINVAL;
17208bf1c0aSAshish Jangam 
17308bf1c0aSAshish Jangam 	for (i = 0; i < DA9052_CURRENT_RANGE; i++) {
17408bf1c0aSAshish Jangam 		if (min_uA <= da9052_current_limits[row][i]) {
17508bf1c0aSAshish Jangam 			reg_val = i;
17608bf1c0aSAshish Jangam 			break;
17708bf1c0aSAshish Jangam 		}
17808bf1c0aSAshish Jangam 	}
17908bf1c0aSAshish Jangam 
18008bf1c0aSAshish Jangam 	/* Determine the even or odd position of the buck current limit
18108bf1c0aSAshish Jangam 	 * register field
18208bf1c0aSAshish Jangam 	*/
18308bf1c0aSAshish Jangam 	if (offset % 2 == 0)
18408bf1c0aSAshish Jangam 		return da9052_reg_update(regulator->da9052,
18508bf1c0aSAshish Jangam 					 DA9052_BUCKA_REG + offset/2,
18608bf1c0aSAshish Jangam 					 DA9052_BUCK_ILIM_MASK_EVEN,
18708bf1c0aSAshish Jangam 					 reg_val << 2);
18808bf1c0aSAshish Jangam 	else
18908bf1c0aSAshish Jangam 		return da9052_reg_update(regulator->da9052,
19008bf1c0aSAshish Jangam 					 DA9052_BUCKA_REG + offset/2,
19108bf1c0aSAshish Jangam 					 DA9052_BUCK_ILIM_MASK_ODD,
19208bf1c0aSAshish Jangam 					 reg_val << 6);
19308bf1c0aSAshish Jangam }
19408bf1c0aSAshish Jangam 
19508bf1c0aSAshish Jangam static int da9052_list_voltage(struct regulator_dev *rdev,
19608bf1c0aSAshish Jangam 				unsigned int selector)
19708bf1c0aSAshish Jangam {
19808bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
19908bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
2000ec446eaSAxel Lin 	int id = rdev_get_id(rdev);
20108bf1c0aSAshish Jangam 	int volt_uV;
20208bf1c0aSAshish Jangam 
2030ec446eaSAxel Lin 	if ((id == DA9052_ID_BUCK4) && (regulator->da9052->chip_id == DA9052)
2040ec446eaSAxel Lin 		&& (selector >= DA9052_BUCK_PERI_REG_MAP_UPTO_3uV)) {
2050ec446eaSAxel Lin 		volt_uV = ((DA9052_BUCK_PERI_REG_MAP_UPTO_3uV * info->step_uV)
2060ec446eaSAxel Lin 			  + info->min_uV);
2070ec446eaSAxel Lin 		volt_uV += (selector - DA9052_BUCK_PERI_REG_MAP_UPTO_3uV)
2080ec446eaSAxel Lin 				    * (DA9052_BUCK_PERI_3uV_STEP);
2090ec446eaSAxel Lin 	} else {
2100ec446eaSAxel Lin 		volt_uV = (selector * info->step_uV) + info->min_uV;
2110ec446eaSAxel Lin 	}
21208bf1c0aSAshish Jangam 
21308bf1c0aSAshish Jangam 	if (volt_uV > info->max_uV)
21408bf1c0aSAshish Jangam 		return -EINVAL;
21508bf1c0aSAshish Jangam 
21608bf1c0aSAshish Jangam 	return volt_uV;
21708bf1c0aSAshish Jangam }
21808bf1c0aSAshish Jangam 
2190ec446eaSAxel Lin static int da9052_regulator_set_voltage(struct regulator_dev *rdev,
22008bf1c0aSAshish Jangam 					     int min_uV, int max_uV,
22108bf1c0aSAshish Jangam 					     unsigned int *selector)
22208bf1c0aSAshish Jangam {
22308bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
22408bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
2250ec446eaSAxel Lin 	int id = rdev_get_id(rdev);
22608bf1c0aSAshish Jangam 	int ret;
22708bf1c0aSAshish Jangam 
22808bf1c0aSAshish Jangam 	ret = verify_range(info, min_uV, max_uV);
22908bf1c0aSAshish Jangam 	if (ret < 0)
23008bf1c0aSAshish Jangam 		return ret;
23108bf1c0aSAshish Jangam 
23208bf1c0aSAshish Jangam 	if (min_uV < info->min_uV)
23308bf1c0aSAshish Jangam 		min_uV = info->min_uV;
23408bf1c0aSAshish Jangam 
2350ec446eaSAxel Lin 	if ((id == DA9052_ID_BUCK4) && (regulator->da9052->chip_id == DA9052)
2360ec446eaSAxel Lin 		&& (min_uV >= DA9052_CONST_3uV)) {
2370ec446eaSAxel Lin 			*selector = DA9052_BUCK_PERI_REG_MAP_UPTO_3uV +
2380ec446eaSAxel Lin 				    DIV_ROUND_UP(min_uV - DA9052_CONST_3uV,
2390ec446eaSAxel Lin 						 DA9052_BUCK_PERI_3uV_STEP);
2400ec446eaSAxel Lin 	} else {
24193651218SAxel Lin 		*selector = DIV_ROUND_UP(min_uV - info->min_uV, info->step_uV);
2420ec446eaSAxel Lin 	}
24308bf1c0aSAshish Jangam 
24408bf1c0aSAshish Jangam 	ret = da9052_list_voltage(rdev, *selector);
24508bf1c0aSAshish Jangam 	if (ret < 0)
24608bf1c0aSAshish Jangam 		return ret;
24708bf1c0aSAshish Jangam 
2480ec446eaSAxel Lin 	ret = da9052_reg_update(regulator->da9052,
2490ec446eaSAxel Lin 				 DA9052_BUCKCORE_REG + id,
25008bf1c0aSAshish Jangam 				 (1 << info->volt_shift) - 1, *selector);
25108bf1c0aSAshish Jangam 	if (ret < 0)
25208bf1c0aSAshish Jangam 		return ret;
25308bf1c0aSAshish Jangam 
2540ec446eaSAxel Lin 	/* Some LDOs and DCDCs are DVC controlled which requires enabling of
2550ec446eaSAxel Lin 	 * the activate bit to implment the changes on the output.
25608bf1c0aSAshish Jangam 	 */
2570ec446eaSAxel Lin 	switch (id) {
2580ec446eaSAxel Lin 	case DA9052_ID_BUCK1:
2590ec446eaSAxel Lin 	case DA9052_ID_BUCK2:
2600ec446eaSAxel Lin 	case DA9052_ID_BUCK3:
2610ec446eaSAxel Lin 	case DA9052_ID_LDO2:
2620ec446eaSAxel Lin 	case DA9052_ID_LDO3:
2630ec446eaSAxel Lin 		ret = da9052_reg_update(regulator->da9052, DA9052_SUPPLY_REG,
2644adf9bedSAxel Lin 					info->activate_bit, info->activate_bit);
2650ec446eaSAxel Lin 		break;
26608bf1c0aSAshish Jangam 	}
26708bf1c0aSAshish Jangam 
26808bf1c0aSAshish Jangam 	return ret;
26908bf1c0aSAshish Jangam }
27008bf1c0aSAshish Jangam 
27108bf1c0aSAshish Jangam static int da9052_get_regulator_voltage_sel(struct regulator_dev *rdev)
27208bf1c0aSAshish Jangam {
27308bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
27408bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
27508bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
27608bf1c0aSAshish Jangam 	int ret;
27708bf1c0aSAshish Jangam 
27808bf1c0aSAshish Jangam 	ret = da9052_reg_read(regulator->da9052, DA9052_BUCKCORE_REG + offset);
27908bf1c0aSAshish Jangam 	if (ret < 0)
28008bf1c0aSAshish Jangam 		return ret;
28108bf1c0aSAshish Jangam 
28208bf1c0aSAshish Jangam 	ret &= ((1 << info->volt_shift) - 1);
28308bf1c0aSAshish Jangam 
28408bf1c0aSAshish Jangam 	return ret;
28508bf1c0aSAshish Jangam }
28608bf1c0aSAshish Jangam 
28708bf1c0aSAshish Jangam static struct regulator_ops da9052_dcdc_ops = {
2880ec446eaSAxel Lin 	.set_voltage = da9052_regulator_set_voltage,
28908bf1c0aSAshish Jangam 	.get_current_limit = da9052_dcdc_get_current_limit,
29008bf1c0aSAshish Jangam 	.set_current_limit = da9052_dcdc_set_current_limit,
29108bf1c0aSAshish Jangam 
29208bf1c0aSAshish Jangam 	.list_voltage = da9052_list_voltage,
29308bf1c0aSAshish Jangam 	.get_voltage_sel = da9052_get_regulator_voltage_sel,
29408bf1c0aSAshish Jangam 	.is_enabled = da9052_regulator_is_enabled,
29508bf1c0aSAshish Jangam 	.enable = da9052_regulator_enable,
29608bf1c0aSAshish Jangam 	.disable = da9052_regulator_disable,
29708bf1c0aSAshish Jangam };
29808bf1c0aSAshish Jangam 
29908bf1c0aSAshish Jangam static struct regulator_ops da9052_ldo_ops = {
3000ec446eaSAxel Lin 	.set_voltage = da9052_regulator_set_voltage,
30108bf1c0aSAshish Jangam 
30208bf1c0aSAshish Jangam 	.list_voltage = da9052_list_voltage,
30308bf1c0aSAshish Jangam 	.get_voltage_sel = da9052_get_regulator_voltage_sel,
30408bf1c0aSAshish Jangam 	.is_enabled = da9052_regulator_is_enabled,
30508bf1c0aSAshish Jangam 	.enable = da9052_regulator_enable,
30608bf1c0aSAshish Jangam 	.disable = da9052_regulator_disable,
30708bf1c0aSAshish Jangam };
30808bf1c0aSAshish Jangam 
30908bf1c0aSAshish Jangam #define DA9052_LDO(_id, step, min, max, sbits, ebits, abits) \
31008bf1c0aSAshish Jangam {\
31108bf1c0aSAshish Jangam 	.reg_desc = {\
3129210f05bSAxel Lin 		.name = #_id,\
31308bf1c0aSAshish Jangam 		.ops = &da9052_ldo_ops,\
31408bf1c0aSAshish Jangam 		.type = REGULATOR_VOLTAGE,\
3159210f05bSAxel Lin 		.id = DA9052_ID_##_id,\
3167b957654SAxel Lin 		.n_voltages = (max - min) / step + 1, \
31708bf1c0aSAshish Jangam 		.owner = THIS_MODULE,\
31808bf1c0aSAshish Jangam 	},\
31908bf1c0aSAshish Jangam 	.min_uV = (min) * 1000,\
32008bf1c0aSAshish Jangam 	.max_uV = (max) * 1000,\
32108bf1c0aSAshish Jangam 	.step_uV = (step) * 1000,\
32208bf1c0aSAshish Jangam 	.volt_shift = (sbits),\
32308bf1c0aSAshish Jangam 	.en_bit = (ebits),\
32408bf1c0aSAshish Jangam 	.activate_bit = (abits),\
32508bf1c0aSAshish Jangam }
32608bf1c0aSAshish Jangam 
32708bf1c0aSAshish Jangam #define DA9052_DCDC(_id, step, min, max, sbits, ebits, abits) \
32808bf1c0aSAshish Jangam {\
32908bf1c0aSAshish Jangam 	.reg_desc = {\
3309210f05bSAxel Lin 		.name = #_id,\
33108bf1c0aSAshish Jangam 		.ops = &da9052_dcdc_ops,\
33208bf1c0aSAshish Jangam 		.type = REGULATOR_VOLTAGE,\
3339210f05bSAxel Lin 		.id = DA9052_ID_##_id,\
3347b957654SAxel Lin 		.n_voltages = (max - min) / step + 1, \
33508bf1c0aSAshish Jangam 		.owner = THIS_MODULE,\
33608bf1c0aSAshish Jangam 	},\
33708bf1c0aSAshish Jangam 	.min_uV = (min) * 1000,\
33808bf1c0aSAshish Jangam 	.max_uV = (max) * 1000,\
33908bf1c0aSAshish Jangam 	.step_uV = (step) * 1000,\
34008bf1c0aSAshish Jangam 	.volt_shift = (sbits),\
34108bf1c0aSAshish Jangam 	.en_bit = (ebits),\
34208bf1c0aSAshish Jangam 	.activate_bit = (abits),\
34308bf1c0aSAshish Jangam }
34408bf1c0aSAshish Jangam 
3456242eae9SAxel Lin static struct da9052_regulator_info da9052_regulator_info[] = {
3469210f05bSAxel Lin 	DA9052_DCDC(BUCK1, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBCOREGO),
3479210f05bSAxel Lin 	DA9052_DCDC(BUCK2, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBPROGO),
3489210f05bSAxel Lin 	DA9052_DCDC(BUCK3, 25, 925, 2500, 6, 6, DA9052_SUPPLY_VBMEMGO),
3490ec446eaSAxel Lin 	DA9052_DCDC(BUCK4, 50, 1800, 3600, 5, 6, 0),
3509210f05bSAxel Lin 	DA9052_LDO(LDO1, 50, 600, 1800, 5, 6, 0),
3510ec446eaSAxel Lin 	DA9052_LDO(LDO2, 25, 600, 1800, 6, 6, DA9052_SUPPLY_VLDO2GO),
3520ec446eaSAxel Lin 	DA9052_LDO(LDO3, 25, 1725, 3300, 6, 6, DA9052_SUPPLY_VLDO3GO),
3539210f05bSAxel Lin 	DA9052_LDO(LDO4, 25, 1725, 3300, 6, 6, 0),
3549210f05bSAxel Lin 	DA9052_LDO(LDO5, 50, 1200, 3600, 6, 6, 0),
3559210f05bSAxel Lin 	DA9052_LDO(LDO6, 50, 1200, 3600, 6, 6, 0),
3569210f05bSAxel Lin 	DA9052_LDO(LDO7, 50, 1200, 3600, 6, 6, 0),
3579210f05bSAxel Lin 	DA9052_LDO(LDO8, 50, 1200, 3600, 6, 6, 0),
3589210f05bSAxel Lin 	DA9052_LDO(LDO9, 50, 1250, 3650, 6, 6, 0),
3599210f05bSAxel Lin 	DA9052_LDO(LDO10, 50, 1200, 3600, 6, 6, 0),
36008bf1c0aSAshish Jangam };
36108bf1c0aSAshish Jangam 
3626242eae9SAxel Lin static struct da9052_regulator_info da9053_regulator_info[] = {
3639210f05bSAxel Lin 	DA9052_DCDC(BUCK1, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBCOREGO),
3649210f05bSAxel Lin 	DA9052_DCDC(BUCK2, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBPROGO),
3659210f05bSAxel Lin 	DA9052_DCDC(BUCK3, 25, 925, 2500, 6, 6, DA9052_SUPPLY_VBMEMGO),
3660ec446eaSAxel Lin 	DA9052_DCDC(BUCK4, 25, 925, 2500, 6, 6, 0),
3679210f05bSAxel Lin 	DA9052_LDO(LDO1, 50, 600, 1800, 5, 6, 0),
3680ec446eaSAxel Lin 	DA9052_LDO(LDO2, 25, 600, 1800, 6, 6, DA9052_SUPPLY_VLDO2GO),
3690ec446eaSAxel Lin 	DA9052_LDO(LDO3, 25, 1725, 3300, 6, 6, DA9052_SUPPLY_VLDO3GO),
3709210f05bSAxel Lin 	DA9052_LDO(LDO4, 25, 1725, 3300, 6, 6, 0),
3719210f05bSAxel Lin 	DA9052_LDO(LDO5, 50, 1200, 3600, 6, 6, 0),
3729210f05bSAxel Lin 	DA9052_LDO(LDO6, 50, 1200, 3600, 6, 6, 0),
3739210f05bSAxel Lin 	DA9052_LDO(LDO7, 50, 1200, 3600, 6, 6, 0),
3749210f05bSAxel Lin 	DA9052_LDO(LDO8, 50, 1200, 3600, 6, 6, 0),
3759210f05bSAxel Lin 	DA9052_LDO(LDO9, 50, 1250, 3650, 6, 6, 0),
3769210f05bSAxel Lin 	DA9052_LDO(LDO10, 50, 1200, 3600, 6, 6, 0),
37708bf1c0aSAshish Jangam };
37808bf1c0aSAshish Jangam 
37908bf1c0aSAshish Jangam static inline struct da9052_regulator_info *find_regulator_info(u8 chip_id,
38008bf1c0aSAshish Jangam 								 int id)
38108bf1c0aSAshish Jangam {
38208bf1c0aSAshish Jangam 	struct da9052_regulator_info *info;
38308bf1c0aSAshish Jangam 	int i;
38408bf1c0aSAshish Jangam 
385984b5a6bSAshish Jangam 	switch (chip_id) {
386984b5a6bSAshish Jangam 	case DA9052:
38708bf1c0aSAshish Jangam 		for (i = 0; i < ARRAY_SIZE(da9052_regulator_info); i++) {
38808bf1c0aSAshish Jangam 			info = &da9052_regulator_info[i];
38908bf1c0aSAshish Jangam 			if (info->reg_desc.id == id)
39008bf1c0aSAshish Jangam 				return info;
39108bf1c0aSAshish Jangam 		}
392984b5a6bSAshish Jangam 		break;
393984b5a6bSAshish Jangam 	case DA9053_AA:
394984b5a6bSAshish Jangam 	case DA9053_BA:
395984b5a6bSAshish Jangam 	case DA9053_BB:
39608bf1c0aSAshish Jangam 		for (i = 0; i < ARRAY_SIZE(da9053_regulator_info); i++) {
39708bf1c0aSAshish Jangam 			info = &da9053_regulator_info[i];
39808bf1c0aSAshish Jangam 			if (info->reg_desc.id == id)
39908bf1c0aSAshish Jangam 				return info;
40008bf1c0aSAshish Jangam 		}
401984b5a6bSAshish Jangam 		break;
40208bf1c0aSAshish Jangam 	}
40308bf1c0aSAshish Jangam 
40408bf1c0aSAshish Jangam 	return NULL;
40508bf1c0aSAshish Jangam }
40608bf1c0aSAshish Jangam 
40708bf1c0aSAshish Jangam static int __devinit da9052_regulator_probe(struct platform_device *pdev)
40808bf1c0aSAshish Jangam {
409c172708dSMark Brown 	struct regulator_config config = { };
41008bf1c0aSAshish Jangam 	struct da9052_regulator *regulator;
41108bf1c0aSAshish Jangam 	struct da9052 *da9052;
41208bf1c0aSAshish Jangam 	struct da9052_pdata *pdata;
41308bf1c0aSAshish Jangam 
414984b5a6bSAshish Jangam 	regulator = devm_kzalloc(&pdev->dev, sizeof(struct da9052_regulator),
415984b5a6bSAshish Jangam 				 GFP_KERNEL);
41608bf1c0aSAshish Jangam 	if (!regulator)
41708bf1c0aSAshish Jangam 		return -ENOMEM;
41808bf1c0aSAshish Jangam 
41908bf1c0aSAshish Jangam 	da9052 = dev_get_drvdata(pdev->dev.parent);
42008bf1c0aSAshish Jangam 	pdata = da9052->dev->platform_data;
42108bf1c0aSAshish Jangam 	regulator->da9052 = da9052;
42208bf1c0aSAshish Jangam 
42308bf1c0aSAshish Jangam 	regulator->info = find_regulator_info(regulator->da9052->chip_id,
42408bf1c0aSAshish Jangam 					      pdev->id);
42508bf1c0aSAshish Jangam 	if (regulator->info == NULL) {
42608bf1c0aSAshish Jangam 		dev_err(&pdev->dev, "invalid regulator ID specified\n");
4277eb6444fSAxel Lin 		return -EINVAL;
42808bf1c0aSAshish Jangam 	}
429c172708dSMark Brown 
430c172708dSMark Brown 	config.dev = &pdev->dev;
431c172708dSMark Brown 	config.driver_data = regulator;
432*88c84c14SYing-Chun Liu (PaulLiu) 	if (pdata && pdata->regulators) {
433*88c84c14SYing-Chun Liu (PaulLiu) 		config.init_data = pdata->regulators[pdev->id];
434*88c84c14SYing-Chun Liu (PaulLiu) 	} else {
435*88c84c14SYing-Chun Liu (PaulLiu) #ifdef CONFIG_OF
436*88c84c14SYing-Chun Liu (PaulLiu) 		struct device_node *nproot = da9052->dev->of_node;
437*88c84c14SYing-Chun Liu (PaulLiu) 		struct device_node *np;
438*88c84c14SYing-Chun Liu (PaulLiu) 
439*88c84c14SYing-Chun Liu (PaulLiu) 		if (!nproot)
440*88c84c14SYing-Chun Liu (PaulLiu) 			return -ENODEV;
441*88c84c14SYing-Chun Liu (PaulLiu) 
442*88c84c14SYing-Chun Liu (PaulLiu) 		nproot = of_find_node_by_name(nproot, "regulators");
443*88c84c14SYing-Chun Liu (PaulLiu) 		if (!nproot)
444*88c84c14SYing-Chun Liu (PaulLiu) 			return -ENODEV;
445*88c84c14SYing-Chun Liu (PaulLiu) 
446*88c84c14SYing-Chun Liu (PaulLiu) 		for (np = of_get_next_child(nproot, NULL); !np;
447*88c84c14SYing-Chun Liu (PaulLiu) 		     np = of_get_next_child(nproot, np)) {
448*88c84c14SYing-Chun Liu (PaulLiu) 			if (!of_node_cmp(np->name,
449*88c84c14SYing-Chun Liu (PaulLiu) 					 regulator->info->reg_desc.name)) {
450*88c84c14SYing-Chun Liu (PaulLiu) 				config.init_data = of_get_regulator_init_data(
451*88c84c14SYing-Chun Liu (PaulLiu) 					&pdev->dev, np);
452*88c84c14SYing-Chun Liu (PaulLiu) 				break;
453*88c84c14SYing-Chun Liu (PaulLiu) 			}
454*88c84c14SYing-Chun Liu (PaulLiu) 		}
455*88c84c14SYing-Chun Liu (PaulLiu) #endif
456*88c84c14SYing-Chun Liu (PaulLiu) 	}
457c172708dSMark Brown 
45808bf1c0aSAshish Jangam 	regulator->rdev = regulator_register(&regulator->info->reg_desc,
459c172708dSMark Brown 					     &config);
46008bf1c0aSAshish Jangam 	if (IS_ERR(regulator->rdev)) {
46108bf1c0aSAshish Jangam 		dev_err(&pdev->dev, "failed to register regulator %s\n",
46208bf1c0aSAshish Jangam 			regulator->info->reg_desc.name);
4637eb6444fSAxel Lin 		return PTR_ERR(regulator->rdev);
46408bf1c0aSAshish Jangam 	}
46508bf1c0aSAshish Jangam 
46608bf1c0aSAshish Jangam 	platform_set_drvdata(pdev, regulator);
46708bf1c0aSAshish Jangam 
46808bf1c0aSAshish Jangam 	return 0;
46908bf1c0aSAshish Jangam }
47008bf1c0aSAshish Jangam 
47108bf1c0aSAshish Jangam static int __devexit da9052_regulator_remove(struct platform_device *pdev)
47208bf1c0aSAshish Jangam {
47308bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = platform_get_drvdata(pdev);
47408bf1c0aSAshish Jangam 
47508bf1c0aSAshish Jangam 	regulator_unregister(regulator->rdev);
47608bf1c0aSAshish Jangam 	return 0;
47708bf1c0aSAshish Jangam }
47808bf1c0aSAshish Jangam 
47908bf1c0aSAshish Jangam static struct platform_driver da9052_regulator_driver = {
48008bf1c0aSAshish Jangam 	.probe = da9052_regulator_probe,
48108bf1c0aSAshish Jangam 	.remove = __devexit_p(da9052_regulator_remove),
48208bf1c0aSAshish Jangam 	.driver = {
48308bf1c0aSAshish Jangam 		.name = "da9052-regulator",
48408bf1c0aSAshish Jangam 		.owner = THIS_MODULE,
48508bf1c0aSAshish Jangam 	},
48608bf1c0aSAshish Jangam };
48708bf1c0aSAshish Jangam 
48808bf1c0aSAshish Jangam static int __init da9052_regulator_init(void)
48908bf1c0aSAshish Jangam {
49008bf1c0aSAshish Jangam 	return platform_driver_register(&da9052_regulator_driver);
49108bf1c0aSAshish Jangam }
49208bf1c0aSAshish Jangam subsys_initcall(da9052_regulator_init);
49308bf1c0aSAshish Jangam 
49408bf1c0aSAshish Jangam static void __exit da9052_regulator_exit(void)
49508bf1c0aSAshish Jangam {
49608bf1c0aSAshish Jangam 	platform_driver_unregister(&da9052_regulator_driver);
49708bf1c0aSAshish Jangam }
49808bf1c0aSAshish Jangam module_exit(da9052_regulator_exit);
49908bf1c0aSAshish Jangam 
50008bf1c0aSAshish Jangam MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
50108bf1c0aSAshish Jangam MODULE_DESCRIPTION("Power Regulator driver for Dialog DA9052 PMIC");
50208bf1c0aSAshish Jangam MODULE_LICENSE("GPL");
50308bf1c0aSAshish Jangam MODULE_ALIAS("platform:da9052-regulator");
504