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 26*9f79e9dbSHaojian Zhuang unsigned int *vol_table; 27*9f79e9dbSHaojian Zhuang unsigned int *vol_suspend; 28*9f79e9dbSHaojian 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 39*9f79e9dbSHaojian Zhuang static const unsigned int BUCK1_table[] = { 40*9f79e9dbSHaojian Zhuang 725000, 750000, 775000, 800000, 825000, 850000, 875000, 900000, 41*9f79e9dbSHaojian Zhuang 925000, 950000, 975000, 1000000, 1025000, 1050000, 1075000, 1100000, 42*9f79e9dbSHaojian Zhuang 1125000, 1150000, 1175000, 1200000, 1225000, 1250000, 1275000, 1300000, 43*9f79e9dbSHaojian Zhuang 1325000, 1350000, 1375000, 1400000, 1425000, 1450000, 1475000, 1500000, 44*9f79e9dbSHaojian Zhuang 0, 25000, 50000, 75000, 100000, 125000, 150000, 175000, 45*9f79e9dbSHaojian Zhuang 200000, 225000, 250000, 275000, 300000, 325000, 350000, 375000, 46*9f79e9dbSHaojian Zhuang 400000, 425000, 450000, 475000, 500000, 525000, 550000, 575000, 47*9f79e9dbSHaojian Zhuang 600000, 625000, 650000, 675000, 700000, 725000, 750000, 775000, 48*9f79e9dbSHaojian Zhuang }; 49be0e2d3eSHaojian Zhuang 50*9f79e9dbSHaojian Zhuang static const unsigned int BUCK1_suspend_table[] = { 51*9f79e9dbSHaojian Zhuang 0, 25000, 50000, 75000, 100000, 125000, 150000, 175000, 52*9f79e9dbSHaojian Zhuang 200000, 225000, 250000, 275000, 300000, 325000, 350000, 375000, 53*9f79e9dbSHaojian Zhuang 400000, 425000, 450000, 475000, 500000, 525000, 550000, 575000, 54*9f79e9dbSHaojian Zhuang 600000, 625000, 650000, 675000, 700000, 725000, 750000, 775000, 55*9f79e9dbSHaojian Zhuang 800000, 825000, 850000, 875000, 900000, 925000, 950000, 975000, 56*9f79e9dbSHaojian Zhuang 1000000, 1025000, 1050000, 1075000, 1100000, 1125000, 1150000, 1175000, 57*9f79e9dbSHaojian Zhuang 1200000, 1225000, 1250000, 1275000, 1300000, 1325000, 1350000, 1375000, 58*9f79e9dbSHaojian Zhuang 1400000, 1425000, 1450000, 1475000, 1500000, 1500000, 1500000, 1500000, 59*9f79e9dbSHaojian Zhuang }; 60*9f79e9dbSHaojian Zhuang 61*9f79e9dbSHaojian Zhuang static const unsigned int BUCK2_table[] = { 62*9f79e9dbSHaojian Zhuang 0, 50000, 100000, 150000, 200000, 250000, 300000, 350000, 63*9f79e9dbSHaojian Zhuang 400000, 450000, 500000, 550000, 600000, 650000, 700000, 750000, 64*9f79e9dbSHaojian Zhuang 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, 1150000, 65*9f79e9dbSHaojian Zhuang 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, 1550000, 66*9f79e9dbSHaojian Zhuang 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, 1950000, 67*9f79e9dbSHaojian Zhuang 2000000, 2050000, 2100000, 2150000, 2200000, 2250000, 2300000, 2350000, 68*9f79e9dbSHaojian Zhuang 2400000, 2450000, 2500000, 2550000, 2600000, 2650000, 2700000, 2750000, 69*9f79e9dbSHaojian Zhuang 2800000, 2850000, 2900000, 2950000, 3000000, 3000000, 3000000, 3000000, 70*9f79e9dbSHaojian Zhuang }; 71*9f79e9dbSHaojian Zhuang 72*9f79e9dbSHaojian Zhuang static const unsigned int BUCK2_suspend_table[] = { 73*9f79e9dbSHaojian Zhuang 0, 50000, 100000, 150000, 200000, 250000, 300000, 350000, 74*9f79e9dbSHaojian Zhuang 400000, 450000, 500000, 550000, 600000, 650000, 700000, 750000, 75*9f79e9dbSHaojian Zhuang 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, 1150000, 76*9f79e9dbSHaojian Zhuang 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, 1550000, 77*9f79e9dbSHaojian Zhuang 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, 1950000, 78*9f79e9dbSHaojian Zhuang 2000000, 2050000, 2100000, 2150000, 2200000, 2250000, 2300000, 2350000, 79*9f79e9dbSHaojian Zhuang 2400000, 2450000, 2500000, 2550000, 2600000, 2650000, 2700000, 2750000, 80*9f79e9dbSHaojian Zhuang 2800000, 2850000, 2900000, 2950000, 3000000, 3000000, 3000000, 3000000, 81*9f79e9dbSHaojian Zhuang }; 82*9f79e9dbSHaojian Zhuang 83*9f79e9dbSHaojian Zhuang static const unsigned int BUCK3_table[] = { 84*9f79e9dbSHaojian Zhuang 0, 25000, 50000, 75000, 100000, 125000, 150000, 175000, 85*9f79e9dbSHaojian Zhuang 200000, 225000, 250000, 275000, 300000, 325000, 350000, 375000, 86*9f79e9dbSHaojian Zhuang 400000, 425000, 450000, 475000, 500000, 525000, 550000, 575000, 87*9f79e9dbSHaojian Zhuang 600000, 625000, 650000, 675000, 700000, 725000, 750000, 775000, 88*9f79e9dbSHaojian Zhuang 800000, 825000, 850000, 875000, 900000, 925000, 950000, 975000, 89*9f79e9dbSHaojian Zhuang 1000000, 1025000, 1050000, 1075000, 1100000, 1125000, 1150000, 1175000, 90*9f79e9dbSHaojian Zhuang 1200000, 1225000, 1250000, 1275000, 1300000, 1325000, 1350000, 1375000, 91*9f79e9dbSHaojian Zhuang 1400000, 1425000, 1450000, 1475000, 1500000, 1500000, 1500000, 1500000, 92*9f79e9dbSHaojian Zhuang }; 93*9f79e9dbSHaojian Zhuang 94*9f79e9dbSHaojian Zhuang static const unsigned int BUCK3_suspend_table[] = { 95*9f79e9dbSHaojian Zhuang 0, 25000, 50000, 75000, 100000, 125000, 150000, 175000, 96*9f79e9dbSHaojian Zhuang 200000, 225000, 250000, 275000, 300000, 325000, 350000, 375000, 97*9f79e9dbSHaojian Zhuang 400000, 425000, 450000, 475000, 500000, 525000, 550000, 575000, 98*9f79e9dbSHaojian Zhuang 600000, 625000, 650000, 675000, 700000, 725000, 750000, 775000, 99*9f79e9dbSHaojian Zhuang 800000, 825000, 850000, 875000, 900000, 925000, 950000, 975000, 100*9f79e9dbSHaojian Zhuang 1000000, 1025000, 1050000, 1075000, 1100000, 1125000, 1150000, 1175000, 101*9f79e9dbSHaojian Zhuang 1200000, 1225000, 1250000, 1275000, 1300000, 1325000, 1350000, 1375000, 102*9f79e9dbSHaojian Zhuang 1400000, 1425000, 1450000, 1475000, 1500000, 1500000, 1500000, 1500000, 103*9f79e9dbSHaojian Zhuang }; 104*9f79e9dbSHaojian Zhuang 105*9f79e9dbSHaojian Zhuang static const unsigned int LDO1_table[] = { 106*9f79e9dbSHaojian Zhuang 1800000, 1200000, 2800000, 0, 107*9f79e9dbSHaojian Zhuang }; 108*9f79e9dbSHaojian Zhuang 109*9f79e9dbSHaojian Zhuang static const unsigned int LDO1_suspend_table[] = { 110*9f79e9dbSHaojian Zhuang 1800000, 1200000, 0, 0, 111*9f79e9dbSHaojian Zhuang }; 112*9f79e9dbSHaojian Zhuang 113*9f79e9dbSHaojian Zhuang static const unsigned int LDO2_table[] = { 114*9f79e9dbSHaojian Zhuang 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000, 115*9f79e9dbSHaojian Zhuang }; 116*9f79e9dbSHaojian Zhuang 117*9f79e9dbSHaojian Zhuang static const unsigned int LDO2_suspend_table[] = { 118*9f79e9dbSHaojian Zhuang 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, 119*9f79e9dbSHaojian Zhuang }; 120*9f79e9dbSHaojian Zhuang 121*9f79e9dbSHaojian Zhuang static const unsigned int LDO3_table[] = { 122*9f79e9dbSHaojian Zhuang 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000, 123*9f79e9dbSHaojian Zhuang }; 124*9f79e9dbSHaojian Zhuang 125*9f79e9dbSHaojian Zhuang static const unsigned int LDO3_suspend_table[] = { 126*9f79e9dbSHaojian Zhuang 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, 127*9f79e9dbSHaojian Zhuang }; 128*9f79e9dbSHaojian Zhuang 129*9f79e9dbSHaojian Zhuang static const unsigned int LDO4_table[] = { 130*9f79e9dbSHaojian Zhuang 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2900000, 3300000, 131*9f79e9dbSHaojian Zhuang }; 132*9f79e9dbSHaojian Zhuang 133*9f79e9dbSHaojian Zhuang static const unsigned int LDO4_suspend_table[] = { 134*9f79e9dbSHaojian Zhuang 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2900000, 2900000, 135*9f79e9dbSHaojian Zhuang }; 136*9f79e9dbSHaojian Zhuang 137*9f79e9dbSHaojian Zhuang static const unsigned int LDO5_table[] = { 138*9f79e9dbSHaojian Zhuang 2900000, 3000000, 3100000, 3300000, 139*9f79e9dbSHaojian Zhuang }; 140*9f79e9dbSHaojian Zhuang 141*9f79e9dbSHaojian Zhuang static const unsigned int LDO5_suspend_table[] = { 142*9f79e9dbSHaojian Zhuang 2900000, 0, 0, 0, 143*9f79e9dbSHaojian Zhuang }; 144*9f79e9dbSHaojian Zhuang 145*9f79e9dbSHaojian Zhuang static const unsigned int LDO6_table[] = { 146*9f79e9dbSHaojian Zhuang 1800000, 1850000, 2600000, 2650000, 2700000, 2750000, 2800000, 3300000, 147*9f79e9dbSHaojian Zhuang }; 148*9f79e9dbSHaojian Zhuang 149*9f79e9dbSHaojian Zhuang static const unsigned int LDO6_suspend_table[] = { 150*9f79e9dbSHaojian Zhuang 1800000, 1850000, 2600000, 2650000, 2700000, 2750000, 2800000, 2900000, 151*9f79e9dbSHaojian Zhuang }; 152*9f79e9dbSHaojian Zhuang 153*9f79e9dbSHaojian Zhuang static const unsigned int LDO7_table[] = { 154*9f79e9dbSHaojian Zhuang 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, 155*9f79e9dbSHaojian Zhuang }; 156*9f79e9dbSHaojian Zhuang 157*9f79e9dbSHaojian Zhuang static const unsigned int LDO7_suspend_table[] = { 158*9f79e9dbSHaojian Zhuang 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, 159*9f79e9dbSHaojian Zhuang }; 160*9f79e9dbSHaojian Zhuang 161*9f79e9dbSHaojian Zhuang static const unsigned int LDO8_table[] = { 162*9f79e9dbSHaojian Zhuang 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, 163*9f79e9dbSHaojian Zhuang }; 164*9f79e9dbSHaojian Zhuang 165*9f79e9dbSHaojian Zhuang static const unsigned int LDO8_suspend_table[] = { 166*9f79e9dbSHaojian Zhuang 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, 167*9f79e9dbSHaojian Zhuang }; 168*9f79e9dbSHaojian Zhuang 169*9f79e9dbSHaojian Zhuang static const unsigned int LDO9_table[] = { 170*9f79e9dbSHaojian Zhuang 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000, 171*9f79e9dbSHaojian Zhuang }; 172*9f79e9dbSHaojian Zhuang 173*9f79e9dbSHaojian Zhuang static const unsigned int LDO9_suspend_table[] = { 174*9f79e9dbSHaojian Zhuang 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, 175*9f79e9dbSHaojian Zhuang }; 176*9f79e9dbSHaojian Zhuang 177*9f79e9dbSHaojian Zhuang static const unsigned int LDO10_table[] = { 178*9f79e9dbSHaojian Zhuang 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000, 179*9f79e9dbSHaojian Zhuang 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 180*9f79e9dbSHaojian Zhuang }; 181*9f79e9dbSHaojian Zhuang 182*9f79e9dbSHaojian Zhuang static const unsigned int LDO10_suspend_table[] = { 183*9f79e9dbSHaojian Zhuang 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, 184*9f79e9dbSHaojian Zhuang 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 185*9f79e9dbSHaojian Zhuang }; 186*9f79e9dbSHaojian Zhuang 187*9f79e9dbSHaojian Zhuang static const unsigned int LDO12_table[] = { 188*9f79e9dbSHaojian Zhuang 1800000, 1900000, 2700000, 2800000, 2900000, 3000000, 3100000, 3300000, 189*9f79e9dbSHaojian Zhuang 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 190*9f79e9dbSHaojian Zhuang }; 191*9f79e9dbSHaojian Zhuang 192*9f79e9dbSHaojian Zhuang static const unsigned int LDO12_suspend_table[] = { 193*9f79e9dbSHaojian Zhuang 1800000, 1900000, 2700000, 2800000, 2900000, 2900000, 2900000, 2900000, 194*9f79e9dbSHaojian Zhuang 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 195*9f79e9dbSHaojian Zhuang }; 196*9f79e9dbSHaojian Zhuang 197*9f79e9dbSHaojian Zhuang static const unsigned int LDO13_table[] = { 198*9f79e9dbSHaojian Zhuang 1300000, 1800000, 2000000, 2500000, 2800000, 3000000, 0, 0, 199*9f79e9dbSHaojian Zhuang }; 200*9f79e9dbSHaojian Zhuang 201*9f79e9dbSHaojian Zhuang static const unsigned int LDO13_suspend_table[] = { 202*9f79e9dbSHaojian Zhuang 0, 203*9f79e9dbSHaojian Zhuang }; 204*9f79e9dbSHaojian Zhuang 205*9f79e9dbSHaojian Zhuang static const unsigned int LDO14_table[] = { 206*9f79e9dbSHaojian Zhuang 1800000, 1850000, 2700000, 2750000, 2800000, 2850000, 2900000, 3300000, 207*9f79e9dbSHaojian Zhuang }; 208*9f79e9dbSHaojian Zhuang 209*9f79e9dbSHaojian Zhuang static const unsigned int LDO14_suspend_table[] = { 210*9f79e9dbSHaojian Zhuang 1800000, 1850000, 2700000, 2750000, 2800000, 2850000, 2900000, 2900000, 211*9f79e9dbSHaojian 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 218*9f79e9dbSHaojian Zhuang if (info->vol_table && (index < (2 << info->vol_nbits))) { 219*9f79e9dbSHaojian 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); 229*9f79e9dbSHaojian Zhuang int i, ret = -ENOENT; 230be0e2d3eSHaojian Zhuang 231*9f79e9dbSHaojian Zhuang if (info->slope_double) { 232be0e2d3eSHaojian Zhuang min_uV = min_uV >> 1; 233*9f79e9dbSHaojian Zhuang max_uV = max_uV >> 1; 234be0e2d3eSHaojian Zhuang } 235*9f79e9dbSHaojian Zhuang if (info->vol_table) { 236*9f79e9dbSHaojian Zhuang for (i = 0; i < (2 << info->vol_nbits); i++) { 237*9f79e9dbSHaojian Zhuang if (!info->vol_table[i]) 238be0e2d3eSHaojian Zhuang break; 239*9f79e9dbSHaojian Zhuang if ((min_uV <= info->vol_table[i]) 240*9f79e9dbSHaojian Zhuang && (max_uV >= info->vol_table[i])) { 241*9f79e9dbSHaojian Zhuang ret = i; 242be0e2d3eSHaojian Zhuang break; 243be0e2d3eSHaojian Zhuang } 244be0e2d3eSHaojian Zhuang } 245*9f79e9dbSHaojian Zhuang } 246*9f79e9dbSHaojian Zhuang if (ret < 0) 247*9f79e9dbSHaojian Zhuang pr_err("invalid voltage range (%d %d) uV\n", min_uV, max_uV); 248*9f79e9dbSHaojian Zhuang return ret; 249be0e2d3eSHaojian Zhuang } 250be0e2d3eSHaojian Zhuang 251be0e2d3eSHaojian Zhuang static int pm8607_set_voltage(struct regulator_dev *rdev, 252be0e2d3eSHaojian Zhuang int min_uV, int max_uV) 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 258*9f79e9dbSHaojian 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; 266be0e2d3eSHaojian Zhuang val = (uint8_t)(ret << info->vol_shift); 267be0e2d3eSHaojian Zhuang mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; 268be0e2d3eSHaojian Zhuang 26953dbab7aSHaojian Zhuang ret = pm860x_set_bits(info->i2c, info->vol_reg, mask, val); 270be0e2d3eSHaojian Zhuang if (ret) 271be0e2d3eSHaojian Zhuang return ret; 272be0e2d3eSHaojian Zhuang switch (info->desc.id) { 273be0e2d3eSHaojian Zhuang case PM8607_ID_BUCK1: 274be0e2d3eSHaojian Zhuang case PM8607_ID_BUCK3: 27553dbab7aSHaojian Zhuang ret = pm860x_set_bits(info->i2c, info->update_reg, 276be0e2d3eSHaojian Zhuang 1 << info->update_bit, 277be0e2d3eSHaojian Zhuang 1 << info->update_bit); 278be0e2d3eSHaojian Zhuang break; 279be0e2d3eSHaojian Zhuang } 280be0e2d3eSHaojian Zhuang return ret; 281be0e2d3eSHaojian Zhuang } 282be0e2d3eSHaojian Zhuang 283be0e2d3eSHaojian Zhuang static int pm8607_get_voltage(struct regulator_dev *rdev) 284be0e2d3eSHaojian Zhuang { 285be0e2d3eSHaojian Zhuang struct pm8607_regulator_info *info = rdev_get_drvdata(rdev); 286be0e2d3eSHaojian Zhuang uint8_t val, mask; 287be0e2d3eSHaojian Zhuang int ret; 288be0e2d3eSHaojian Zhuang 28953dbab7aSHaojian Zhuang ret = pm860x_reg_read(info->i2c, info->vol_reg); 290be0e2d3eSHaojian Zhuang if (ret < 0) 291be0e2d3eSHaojian Zhuang return ret; 292be0e2d3eSHaojian Zhuang 293be0e2d3eSHaojian Zhuang mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; 294be0e2d3eSHaojian Zhuang val = ((unsigned char)ret & mask) >> info->vol_shift; 295be0e2d3eSHaojian Zhuang 296be0e2d3eSHaojian Zhuang return pm8607_list_voltage(rdev, val); 297be0e2d3eSHaojian Zhuang } 298be0e2d3eSHaojian Zhuang 299be0e2d3eSHaojian Zhuang static int pm8607_enable(struct regulator_dev *rdev) 300be0e2d3eSHaojian Zhuang { 301be0e2d3eSHaojian Zhuang struct pm8607_regulator_info *info = rdev_get_drvdata(rdev); 302be0e2d3eSHaojian Zhuang 30353dbab7aSHaojian Zhuang return pm860x_set_bits(info->i2c, info->enable_reg, 304be0e2d3eSHaojian Zhuang 1 << info->enable_bit, 305be0e2d3eSHaojian Zhuang 1 << info->enable_bit); 306be0e2d3eSHaojian Zhuang } 307be0e2d3eSHaojian Zhuang 308be0e2d3eSHaojian Zhuang static int pm8607_disable(struct regulator_dev *rdev) 309be0e2d3eSHaojian Zhuang { 310be0e2d3eSHaojian Zhuang struct pm8607_regulator_info *info = rdev_get_drvdata(rdev); 311be0e2d3eSHaojian Zhuang 31253dbab7aSHaojian Zhuang return pm860x_set_bits(info->i2c, info->enable_reg, 313be0e2d3eSHaojian Zhuang 1 << info->enable_bit, 0); 314be0e2d3eSHaojian Zhuang } 315be0e2d3eSHaojian Zhuang 316be0e2d3eSHaojian Zhuang static int pm8607_is_enabled(struct regulator_dev *rdev) 317be0e2d3eSHaojian Zhuang { 318be0e2d3eSHaojian Zhuang struct pm8607_regulator_info *info = rdev_get_drvdata(rdev); 319be0e2d3eSHaojian Zhuang int ret; 320be0e2d3eSHaojian Zhuang 32153dbab7aSHaojian Zhuang ret = pm860x_reg_read(info->i2c, info->enable_reg); 322be0e2d3eSHaojian Zhuang if (ret < 0) 323be0e2d3eSHaojian Zhuang return ret; 324be0e2d3eSHaojian Zhuang 325be0e2d3eSHaojian Zhuang return !!((unsigned char)ret & (1 << info->enable_bit)); 326be0e2d3eSHaojian Zhuang } 327be0e2d3eSHaojian Zhuang 328be0e2d3eSHaojian Zhuang static struct regulator_ops pm8607_regulator_ops = { 329be0e2d3eSHaojian Zhuang .set_voltage = pm8607_set_voltage, 330be0e2d3eSHaojian Zhuang .get_voltage = pm8607_get_voltage, 331be0e2d3eSHaojian Zhuang .enable = pm8607_enable, 332be0e2d3eSHaojian Zhuang .disable = pm8607_disable, 333be0e2d3eSHaojian Zhuang .is_enabled = pm8607_is_enabled, 334be0e2d3eSHaojian Zhuang }; 335be0e2d3eSHaojian Zhuang 336*9f79e9dbSHaojian Zhuang #define PM8607_DVC(vreg, nbits, ureg, ubit, ereg, ebit) \ 337be0e2d3eSHaojian Zhuang { \ 338be0e2d3eSHaojian Zhuang .desc = { \ 339*9f79e9dbSHaojian Zhuang .name = #vreg, \ 340be0e2d3eSHaojian Zhuang .ops = &pm8607_regulator_ops, \ 341be0e2d3eSHaojian Zhuang .type = REGULATOR_VOLTAGE, \ 342*9f79e9dbSHaojian Zhuang .id = PM8607_ID_##vreg, \ 343be0e2d3eSHaojian Zhuang .owner = THIS_MODULE, \ 344be0e2d3eSHaojian Zhuang }, \ 345be0e2d3eSHaojian Zhuang .vol_reg = PM8607_##vreg, \ 346be0e2d3eSHaojian Zhuang .vol_shift = (0), \ 347be0e2d3eSHaojian Zhuang .vol_nbits = (nbits), \ 348be0e2d3eSHaojian Zhuang .update_reg = PM8607_##ureg, \ 349be0e2d3eSHaojian Zhuang .update_bit = (ubit), \ 350be0e2d3eSHaojian Zhuang .enable_reg = PM8607_##ereg, \ 351be0e2d3eSHaojian Zhuang .enable_bit = (ebit), \ 352be0e2d3eSHaojian Zhuang .slope_double = (0), \ 353*9f79e9dbSHaojian Zhuang .vol_table = (unsigned int *)&vreg##_table, \ 354*9f79e9dbSHaojian Zhuang .vol_suspend = (unsigned int *)&vreg##_suspend_table, \ 355be0e2d3eSHaojian Zhuang } 356be0e2d3eSHaojian Zhuang 357*9f79e9dbSHaojian Zhuang #define PM8607_LDO(_id, vreg, shift, nbits, ereg, ebit) \ 358be0e2d3eSHaojian Zhuang { \ 359be0e2d3eSHaojian Zhuang .desc = { \ 360be0e2d3eSHaojian Zhuang .name = "LDO" #_id, \ 361be0e2d3eSHaojian Zhuang .ops = &pm8607_regulator_ops, \ 362be0e2d3eSHaojian Zhuang .type = REGULATOR_VOLTAGE, \ 363be0e2d3eSHaojian Zhuang .id = PM8607_ID_LDO##_id, \ 364be0e2d3eSHaojian Zhuang .owner = THIS_MODULE, \ 365be0e2d3eSHaojian Zhuang }, \ 366be0e2d3eSHaojian Zhuang .vol_reg = PM8607_##vreg, \ 367be0e2d3eSHaojian Zhuang .vol_shift = (shift), \ 368be0e2d3eSHaojian Zhuang .vol_nbits = (nbits), \ 369be0e2d3eSHaojian Zhuang .enable_reg = PM8607_##ereg, \ 370be0e2d3eSHaojian Zhuang .enable_bit = (ebit), \ 371be0e2d3eSHaojian Zhuang .slope_double = (0), \ 372*9f79e9dbSHaojian Zhuang .vol_table = (unsigned int *)&LDO##_id##_table, \ 373*9f79e9dbSHaojian Zhuang .vol_suspend = (unsigned int *)&LDO##_id##_suspend_table, \ 374be0e2d3eSHaojian Zhuang } 375be0e2d3eSHaojian Zhuang 376be0e2d3eSHaojian Zhuang static struct pm8607_regulator_info pm8607_regulator_info[] = { 377*9f79e9dbSHaojian Zhuang PM8607_DVC(BUCK1, 6, GO, 0, SUPPLIES_EN11, 0), 378*9f79e9dbSHaojian Zhuang PM8607_DVC(BUCK2, 6, GO, 1, SUPPLIES_EN11, 1), 379*9f79e9dbSHaojian Zhuang PM8607_DVC(BUCK3, 6, GO, 2, SUPPLIES_EN11, 2), 380be0e2d3eSHaojian Zhuang 381*9f79e9dbSHaojian Zhuang PM8607_LDO( 1, LDO1, 0, 2, SUPPLIES_EN11, 3), 382*9f79e9dbSHaojian Zhuang PM8607_LDO( 2, LDO2, 0, 3, SUPPLIES_EN11, 4), 383*9f79e9dbSHaojian Zhuang PM8607_LDO( 3, LDO3, 0, 3, SUPPLIES_EN11, 5), 384*9f79e9dbSHaojian Zhuang PM8607_LDO( 4, LDO4, 0, 3, SUPPLIES_EN11, 6), 385*9f79e9dbSHaojian Zhuang PM8607_LDO( 5, LDO5, 0, 2, SUPPLIES_EN11, 7), 386*9f79e9dbSHaojian Zhuang PM8607_LDO( 6, LDO6, 0, 3, SUPPLIES_EN12, 0), 387*9f79e9dbSHaojian Zhuang PM8607_LDO( 7, LDO7, 0, 3, SUPPLIES_EN12, 1), 388*9f79e9dbSHaojian Zhuang PM8607_LDO( 8, LDO8, 0, 3, SUPPLIES_EN12, 2), 389*9f79e9dbSHaojian Zhuang PM8607_LDO( 9, LDO9, 0, 3, SUPPLIES_EN12, 3), 390*9f79e9dbSHaojian Zhuang PM8607_LDO(10, LDO10, 0, 3, SUPPLIES_EN12, 4), 391*9f79e9dbSHaojian Zhuang PM8607_LDO(12, LDO12, 0, 4, SUPPLIES_EN12, 5), 392*9f79e9dbSHaojian Zhuang PM8607_LDO(13, VIBRATOR_SET, 1, 3, VIBRATOR_SET, 0), 393*9f79e9dbSHaojian Zhuang PM8607_LDO(14, LDO14, 0, 4, SUPPLIES_EN12, 6), 394be0e2d3eSHaojian Zhuang }; 395be0e2d3eSHaojian Zhuang 396be0e2d3eSHaojian Zhuang static inline struct pm8607_regulator_info *find_regulator_info(int id) 397be0e2d3eSHaojian Zhuang { 398be0e2d3eSHaojian Zhuang struct pm8607_regulator_info *info; 399be0e2d3eSHaojian Zhuang int i; 400be0e2d3eSHaojian Zhuang 401be0e2d3eSHaojian Zhuang for (i = 0; i < ARRAY_SIZE(pm8607_regulator_info); i++) { 402be0e2d3eSHaojian Zhuang info = &pm8607_regulator_info[i]; 403be0e2d3eSHaojian Zhuang if (info->desc.id == id) 404be0e2d3eSHaojian Zhuang return info; 405be0e2d3eSHaojian Zhuang } 406be0e2d3eSHaojian Zhuang return NULL; 407be0e2d3eSHaojian Zhuang } 408be0e2d3eSHaojian Zhuang 409be0e2d3eSHaojian Zhuang static int __devinit pm8607_regulator_probe(struct platform_device *pdev) 410be0e2d3eSHaojian Zhuang { 41153dbab7aSHaojian Zhuang struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); 41253dbab7aSHaojian Zhuang struct pm860x_platform_data *pdata = chip->dev->platform_data; 413be0e2d3eSHaojian Zhuang struct pm8607_regulator_info *info = NULL; 414be0e2d3eSHaojian Zhuang 415be0e2d3eSHaojian Zhuang info = find_regulator_info(pdev->id); 416be0e2d3eSHaojian Zhuang if (info == NULL) { 417be0e2d3eSHaojian Zhuang dev_err(&pdev->dev, "invalid regulator ID specified\n"); 418be0e2d3eSHaojian Zhuang return -EINVAL; 419be0e2d3eSHaojian Zhuang } 420be0e2d3eSHaojian Zhuang 42153dbab7aSHaojian Zhuang info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; 422be0e2d3eSHaojian Zhuang info->chip = chip; 423be0e2d3eSHaojian Zhuang 424be0e2d3eSHaojian Zhuang info->regulator = regulator_register(&info->desc, &pdev->dev, 425be0e2d3eSHaojian Zhuang pdata->regulator[pdev->id], info); 426be0e2d3eSHaojian Zhuang if (IS_ERR(info->regulator)) { 427be0e2d3eSHaojian Zhuang dev_err(&pdev->dev, "failed to register regulator %s\n", 428be0e2d3eSHaojian Zhuang info->desc.name); 429be0e2d3eSHaojian Zhuang return PTR_ERR(info->regulator); 430be0e2d3eSHaojian Zhuang } 431be0e2d3eSHaojian Zhuang 432be0e2d3eSHaojian Zhuang /* check DVC ramp slope double */ 433be0e2d3eSHaojian Zhuang if (info->desc.id == PM8607_ID_BUCK3) 434be0e2d3eSHaojian Zhuang if (info->chip->buck3_double) 435be0e2d3eSHaojian Zhuang info->slope_double = 1; 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