xref: /openbmc/linux/drivers/regulator/da9052-regulator.c (revision 47aed92c7740c33f3acbd9dbdefb5ec6f9effdc0)
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>
2208bf1c0aSAshish Jangam 
2308bf1c0aSAshish Jangam #include <linux/mfd/da9052/da9052.h>
2408bf1c0aSAshish Jangam #include <linux/mfd/da9052/reg.h>
2508bf1c0aSAshish Jangam #include <linux/mfd/da9052/pdata.h>
2608bf1c0aSAshish Jangam 
2708bf1c0aSAshish Jangam /* Buck step size */
2808bf1c0aSAshish Jangam #define DA9052_BUCK_PERI_3uV_STEP		100000
2908bf1c0aSAshish Jangam #define DA9052_BUCK_PERI_REG_MAP_UPTO_3uV	24
3008bf1c0aSAshish Jangam #define DA9052_CONST_3uV			3000000
3108bf1c0aSAshish Jangam 
3208bf1c0aSAshish Jangam #define DA9052_MIN_UA		0
3308bf1c0aSAshish Jangam #define DA9052_MAX_UA		3
3408bf1c0aSAshish Jangam #define DA9052_CURRENT_RANGE	4
3508bf1c0aSAshish Jangam 
3608bf1c0aSAshish Jangam /* Bit masks */
3708bf1c0aSAshish Jangam #define DA9052_BUCK_ILIM_MASK_EVEN	0x0c
3808bf1c0aSAshish Jangam #define DA9052_BUCK_ILIM_MASK_ODD	0xc0
3908bf1c0aSAshish Jangam 
4008bf1c0aSAshish Jangam static const u32 da9052_current_limits[3][4] = {
4108bf1c0aSAshish Jangam 	{700000, 800000, 1000000, 1200000},	/* DA9052-BC BUCKs */
4208bf1c0aSAshish Jangam 	{1600000, 2000000, 2400000, 3000000},	/* DA9053-AA/Bx BUCK-CORE */
4308bf1c0aSAshish Jangam 	{800000, 1000000, 1200000, 1500000},	/* DA9053-AA/Bx BUCK-PRO,
4408bf1c0aSAshish Jangam 						 * BUCK-MEM and BUCK-PERI
4508bf1c0aSAshish Jangam 						*/
4608bf1c0aSAshish Jangam };
4708bf1c0aSAshish Jangam 
4808bf1c0aSAshish Jangam struct da9052_regulator_info {
4908bf1c0aSAshish Jangam 	struct regulator_desc reg_desc;
5008bf1c0aSAshish Jangam 	int step_uV;
5108bf1c0aSAshish Jangam 	int min_uV;
5208bf1c0aSAshish Jangam 	int max_uV;
5308bf1c0aSAshish Jangam 	unsigned char volt_shift;
5408bf1c0aSAshish Jangam 	unsigned char en_bit;
5508bf1c0aSAshish Jangam 	unsigned char activate_bit;
5608bf1c0aSAshish Jangam };
5708bf1c0aSAshish Jangam 
5808bf1c0aSAshish Jangam struct da9052_regulator {
5908bf1c0aSAshish Jangam 	struct da9052 *da9052;
6008bf1c0aSAshish Jangam 	struct da9052_regulator_info *info;
6108bf1c0aSAshish Jangam 	struct regulator_dev *rdev;
6208bf1c0aSAshish Jangam };
6308bf1c0aSAshish Jangam 
6408bf1c0aSAshish Jangam static int verify_range(struct da9052_regulator_info *info,
6508bf1c0aSAshish Jangam 			 int min_uV, int max_uV)
6608bf1c0aSAshish Jangam {
6708bf1c0aSAshish Jangam 	if (min_uV > info->max_uV || max_uV < info->min_uV)
6808bf1c0aSAshish Jangam 		return -EINVAL;
6908bf1c0aSAshish Jangam 
7008bf1c0aSAshish Jangam 	return 0;
7108bf1c0aSAshish Jangam }
7208bf1c0aSAshish Jangam 
7308bf1c0aSAshish Jangam static int da9052_regulator_enable(struct regulator_dev *rdev)
7408bf1c0aSAshish Jangam {
7508bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
7608bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
7708bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
7808bf1c0aSAshish Jangam 
7908bf1c0aSAshish Jangam 	return da9052_reg_update(regulator->da9052,
8008bf1c0aSAshish Jangam 				 DA9052_BUCKCORE_REG + offset,
8108bf1c0aSAshish Jangam 				 1 << info->en_bit, 1 << info->en_bit);
8208bf1c0aSAshish Jangam }
8308bf1c0aSAshish Jangam 
8408bf1c0aSAshish Jangam static int da9052_regulator_disable(struct regulator_dev *rdev)
8508bf1c0aSAshish Jangam {
8608bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
8708bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
8808bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
8908bf1c0aSAshish Jangam 
9008bf1c0aSAshish Jangam 	return da9052_reg_update(regulator->da9052,
9108bf1c0aSAshish Jangam 				 DA9052_BUCKCORE_REG + offset,
9208bf1c0aSAshish Jangam 				 1 << info->en_bit, 0);
9308bf1c0aSAshish Jangam }
9408bf1c0aSAshish Jangam 
9508bf1c0aSAshish Jangam static int da9052_regulator_is_enabled(struct regulator_dev *rdev)
9608bf1c0aSAshish Jangam {
9708bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
9808bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
9908bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
10008bf1c0aSAshish Jangam 	int ret;
10108bf1c0aSAshish Jangam 
10208bf1c0aSAshish Jangam 	ret = da9052_reg_read(regulator->da9052, DA9052_BUCKCORE_REG + offset);
10308bf1c0aSAshish Jangam 	if (ret < 0)
10408bf1c0aSAshish Jangam 		return ret;
10508bf1c0aSAshish Jangam 
10608bf1c0aSAshish Jangam 	return ret & (1 << info->en_bit);
10708bf1c0aSAshish Jangam }
10808bf1c0aSAshish Jangam 
10908bf1c0aSAshish Jangam static int da9052_dcdc_get_current_limit(struct regulator_dev *rdev)
11008bf1c0aSAshish Jangam {
11108bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
11208bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
11308bf1c0aSAshish Jangam 	int ret, row = 2;
11408bf1c0aSAshish Jangam 
11508bf1c0aSAshish Jangam 	ret = da9052_reg_read(regulator->da9052, DA9052_BUCKA_REG + offset/2);
11608bf1c0aSAshish Jangam 	if (ret < 0)
11708bf1c0aSAshish Jangam 		return ret;
11808bf1c0aSAshish Jangam 
11908bf1c0aSAshish Jangam 	/* Determine the even or odd position of the buck current limit
12008bf1c0aSAshish Jangam 	 * register field
12108bf1c0aSAshish Jangam 	*/
12208bf1c0aSAshish Jangam 	if (offset % 2 == 0)
12308bf1c0aSAshish Jangam 		ret = (ret & DA9052_BUCK_ILIM_MASK_EVEN) >> 2;
12408bf1c0aSAshish Jangam 	else
12508bf1c0aSAshish Jangam 		ret = (ret & DA9052_BUCK_ILIM_MASK_ODD) >> 6;
12608bf1c0aSAshish Jangam 
12708bf1c0aSAshish Jangam 	/* Select the appropriate current limit range */
12808bf1c0aSAshish Jangam 	if (regulator->da9052->chip_id == DA9052)
12908bf1c0aSAshish Jangam 		row = 0;
13008bf1c0aSAshish Jangam 	else if (offset == 0)
13108bf1c0aSAshish Jangam 		row = 1;
13208bf1c0aSAshish Jangam 
13308bf1c0aSAshish Jangam 	return da9052_current_limits[row][ret];
13408bf1c0aSAshish Jangam }
13508bf1c0aSAshish Jangam 
13608bf1c0aSAshish Jangam static int da9052_dcdc_set_current_limit(struct regulator_dev *rdev, int min_uA,
13708bf1c0aSAshish Jangam 					  int max_uA)
13808bf1c0aSAshish Jangam {
13908bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
14008bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
14108bf1c0aSAshish Jangam 	int reg_val = 0;
14208bf1c0aSAshish Jangam 	int i, row = 2;
14308bf1c0aSAshish Jangam 
14408bf1c0aSAshish Jangam 	/* Select the appropriate current limit range */
14508bf1c0aSAshish Jangam 	if (regulator->da9052->chip_id == DA9052)
14608bf1c0aSAshish Jangam 		row = 0;
14708bf1c0aSAshish Jangam 	else if (offset == 0)
14808bf1c0aSAshish Jangam 		row = 1;
14908bf1c0aSAshish Jangam 
15008bf1c0aSAshish Jangam 	if (min_uA > da9052_current_limits[row][DA9052_MAX_UA] ||
15108bf1c0aSAshish Jangam 	    max_uA < da9052_current_limits[row][DA9052_MIN_UA])
15208bf1c0aSAshish Jangam 		return -EINVAL;
15308bf1c0aSAshish Jangam 
15408bf1c0aSAshish Jangam 	for (i = 0; i < DA9052_CURRENT_RANGE; i++) {
15508bf1c0aSAshish Jangam 		if (min_uA <= da9052_current_limits[row][i]) {
15608bf1c0aSAshish Jangam 			reg_val = i;
15708bf1c0aSAshish Jangam 			break;
15808bf1c0aSAshish Jangam 		}
15908bf1c0aSAshish Jangam 	}
16008bf1c0aSAshish Jangam 
16108bf1c0aSAshish Jangam 	/* Determine the even or odd position of the buck current limit
16208bf1c0aSAshish Jangam 	 * register field
16308bf1c0aSAshish Jangam 	*/
16408bf1c0aSAshish Jangam 	if (offset % 2 == 0)
16508bf1c0aSAshish Jangam 		return da9052_reg_update(regulator->da9052,
16608bf1c0aSAshish Jangam 					 DA9052_BUCKA_REG + offset/2,
16708bf1c0aSAshish Jangam 					 DA9052_BUCK_ILIM_MASK_EVEN,
16808bf1c0aSAshish Jangam 					 reg_val << 2);
16908bf1c0aSAshish Jangam 	else
17008bf1c0aSAshish Jangam 		return da9052_reg_update(regulator->da9052,
17108bf1c0aSAshish Jangam 					 DA9052_BUCKA_REG + offset/2,
17208bf1c0aSAshish Jangam 					 DA9052_BUCK_ILIM_MASK_ODD,
17308bf1c0aSAshish Jangam 					 reg_val << 6);
17408bf1c0aSAshish Jangam }
17508bf1c0aSAshish Jangam 
17608bf1c0aSAshish Jangam static int da9052_list_buckperi_voltage(struct regulator_dev *rdev,
17708bf1c0aSAshish Jangam 					 unsigned int selector)
17808bf1c0aSAshish Jangam {
17908bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
18008bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
18108bf1c0aSAshish Jangam 	int volt_uV;
18208bf1c0aSAshish Jangam 
18308bf1c0aSAshish Jangam 	if ((regulator->da9052->chip_id == DA9052) &&
18408bf1c0aSAshish Jangam 	    (selector >= DA9052_BUCK_PERI_REG_MAP_UPTO_3uV)) {
18508bf1c0aSAshish Jangam 		volt_uV = ((DA9052_BUCK_PERI_REG_MAP_UPTO_3uV * info->step_uV)
18608bf1c0aSAshish Jangam 			    + info->min_uV);
18708bf1c0aSAshish Jangam 		volt_uV += (selector - DA9052_BUCK_PERI_REG_MAP_UPTO_3uV)
18808bf1c0aSAshish Jangam 			    * (DA9052_BUCK_PERI_3uV_STEP);
18908bf1c0aSAshish Jangam 	} else
19008bf1c0aSAshish Jangam 			volt_uV = (selector * info->step_uV) + info->min_uV;
19108bf1c0aSAshish Jangam 
19208bf1c0aSAshish Jangam 	if (volt_uV > info->max_uV)
19308bf1c0aSAshish Jangam 		return -EINVAL;
19408bf1c0aSAshish Jangam 
19508bf1c0aSAshish Jangam 	return volt_uV;
19608bf1c0aSAshish Jangam }
19708bf1c0aSAshish Jangam 
19808bf1c0aSAshish Jangam static int da9052_list_voltage(struct regulator_dev *rdev,
19908bf1c0aSAshish Jangam 				unsigned int selector)
20008bf1c0aSAshish Jangam {
20108bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
20208bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
20308bf1c0aSAshish Jangam 	int volt_uV;
20408bf1c0aSAshish Jangam 
20508bf1c0aSAshish Jangam 	volt_uV = info->min_uV + info->step_uV * selector;
20608bf1c0aSAshish Jangam 
20708bf1c0aSAshish Jangam 	if (volt_uV > info->max_uV)
20808bf1c0aSAshish Jangam 		return -EINVAL;
20908bf1c0aSAshish Jangam 
21008bf1c0aSAshish Jangam 	return volt_uV;
21108bf1c0aSAshish Jangam }
21208bf1c0aSAshish Jangam 
21308bf1c0aSAshish Jangam static int da9052_regulator_set_voltage_int(struct regulator_dev *rdev,
21408bf1c0aSAshish Jangam 					     int min_uV, int max_uV,
21508bf1c0aSAshish Jangam 					     unsigned int *selector)
21608bf1c0aSAshish Jangam {
21708bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
21808bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
21908bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
22008bf1c0aSAshish Jangam 	int ret;
22108bf1c0aSAshish Jangam 
22208bf1c0aSAshish Jangam 	ret = verify_range(info, min_uV, max_uV);
22308bf1c0aSAshish Jangam 	if (ret < 0)
22408bf1c0aSAshish Jangam 		return ret;
22508bf1c0aSAshish Jangam 
22608bf1c0aSAshish Jangam 	if (min_uV < info->min_uV)
22708bf1c0aSAshish Jangam 		min_uV = info->min_uV;
22808bf1c0aSAshish Jangam 
22908bf1c0aSAshish Jangam 	*selector = (min_uV - info->min_uV) / info->step_uV;
23008bf1c0aSAshish Jangam 
23108bf1c0aSAshish Jangam 	ret = da9052_list_voltage(rdev, *selector);
23208bf1c0aSAshish Jangam 	if (ret < 0)
23308bf1c0aSAshish Jangam 		return ret;
23408bf1c0aSAshish Jangam 
23508bf1c0aSAshish Jangam 	return da9052_reg_update(regulator->da9052,
23608bf1c0aSAshish Jangam 				 DA9052_BUCKCORE_REG + offset,
23708bf1c0aSAshish Jangam 				 (1 << info->volt_shift) - 1, *selector);
23808bf1c0aSAshish Jangam }
23908bf1c0aSAshish Jangam 
24008bf1c0aSAshish Jangam static int da9052_set_ldo_voltage(struct regulator_dev *rdev,
24108bf1c0aSAshish Jangam 				   int min_uV, int max_uV,
24208bf1c0aSAshish Jangam 				   unsigned int *selector)
24308bf1c0aSAshish Jangam {
24408bf1c0aSAshish Jangam 	return da9052_regulator_set_voltage_int(rdev, min_uV, max_uV, selector);
24508bf1c0aSAshish Jangam }
24608bf1c0aSAshish Jangam 
24708bf1c0aSAshish Jangam static int da9052_set_ldo5_6_voltage(struct regulator_dev *rdev,
24808bf1c0aSAshish Jangam 				      int min_uV, int max_uV,
24908bf1c0aSAshish Jangam 				      unsigned int *selector)
25008bf1c0aSAshish Jangam {
25108bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
25208bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
25308bf1c0aSAshish Jangam 	int ret;
25408bf1c0aSAshish Jangam 
25508bf1c0aSAshish Jangam 	ret = da9052_regulator_set_voltage_int(rdev, min_uV, max_uV, selector);
25608bf1c0aSAshish Jangam 	if (ret < 0)
25708bf1c0aSAshish Jangam 		return ret;
25808bf1c0aSAshish Jangam 
25908bf1c0aSAshish Jangam 	/* Some LDOs are DVC controlled which requires enabling of
26008bf1c0aSAshish Jangam 	 * the LDO activate bit to implment the changes on the
26108bf1c0aSAshish Jangam 	 * LDO output.
26208bf1c0aSAshish Jangam 	*/
26308bf1c0aSAshish Jangam 	return da9052_reg_update(regulator->da9052, DA9052_SUPPLY_REG, 0,
26408bf1c0aSAshish Jangam 				 info->activate_bit);
26508bf1c0aSAshish Jangam }
26608bf1c0aSAshish Jangam 
26708bf1c0aSAshish Jangam static int da9052_set_dcdc_voltage(struct regulator_dev *rdev,
26808bf1c0aSAshish Jangam 				    int min_uV, int max_uV,
26908bf1c0aSAshish Jangam 				    unsigned int *selector)
27008bf1c0aSAshish Jangam {
27108bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
27208bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
27308bf1c0aSAshish Jangam 	int ret;
27408bf1c0aSAshish Jangam 
27508bf1c0aSAshish Jangam 	ret = da9052_regulator_set_voltage_int(rdev, min_uV, max_uV, selector);
27608bf1c0aSAshish Jangam 	if (ret < 0)
27708bf1c0aSAshish Jangam 		return ret;
27808bf1c0aSAshish Jangam 
27908bf1c0aSAshish Jangam 	/* Some DCDCs are DVC controlled which requires enabling of
28008bf1c0aSAshish Jangam 	 * the DCDC activate bit to implment the changes on the
28108bf1c0aSAshish Jangam 	 * DCDC output.
28208bf1c0aSAshish Jangam 	*/
28308bf1c0aSAshish Jangam 	return da9052_reg_update(regulator->da9052, DA9052_SUPPLY_REG, 0,
28408bf1c0aSAshish Jangam 				 info->activate_bit);
28508bf1c0aSAshish Jangam }
28608bf1c0aSAshish Jangam 
28708bf1c0aSAshish Jangam static int da9052_get_regulator_voltage_sel(struct regulator_dev *rdev)
28808bf1c0aSAshish Jangam {
28908bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
29008bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
29108bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
29208bf1c0aSAshish Jangam 	int ret;
29308bf1c0aSAshish Jangam 
29408bf1c0aSAshish Jangam 	ret = da9052_reg_read(regulator->da9052, DA9052_BUCKCORE_REG + offset);
29508bf1c0aSAshish Jangam 	if (ret < 0)
29608bf1c0aSAshish Jangam 		return ret;
29708bf1c0aSAshish Jangam 
29808bf1c0aSAshish Jangam 	ret &= ((1 << info->volt_shift) - 1);
29908bf1c0aSAshish Jangam 
30008bf1c0aSAshish Jangam 	return ret;
30108bf1c0aSAshish Jangam }
30208bf1c0aSAshish Jangam 
30308bf1c0aSAshish Jangam static int da9052_set_buckperi_voltage(struct regulator_dev *rdev, int min_uV,
30408bf1c0aSAshish Jangam 					int max_uV, unsigned int *selector)
30508bf1c0aSAshish Jangam {
30608bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
30708bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
30808bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
30908bf1c0aSAshish Jangam 	int ret;
31008bf1c0aSAshish Jangam 
31108bf1c0aSAshish Jangam 	ret = verify_range(info, min_uV, max_uV);
31208bf1c0aSAshish Jangam 	if (ret < 0)
31308bf1c0aSAshish Jangam 		return ret;
31408bf1c0aSAshish Jangam 
31508bf1c0aSAshish Jangam 	if (min_uV < info->min_uV)
31608bf1c0aSAshish Jangam 		min_uV = info->min_uV;
31708bf1c0aSAshish Jangam 
31808bf1c0aSAshish Jangam 	if ((regulator->da9052->chip_id == DA9052) &&
31908bf1c0aSAshish Jangam 	    (min_uV >= DA9052_CONST_3uV))
32008bf1c0aSAshish Jangam 		*selector = DA9052_BUCK_PERI_REG_MAP_UPTO_3uV +
32108bf1c0aSAshish Jangam 			    ((min_uV - DA9052_CONST_3uV) /
32208bf1c0aSAshish Jangam 			    (DA9052_BUCK_PERI_3uV_STEP));
32308bf1c0aSAshish Jangam 	else
32408bf1c0aSAshish Jangam 		*selector = (min_uV - info->min_uV) / info->step_uV;
32508bf1c0aSAshish Jangam 
32608bf1c0aSAshish Jangam 	ret = da9052_list_buckperi_voltage(rdev, *selector);
32708bf1c0aSAshish Jangam 	if (ret < 0)
32808bf1c0aSAshish Jangam 		return ret;
32908bf1c0aSAshish Jangam 
33008bf1c0aSAshish Jangam 	return da9052_reg_update(regulator->da9052,
33108bf1c0aSAshish Jangam 				 DA9052_BUCKCORE_REG + offset,
33208bf1c0aSAshish Jangam 				 (1 << info->volt_shift) - 1, *selector);
33308bf1c0aSAshish Jangam }
33408bf1c0aSAshish Jangam 
33508bf1c0aSAshish Jangam static int da9052_get_buckperi_voltage_sel(struct regulator_dev *rdev)
33608bf1c0aSAshish Jangam {
33708bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
33808bf1c0aSAshish Jangam 	struct da9052_regulator_info *info = regulator->info;
33908bf1c0aSAshish Jangam 	int offset = rdev_get_id(rdev);
34008bf1c0aSAshish Jangam 	int ret;
34108bf1c0aSAshish Jangam 
34208bf1c0aSAshish Jangam 	ret = da9052_reg_read(regulator->da9052, DA9052_BUCKCORE_REG + offset);
34308bf1c0aSAshish Jangam 	if (ret < 0)
34408bf1c0aSAshish Jangam 		return ret;
34508bf1c0aSAshish Jangam 
34608bf1c0aSAshish Jangam 	ret &= ((1 << info->volt_shift) - 1);
34708bf1c0aSAshish Jangam 
34808bf1c0aSAshish Jangam 	return ret;
34908bf1c0aSAshish Jangam }
35008bf1c0aSAshish Jangam 
35108bf1c0aSAshish Jangam static struct regulator_ops da9052_buckperi_ops = {
35208bf1c0aSAshish Jangam 	.list_voltage = da9052_list_buckperi_voltage,
35308bf1c0aSAshish Jangam 	.get_voltage_sel = da9052_get_buckperi_voltage_sel,
35408bf1c0aSAshish Jangam 	.set_voltage = da9052_set_buckperi_voltage,
35508bf1c0aSAshish Jangam 
35608bf1c0aSAshish Jangam 	.get_current_limit = da9052_dcdc_get_current_limit,
35708bf1c0aSAshish Jangam 	.set_current_limit = da9052_dcdc_set_current_limit,
35808bf1c0aSAshish Jangam 
35908bf1c0aSAshish Jangam 	.is_enabled = da9052_regulator_is_enabled,
36008bf1c0aSAshish Jangam 	.enable = da9052_regulator_enable,
36108bf1c0aSAshish Jangam 	.disable = da9052_regulator_disable,
36208bf1c0aSAshish Jangam };
36308bf1c0aSAshish Jangam 
36408bf1c0aSAshish Jangam static struct regulator_ops da9052_dcdc_ops = {
36508bf1c0aSAshish Jangam 	.set_voltage = da9052_set_dcdc_voltage,
36608bf1c0aSAshish Jangam 	.get_current_limit = da9052_dcdc_get_current_limit,
36708bf1c0aSAshish Jangam 	.set_current_limit = da9052_dcdc_set_current_limit,
36808bf1c0aSAshish Jangam 
36908bf1c0aSAshish Jangam 	.list_voltage = da9052_list_voltage,
37008bf1c0aSAshish Jangam 	.get_voltage_sel = da9052_get_regulator_voltage_sel,
37108bf1c0aSAshish Jangam 	.is_enabled = da9052_regulator_is_enabled,
37208bf1c0aSAshish Jangam 	.enable = da9052_regulator_enable,
37308bf1c0aSAshish Jangam 	.disable = da9052_regulator_disable,
37408bf1c0aSAshish Jangam };
37508bf1c0aSAshish Jangam 
37608bf1c0aSAshish Jangam static struct regulator_ops da9052_ldo5_6_ops = {
37708bf1c0aSAshish Jangam 	.set_voltage = da9052_set_ldo5_6_voltage,
37808bf1c0aSAshish Jangam 
37908bf1c0aSAshish Jangam 	.list_voltage = da9052_list_voltage,
38008bf1c0aSAshish Jangam 	.get_voltage_sel = da9052_get_regulator_voltage_sel,
38108bf1c0aSAshish Jangam 	.is_enabled = da9052_regulator_is_enabled,
38208bf1c0aSAshish Jangam 	.enable = da9052_regulator_enable,
38308bf1c0aSAshish Jangam 	.disable = da9052_regulator_disable,
38408bf1c0aSAshish Jangam };
38508bf1c0aSAshish Jangam 
38608bf1c0aSAshish Jangam static struct regulator_ops da9052_ldo_ops = {
38708bf1c0aSAshish Jangam 	.set_voltage = da9052_set_ldo_voltage,
38808bf1c0aSAshish Jangam 
38908bf1c0aSAshish Jangam 	.list_voltage = da9052_list_voltage,
39008bf1c0aSAshish Jangam 	.get_voltage_sel = da9052_get_regulator_voltage_sel,
39108bf1c0aSAshish Jangam 	.is_enabled = da9052_regulator_is_enabled,
39208bf1c0aSAshish Jangam 	.enable = da9052_regulator_enable,
39308bf1c0aSAshish Jangam 	.disable = da9052_regulator_disable,
39408bf1c0aSAshish Jangam };
39508bf1c0aSAshish Jangam 
39608bf1c0aSAshish Jangam #define DA9052_LDO5_6(_id, step, min, max, sbits, ebits, abits) \
39708bf1c0aSAshish Jangam {\
39808bf1c0aSAshish Jangam 	.reg_desc = {\
39908bf1c0aSAshish Jangam 		.name = "LDO" #_id,\
40008bf1c0aSAshish Jangam 		.ops = &da9052_ldo5_6_ops,\
40108bf1c0aSAshish Jangam 		.type = REGULATOR_VOLTAGE,\
40208bf1c0aSAshish Jangam 		.id = _id,\
40308bf1c0aSAshish Jangam 		.owner = THIS_MODULE,\
40408bf1c0aSAshish Jangam 	},\
40508bf1c0aSAshish Jangam 	.min_uV = (min) * 1000,\
40608bf1c0aSAshish Jangam 	.max_uV = (max) * 1000,\
40708bf1c0aSAshish Jangam 	.step_uV = (step) * 1000,\
40808bf1c0aSAshish Jangam 	.volt_shift = (sbits),\
40908bf1c0aSAshish Jangam 	.en_bit = (ebits),\
41008bf1c0aSAshish Jangam 	.activate_bit = (abits),\
41108bf1c0aSAshish Jangam }
41208bf1c0aSAshish Jangam 
41308bf1c0aSAshish Jangam #define DA9052_LDO(_id, step, min, max, sbits, ebits, abits) \
41408bf1c0aSAshish Jangam {\
41508bf1c0aSAshish Jangam 	.reg_desc = {\
41608bf1c0aSAshish Jangam 		.name = "LDO" #_id,\
41708bf1c0aSAshish Jangam 		.ops = &da9052_ldo_ops,\
41808bf1c0aSAshish Jangam 		.type = REGULATOR_VOLTAGE,\
41908bf1c0aSAshish Jangam 		.id = _id,\
42008bf1c0aSAshish Jangam 		.owner = THIS_MODULE,\
42108bf1c0aSAshish Jangam 	},\
42208bf1c0aSAshish Jangam 	.min_uV = (min) * 1000,\
42308bf1c0aSAshish Jangam 	.max_uV = (max) * 1000,\
42408bf1c0aSAshish Jangam 	.step_uV = (step) * 1000,\
42508bf1c0aSAshish Jangam 	.volt_shift = (sbits),\
42608bf1c0aSAshish Jangam 	.en_bit = (ebits),\
42708bf1c0aSAshish Jangam 	.activate_bit = (abits),\
42808bf1c0aSAshish Jangam }
42908bf1c0aSAshish Jangam 
43008bf1c0aSAshish Jangam #define DA9052_DCDC(_id, step, min, max, sbits, ebits, abits) \
43108bf1c0aSAshish Jangam {\
43208bf1c0aSAshish Jangam 	.reg_desc = {\
43308bf1c0aSAshish Jangam 		.name = "BUCK" #_id,\
43408bf1c0aSAshish Jangam 		.ops = &da9052_dcdc_ops,\
43508bf1c0aSAshish Jangam 		.type = REGULATOR_VOLTAGE,\
43608bf1c0aSAshish Jangam 		.id = _id,\
43708bf1c0aSAshish Jangam 		.owner = THIS_MODULE,\
43808bf1c0aSAshish Jangam 	},\
43908bf1c0aSAshish Jangam 	.min_uV = (min) * 1000,\
44008bf1c0aSAshish Jangam 	.max_uV = (max) * 1000,\
44108bf1c0aSAshish Jangam 	.step_uV = (step) * 1000,\
44208bf1c0aSAshish Jangam 	.volt_shift = (sbits),\
44308bf1c0aSAshish Jangam 	.en_bit = (ebits),\
44408bf1c0aSAshish Jangam 	.activate_bit = (abits),\
44508bf1c0aSAshish Jangam }
44608bf1c0aSAshish Jangam 
44708bf1c0aSAshish Jangam #define DA9052_BUCKPERI(_id, step, min, max, sbits, ebits, abits) \
44808bf1c0aSAshish Jangam {\
44908bf1c0aSAshish Jangam 	.reg_desc = {\
45008bf1c0aSAshish Jangam 		.name = "BUCK" #_id,\
45108bf1c0aSAshish Jangam 		.ops = &da9052_buckperi_ops,\
45208bf1c0aSAshish Jangam 		.type = REGULATOR_VOLTAGE,\
45308bf1c0aSAshish Jangam 		.id = _id,\
45408bf1c0aSAshish Jangam 		.owner = THIS_MODULE,\
45508bf1c0aSAshish Jangam 	},\
45608bf1c0aSAshish Jangam 	.min_uV = (min) * 1000,\
45708bf1c0aSAshish Jangam 	.max_uV = (max) * 1000,\
45808bf1c0aSAshish Jangam 	.step_uV = (step) * 1000,\
45908bf1c0aSAshish Jangam 	.volt_shift = (sbits),\
46008bf1c0aSAshish Jangam 	.en_bit = (ebits),\
46108bf1c0aSAshish Jangam 	.activate_bit = (abits),\
46208bf1c0aSAshish Jangam }
46308bf1c0aSAshish Jangam 
46408bf1c0aSAshish Jangam struct da9052_regulator_info da9052_regulator_info[] = {
46508bf1c0aSAshish Jangam 	/* Buck1 - 4 */
46608bf1c0aSAshish Jangam 	DA9052_DCDC(0, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBCOREGO),
46708bf1c0aSAshish Jangam 	DA9052_DCDC(1, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBPROGO),
46808bf1c0aSAshish Jangam 	DA9052_DCDC(2, 25, 925, 2500, 6, 6, DA9052_SUPPLY_VBMEMGO),
46908bf1c0aSAshish Jangam 	DA9052_BUCKPERI(3, 50, 1800, 3600, 5, 6, 0),
47008bf1c0aSAshish Jangam 	/* LD01 - LDO10 */
47108bf1c0aSAshish Jangam 	DA9052_LDO(4, 50, 600, 1800, 5, 6, 0),
47208bf1c0aSAshish Jangam 	DA9052_LDO5_6(5, 25, 600, 1800, 6, 6, DA9052_SUPPLY_VLDO2GO),
47308bf1c0aSAshish Jangam 	DA9052_LDO5_6(6, 25, 1725, 3300, 6, 6, DA9052_SUPPLY_VLDO3GO),
47408bf1c0aSAshish Jangam 	DA9052_LDO(7, 25, 1725, 3300, 6, 6, 0),
47508bf1c0aSAshish Jangam 	DA9052_LDO(8, 50, 1200, 3600, 6, 6, 0),
47608bf1c0aSAshish Jangam 	DA9052_LDO(9, 50, 1200, 3600, 6, 6, 0),
47708bf1c0aSAshish Jangam 	DA9052_LDO(10, 50, 1200, 3600, 6, 6, 0),
47808bf1c0aSAshish Jangam 	DA9052_LDO(11, 50, 1200, 3600, 6, 6, 0),
47908bf1c0aSAshish Jangam 	DA9052_LDO(12, 50, 1250, 3650, 6, 6, 0),
48008bf1c0aSAshish Jangam 	DA9052_LDO(13, 50, 1200, 3600, 6, 6, 0),
48108bf1c0aSAshish Jangam };
48208bf1c0aSAshish Jangam 
48308bf1c0aSAshish Jangam struct da9052_regulator_info da9053_regulator_info[] = {
48408bf1c0aSAshish Jangam 	/* Buck1 - 4 */
48508bf1c0aSAshish Jangam 	DA9052_DCDC(0, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBCOREGO),
48608bf1c0aSAshish Jangam 	DA9052_DCDC(1, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBPROGO),
48708bf1c0aSAshish Jangam 	DA9052_DCDC(2, 25, 925, 2500, 6, 6, DA9052_SUPPLY_VBMEMGO),
48808bf1c0aSAshish Jangam 	DA9052_BUCKPERI(3, 25, 925, 2500, 6, 6, 0),
48908bf1c0aSAshish Jangam 	/* LD01 - LDO10 */
49008bf1c0aSAshish Jangam 	DA9052_LDO(4, 50, 600, 1800, 5, 6, 0),
49108bf1c0aSAshish Jangam 	DA9052_LDO5_6(5, 25, 600, 1800, 6, 6, DA9052_SUPPLY_VLDO2GO),
49208bf1c0aSAshish Jangam 	DA9052_LDO5_6(6, 25, 1725, 3300, 6, 6, DA9052_SUPPLY_VLDO3GO),
49308bf1c0aSAshish Jangam 	DA9052_LDO(7, 25, 1725, 3300, 6, 6, 0),
49408bf1c0aSAshish Jangam 	DA9052_LDO(8, 50, 1200, 3600, 6, 6, 0),
49508bf1c0aSAshish Jangam 	DA9052_LDO(9, 50, 1200, 3600, 6, 6, 0),
49608bf1c0aSAshish Jangam 	DA9052_LDO(10, 50, 1200, 3600, 6, 6, 0),
49708bf1c0aSAshish Jangam 	DA9052_LDO(11, 50, 1200, 3600, 6, 6, 0),
49808bf1c0aSAshish Jangam 	DA9052_LDO(12, 50, 1250, 3650, 6, 6, 0),
49908bf1c0aSAshish Jangam 	DA9052_LDO(13, 50, 1200, 3600, 6, 6, 0),
50008bf1c0aSAshish Jangam };
50108bf1c0aSAshish Jangam 
50208bf1c0aSAshish Jangam static inline struct da9052_regulator_info *find_regulator_info(u8 chip_id,
50308bf1c0aSAshish Jangam 								 int id)
50408bf1c0aSAshish Jangam {
50508bf1c0aSAshish Jangam 	struct da9052_regulator_info *info;
50608bf1c0aSAshish Jangam 	int i;
50708bf1c0aSAshish Jangam 
50808bf1c0aSAshish Jangam 	if (chip_id == DA9052) {
50908bf1c0aSAshish Jangam 		for (i = 0; i < ARRAY_SIZE(da9052_regulator_info); i++) {
51008bf1c0aSAshish Jangam 			info = &da9052_regulator_info[i];
51108bf1c0aSAshish Jangam 			if (info->reg_desc.id == id)
51208bf1c0aSAshish Jangam 				return info;
51308bf1c0aSAshish Jangam 		}
51408bf1c0aSAshish Jangam 	} else {
51508bf1c0aSAshish Jangam 		for (i = 0; i < ARRAY_SIZE(da9053_regulator_info); i++) {
51608bf1c0aSAshish Jangam 			info = &da9053_regulator_info[i];
51708bf1c0aSAshish Jangam 			if (info->reg_desc.id == id)
51808bf1c0aSAshish Jangam 				return info;
51908bf1c0aSAshish Jangam 		}
52008bf1c0aSAshish Jangam 	}
52108bf1c0aSAshish Jangam 
52208bf1c0aSAshish Jangam 	return NULL;
52308bf1c0aSAshish Jangam }
52408bf1c0aSAshish Jangam 
52508bf1c0aSAshish Jangam static int __devinit da9052_regulator_probe(struct platform_device *pdev)
52608bf1c0aSAshish Jangam {
52708bf1c0aSAshish Jangam 	struct da9052_regulator *regulator;
52808bf1c0aSAshish Jangam 	struct da9052 *da9052;
52908bf1c0aSAshish Jangam 	struct da9052_pdata *pdata;
53008bf1c0aSAshish Jangam 	int ret;
53108bf1c0aSAshish Jangam 
53208bf1c0aSAshish Jangam 	regulator = kzalloc(sizeof(struct da9052_regulator), GFP_KERNEL);
53308bf1c0aSAshish Jangam 	if (!regulator)
53408bf1c0aSAshish Jangam 		return -ENOMEM;
53508bf1c0aSAshish Jangam 
53608bf1c0aSAshish Jangam 	da9052 = dev_get_drvdata(pdev->dev.parent);
53708bf1c0aSAshish Jangam 	pdata = da9052->dev->platform_data;
53808bf1c0aSAshish Jangam 	regulator->da9052 = da9052;
53908bf1c0aSAshish Jangam 
54008bf1c0aSAshish Jangam 	regulator->info = find_regulator_info(regulator->da9052->chip_id,
54108bf1c0aSAshish Jangam 					      pdev->id);
54208bf1c0aSAshish Jangam 	if (regulator->info == NULL) {
54308bf1c0aSAshish Jangam 		dev_err(&pdev->dev, "invalid regulator ID specified\n");
54408bf1c0aSAshish Jangam 		ret = -EINVAL;
54508bf1c0aSAshish Jangam 		goto err;
54608bf1c0aSAshish Jangam 	}
54708bf1c0aSAshish Jangam 	regulator->rdev = regulator_register(&regulator->info->reg_desc,
54808bf1c0aSAshish Jangam 					     &pdev->dev,
54908bf1c0aSAshish Jangam 					     pdata->regulators[pdev->id],
550*47aed92cSMark Brown 					     regulator, NULL);
55108bf1c0aSAshish Jangam 	if (IS_ERR(regulator->rdev)) {
55208bf1c0aSAshish Jangam 		dev_err(&pdev->dev, "failed to register regulator %s\n",
55308bf1c0aSAshish Jangam 			regulator->info->reg_desc.name);
55408bf1c0aSAshish Jangam 		ret = PTR_ERR(regulator->rdev);
55508bf1c0aSAshish Jangam 		goto err;
55608bf1c0aSAshish Jangam 	}
55708bf1c0aSAshish Jangam 
55808bf1c0aSAshish Jangam 	platform_set_drvdata(pdev, regulator);
55908bf1c0aSAshish Jangam 
56008bf1c0aSAshish Jangam 	return 0;
56108bf1c0aSAshish Jangam err:
56208bf1c0aSAshish Jangam 	kfree(regulator);
56308bf1c0aSAshish Jangam 	return ret;
56408bf1c0aSAshish Jangam }
56508bf1c0aSAshish Jangam 
56608bf1c0aSAshish Jangam static int __devexit da9052_regulator_remove(struct platform_device *pdev)
56708bf1c0aSAshish Jangam {
56808bf1c0aSAshish Jangam 	struct da9052_regulator *regulator = platform_get_drvdata(pdev);
56908bf1c0aSAshish Jangam 
57008bf1c0aSAshish Jangam 	regulator_unregister(regulator->rdev);
57108bf1c0aSAshish Jangam 	kfree(regulator);
57208bf1c0aSAshish Jangam 
57308bf1c0aSAshish Jangam 	return 0;
57408bf1c0aSAshish Jangam }
57508bf1c0aSAshish Jangam 
57608bf1c0aSAshish Jangam static struct platform_driver da9052_regulator_driver = {
57708bf1c0aSAshish Jangam 	.probe = da9052_regulator_probe,
57808bf1c0aSAshish Jangam 	.remove = __devexit_p(da9052_regulator_remove),
57908bf1c0aSAshish Jangam 	.driver = {
58008bf1c0aSAshish Jangam 		.name = "da9052-regulator",
58108bf1c0aSAshish Jangam 		.owner = THIS_MODULE,
58208bf1c0aSAshish Jangam 	},
58308bf1c0aSAshish Jangam };
58408bf1c0aSAshish Jangam 
58508bf1c0aSAshish Jangam static int __init da9052_regulator_init(void)
58608bf1c0aSAshish Jangam {
58708bf1c0aSAshish Jangam 	return platform_driver_register(&da9052_regulator_driver);
58808bf1c0aSAshish Jangam }
58908bf1c0aSAshish Jangam subsys_initcall(da9052_regulator_init);
59008bf1c0aSAshish Jangam 
59108bf1c0aSAshish Jangam static void __exit da9052_regulator_exit(void)
59208bf1c0aSAshish Jangam {
59308bf1c0aSAshish Jangam 	platform_driver_unregister(&da9052_regulator_driver);
59408bf1c0aSAshish Jangam }
59508bf1c0aSAshish Jangam module_exit(da9052_regulator_exit);
59608bf1c0aSAshish Jangam 
59708bf1c0aSAshish Jangam MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
59808bf1c0aSAshish Jangam MODULE_DESCRIPTION("Power Regulator driver for Dialog DA9052 PMIC");
59908bf1c0aSAshish Jangam MODULE_LICENSE("GPL");
60008bf1c0aSAshish Jangam MODULE_ALIAS("platform:da9052-regulator");
601