xref: /linux/drivers/regulator/88pm8607.c (revision 65602c32ee9b5500e3cb617ccec2154ee2191898)
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>
19*65602c32SPaul Gortmaker #include <linux/module.h>
20be0e2d3eSHaojian Zhuang 
21be0e2d3eSHaojian Zhuang struct pm8607_regulator_info {
22be0e2d3eSHaojian Zhuang 	struct regulator_desc	desc;
2353dbab7aSHaojian Zhuang 	struct pm860x_chip	*chip;
24be0e2d3eSHaojian Zhuang 	struct regulator_dev	*regulator;
2553dbab7aSHaojian Zhuang 	struct i2c_client	*i2c;
26be0e2d3eSHaojian Zhuang 
279f79e9dbSHaojian Zhuang 	unsigned int	*vol_table;
289f79e9dbSHaojian Zhuang 	unsigned int	*vol_suspend;
299f79e9dbSHaojian Zhuang 
30be0e2d3eSHaojian Zhuang 	int	vol_reg;
31be0e2d3eSHaojian Zhuang 	int	vol_shift;
32be0e2d3eSHaojian Zhuang 	int	vol_nbits;
33be0e2d3eSHaojian Zhuang 	int	update_reg;
34be0e2d3eSHaojian Zhuang 	int	update_bit;
35be0e2d3eSHaojian Zhuang 	int	enable_reg;
36be0e2d3eSHaojian Zhuang 	int	enable_bit;
37be0e2d3eSHaojian Zhuang 	int	slope_double;
38be0e2d3eSHaojian Zhuang };
39be0e2d3eSHaojian Zhuang 
409f79e9dbSHaojian Zhuang static const unsigned int BUCK1_table[] = {
419f79e9dbSHaojian Zhuang 	 725000,  750000,  775000,  800000,  825000,  850000,  875000,  900000,
429f79e9dbSHaojian Zhuang 	 925000,  950000,  975000, 1000000, 1025000, 1050000, 1075000, 1100000,
439f79e9dbSHaojian Zhuang 	1125000, 1150000, 1175000, 1200000, 1225000, 1250000, 1275000, 1300000,
449f79e9dbSHaojian Zhuang 	1325000, 1350000, 1375000, 1400000, 1425000, 1450000, 1475000, 1500000,
459f79e9dbSHaojian Zhuang 	      0,   25000,   50000,   75000,  100000,  125000,  150000,  175000,
469f79e9dbSHaojian Zhuang 	 200000,  225000,  250000,  275000,  300000,  325000,  350000,  375000,
479f79e9dbSHaojian Zhuang 	 400000,  425000,  450000,  475000,  500000,  525000,  550000,  575000,
489f79e9dbSHaojian Zhuang 	 600000,  625000,  650000,  675000,  700000,  725000,  750000,  775000,
499f79e9dbSHaojian Zhuang };
50be0e2d3eSHaojian Zhuang 
519f79e9dbSHaojian Zhuang static const unsigned int BUCK1_suspend_table[] = {
529f79e9dbSHaojian Zhuang 	      0,   25000,   50000,   75000,  100000,  125000,  150000,  175000,
539f79e9dbSHaojian Zhuang 	 200000,  225000,  250000,  275000,  300000,  325000,  350000,  375000,
549f79e9dbSHaojian Zhuang 	 400000,  425000,  450000,  475000,  500000,  525000,  550000,  575000,
559f79e9dbSHaojian Zhuang 	 600000,  625000,  650000,  675000,  700000,  725000,  750000,  775000,
569f79e9dbSHaojian Zhuang 	 800000,  825000,  850000,  875000,  900000,  925000,  950000,  975000,
579f79e9dbSHaojian Zhuang 	1000000, 1025000, 1050000, 1075000, 1100000, 1125000, 1150000, 1175000,
589f79e9dbSHaojian Zhuang 	1200000, 1225000, 1250000, 1275000, 1300000, 1325000, 1350000, 1375000,
599f79e9dbSHaojian Zhuang 	1400000, 1425000, 1450000, 1475000, 1500000, 1500000, 1500000, 1500000,
609f79e9dbSHaojian Zhuang };
619f79e9dbSHaojian Zhuang 
629f79e9dbSHaojian Zhuang static const unsigned int BUCK2_table[] = {
639f79e9dbSHaojian Zhuang 	      0,   50000,  100000,  150000,  200000,  250000,  300000,  350000,
649f79e9dbSHaojian Zhuang 	 400000,  450000,  500000,  550000,  600000,  650000,  700000,  750000,
659f79e9dbSHaojian Zhuang 	 800000,  850000,  900000,  950000, 1000000, 1050000, 1100000, 1150000,
669f79e9dbSHaojian Zhuang 	1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, 1550000,
679f79e9dbSHaojian Zhuang 	1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, 1950000,
689f79e9dbSHaojian Zhuang 	2000000, 2050000, 2100000, 2150000, 2200000, 2250000, 2300000, 2350000,
699f79e9dbSHaojian Zhuang 	2400000, 2450000, 2500000, 2550000, 2600000, 2650000, 2700000, 2750000,
709f79e9dbSHaojian Zhuang 	2800000, 2850000, 2900000, 2950000, 3000000, 3000000, 3000000, 3000000,
719f79e9dbSHaojian Zhuang };
729f79e9dbSHaojian Zhuang 
739f79e9dbSHaojian Zhuang static const unsigned int BUCK2_suspend_table[] = {
749f79e9dbSHaojian Zhuang 	      0,   50000,  100000,  150000,  200000,  250000,  300000,  350000,
759f79e9dbSHaojian Zhuang 	 400000,  450000,  500000,  550000,  600000,  650000,  700000,  750000,
769f79e9dbSHaojian Zhuang 	 800000,  850000,  900000,  950000, 1000000, 1050000, 1100000, 1150000,
779f79e9dbSHaojian Zhuang 	1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, 1550000,
789f79e9dbSHaojian Zhuang 	1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, 1950000,
799f79e9dbSHaojian Zhuang 	2000000, 2050000, 2100000, 2150000, 2200000, 2250000, 2300000, 2350000,
809f79e9dbSHaojian Zhuang 	2400000, 2450000, 2500000, 2550000, 2600000, 2650000, 2700000, 2750000,
819f79e9dbSHaojian Zhuang 	2800000, 2850000, 2900000, 2950000, 3000000, 3000000, 3000000, 3000000,
829f79e9dbSHaojian Zhuang };
839f79e9dbSHaojian Zhuang 
849f79e9dbSHaojian Zhuang static const unsigned int BUCK3_table[] = {
859f79e9dbSHaojian Zhuang               0,   25000,   50000,   75000,  100000,  125000,  150000,  175000,
869f79e9dbSHaojian Zhuang 	 200000,  225000,  250000,  275000,  300000,  325000,  350000,  375000,
879f79e9dbSHaojian Zhuang 	 400000,  425000,  450000,  475000,  500000,  525000,  550000,  575000,
889f79e9dbSHaojian Zhuang 	 600000,  625000,  650000,  675000,  700000,  725000,  750000,  775000,
899f79e9dbSHaojian Zhuang 	 800000,  825000,  850000,  875000,  900000,  925000,  950000,  975000,
909f79e9dbSHaojian Zhuang 	1000000, 1025000, 1050000, 1075000, 1100000, 1125000, 1150000, 1175000,
919f79e9dbSHaojian Zhuang 	1200000, 1225000, 1250000, 1275000, 1300000, 1325000, 1350000, 1375000,
929f79e9dbSHaojian Zhuang 	1400000, 1425000, 1450000, 1475000, 1500000, 1500000, 1500000, 1500000,
939f79e9dbSHaojian Zhuang };
949f79e9dbSHaojian Zhuang 
959f79e9dbSHaojian Zhuang static const unsigned int BUCK3_suspend_table[] = {
969f79e9dbSHaojian Zhuang               0,   25000,   50000,   75000,  100000,  125000,  150000,  175000,
979f79e9dbSHaojian Zhuang 	 200000,  225000,  250000,  275000,  300000,  325000,  350000,  375000,
989f79e9dbSHaojian Zhuang 	 400000,  425000,  450000,  475000,  500000,  525000,  550000,  575000,
999f79e9dbSHaojian Zhuang 	 600000,  625000,  650000,  675000,  700000,  725000,  750000,  775000,
1009f79e9dbSHaojian Zhuang 	 800000,  825000,  850000,  875000,  900000,  925000,  950000,  975000,
1019f79e9dbSHaojian Zhuang 	1000000, 1025000, 1050000, 1075000, 1100000, 1125000, 1150000, 1175000,
1029f79e9dbSHaojian Zhuang 	1200000, 1225000, 1250000, 1275000, 1300000, 1325000, 1350000, 1375000,
1039f79e9dbSHaojian Zhuang 	1400000, 1425000, 1450000, 1475000, 1500000, 1500000, 1500000, 1500000,
1049f79e9dbSHaojian Zhuang };
1059f79e9dbSHaojian Zhuang 
1069f79e9dbSHaojian Zhuang static const unsigned int LDO1_table[] = {
1079f79e9dbSHaojian Zhuang 	1800000, 1200000, 2800000, 0,
1089f79e9dbSHaojian Zhuang };
1099f79e9dbSHaojian Zhuang 
1109f79e9dbSHaojian Zhuang static const unsigned int LDO1_suspend_table[] = {
1119f79e9dbSHaojian Zhuang 	1800000, 1200000, 0, 0,
1129f79e9dbSHaojian Zhuang };
1139f79e9dbSHaojian Zhuang 
1149f79e9dbSHaojian Zhuang static const unsigned int LDO2_table[] = {
1159f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000,
1169f79e9dbSHaojian Zhuang };
1179f79e9dbSHaojian Zhuang 
1189f79e9dbSHaojian Zhuang static const unsigned int LDO2_suspend_table[] = {
1199f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1209f79e9dbSHaojian Zhuang };
1219f79e9dbSHaojian Zhuang 
1229f79e9dbSHaojian Zhuang static const unsigned int LDO3_table[] = {
1239f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000,
1249f79e9dbSHaojian Zhuang };
1259f79e9dbSHaojian Zhuang 
1269f79e9dbSHaojian Zhuang static const unsigned int LDO3_suspend_table[] = {
1279f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1289f79e9dbSHaojian Zhuang };
1299f79e9dbSHaojian Zhuang 
1309f79e9dbSHaojian Zhuang static const unsigned int LDO4_table[] = {
1319f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2900000, 3300000,
1329f79e9dbSHaojian Zhuang };
1339f79e9dbSHaojian Zhuang 
1349f79e9dbSHaojian Zhuang static const unsigned int LDO4_suspend_table[] = {
1359f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2900000, 2900000,
1369f79e9dbSHaojian Zhuang };
1379f79e9dbSHaojian Zhuang 
1389f79e9dbSHaojian Zhuang static const unsigned int LDO5_table[] = {
1399f79e9dbSHaojian Zhuang 	2900000, 3000000, 3100000, 3300000,
1409f79e9dbSHaojian Zhuang };
1419f79e9dbSHaojian Zhuang 
1429f79e9dbSHaojian Zhuang static const unsigned int LDO5_suspend_table[] = {
1439f79e9dbSHaojian Zhuang 	2900000, 0, 0, 0,
1449f79e9dbSHaojian Zhuang };
1459f79e9dbSHaojian Zhuang 
1469f79e9dbSHaojian Zhuang static const unsigned int LDO6_table[] = {
1479f79e9dbSHaojian Zhuang 	1800000, 1850000, 2600000, 2650000, 2700000, 2750000, 2800000, 3300000,
1489f79e9dbSHaojian Zhuang };
1499f79e9dbSHaojian Zhuang 
1509f79e9dbSHaojian Zhuang static const unsigned int LDO6_suspend_table[] = {
1519f79e9dbSHaojian Zhuang 	1800000, 1850000, 2600000, 2650000, 2700000, 2750000, 2800000, 2900000,
1529f79e9dbSHaojian Zhuang };
1539f79e9dbSHaojian Zhuang 
1549f79e9dbSHaojian Zhuang static const unsigned int LDO7_table[] = {
1559f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1569f79e9dbSHaojian Zhuang };
1579f79e9dbSHaojian Zhuang 
1589f79e9dbSHaojian Zhuang static const unsigned int LDO7_suspend_table[] = {
1599f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1609f79e9dbSHaojian Zhuang };
1619f79e9dbSHaojian Zhuang 
1629f79e9dbSHaojian Zhuang static const unsigned int LDO8_table[] = {
1639f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1649f79e9dbSHaojian Zhuang };
1659f79e9dbSHaojian Zhuang 
1669f79e9dbSHaojian Zhuang static const unsigned int LDO8_suspend_table[] = {
1679f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1689f79e9dbSHaojian Zhuang };
1699f79e9dbSHaojian Zhuang 
1709f79e9dbSHaojian Zhuang static const unsigned int LDO9_table[] = {
1719f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000,
1729f79e9dbSHaojian Zhuang };
1739f79e9dbSHaojian Zhuang 
1749f79e9dbSHaojian Zhuang static const unsigned int LDO9_suspend_table[] = {
1759f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1769f79e9dbSHaojian Zhuang };
1779f79e9dbSHaojian Zhuang 
1789f79e9dbSHaojian Zhuang static const unsigned int LDO10_table[] = {
1799f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000,
1809f79e9dbSHaojian Zhuang 	1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000,
1819f79e9dbSHaojian Zhuang };
1829f79e9dbSHaojian Zhuang 
1839f79e9dbSHaojian Zhuang static const unsigned int LDO10_suspend_table[] = {
1849f79e9dbSHaojian Zhuang 	1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000,
1859f79e9dbSHaojian Zhuang 	1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000,
1869f79e9dbSHaojian Zhuang };
1879f79e9dbSHaojian Zhuang 
1889f79e9dbSHaojian Zhuang static const unsigned int LDO12_table[] = {
1899f79e9dbSHaojian Zhuang 	1800000, 1900000, 2700000, 2800000, 2900000, 3000000, 3100000, 3300000,
1909f79e9dbSHaojian Zhuang 	1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000,
1919f79e9dbSHaojian Zhuang };
1929f79e9dbSHaojian Zhuang 
1939f79e9dbSHaojian Zhuang static const unsigned int LDO12_suspend_table[] = {
1949f79e9dbSHaojian Zhuang 	1800000, 1900000, 2700000, 2800000, 2900000, 2900000, 2900000, 2900000,
1959f79e9dbSHaojian Zhuang 	1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000,
1969f79e9dbSHaojian Zhuang };
1979f79e9dbSHaojian Zhuang 
1989f79e9dbSHaojian Zhuang static const unsigned int LDO13_table[] = {
1999f79e9dbSHaojian Zhuang 	1300000, 1800000, 2000000, 2500000, 2800000, 3000000, 0, 0,
2009f79e9dbSHaojian Zhuang };
2019f79e9dbSHaojian Zhuang 
2029f79e9dbSHaojian Zhuang static const unsigned int LDO13_suspend_table[] = {
2039f79e9dbSHaojian Zhuang 	0,
2049f79e9dbSHaojian Zhuang };
2059f79e9dbSHaojian Zhuang 
2069f79e9dbSHaojian Zhuang static const unsigned int LDO14_table[] = {
2079f79e9dbSHaojian Zhuang 	1800000, 1850000, 2700000, 2750000, 2800000, 2850000, 2900000, 3300000,
2089f79e9dbSHaojian Zhuang };
2099f79e9dbSHaojian Zhuang 
2109f79e9dbSHaojian Zhuang static const unsigned int LDO14_suspend_table[] = {
2119f79e9dbSHaojian Zhuang 	1800000, 1850000, 2700000, 2750000, 2800000, 2850000, 2900000, 2900000,
2129f79e9dbSHaojian Zhuang };
213be0e2d3eSHaojian Zhuang 
214be0e2d3eSHaojian Zhuang static int pm8607_list_voltage(struct regulator_dev *rdev, unsigned index)
215be0e2d3eSHaojian Zhuang {
216be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
217be0e2d3eSHaojian Zhuang 	int ret = -EINVAL;
218be0e2d3eSHaojian Zhuang 
219d06563cbSAxel Lin 	if (info->vol_table && (index < (1 << info->vol_nbits))) {
2209f79e9dbSHaojian Zhuang 		ret = info->vol_table[index];
221be0e2d3eSHaojian Zhuang 		if (info->slope_double)
222be0e2d3eSHaojian Zhuang 			ret <<= 1;
223be0e2d3eSHaojian Zhuang 	}
224be0e2d3eSHaojian Zhuang 	return ret;
225be0e2d3eSHaojian Zhuang }
226be0e2d3eSHaojian Zhuang 
227be0e2d3eSHaojian Zhuang static int choose_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
228be0e2d3eSHaojian Zhuang {
229be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
2309f79e9dbSHaojian Zhuang 	int i, ret = -ENOENT;
231be0e2d3eSHaojian Zhuang 
2329f79e9dbSHaojian Zhuang 	if (info->slope_double) {
233be0e2d3eSHaojian Zhuang 		min_uV = min_uV >> 1;
2349f79e9dbSHaojian Zhuang 		max_uV = max_uV >> 1;
235be0e2d3eSHaojian Zhuang 	}
2369f79e9dbSHaojian Zhuang 	if (info->vol_table) {
237d06563cbSAxel Lin 		for (i = 0; i < (1 << info->vol_nbits); i++) {
2389f79e9dbSHaojian Zhuang 			if (!info->vol_table[i])
239be0e2d3eSHaojian Zhuang 				break;
2409f79e9dbSHaojian Zhuang 			if ((min_uV <= info->vol_table[i])
2419f79e9dbSHaojian Zhuang 				&& (max_uV >= info->vol_table[i])) {
2429f79e9dbSHaojian Zhuang 				ret = i;
243be0e2d3eSHaojian Zhuang 				break;
244be0e2d3eSHaojian Zhuang 			}
245be0e2d3eSHaojian Zhuang 		}
2469f79e9dbSHaojian Zhuang 	}
2479f79e9dbSHaojian Zhuang 	if (ret < 0)
2489f79e9dbSHaojian Zhuang 		pr_err("invalid voltage range (%d %d) uV\n", min_uV, max_uV);
2499f79e9dbSHaojian Zhuang 	return ret;
250be0e2d3eSHaojian Zhuang }
251be0e2d3eSHaojian Zhuang 
252be0e2d3eSHaojian Zhuang static int pm8607_set_voltage(struct regulator_dev *rdev,
2533a93f2a9SMark Brown 			      int min_uV, int max_uV, unsigned *selector)
254be0e2d3eSHaojian Zhuang {
255be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
256be0e2d3eSHaojian Zhuang 	uint8_t val, mask;
257be0e2d3eSHaojian Zhuang 	int ret;
258be0e2d3eSHaojian Zhuang 
2599f79e9dbSHaojian Zhuang 	if (min_uV > max_uV) {
260be0e2d3eSHaojian Zhuang 		pr_err("invalid voltage range (%d, %d) uV\n", min_uV, max_uV);
261be0e2d3eSHaojian Zhuang 		return -EINVAL;
262be0e2d3eSHaojian Zhuang 	}
263be0e2d3eSHaojian Zhuang 
264be0e2d3eSHaojian Zhuang 	ret = choose_voltage(rdev, min_uV, max_uV);
265be0e2d3eSHaojian Zhuang 	if (ret < 0)
266be0e2d3eSHaojian Zhuang 		return -EINVAL;
2673a93f2a9SMark Brown 	*selector = ret;
268be0e2d3eSHaojian Zhuang 	val = (uint8_t)(ret << info->vol_shift);
269be0e2d3eSHaojian Zhuang 	mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
270be0e2d3eSHaojian Zhuang 
27153dbab7aSHaojian Zhuang 	ret = pm860x_set_bits(info->i2c, info->vol_reg, mask, val);
272be0e2d3eSHaojian Zhuang 	if (ret)
273be0e2d3eSHaojian Zhuang 		return ret;
274be0e2d3eSHaojian Zhuang 	switch (info->desc.id) {
275be0e2d3eSHaojian Zhuang 	case PM8607_ID_BUCK1:
276be0e2d3eSHaojian Zhuang 	case PM8607_ID_BUCK3:
27753dbab7aSHaojian Zhuang 		ret = pm860x_set_bits(info->i2c, info->update_reg,
278be0e2d3eSHaojian Zhuang 				      1 << info->update_bit,
279be0e2d3eSHaojian Zhuang 				      1 << info->update_bit);
280be0e2d3eSHaojian Zhuang 		break;
281be0e2d3eSHaojian Zhuang 	}
282be0e2d3eSHaojian Zhuang 	return ret;
283be0e2d3eSHaojian Zhuang }
284be0e2d3eSHaojian Zhuang 
285be0e2d3eSHaojian Zhuang static int pm8607_get_voltage(struct regulator_dev *rdev)
286be0e2d3eSHaojian Zhuang {
287be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
288be0e2d3eSHaojian Zhuang 	uint8_t val, mask;
289be0e2d3eSHaojian Zhuang 	int ret;
290be0e2d3eSHaojian Zhuang 
29153dbab7aSHaojian Zhuang 	ret = pm860x_reg_read(info->i2c, info->vol_reg);
292be0e2d3eSHaojian Zhuang 	if (ret < 0)
293be0e2d3eSHaojian Zhuang 		return ret;
294be0e2d3eSHaojian Zhuang 
295be0e2d3eSHaojian Zhuang 	mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
296be0e2d3eSHaojian Zhuang 	val = ((unsigned char)ret & mask) >> info->vol_shift;
297be0e2d3eSHaojian Zhuang 
298be0e2d3eSHaojian Zhuang 	return pm8607_list_voltage(rdev, val);
299be0e2d3eSHaojian Zhuang }
300be0e2d3eSHaojian Zhuang 
301be0e2d3eSHaojian Zhuang static int pm8607_enable(struct regulator_dev *rdev)
302be0e2d3eSHaojian Zhuang {
303be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
304be0e2d3eSHaojian Zhuang 
30553dbab7aSHaojian Zhuang 	return pm860x_set_bits(info->i2c, info->enable_reg,
306be0e2d3eSHaojian Zhuang 			       1 << info->enable_bit,
307be0e2d3eSHaojian Zhuang 			       1 << info->enable_bit);
308be0e2d3eSHaojian Zhuang }
309be0e2d3eSHaojian Zhuang 
310be0e2d3eSHaojian Zhuang static int pm8607_disable(struct regulator_dev *rdev)
311be0e2d3eSHaojian Zhuang {
312be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
313be0e2d3eSHaojian Zhuang 
31453dbab7aSHaojian Zhuang 	return pm860x_set_bits(info->i2c, info->enable_reg,
315be0e2d3eSHaojian Zhuang 			       1 << info->enable_bit, 0);
316be0e2d3eSHaojian Zhuang }
317be0e2d3eSHaojian Zhuang 
318be0e2d3eSHaojian Zhuang static int pm8607_is_enabled(struct regulator_dev *rdev)
319be0e2d3eSHaojian Zhuang {
320be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
321be0e2d3eSHaojian Zhuang 	int ret;
322be0e2d3eSHaojian Zhuang 
32353dbab7aSHaojian Zhuang 	ret = pm860x_reg_read(info->i2c, info->enable_reg);
324be0e2d3eSHaojian Zhuang 	if (ret < 0)
325be0e2d3eSHaojian Zhuang 		return ret;
326be0e2d3eSHaojian Zhuang 
327be0e2d3eSHaojian Zhuang 	return !!((unsigned char)ret & (1 << info->enable_bit));
328be0e2d3eSHaojian Zhuang }
329be0e2d3eSHaojian Zhuang 
330be0e2d3eSHaojian Zhuang static struct regulator_ops pm8607_regulator_ops = {
331be0e2d3eSHaojian Zhuang 	.set_voltage	= pm8607_set_voltage,
332be0e2d3eSHaojian Zhuang 	.get_voltage	= pm8607_get_voltage,
333be0e2d3eSHaojian Zhuang 	.enable		= pm8607_enable,
334be0e2d3eSHaojian Zhuang 	.disable	= pm8607_disable,
335be0e2d3eSHaojian Zhuang 	.is_enabled	= pm8607_is_enabled,
336be0e2d3eSHaojian Zhuang };
337be0e2d3eSHaojian Zhuang 
3389f79e9dbSHaojian Zhuang #define PM8607_DVC(vreg, nbits, ureg, ubit, ereg, ebit)			\
339be0e2d3eSHaojian Zhuang {									\
340be0e2d3eSHaojian Zhuang 	.desc	= {							\
3419f79e9dbSHaojian Zhuang 		.name	= #vreg,					\
342be0e2d3eSHaojian Zhuang 		.ops	= &pm8607_regulator_ops,			\
343be0e2d3eSHaojian Zhuang 		.type	= REGULATOR_VOLTAGE,				\
3449f79e9dbSHaojian Zhuang 		.id	= PM8607_ID_##vreg,				\
345be0e2d3eSHaojian Zhuang 		.owner	= THIS_MODULE,					\
346be0e2d3eSHaojian Zhuang 	},								\
347be0e2d3eSHaojian Zhuang 	.vol_reg	= PM8607_##vreg,				\
348be0e2d3eSHaojian Zhuang 	.vol_shift	= (0),						\
349be0e2d3eSHaojian Zhuang 	.vol_nbits	= (nbits),					\
350be0e2d3eSHaojian Zhuang 	.update_reg	= PM8607_##ureg,				\
351be0e2d3eSHaojian Zhuang 	.update_bit	= (ubit),					\
352be0e2d3eSHaojian Zhuang 	.enable_reg	= PM8607_##ereg,				\
353be0e2d3eSHaojian Zhuang 	.enable_bit	= (ebit),					\
354be0e2d3eSHaojian Zhuang 	.slope_double	= (0),						\
3559f79e9dbSHaojian Zhuang 	.vol_table	= (unsigned int *)&vreg##_table,		\
3569f79e9dbSHaojian Zhuang 	.vol_suspend	= (unsigned int *)&vreg##_suspend_table,	\
357be0e2d3eSHaojian Zhuang }
358be0e2d3eSHaojian Zhuang 
3599f79e9dbSHaojian Zhuang #define PM8607_LDO(_id, vreg, shift, nbits, ereg, ebit)			\
360be0e2d3eSHaojian Zhuang {									\
361be0e2d3eSHaojian Zhuang 	.desc	= {							\
362be0e2d3eSHaojian Zhuang 		.name	= "LDO" #_id,					\
363be0e2d3eSHaojian Zhuang 		.ops	= &pm8607_regulator_ops,			\
364be0e2d3eSHaojian Zhuang 		.type	= REGULATOR_VOLTAGE,				\
365be0e2d3eSHaojian Zhuang 		.id	= PM8607_ID_LDO##_id,				\
366be0e2d3eSHaojian Zhuang 		.owner	= THIS_MODULE,					\
367be0e2d3eSHaojian Zhuang 	},								\
368be0e2d3eSHaojian Zhuang 	.vol_reg	= PM8607_##vreg,				\
369be0e2d3eSHaojian Zhuang 	.vol_shift	= (shift),					\
370be0e2d3eSHaojian Zhuang 	.vol_nbits	= (nbits),					\
371be0e2d3eSHaojian Zhuang 	.enable_reg	= PM8607_##ereg,				\
372be0e2d3eSHaojian Zhuang 	.enable_bit	= (ebit),					\
373be0e2d3eSHaojian Zhuang 	.slope_double	= (0),						\
3749f79e9dbSHaojian Zhuang 	.vol_table	= (unsigned int *)&LDO##_id##_table,		\
3759f79e9dbSHaojian Zhuang 	.vol_suspend	= (unsigned int *)&LDO##_id##_suspend_table,	\
376be0e2d3eSHaojian Zhuang }
377be0e2d3eSHaojian Zhuang 
378be0e2d3eSHaojian Zhuang static struct pm8607_regulator_info pm8607_regulator_info[] = {
3799f79e9dbSHaojian Zhuang 	PM8607_DVC(BUCK1, 6, GO, 0, SUPPLIES_EN11, 0),
3809f79e9dbSHaojian Zhuang 	PM8607_DVC(BUCK2, 6, GO, 1, SUPPLIES_EN11, 1),
3819f79e9dbSHaojian Zhuang 	PM8607_DVC(BUCK3, 6, GO, 2, SUPPLIES_EN11, 2),
382be0e2d3eSHaojian Zhuang 
3839f79e9dbSHaojian Zhuang 	PM8607_LDO( 1,         LDO1, 0, 2, SUPPLIES_EN11, 3),
3849f79e9dbSHaojian Zhuang 	PM8607_LDO( 2,         LDO2, 0, 3, SUPPLIES_EN11, 4),
3859f79e9dbSHaojian Zhuang 	PM8607_LDO( 3,         LDO3, 0, 3, SUPPLIES_EN11, 5),
3869f79e9dbSHaojian Zhuang 	PM8607_LDO( 4,         LDO4, 0, 3, SUPPLIES_EN11, 6),
3879f79e9dbSHaojian Zhuang 	PM8607_LDO( 5,         LDO5, 0, 2, SUPPLIES_EN11, 7),
3889f79e9dbSHaojian Zhuang 	PM8607_LDO( 6,         LDO6, 0, 3, SUPPLIES_EN12, 0),
3899f79e9dbSHaojian Zhuang 	PM8607_LDO( 7,         LDO7, 0, 3, SUPPLIES_EN12, 1),
3909f79e9dbSHaojian Zhuang 	PM8607_LDO( 8,         LDO8, 0, 3, SUPPLIES_EN12, 2),
3919f79e9dbSHaojian Zhuang 	PM8607_LDO( 9,         LDO9, 0, 3, SUPPLIES_EN12, 3),
3929f79e9dbSHaojian Zhuang 	PM8607_LDO(10,        LDO10, 0, 3, SUPPLIES_EN12, 4),
3939f79e9dbSHaojian Zhuang 	PM8607_LDO(12,        LDO12, 0, 4, SUPPLIES_EN12, 5),
3949f79e9dbSHaojian Zhuang 	PM8607_LDO(13, VIBRATOR_SET, 1, 3,  VIBRATOR_SET, 0),
3959f79e9dbSHaojian Zhuang 	PM8607_LDO(14,        LDO14, 0, 4, SUPPLIES_EN12, 6),
396be0e2d3eSHaojian Zhuang };
397be0e2d3eSHaojian Zhuang 
398be0e2d3eSHaojian Zhuang static int __devinit pm8607_regulator_probe(struct platform_device *pdev)
399be0e2d3eSHaojian Zhuang {
40053dbab7aSHaojian Zhuang 	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
401be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = NULL;
402586e1a17SHaojian Zhuang 	struct regulator_init_data *pdata = pdev->dev.platform_data;
403586e1a17SHaojian Zhuang 	struct resource *res;
40422aad001SHaojian Zhuang 	int i;
405be0e2d3eSHaojian Zhuang 
406586e1a17SHaojian Zhuang 	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
407586e1a17SHaojian Zhuang 	if (res == NULL) {
408586e1a17SHaojian Zhuang 		dev_err(&pdev->dev, "No I/O resource!\n");
40922aad001SHaojian Zhuang 		return -EINVAL;
410586e1a17SHaojian Zhuang 	}
41122aad001SHaojian Zhuang 	for (i = 0; i < ARRAY_SIZE(pm8607_regulator_info); i++) {
41222aad001SHaojian Zhuang 		info = &pm8607_regulator_info[i];
413586e1a17SHaojian Zhuang 		if (info->desc.id == res->start)
41422aad001SHaojian Zhuang 			break;
41522aad001SHaojian Zhuang 	}
416586e1a17SHaojian Zhuang 	if ((i < 0) || (i > PM8607_ID_RG_MAX)) {
41709969108SRandy Dunlap 		dev_err(&pdev->dev, "Failed to find regulator %llu\n",
41809969108SRandy Dunlap 			(unsigned long long)res->start);
419be0e2d3eSHaojian Zhuang 		return -EINVAL;
420be0e2d3eSHaojian Zhuang 	}
42153dbab7aSHaojian Zhuang 	info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
422be0e2d3eSHaojian Zhuang 	info->chip = chip;
423be0e2d3eSHaojian Zhuang 
42422aad001SHaojian Zhuang 	/* check DVC ramp slope double */
425586e1a17SHaojian Zhuang 	if ((i == PM8607_ID_BUCK3) && info->chip->buck3_double)
42622aad001SHaojian Zhuang 		info->slope_double = 1;
42722aad001SHaojian Zhuang 
428586e1a17SHaojian Zhuang 	/* replace driver_data with info */
429be0e2d3eSHaojian Zhuang 	info->regulator = regulator_register(&info->desc, &pdev->dev,
43022aad001SHaojian Zhuang 					     pdata, info);
431be0e2d3eSHaojian Zhuang 	if (IS_ERR(info->regulator)) {
432be0e2d3eSHaojian Zhuang 		dev_err(&pdev->dev, "failed to register regulator %s\n",
433be0e2d3eSHaojian Zhuang 			info->desc.name);
434be0e2d3eSHaojian Zhuang 		return PTR_ERR(info->regulator);
435be0e2d3eSHaojian Zhuang 	}
436be0e2d3eSHaojian Zhuang 
437be0e2d3eSHaojian Zhuang 	platform_set_drvdata(pdev, info);
438be0e2d3eSHaojian Zhuang 	return 0;
439be0e2d3eSHaojian Zhuang }
440be0e2d3eSHaojian Zhuang 
441be0e2d3eSHaojian Zhuang static int __devexit pm8607_regulator_remove(struct platform_device *pdev)
442be0e2d3eSHaojian Zhuang {
443be0e2d3eSHaojian Zhuang 	struct pm8607_regulator_info *info = platform_get_drvdata(pdev);
444be0e2d3eSHaojian Zhuang 
445192bbb95SHaojian Zhuang 	platform_set_drvdata(pdev, NULL);
446be0e2d3eSHaojian Zhuang 	regulator_unregister(info->regulator);
447be0e2d3eSHaojian Zhuang 	return 0;
448be0e2d3eSHaojian Zhuang }
449be0e2d3eSHaojian Zhuang 
450192bbb95SHaojian Zhuang static struct platform_driver pm8607_regulator_driver = {
451192bbb95SHaojian Zhuang 	.driver		= {
452192bbb95SHaojian Zhuang 		.name	= "88pm860x-regulator",
453192bbb95SHaojian Zhuang 		.owner	= THIS_MODULE,
454192bbb95SHaojian Zhuang 	},
455192bbb95SHaojian Zhuang 	.probe		= pm8607_regulator_probe,
456192bbb95SHaojian Zhuang 	.remove		= __devexit_p(pm8607_regulator_remove),
457be0e2d3eSHaojian Zhuang };
458be0e2d3eSHaojian Zhuang 
459be0e2d3eSHaojian Zhuang static int __init pm8607_regulator_init(void)
460be0e2d3eSHaojian Zhuang {
461192bbb95SHaojian Zhuang 	return platform_driver_register(&pm8607_regulator_driver);
462be0e2d3eSHaojian Zhuang }
463be0e2d3eSHaojian Zhuang subsys_initcall(pm8607_regulator_init);
464be0e2d3eSHaojian Zhuang 
465be0e2d3eSHaojian Zhuang static void __exit pm8607_regulator_exit(void)
466be0e2d3eSHaojian Zhuang {
467192bbb95SHaojian Zhuang 	platform_driver_unregister(&pm8607_regulator_driver);
468be0e2d3eSHaojian Zhuang }
469be0e2d3eSHaojian Zhuang module_exit(pm8607_regulator_exit);
470be0e2d3eSHaojian Zhuang 
471be0e2d3eSHaojian Zhuang MODULE_LICENSE("GPL");
472be0e2d3eSHaojian Zhuang MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
473be0e2d3eSHaojian Zhuang MODULE_DESCRIPTION("Regulator Driver for Marvell 88PM8607 PMIC");
474be0e2d3eSHaojian Zhuang MODULE_ALIAS("platform:88pm8607-regulator");
475