xref: /openbmc/linux/drivers/regulator/88pm8607.c (revision 586e1a17)
1be0e2d3eSHaojian Zhuang /*
2be0e2d3eSHaojian Zhuang  * Regulators driver for Marvell 88PM8607
3be0e2d3eSHaojian Zhuang  *
4be0e2d3eSHaojian Zhuang  * Copyright (C) 2009 Marvell International Ltd.
5be0e2d3eSHaojian Zhuang  * 	Haojian Zhuang <haojian.zhuang@marvell.com>
6be0e2d3eSHaojian Zhuang  *
7be0e2d3eSHaojian Zhuang  * This program is free software; you can redistribute it and/or modify
8be0e2d3eSHaojian Zhuang  * it under the terms of the GNU General Public License version 2 as
9be0e2d3eSHaojian Zhuang  * published by the Free Software Foundation.
10be0e2d3eSHaojian Zhuang  */
11be0e2d3eSHaojian Zhuang #include <linux/kernel.h>
12be0e2d3eSHaojian Zhuang #include <linux/init.h>
13be0e2d3eSHaojian Zhuang #include <linux/err.h>
1453dbab7aSHaojian Zhuang #include <linux/i2c.h>
15be0e2d3eSHaojian Zhuang #include <linux/platform_device.h>
16be0e2d3eSHaojian Zhuang #include <linux/regulator/driver.h>
17be0e2d3eSHaojian Zhuang #include <linux/regulator/machine.h>
1853dbab7aSHaojian Zhuang #include <linux/mfd/88pm860x.h>
19be0e2d3eSHaojian Zhuang 
20be0e2d3eSHaojian Zhuang struct pm8607_regulator_info {
21be0e2d3eSHaojian Zhuang 	struct regulator_desc	desc;
2253dbab7aSHaojian Zhuang 	struct pm860x_chip	*chip;
23be0e2d3eSHaojian Zhuang 	struct regulator_dev	*regulator;
2453dbab7aSHaojian Zhuang 	struct i2c_client	*i2c;
25be0e2d3eSHaojian Zhuang 
269f79e9dbSHaojian Zhuang 	unsigned int	*vol_table;
279f79e9dbSHaojian Zhuang 	unsigned int	*vol_suspend;
289f79e9dbSHaojian Zhuang 
29be0e2d3eSHaojian Zhuang 	int	vol_reg;
30be0e2d3eSHaojian Zhuang 	int	vol_shift;
31be0e2d3eSHaojian Zhuang 	int	vol_nbits;
32be0e2d3eSHaojian Zhuang 	int	update_reg;
33be0e2d3eSHaojian Zhuang 	int	update_bit;
34be0e2d3eSHaojian Zhuang 	int	enable_reg;
35be0e2d3eSHaojian Zhuang 	int	enable_bit;
36be0e2d3eSHaojian Zhuang 	int	slope_double;
37be0e2d3eSHaojian Zhuang };
38be0e2d3eSHaojian Zhuang 
399f79e9dbSHaojian Zhuang static const unsigned int BUCK1_table[] = {
409f79e9dbSHaojian Zhuang 	 725000,  750000,  775000,  800000,  825000,  850000,  875000,  900000,
419f79e9dbSHaojian Zhuang 	 925000,  950000,  975000, 1000000, 1025000, 1050000, 1075000, 1100000,
429f79e9dbSHaojian Zhuang 	1125000, 1150000, 1175000, 1200000, 1225000, 1250000, 1275000, 1300000,
439f79e9dbSHaojian Zhuang 	1325000, 1350000, 1375000, 1400000, 1425000, 1450000, 1475000, 1500000,
449f79e9dbSHaojian Zhuang 	      0,   25000,   50000,   75000,  100000,  125000,  150000,  175000,
459f79e9dbSHaojian Zhuang 	 200000,  225000,  250000,  275000,  300000,  325000,  350000,  375000,
469f79e9dbSHaojian Zhuang 	 400000,  425000,  450000,  475000,  500000,  525000,  550000,  575000,
479f79e9dbSHaojian Zhuang 	 600000,  625000,  650000,  675000,  700000,  725000,  750000,  775000,
489f79e9dbSHaojian Zhuang };
49be0e2d3eSHaojian Zhuang 
509f79e9dbSHaojian Zhuang static const unsigned int BUCK1_suspend_table[] = {
519f79e9dbSHaojian Zhuang 	      0,   25000,   50000,   75000,  100000,  125000,  150000,  175000,
529f79e9dbSHaojian Zhuang 	 200000,  225000,  250000,  275000,  300000,  325000,  350000,  375000,
539f79e9dbSHaojian Zhuang 	 400000,  425000,  450000,  475000,  500000,  525000,  550000,  575000,
549f79e9dbSHaojian Zhuang 	 600000,  625000,  650000,  675000,  700000,  725000,  750000,  775000,
559f79e9dbSHaojian Zhuang 	 800000,  825000,  850000,  875000,  900000,  925000,  950000,  975000,
569f79e9dbSHaojian Zhuang 	1000000, 1025000, 1050000, 1075000, 1100000, 1125000, 1150000, 1175000,
579f79e9dbSHaojian Zhuang 	1200000, 1225000, 1250000, 1275000, 1300000, 1325000, 1350000, 1375000,
589f79e9dbSHaojian Zhuang 	1400000, 1425000, 1450000, 1475000, 1500000, 1500000, 1500000, 1500000,
599f79e9dbSHaojian Zhuang };
609f79e9dbSHaojian Zhuang 
619f79e9dbSHaojian Zhuang static const unsigned int BUCK2_table[] = {
629f79e9dbSHaojian Zhuang 	      0,   50000,  100000,  150000,  200000,  250000,  300000,  350000,
639f79e9dbSHaojian Zhuang 	 400000,  450000,  500000,  550000,  600000,  650000,  700000,  750000,
649f79e9dbSHaojian Zhuang 	 800000,  850000,  900000,  950000, 1000000, 1050000, 1100000, 1150000,
659f79e9dbSHaojian Zhuang 	1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, 1550000,
669f79e9dbSHaojian Zhuang 	1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, 1950000,
679f79e9dbSHaojian Zhuang 	2000000, 2050000, 2100000, 2150000, 2200000, 2250000, 2300000, 2350000,
689f79e9dbSHaojian Zhuang 	2400000, 2450000, 2500000, 2550000, 2600000, 2650000, 2700000, 2750000,
699f79e9dbSHaojian Zhuang 	2800000, 2850000, 2900000, 2950000, 3000000, 3000000, 3000000, 3000000,
709f79e9dbSHaojian Zhuang };
719f79e9dbSHaojian Zhuang 
729f79e9dbSHaojian Zhuang static const unsigned int BUCK2_suspend_table[] = {
739f79e9dbSHaojian Zhuang 	      0,   50000,  100000,  150000,  200000,  250000,  300000,  350000,
749f79e9dbSHaojian Zhuang 	 400000,  450000,  500000,  550000,  600000,  650000,  700000,  750000,
759f79e9dbSHaojian Zhuang 	 800000,  850000,  900000,  950000, 1000000, 1050000, 1100000, 1150000,
769f79e9dbSHaojian Zhuang 	1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, 1550000,
779f79e9dbSHaojian Zhuang 	1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, 1950000,
789f79e9dbSHaojian Zhuang 	2000000, 2050000, 2100000, 2150000, 2200000, 2250000, 2300000, 2350000,
799f79e9dbSHaojian Zhuang 	2400000, 2450000, 2500000, 2550000, 2600000, 2650000, 2700000, 2750000,
809f79e9dbSHaojian Zhuang 	2800000, 2850000, 2900000, 2950000, 3000000, 3000000, 3000000, 3000000,
819f79e9dbSHaojian Zhuang };
829f79e9dbSHaojian Zhuang 
839f79e9dbSHaojian Zhuang static const unsigned int BUCK3_table[] = {
849f79e9dbSHaojian Zhuang               0,   25000,   50000,   75000,  100000,  125000,  150000,  175000,
859f79e9dbSHaojian Zhuang 	 200000,  225000,  250000,  275000,  300000,  325000,  350000,  375000,
869f79e9dbSHaojian Zhuang 	 400000,  425000,  450000,  475000,  500000,  525000,  550000,  575000,
879f79e9dbSHaojian Zhuang 	 600000,  625000,  650000,  675000,  700000,  725000,  750000,  775000,
889f79e9dbSHaojian Zhuang 	 800000,  825000,  850000,  875000,  900000,  925000,  950000,  975000,
899f79e9dbSHaojian Zhuang 	1000000, 1025000, 1050000, 1075000, 1100000, 1125000, 1150000, 1175000,
909f79e9dbSHaojian Zhuang 	1200000, 1225000, 1250000, 1275000, 1300000, 1325000, 1350000, 1375000,
919f79e9dbSHaojian Zhuang 	1400000, 1425000, 1450000, 1475000, 1500000, 1500000, 1500000, 1500000,
929f79e9dbSHaojian Zhuang };
939f79e9dbSHaojian Zhuang 
949f79e9dbSHaojian Zhuang static const unsigned int BUCK3_suspend_table[] = {
959f79e9dbSHaojian Zhuang               0,   25000,   50000,   75000,  100000,  125000,  150000,  175000,
969f79e9dbSHaojian Zhuang 	 200000,  225000,  250000,  275000,  300000,  325000,  350000,  375000,
979f79e9dbSHaojian Zhuang 	 400000,  425000,  450000,  475000,  500000,  525000,  550000,  575000,
989f79e9dbSHaojian Zhuang 	 600000,  625000,  650000,  675000,  700000,  725000,  750000,  775000,
999f79e9dbSHaojian Zhuang 	 800000,  825000,  850000,  875000,  900000,  925000,  950000,  975000,
1009f79e9dbSHaojian Zhuang 	1000000, 1025000, 1050000, 1075000, 1100000, 1125000, 1150000, 1175000,
1019f79e9dbSHaojian Zhuang 	1200000, 1225000, 1250000, 1275000, 1300000, 1325000, 1350000, 1375000,
1029f79e9dbSHaojian Zhuang 	1400000, 1425000, 1450000, 1475000, 1500000, 1500000, 1500000, 1500000,
1039f79e9dbSHaojian Zhuang };
1049f79e9dbSHaojian Zhuang 
1059f79e9dbSHaojian Zhuang static const unsigned int LDO1_table[] = {
1069f79e9dbSHaojian Zhuang 	1800000, 1200000, 2800000, 0,
1079f79e9dbSHaojian Zhuang };
1089f79e9dbSHaojian Zhuang 
1099f79e9dbSHaojian Zhuang static const unsigned int LDO1_suspend_table[] = {
1109f79e9dbSHaojian Zhuang 	1800000, 1200000, 0, 0,
1119f79e9dbSHaojian Zhuang };
1129f79e9dbSHaojian Zhuang 
1139f79e9dbSHaojian Zhuang static const unsigned int LDO2_table[] = {
1149f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000,
1159f79e9dbSHaojian Zhuang };
1169f79e9dbSHaojian Zhuang 
1179f79e9dbSHaojian Zhuang static const unsigned int LDO2_suspend_table[] = {
1189f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1199f79e9dbSHaojian Zhuang };
1209f79e9dbSHaojian Zhuang 
1219f79e9dbSHaojian Zhuang static const unsigned int LDO3_table[] = {
1229f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000,
1239f79e9dbSHaojian Zhuang };
1249f79e9dbSHaojian Zhuang 
1259f79e9dbSHaojian Zhuang static const unsigned int LDO3_suspend_table[] = {
1269f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1279f79e9dbSHaojian Zhuang };
1289f79e9dbSHaojian Zhuang 
1299f79e9dbSHaojian Zhuang static const unsigned int LDO4_table[] = {
1309f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2900000, 3300000,
1319f79e9dbSHaojian Zhuang };
1329f79e9dbSHaojian Zhuang 
1339f79e9dbSHaojian Zhuang static const unsigned int LDO4_suspend_table[] = {
1349f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2900000, 2900000,
1359f79e9dbSHaojian Zhuang };
1369f79e9dbSHaojian Zhuang 
1379f79e9dbSHaojian Zhuang static const unsigned int LDO5_table[] = {
1389f79e9dbSHaojian Zhuang 	2900000, 3000000, 3100000, 3300000,
1399f79e9dbSHaojian Zhuang };
1409f79e9dbSHaojian Zhuang 
1419f79e9dbSHaojian Zhuang static const unsigned int LDO5_suspend_table[] = {
1429f79e9dbSHaojian Zhuang 	2900000, 0, 0, 0,
1439f79e9dbSHaojian Zhuang };
1449f79e9dbSHaojian Zhuang 
1459f79e9dbSHaojian Zhuang static const unsigned int LDO6_table[] = {
1469f79e9dbSHaojian Zhuang 	1800000, 1850000, 2600000, 2650000, 2700000, 2750000, 2800000, 3300000,
1479f79e9dbSHaojian Zhuang };
1489f79e9dbSHaojian Zhuang 
1499f79e9dbSHaojian Zhuang static const unsigned int LDO6_suspend_table[] = {
1509f79e9dbSHaojian Zhuang 	1800000, 1850000, 2600000, 2650000, 2700000, 2750000, 2800000, 2900000,
1519f79e9dbSHaojian Zhuang };
1529f79e9dbSHaojian Zhuang 
1539f79e9dbSHaojian Zhuang static const unsigned int LDO7_table[] = {
1549f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1559f79e9dbSHaojian Zhuang };
1569f79e9dbSHaojian Zhuang 
1579f79e9dbSHaojian Zhuang static const unsigned int LDO7_suspend_table[] = {
1589f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1599f79e9dbSHaojian Zhuang };
1609f79e9dbSHaojian Zhuang 
1619f79e9dbSHaojian Zhuang static const unsigned int LDO8_table[] = {
1629f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1639f79e9dbSHaojian Zhuang };
1649f79e9dbSHaojian Zhuang 
1659f79e9dbSHaojian Zhuang static const unsigned int LDO8_suspend_table[] = {
1669f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1679f79e9dbSHaojian Zhuang };
1689f79e9dbSHaojian Zhuang 
1699f79e9dbSHaojian Zhuang static const unsigned int LDO9_table[] = {
1709f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000,
1719f79e9dbSHaojian Zhuang };
1729f79e9dbSHaojian Zhuang 
1739f79e9dbSHaojian Zhuang static const unsigned int LDO9_suspend_table[] = {
1749f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1759f79e9dbSHaojian Zhuang };
1769f79e9dbSHaojian Zhuang 
1779f79e9dbSHaojian Zhuang static const unsigned int LDO10_table[] = {
1789f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000,
1799f79e9dbSHaojian Zhuang 	1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000,
1809f79e9dbSHaojian Zhuang };
1819f79e9dbSHaojian Zhuang 
1829f79e9dbSHaojian Zhuang static const unsigned int LDO10_suspend_table[] = {
1839f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1849f79e9dbSHaojian Zhuang 	1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000,
1859f79e9dbSHaojian Zhuang };
1869f79e9dbSHaojian Zhuang 
1879f79e9dbSHaojian Zhuang static const unsigned int LDO12_table[] = {
1889f79e9dbSHaojian Zhuang 	1800000, 1900000, 2700000, 2800000, 2900000, 3000000, 3100000, 3300000,
1899f79e9dbSHaojian Zhuang 	1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000,
1909f79e9dbSHaojian Zhuang };
1919f79e9dbSHaojian Zhuang 
1929f79e9dbSHaojian Zhuang static const unsigned int LDO12_suspend_table[] = {
1939f79e9dbSHaojian Zhuang 	1800000, 1900000, 2700000, 2800000, 2900000, 2900000, 2900000, 2900000,
1949f79e9dbSHaojian Zhuang 	1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000,
1959f79e9dbSHaojian Zhuang };
1969f79e9dbSHaojian Zhuang 
1979f79e9dbSHaojian Zhuang static const unsigned int LDO13_table[] = {
1989f79e9dbSHaojian Zhuang 	1300000, 1800000, 2000000, 2500000, 2800000, 3000000, 0, 0,
1999f79e9dbSHaojian Zhuang };
2009f79e9dbSHaojian Zhuang 
2019f79e9dbSHaojian Zhuang static const unsigned int LDO13_suspend_table[] = {
2029f79e9dbSHaojian Zhuang 	0,
2039f79e9dbSHaojian Zhuang };
2049f79e9dbSHaojian Zhuang 
2059f79e9dbSHaojian Zhuang static const unsigned int LDO14_table[] = {
2069f79e9dbSHaojian Zhuang 	1800000, 1850000, 2700000, 2750000, 2800000, 2850000, 2900000, 3300000,
2079f79e9dbSHaojian Zhuang };
2089f79e9dbSHaojian Zhuang 
2099f79e9dbSHaojian Zhuang static const unsigned int LDO14_suspend_table[] = {
2109f79e9dbSHaojian Zhuang 	1800000, 1850000, 2700000, 2750000, 2800000, 2850000, 2900000, 2900000,
2119f79e9dbSHaojian Zhuang };
212be0e2d3eSHaojian Zhuang 
213be0e2d3eSHaojian Zhuang static int pm8607_list_voltage(struct regulator_dev *rdev, unsigned index)
214be0e2d3eSHaojian Zhuang {
215be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
216be0e2d3eSHaojian Zhuang 	int ret = -EINVAL;
217be0e2d3eSHaojian Zhuang 
218d06563cbSAxel Lin 	if (info->vol_table && (index < (1 << info->vol_nbits))) {
2199f79e9dbSHaojian Zhuang 		ret = info->vol_table[index];
220be0e2d3eSHaojian Zhuang 		if (info->slope_double)
221be0e2d3eSHaojian Zhuang 			ret <<= 1;
222be0e2d3eSHaojian Zhuang 	}
223be0e2d3eSHaojian Zhuang 	return ret;
224be0e2d3eSHaojian Zhuang }
225be0e2d3eSHaojian Zhuang 
226be0e2d3eSHaojian Zhuang static int choose_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
227be0e2d3eSHaojian Zhuang {
228be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
2299f79e9dbSHaojian Zhuang 	int i, ret = -ENOENT;
230be0e2d3eSHaojian Zhuang 
2319f79e9dbSHaojian Zhuang 	if (info->slope_double) {
232be0e2d3eSHaojian Zhuang 		min_uV = min_uV >> 1;
2339f79e9dbSHaojian Zhuang 		max_uV = max_uV >> 1;
234be0e2d3eSHaojian Zhuang 	}
2359f79e9dbSHaojian Zhuang 	if (info->vol_table) {
236d06563cbSAxel Lin 		for (i = 0; i < (1 << info->vol_nbits); i++) {
2379f79e9dbSHaojian Zhuang 			if (!info->vol_table[i])
238be0e2d3eSHaojian Zhuang 				break;
2399f79e9dbSHaojian Zhuang 			if ((min_uV <= info->vol_table[i])
2409f79e9dbSHaojian Zhuang 				&& (max_uV >= info->vol_table[i])) {
2419f79e9dbSHaojian Zhuang 				ret = i;
242be0e2d3eSHaojian Zhuang 				break;
243be0e2d3eSHaojian Zhuang 			}
244be0e2d3eSHaojian Zhuang 		}
2459f79e9dbSHaojian Zhuang 	}
2469f79e9dbSHaojian Zhuang 	if (ret < 0)
2479f79e9dbSHaojian Zhuang 		pr_err("invalid voltage range (%d %d) uV\n", min_uV, max_uV);
2489f79e9dbSHaojian Zhuang 	return ret;
249be0e2d3eSHaojian Zhuang }
250be0e2d3eSHaojian Zhuang 
251be0e2d3eSHaojian Zhuang static int pm8607_set_voltage(struct regulator_dev *rdev,
2523a93f2a9SMark Brown 			      int min_uV, int max_uV, unsigned *selector)
253be0e2d3eSHaojian Zhuang {
254be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
255be0e2d3eSHaojian Zhuang 	uint8_t val, mask;
256be0e2d3eSHaojian Zhuang 	int ret;
257be0e2d3eSHaojian Zhuang 
2589f79e9dbSHaojian Zhuang 	if (min_uV > max_uV) {
259be0e2d3eSHaojian Zhuang 		pr_err("invalid voltage range (%d, %d) uV\n", min_uV, max_uV);
260be0e2d3eSHaojian Zhuang 		return -EINVAL;
261be0e2d3eSHaojian Zhuang 	}
262be0e2d3eSHaojian Zhuang 
263be0e2d3eSHaojian Zhuang 	ret = choose_voltage(rdev, min_uV, max_uV);
264be0e2d3eSHaojian Zhuang 	if (ret < 0)
265be0e2d3eSHaojian Zhuang 		return -EINVAL;
2663a93f2a9SMark Brown 	*selector = ret;
267be0e2d3eSHaojian Zhuang 	val = (uint8_t)(ret << info->vol_shift);
268be0e2d3eSHaojian Zhuang 	mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
269be0e2d3eSHaojian Zhuang 
27053dbab7aSHaojian Zhuang 	ret = pm860x_set_bits(info->i2c, info->vol_reg, mask, val);
271be0e2d3eSHaojian Zhuang 	if (ret)
272be0e2d3eSHaojian Zhuang 		return ret;
273be0e2d3eSHaojian Zhuang 	switch (info->desc.id) {
274be0e2d3eSHaojian Zhuang 	case PM8607_ID_BUCK1:
275be0e2d3eSHaojian Zhuang 	case PM8607_ID_BUCK3:
27653dbab7aSHaojian Zhuang 		ret = pm860x_set_bits(info->i2c, info->update_reg,
277be0e2d3eSHaojian Zhuang 				      1 << info->update_bit,
278be0e2d3eSHaojian Zhuang 				      1 << info->update_bit);
279be0e2d3eSHaojian Zhuang 		break;
280be0e2d3eSHaojian Zhuang 	}
281be0e2d3eSHaojian Zhuang 	return ret;
282be0e2d3eSHaojian Zhuang }
283be0e2d3eSHaojian Zhuang 
284be0e2d3eSHaojian Zhuang static int pm8607_get_voltage(struct regulator_dev *rdev)
285be0e2d3eSHaojian Zhuang {
286be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
287be0e2d3eSHaojian Zhuang 	uint8_t val, mask;
288be0e2d3eSHaojian Zhuang 	int ret;
289be0e2d3eSHaojian Zhuang 
29053dbab7aSHaojian Zhuang 	ret = pm860x_reg_read(info->i2c, info->vol_reg);
291be0e2d3eSHaojian Zhuang 	if (ret < 0)
292be0e2d3eSHaojian Zhuang 		return ret;
293be0e2d3eSHaojian Zhuang 
294be0e2d3eSHaojian Zhuang 	mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
295be0e2d3eSHaojian Zhuang 	val = ((unsigned char)ret & mask) >> info->vol_shift;
296be0e2d3eSHaojian Zhuang 
297be0e2d3eSHaojian Zhuang 	return pm8607_list_voltage(rdev, val);
298be0e2d3eSHaojian Zhuang }
299be0e2d3eSHaojian Zhuang 
300be0e2d3eSHaojian Zhuang static int pm8607_enable(struct regulator_dev *rdev)
301be0e2d3eSHaojian Zhuang {
302be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
303be0e2d3eSHaojian Zhuang 
30453dbab7aSHaojian Zhuang 	return pm860x_set_bits(info->i2c, info->enable_reg,
305be0e2d3eSHaojian Zhuang 			       1 << info->enable_bit,
306be0e2d3eSHaojian Zhuang 			       1 << info->enable_bit);
307be0e2d3eSHaojian Zhuang }
308be0e2d3eSHaojian Zhuang 
309be0e2d3eSHaojian Zhuang static int pm8607_disable(struct regulator_dev *rdev)
310be0e2d3eSHaojian Zhuang {
311be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
312be0e2d3eSHaojian Zhuang 
31353dbab7aSHaojian Zhuang 	return pm860x_set_bits(info->i2c, info->enable_reg,
314be0e2d3eSHaojian Zhuang 			       1 << info->enable_bit, 0);
315be0e2d3eSHaojian Zhuang }
316be0e2d3eSHaojian Zhuang 
317be0e2d3eSHaojian Zhuang static int pm8607_is_enabled(struct regulator_dev *rdev)
318be0e2d3eSHaojian Zhuang {
319be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
320be0e2d3eSHaojian Zhuang 	int ret;
321be0e2d3eSHaojian Zhuang 
32253dbab7aSHaojian Zhuang 	ret = pm860x_reg_read(info->i2c, info->enable_reg);
323be0e2d3eSHaojian Zhuang 	if (ret < 0)
324be0e2d3eSHaojian Zhuang 		return ret;
325be0e2d3eSHaojian Zhuang 
326be0e2d3eSHaojian Zhuang 	return !!((unsigned char)ret & (1 << info->enable_bit));
327be0e2d3eSHaojian Zhuang }
328be0e2d3eSHaojian Zhuang 
329be0e2d3eSHaojian Zhuang static struct regulator_ops pm8607_regulator_ops = {
330be0e2d3eSHaojian Zhuang 	.set_voltage	= pm8607_set_voltage,
331be0e2d3eSHaojian Zhuang 	.get_voltage	= pm8607_get_voltage,
332be0e2d3eSHaojian Zhuang 	.enable		= pm8607_enable,
333be0e2d3eSHaojian Zhuang 	.disable	= pm8607_disable,
334be0e2d3eSHaojian Zhuang 	.is_enabled	= pm8607_is_enabled,
335be0e2d3eSHaojian Zhuang };
336be0e2d3eSHaojian Zhuang 
3379f79e9dbSHaojian Zhuang #define PM8607_DVC(vreg, nbits, ureg, ubit, ereg, ebit)			\
338be0e2d3eSHaojian Zhuang {									\
339be0e2d3eSHaojian Zhuang 	.desc	= {							\
3409f79e9dbSHaojian Zhuang 		.name	= #vreg,					\
341be0e2d3eSHaojian Zhuang 		.ops	= &pm8607_regulator_ops,			\
342be0e2d3eSHaojian Zhuang 		.type	= REGULATOR_VOLTAGE,				\
3439f79e9dbSHaojian Zhuang 		.id	= PM8607_ID_##vreg,				\
344be0e2d3eSHaojian Zhuang 		.owner	= THIS_MODULE,					\
345be0e2d3eSHaojian Zhuang 	},								\
346be0e2d3eSHaojian Zhuang 	.vol_reg	= PM8607_##vreg,				\
347be0e2d3eSHaojian Zhuang 	.vol_shift	= (0),						\
348be0e2d3eSHaojian Zhuang 	.vol_nbits	= (nbits),					\
349be0e2d3eSHaojian Zhuang 	.update_reg	= PM8607_##ureg,				\
350be0e2d3eSHaojian Zhuang 	.update_bit	= (ubit),					\
351be0e2d3eSHaojian Zhuang 	.enable_reg	= PM8607_##ereg,				\
352be0e2d3eSHaojian Zhuang 	.enable_bit	= (ebit),					\
353be0e2d3eSHaojian Zhuang 	.slope_double	= (0),						\
3549f79e9dbSHaojian Zhuang 	.vol_table	= (unsigned int *)&vreg##_table,		\
3559f79e9dbSHaojian Zhuang 	.vol_suspend	= (unsigned int *)&vreg##_suspend_table,	\
356be0e2d3eSHaojian Zhuang }
357be0e2d3eSHaojian Zhuang 
3589f79e9dbSHaojian Zhuang #define PM8607_LDO(_id, vreg, shift, nbits, ereg, ebit)			\
359be0e2d3eSHaojian Zhuang {									\
360be0e2d3eSHaojian Zhuang 	.desc	= {							\
361be0e2d3eSHaojian Zhuang 		.name	= "LDO" #_id,					\
362be0e2d3eSHaojian Zhuang 		.ops	= &pm8607_regulator_ops,			\
363be0e2d3eSHaojian Zhuang 		.type	= REGULATOR_VOLTAGE,				\
364be0e2d3eSHaojian Zhuang 		.id	= PM8607_ID_LDO##_id,				\
365be0e2d3eSHaojian Zhuang 		.owner	= THIS_MODULE,					\
366be0e2d3eSHaojian Zhuang 	},								\
367be0e2d3eSHaojian Zhuang 	.vol_reg	= PM8607_##vreg,				\
368be0e2d3eSHaojian Zhuang 	.vol_shift	= (shift),					\
369be0e2d3eSHaojian Zhuang 	.vol_nbits	= (nbits),					\
370be0e2d3eSHaojian Zhuang 	.enable_reg	= PM8607_##ereg,				\
371be0e2d3eSHaojian Zhuang 	.enable_bit	= (ebit),					\
372be0e2d3eSHaojian Zhuang 	.slope_double	= (0),						\
3739f79e9dbSHaojian Zhuang 	.vol_table	= (unsigned int *)&LDO##_id##_table,		\
3749f79e9dbSHaojian Zhuang 	.vol_suspend	= (unsigned int *)&LDO##_id##_suspend_table,	\
375be0e2d3eSHaojian Zhuang }
376be0e2d3eSHaojian Zhuang 
377be0e2d3eSHaojian Zhuang static struct pm8607_regulator_info pm8607_regulator_info[] = {
3789f79e9dbSHaojian Zhuang 	PM8607_DVC(BUCK1, 6, GO, 0, SUPPLIES_EN11, 0),
3799f79e9dbSHaojian Zhuang 	PM8607_DVC(BUCK2, 6, GO, 1, SUPPLIES_EN11, 1),
3809f79e9dbSHaojian Zhuang 	PM8607_DVC(BUCK3, 6, GO, 2, SUPPLIES_EN11, 2),
381be0e2d3eSHaojian Zhuang 
3829f79e9dbSHaojian Zhuang 	PM8607_LDO( 1,         LDO1, 0, 2, SUPPLIES_EN11, 3),
3839f79e9dbSHaojian Zhuang 	PM8607_LDO( 2,         LDO2, 0, 3, SUPPLIES_EN11, 4),
3849f79e9dbSHaojian Zhuang 	PM8607_LDO( 3,         LDO3, 0, 3, SUPPLIES_EN11, 5),
3859f79e9dbSHaojian Zhuang 	PM8607_LDO( 4,         LDO4, 0, 3, SUPPLIES_EN11, 6),
3869f79e9dbSHaojian Zhuang 	PM8607_LDO( 5,         LDO5, 0, 2, SUPPLIES_EN11, 7),
3879f79e9dbSHaojian Zhuang 	PM8607_LDO( 6,         LDO6, 0, 3, SUPPLIES_EN12, 0),
3889f79e9dbSHaojian Zhuang 	PM8607_LDO( 7,         LDO7, 0, 3, SUPPLIES_EN12, 1),
3899f79e9dbSHaojian Zhuang 	PM8607_LDO( 8,         LDO8, 0, 3, SUPPLIES_EN12, 2),
3909f79e9dbSHaojian Zhuang 	PM8607_LDO( 9,         LDO9, 0, 3, SUPPLIES_EN12, 3),
3919f79e9dbSHaojian Zhuang 	PM8607_LDO(10,        LDO10, 0, 3, SUPPLIES_EN12, 4),
3929f79e9dbSHaojian Zhuang 	PM8607_LDO(12,        LDO12, 0, 4, SUPPLIES_EN12, 5),
3939f79e9dbSHaojian Zhuang 	PM8607_LDO(13, VIBRATOR_SET, 1, 3,  VIBRATOR_SET, 0),
3949f79e9dbSHaojian Zhuang 	PM8607_LDO(14,        LDO14, 0, 4, SUPPLIES_EN12, 6),
395be0e2d3eSHaojian Zhuang };
396be0e2d3eSHaojian Zhuang 
397be0e2d3eSHaojian Zhuang static int __devinit pm8607_regulator_probe(struct platform_device *pdev)
398be0e2d3eSHaojian Zhuang {
39953dbab7aSHaojian Zhuang 	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
400be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = NULL;
401586e1a17SHaojian Zhuang 	struct regulator_init_data *pdata = pdev->dev.platform_data;
402586e1a17SHaojian Zhuang 	struct resource *res;
40322aad001SHaojian Zhuang 	int i;
404be0e2d3eSHaojian Zhuang 
405586e1a17SHaojian Zhuang 	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
406586e1a17SHaojian Zhuang 	if (res == NULL) {
407586e1a17SHaojian Zhuang 		dev_err(&pdev->dev, "No I/O resource!\n");
40822aad001SHaojian Zhuang 		return -EINVAL;
409586e1a17SHaojian Zhuang 	}
41022aad001SHaojian Zhuang 	for (i = 0; i < ARRAY_SIZE(pm8607_regulator_info); i++) {
41122aad001SHaojian Zhuang 		info = &pm8607_regulator_info[i];
412586e1a17SHaojian Zhuang 		if (info->desc.id == res->start)
41322aad001SHaojian Zhuang 			break;
41422aad001SHaojian Zhuang 	}
415586e1a17SHaojian Zhuang 	if ((i < 0) || (i > PM8607_ID_RG_MAX)) {
416586e1a17SHaojian Zhuang 		dev_err(&pdev->dev, "Failed to find regulator %d\n",
417586e1a17SHaojian Zhuang 			res->start);
418be0e2d3eSHaojian Zhuang 		return -EINVAL;
419be0e2d3eSHaojian Zhuang 	}
42053dbab7aSHaojian Zhuang 	info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
421be0e2d3eSHaojian Zhuang 	info->chip = chip;
422be0e2d3eSHaojian Zhuang 
42322aad001SHaojian Zhuang 	/* check DVC ramp slope double */
424586e1a17SHaojian Zhuang 	if ((i == PM8607_ID_BUCK3) && info->chip->buck3_double)
42522aad001SHaojian Zhuang 		info->slope_double = 1;
42622aad001SHaojian Zhuang 
427586e1a17SHaojian Zhuang 	/* replace driver_data with info */
428be0e2d3eSHaojian Zhuang 	info->regulator = regulator_register(&info->desc, &pdev->dev,
42922aad001SHaojian Zhuang 					     pdata, info);
430be0e2d3eSHaojian Zhuang 	if (IS_ERR(info->regulator)) {
431be0e2d3eSHaojian Zhuang 		dev_err(&pdev->dev, "failed to register regulator %s\n",
432be0e2d3eSHaojian Zhuang 			info->desc.name);
433be0e2d3eSHaojian Zhuang 		return PTR_ERR(info->regulator);
434be0e2d3eSHaojian Zhuang 	}
435be0e2d3eSHaojian Zhuang 
436be0e2d3eSHaojian Zhuang 	platform_set_drvdata(pdev, info);
437be0e2d3eSHaojian Zhuang 	return 0;
438be0e2d3eSHaojian Zhuang }
439be0e2d3eSHaojian Zhuang 
440be0e2d3eSHaojian Zhuang static int __devexit pm8607_regulator_remove(struct platform_device *pdev)
441be0e2d3eSHaojian Zhuang {
442be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = platform_get_drvdata(pdev);
443be0e2d3eSHaojian Zhuang 
444192bbb95SHaojian Zhuang 	platform_set_drvdata(pdev, NULL);
445be0e2d3eSHaojian Zhuang 	regulator_unregister(info->regulator);
446be0e2d3eSHaojian Zhuang 	return 0;
447be0e2d3eSHaojian Zhuang }
448be0e2d3eSHaojian Zhuang 
449192bbb95SHaojian Zhuang static struct platform_driver pm8607_regulator_driver = {
450192bbb95SHaojian Zhuang 	.driver		= {
451192bbb95SHaojian Zhuang 		.name	= "88pm860x-regulator",
452192bbb95SHaojian Zhuang 		.owner	= THIS_MODULE,
453192bbb95SHaojian Zhuang 	},
454192bbb95SHaojian Zhuang 	.probe		= pm8607_regulator_probe,
455192bbb95SHaojian Zhuang 	.remove		= __devexit_p(pm8607_regulator_remove),
456be0e2d3eSHaojian Zhuang };
457be0e2d3eSHaojian Zhuang 
458be0e2d3eSHaojian Zhuang static int __init pm8607_regulator_init(void)
459be0e2d3eSHaojian Zhuang {
460192bbb95SHaojian Zhuang 	return platform_driver_register(&pm8607_regulator_driver);
461be0e2d3eSHaojian Zhuang }
462be0e2d3eSHaojian Zhuang subsys_initcall(pm8607_regulator_init);
463be0e2d3eSHaojian Zhuang 
464be0e2d3eSHaojian Zhuang static void __exit pm8607_regulator_exit(void)
465be0e2d3eSHaojian Zhuang {
466192bbb95SHaojian Zhuang 	platform_driver_unregister(&pm8607_regulator_driver);
467be0e2d3eSHaojian Zhuang }
468be0e2d3eSHaojian Zhuang module_exit(pm8607_regulator_exit);
469be0e2d3eSHaojian Zhuang 
470be0e2d3eSHaojian Zhuang MODULE_LICENSE("GPL");
471be0e2d3eSHaojian Zhuang MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
472be0e2d3eSHaojian Zhuang MODULE_DESCRIPTION("Regulator Driver for Marvell 88PM8607 PMIC");
473be0e2d3eSHaojian Zhuang MODULE_ALIAS("platform:88pm8607-regulator");
474