13fa5b8e0SAnuj Aggarwal /* 23fa5b8e0SAnuj Aggarwal * tps6507x-regulator.c 33fa5b8e0SAnuj Aggarwal * 43fa5b8e0SAnuj Aggarwal * Regulator driver for TPS65073 PMIC 53fa5b8e0SAnuj Aggarwal * 63fa5b8e0SAnuj Aggarwal * Copyright (C) 2009 Texas Instrument Incorporated - http://www.ti.com/ 73fa5b8e0SAnuj Aggarwal * 83fa5b8e0SAnuj Aggarwal * This program is free software; you can redistribute it and/or 93fa5b8e0SAnuj Aggarwal * modify it under the terms of the GNU General Public License as 103fa5b8e0SAnuj Aggarwal * published by the Free Software Foundation version 2. 113fa5b8e0SAnuj Aggarwal * 123fa5b8e0SAnuj Aggarwal * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, 133fa5b8e0SAnuj Aggarwal * whether express or implied; without even the implied warranty of 143fa5b8e0SAnuj Aggarwal * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 153fa5b8e0SAnuj Aggarwal * General Public License for more details. 163fa5b8e0SAnuj Aggarwal */ 173fa5b8e0SAnuj Aggarwal 183fa5b8e0SAnuj Aggarwal #include <linux/kernel.h> 193fa5b8e0SAnuj Aggarwal #include <linux/module.h> 203fa5b8e0SAnuj Aggarwal #include <linux/init.h> 213fa5b8e0SAnuj Aggarwal #include <linux/err.h> 223fa5b8e0SAnuj Aggarwal #include <linux/platform_device.h> 233fa5b8e0SAnuj Aggarwal #include <linux/regulator/driver.h> 243fa5b8e0SAnuj Aggarwal #include <linux/regulator/machine.h> 257d14831eSAnuj Aggarwal #include <linux/regulator/tps6507x.h> 263fa5b8e0SAnuj Aggarwal #include <linux/delay.h> 275a0e3ad6STejun Heo #include <linux/slab.h> 28d183fcc9STodd Fischer #include <linux/mfd/tps6507x.h> 293fa5b8e0SAnuj Aggarwal 303fa5b8e0SAnuj Aggarwal /* DCDC's */ 313fa5b8e0SAnuj Aggarwal #define TPS6507X_DCDC_1 0 323fa5b8e0SAnuj Aggarwal #define TPS6507X_DCDC_2 1 333fa5b8e0SAnuj Aggarwal #define TPS6507X_DCDC_3 2 343fa5b8e0SAnuj Aggarwal /* LDOs */ 353fa5b8e0SAnuj Aggarwal #define TPS6507X_LDO_1 3 363fa5b8e0SAnuj Aggarwal #define TPS6507X_LDO_2 4 373fa5b8e0SAnuj Aggarwal 383fa5b8e0SAnuj Aggarwal #define TPS6507X_MAX_REG_ID TPS6507X_LDO_2 393fa5b8e0SAnuj Aggarwal 403fa5b8e0SAnuj Aggarwal /* Number of step-down converters available */ 413fa5b8e0SAnuj Aggarwal #define TPS6507X_NUM_DCDC 3 423fa5b8e0SAnuj Aggarwal /* Number of LDO voltage regulators available */ 433fa5b8e0SAnuj Aggarwal #define TPS6507X_NUM_LDO 2 443fa5b8e0SAnuj Aggarwal /* Number of total regulators available */ 453fa5b8e0SAnuj Aggarwal #define TPS6507X_NUM_REGULATOR (TPS6507X_NUM_DCDC + TPS6507X_NUM_LDO) 463fa5b8e0SAnuj Aggarwal 473fa5b8e0SAnuj Aggarwal /* Supported voltage values for regulators (in milliVolts) */ 483fa5b8e0SAnuj Aggarwal static const u16 VDCDCx_VSEL_table[] = { 493fa5b8e0SAnuj Aggarwal 725, 750, 775, 800, 503fa5b8e0SAnuj Aggarwal 825, 850, 875, 900, 513fa5b8e0SAnuj Aggarwal 925, 950, 975, 1000, 523fa5b8e0SAnuj Aggarwal 1025, 1050, 1075, 1100, 533fa5b8e0SAnuj Aggarwal 1125, 1150, 1175, 1200, 543fa5b8e0SAnuj Aggarwal 1225, 1250, 1275, 1300, 553fa5b8e0SAnuj Aggarwal 1325, 1350, 1375, 1400, 563fa5b8e0SAnuj Aggarwal 1425, 1450, 1475, 1500, 573fa5b8e0SAnuj Aggarwal 1550, 1600, 1650, 1700, 583fa5b8e0SAnuj Aggarwal 1750, 1800, 1850, 1900, 593fa5b8e0SAnuj Aggarwal 1950, 2000, 2050, 2100, 603fa5b8e0SAnuj Aggarwal 2150, 2200, 2250, 2300, 613fa5b8e0SAnuj Aggarwal 2350, 2400, 2450, 2500, 623fa5b8e0SAnuj Aggarwal 2550, 2600, 2650, 2700, 633fa5b8e0SAnuj Aggarwal 2750, 2800, 2850, 2900, 643fa5b8e0SAnuj Aggarwal 3000, 3100, 3200, 3300, 653fa5b8e0SAnuj Aggarwal }; 663fa5b8e0SAnuj Aggarwal 673fa5b8e0SAnuj Aggarwal static const u16 LDO1_VSEL_table[] = { 683fa5b8e0SAnuj Aggarwal 1000, 1100, 1200, 1250, 693fa5b8e0SAnuj Aggarwal 1300, 1350, 1400, 1500, 703fa5b8e0SAnuj Aggarwal 1600, 1800, 2500, 2750, 713fa5b8e0SAnuj Aggarwal 2800, 3000, 3100, 3300, 723fa5b8e0SAnuj Aggarwal }; 733fa5b8e0SAnuj Aggarwal 743fa5b8e0SAnuj Aggarwal static const u16 LDO2_VSEL_table[] = { 753fa5b8e0SAnuj Aggarwal 725, 750, 775, 800, 763fa5b8e0SAnuj Aggarwal 825, 850, 875, 900, 773fa5b8e0SAnuj Aggarwal 925, 950, 975, 1000, 783fa5b8e0SAnuj Aggarwal 1025, 1050, 1075, 1100, 793fa5b8e0SAnuj Aggarwal 1125, 1150, 1175, 1200, 803fa5b8e0SAnuj Aggarwal 1225, 1250, 1275, 1300, 813fa5b8e0SAnuj Aggarwal 1325, 1350, 1375, 1400, 823fa5b8e0SAnuj Aggarwal 1425, 1450, 1475, 1500, 833fa5b8e0SAnuj Aggarwal 1550, 1600, 1650, 1700, 843fa5b8e0SAnuj Aggarwal 1750, 1800, 1850, 1900, 853fa5b8e0SAnuj Aggarwal 1950, 2000, 2050, 2100, 863fa5b8e0SAnuj Aggarwal 2150, 2200, 2250, 2300, 873fa5b8e0SAnuj Aggarwal 2350, 2400, 2450, 2500, 883fa5b8e0SAnuj Aggarwal 2550, 2600, 2650, 2700, 893fa5b8e0SAnuj Aggarwal 2750, 2800, 2850, 2900, 903fa5b8e0SAnuj Aggarwal 3000, 3100, 3200, 3300, 913fa5b8e0SAnuj Aggarwal }; 923fa5b8e0SAnuj Aggarwal 933fa5b8e0SAnuj Aggarwal struct tps_info { 943fa5b8e0SAnuj Aggarwal const char *name; 953fa5b8e0SAnuj Aggarwal unsigned min_uV; 963fa5b8e0SAnuj Aggarwal unsigned max_uV; 973fa5b8e0SAnuj Aggarwal u8 table_len; 983fa5b8e0SAnuj Aggarwal const u16 *table; 997d14831eSAnuj Aggarwal 1007d14831eSAnuj Aggarwal /* Does DCDC high or the low register defines output voltage? */ 1017d14831eSAnuj Aggarwal bool defdcdc_default; 1023fa5b8e0SAnuj Aggarwal }; 1033fa5b8e0SAnuj Aggarwal 1047d14831eSAnuj Aggarwal static struct tps_info tps6507x_pmic_regs[] = { 10531dd6a26STodd Fischer { 10631dd6a26STodd Fischer .name = "VDCDC1", 10731dd6a26STodd Fischer .min_uV = 725000, 10831dd6a26STodd Fischer .max_uV = 3300000, 10931dd6a26STodd Fischer .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), 11031dd6a26STodd Fischer .table = VDCDCx_VSEL_table, 11131dd6a26STodd Fischer }, 11231dd6a26STodd Fischer { 11331dd6a26STodd Fischer .name = "VDCDC2", 11431dd6a26STodd Fischer .min_uV = 725000, 11531dd6a26STodd Fischer .max_uV = 3300000, 11631dd6a26STodd Fischer .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), 11731dd6a26STodd Fischer .table = VDCDCx_VSEL_table, 11831dd6a26STodd Fischer }, 11931dd6a26STodd Fischer { 12031dd6a26STodd Fischer .name = "VDCDC3", 12131dd6a26STodd Fischer .min_uV = 725000, 12231dd6a26STodd Fischer .max_uV = 3300000, 12331dd6a26STodd Fischer .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), 12431dd6a26STodd Fischer .table = VDCDCx_VSEL_table, 12531dd6a26STodd Fischer }, 12631dd6a26STodd Fischer { 12731dd6a26STodd Fischer .name = "LDO1", 12831dd6a26STodd Fischer .min_uV = 1000000, 12931dd6a26STodd Fischer .max_uV = 3300000, 13031dd6a26STodd Fischer .table_len = ARRAY_SIZE(LDO1_VSEL_table), 13131dd6a26STodd Fischer .table = LDO1_VSEL_table, 13231dd6a26STodd Fischer }, 13331dd6a26STodd Fischer { 13431dd6a26STodd Fischer .name = "LDO2", 13531dd6a26STodd Fischer .min_uV = 725000, 13631dd6a26STodd Fischer .max_uV = 3300000, 13731dd6a26STodd Fischer .table_len = ARRAY_SIZE(LDO2_VSEL_table), 13831dd6a26STodd Fischer .table = LDO2_VSEL_table, 13931dd6a26STodd Fischer }, 14031dd6a26STodd Fischer }; 14131dd6a26STodd Fischer 1424ce5ba5bSTodd Fischer struct tps6507x_pmic { 1433fa5b8e0SAnuj Aggarwal struct regulator_desc desc[TPS6507X_NUM_REGULATOR]; 14431dd6a26STodd Fischer struct tps6507x_dev *mfd; 1453fa5b8e0SAnuj Aggarwal struct regulator_dev *rdev[TPS6507X_NUM_REGULATOR]; 1467d14831eSAnuj Aggarwal struct tps_info *info[TPS6507X_NUM_REGULATOR]; 1473fa5b8e0SAnuj Aggarwal struct mutex io_lock; 1483fa5b8e0SAnuj Aggarwal }; 1494ce5ba5bSTodd Fischer static inline int tps6507x_pmic_read(struct tps6507x_pmic *tps, u8 reg) 1503fa5b8e0SAnuj Aggarwal { 15131dd6a26STodd Fischer u8 val; 15231dd6a26STodd Fischer int err; 15331dd6a26STodd Fischer 15431dd6a26STodd Fischer err = tps->mfd->read_dev(tps->mfd, reg, 1, &val); 15531dd6a26STodd Fischer 15631dd6a26STodd Fischer if (err) 15731dd6a26STodd Fischer return err; 15831dd6a26STodd Fischer 15931dd6a26STodd Fischer return val; 1603fa5b8e0SAnuj Aggarwal } 1613fa5b8e0SAnuj Aggarwal 1624ce5ba5bSTodd Fischer static inline int tps6507x_pmic_write(struct tps6507x_pmic *tps, u8 reg, u8 val) 1633fa5b8e0SAnuj Aggarwal { 16431dd6a26STodd Fischer return tps->mfd->write_dev(tps->mfd, reg, 1, &val); 1653fa5b8e0SAnuj Aggarwal } 1663fa5b8e0SAnuj Aggarwal 1674ce5ba5bSTodd Fischer static int tps6507x_pmic_set_bits(struct tps6507x_pmic *tps, u8 reg, u8 mask) 1683fa5b8e0SAnuj Aggarwal { 1693fa5b8e0SAnuj Aggarwal int err, data; 1703fa5b8e0SAnuj Aggarwal 1713fa5b8e0SAnuj Aggarwal mutex_lock(&tps->io_lock); 1723fa5b8e0SAnuj Aggarwal 1734ce5ba5bSTodd Fischer data = tps6507x_pmic_read(tps, reg); 1743fa5b8e0SAnuj Aggarwal if (data < 0) { 17531dd6a26STodd Fischer dev_err(tps->mfd->dev, "Read from reg 0x%x failed\n", reg); 1763fa5b8e0SAnuj Aggarwal err = data; 1773fa5b8e0SAnuj Aggarwal goto out; 1783fa5b8e0SAnuj Aggarwal } 1793fa5b8e0SAnuj Aggarwal 1803fa5b8e0SAnuj Aggarwal data |= mask; 1814ce5ba5bSTodd Fischer err = tps6507x_pmic_write(tps, reg, data); 1823fa5b8e0SAnuj Aggarwal if (err) 18331dd6a26STodd Fischer dev_err(tps->mfd->dev, "Write for reg 0x%x failed\n", reg); 1843fa5b8e0SAnuj Aggarwal 1853fa5b8e0SAnuj Aggarwal out: 1863fa5b8e0SAnuj Aggarwal mutex_unlock(&tps->io_lock); 1873fa5b8e0SAnuj Aggarwal return err; 1883fa5b8e0SAnuj Aggarwal } 1893fa5b8e0SAnuj Aggarwal 1904ce5ba5bSTodd Fischer static int tps6507x_pmic_clear_bits(struct tps6507x_pmic *tps, u8 reg, u8 mask) 1913fa5b8e0SAnuj Aggarwal { 1923fa5b8e0SAnuj Aggarwal int err, data; 1933fa5b8e0SAnuj Aggarwal 1943fa5b8e0SAnuj Aggarwal mutex_lock(&tps->io_lock); 1953fa5b8e0SAnuj Aggarwal 1964ce5ba5bSTodd Fischer data = tps6507x_pmic_read(tps, reg); 1973fa5b8e0SAnuj Aggarwal if (data < 0) { 19831dd6a26STodd Fischer dev_err(tps->mfd->dev, "Read from reg 0x%x failed\n", reg); 1993fa5b8e0SAnuj Aggarwal err = data; 2003fa5b8e0SAnuj Aggarwal goto out; 2013fa5b8e0SAnuj Aggarwal } 2023fa5b8e0SAnuj Aggarwal 2033fa5b8e0SAnuj Aggarwal data &= ~mask; 2044ce5ba5bSTodd Fischer err = tps6507x_pmic_write(tps, reg, data); 2053fa5b8e0SAnuj Aggarwal if (err) 20631dd6a26STodd Fischer dev_err(tps->mfd->dev, "Write for reg 0x%x failed\n", reg); 2073fa5b8e0SAnuj Aggarwal 2083fa5b8e0SAnuj Aggarwal out: 2093fa5b8e0SAnuj Aggarwal mutex_unlock(&tps->io_lock); 2103fa5b8e0SAnuj Aggarwal return err; 2113fa5b8e0SAnuj Aggarwal } 2123fa5b8e0SAnuj Aggarwal 2134ce5ba5bSTodd Fischer static int tps6507x_pmic_reg_read(struct tps6507x_pmic *tps, u8 reg) 2143fa5b8e0SAnuj Aggarwal { 2153fa5b8e0SAnuj Aggarwal int data; 2163fa5b8e0SAnuj Aggarwal 2173fa5b8e0SAnuj Aggarwal mutex_lock(&tps->io_lock); 2183fa5b8e0SAnuj Aggarwal 2194ce5ba5bSTodd Fischer data = tps6507x_pmic_read(tps, reg); 2203fa5b8e0SAnuj Aggarwal if (data < 0) 22131dd6a26STodd Fischer dev_err(tps->mfd->dev, "Read from reg 0x%x failed\n", reg); 2223fa5b8e0SAnuj Aggarwal 2233fa5b8e0SAnuj Aggarwal mutex_unlock(&tps->io_lock); 2243fa5b8e0SAnuj Aggarwal return data; 2253fa5b8e0SAnuj Aggarwal } 2263fa5b8e0SAnuj Aggarwal 2274ce5ba5bSTodd Fischer static int tps6507x_pmic_reg_write(struct tps6507x_pmic *tps, u8 reg, u8 val) 2283fa5b8e0SAnuj Aggarwal { 2293fa5b8e0SAnuj Aggarwal int err; 2303fa5b8e0SAnuj Aggarwal 2313fa5b8e0SAnuj Aggarwal mutex_lock(&tps->io_lock); 2323fa5b8e0SAnuj Aggarwal 2334ce5ba5bSTodd Fischer err = tps6507x_pmic_write(tps, reg, val); 2343fa5b8e0SAnuj Aggarwal if (err < 0) 23531dd6a26STodd Fischer dev_err(tps->mfd->dev, "Write for reg 0x%x failed\n", reg); 2363fa5b8e0SAnuj Aggarwal 2373fa5b8e0SAnuj Aggarwal mutex_unlock(&tps->io_lock); 2383fa5b8e0SAnuj Aggarwal return err; 2393fa5b8e0SAnuj Aggarwal } 2403fa5b8e0SAnuj Aggarwal 241*f2933d33SAxel Lin static int tps6507x_pmic_is_enabled(struct regulator_dev *dev) 2423fa5b8e0SAnuj Aggarwal { 2434ce5ba5bSTodd Fischer struct tps6507x_pmic *tps = rdev_get_drvdata(dev); 244*f2933d33SAxel Lin int data, rid = rdev_get_id(dev); 2453fa5b8e0SAnuj Aggarwal u8 shift; 2463fa5b8e0SAnuj Aggarwal 247*f2933d33SAxel Lin if (rid < TPS6507X_DCDC_1 || rid > TPS6507X_LDO_2) 2483fa5b8e0SAnuj Aggarwal return -EINVAL; 2493fa5b8e0SAnuj Aggarwal 250*f2933d33SAxel Lin shift = TPS6507X_MAX_REG_ID - rid; 2514ce5ba5bSTodd Fischer data = tps6507x_pmic_reg_read(tps, TPS6507X_REG_CON_CTRL1); 2523fa5b8e0SAnuj Aggarwal 2533fa5b8e0SAnuj Aggarwal if (data < 0) 2543fa5b8e0SAnuj Aggarwal return data; 2553fa5b8e0SAnuj Aggarwal else 2563fa5b8e0SAnuj Aggarwal return (data & 1<<shift) ? 1 : 0; 2573fa5b8e0SAnuj Aggarwal } 2583fa5b8e0SAnuj Aggarwal 259*f2933d33SAxel Lin static int tps6507x_pmic_enable(struct regulator_dev *dev) 2603fa5b8e0SAnuj Aggarwal { 2614ce5ba5bSTodd Fischer struct tps6507x_pmic *tps = rdev_get_drvdata(dev); 262*f2933d33SAxel Lin int rid = rdev_get_id(dev); 2633fa5b8e0SAnuj Aggarwal u8 shift; 2643fa5b8e0SAnuj Aggarwal 265*f2933d33SAxel Lin if (rid < TPS6507X_DCDC_1 || rid > TPS6507X_LDO_2) 2663fa5b8e0SAnuj Aggarwal return -EINVAL; 2673fa5b8e0SAnuj Aggarwal 268*f2933d33SAxel Lin shift = TPS6507X_MAX_REG_ID - rid; 2694ce5ba5bSTodd Fischer return tps6507x_pmic_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift); 2703fa5b8e0SAnuj Aggarwal } 2713fa5b8e0SAnuj Aggarwal 272*f2933d33SAxel Lin static int tps6507x_pmic_disable(struct regulator_dev *dev) 2733fa5b8e0SAnuj Aggarwal { 2744ce5ba5bSTodd Fischer struct tps6507x_pmic *tps = rdev_get_drvdata(dev); 275*f2933d33SAxel Lin int rid = rdev_get_id(dev); 2763fa5b8e0SAnuj Aggarwal u8 shift; 2773fa5b8e0SAnuj Aggarwal 278*f2933d33SAxel Lin if (rid < TPS6507X_DCDC_1 || rid > TPS6507X_LDO_2) 2793fa5b8e0SAnuj Aggarwal return -EINVAL; 2803fa5b8e0SAnuj Aggarwal 281*f2933d33SAxel Lin shift = TPS6507X_MAX_REG_ID - rid; 2824ce5ba5bSTodd Fischer return tps6507x_pmic_clear_bits(tps, TPS6507X_REG_CON_CTRL1, 2834ce5ba5bSTodd Fischer 1 << shift); 2843fa5b8e0SAnuj Aggarwal } 2853fa5b8e0SAnuj Aggarwal 286*f2933d33SAxel Lin static int tps6507x_pmic_get_voltage(struct regulator_dev *dev) 2873fa5b8e0SAnuj Aggarwal { 2884ce5ba5bSTodd Fischer struct tps6507x_pmic *tps = rdev_get_drvdata(dev); 289*f2933d33SAxel Lin int data, rid = rdev_get_id(dev); 290*f2933d33SAxel Lin u8 reg, mask; 2913fa5b8e0SAnuj Aggarwal 292*f2933d33SAxel Lin switch (rid) { 2933fa5b8e0SAnuj Aggarwal case TPS6507X_DCDC_1: 2943fa5b8e0SAnuj Aggarwal reg = TPS6507X_REG_DEFDCDC1; 295*f2933d33SAxel Lin mask = TPS6507X_DEFDCDCX_DCDC_MASK; 2963fa5b8e0SAnuj Aggarwal break; 2973fa5b8e0SAnuj Aggarwal case TPS6507X_DCDC_2: 298*f2933d33SAxel Lin if (tps->info[rid]->defdcdc_default) 2997d14831eSAnuj Aggarwal reg = TPS6507X_REG_DEFDCDC2_HIGH; 3007d14831eSAnuj Aggarwal else 3013fa5b8e0SAnuj Aggarwal reg = TPS6507X_REG_DEFDCDC2_LOW; 302*f2933d33SAxel Lin mask = TPS6507X_DEFDCDCX_DCDC_MASK; 3033fa5b8e0SAnuj Aggarwal break; 3043fa5b8e0SAnuj Aggarwal case TPS6507X_DCDC_3: 305*f2933d33SAxel Lin if (tps->info[rid]->defdcdc_default) 3067d14831eSAnuj Aggarwal reg = TPS6507X_REG_DEFDCDC3_HIGH; 3077d14831eSAnuj Aggarwal else 3083fa5b8e0SAnuj Aggarwal reg = TPS6507X_REG_DEFDCDC3_LOW; 309*f2933d33SAxel Lin mask = TPS6507X_DEFDCDCX_DCDC_MASK; 310*f2933d33SAxel Lin break; 311*f2933d33SAxel Lin case TPS6507X_LDO_1: 312*f2933d33SAxel Lin reg = TPS6507X_REG_LDO_CTRL1; 313*f2933d33SAxel Lin mask = TPS6507X_REG_LDO_CTRL1_LDO1_MASK; 314*f2933d33SAxel Lin break; 315*f2933d33SAxel Lin case TPS6507X_LDO_2: 316*f2933d33SAxel Lin reg = TPS6507X_REG_DEFLDO2; 317*f2933d33SAxel Lin mask = TPS6507X_REG_DEFLDO2_LDO2_MASK; 3183fa5b8e0SAnuj Aggarwal break; 3193fa5b8e0SAnuj Aggarwal default: 3203fa5b8e0SAnuj Aggarwal return -EINVAL; 3213fa5b8e0SAnuj Aggarwal } 3223fa5b8e0SAnuj Aggarwal 3234ce5ba5bSTodd Fischer data = tps6507x_pmic_reg_read(tps, reg); 3243fa5b8e0SAnuj Aggarwal if (data < 0) 3253fa5b8e0SAnuj Aggarwal return data; 3263fa5b8e0SAnuj Aggarwal 327*f2933d33SAxel Lin data &= mask; 328*f2933d33SAxel Lin return tps->info[rid]->table[data] * 1000; 3293fa5b8e0SAnuj Aggarwal } 3303fa5b8e0SAnuj Aggarwal 331*f2933d33SAxel Lin static int tps6507x_pmic_set_voltage(struct regulator_dev *dev, 3323a93f2a9SMark Brown int min_uV, int max_uV, 3333a93f2a9SMark Brown unsigned *selector) 3343fa5b8e0SAnuj Aggarwal { 3354ce5ba5bSTodd Fischer struct tps6507x_pmic *tps = rdev_get_drvdata(dev); 336*f2933d33SAxel Lin int data, vsel, rid = rdev_get_id(dev); 337*f2933d33SAxel Lin u8 reg, mask; 3383fa5b8e0SAnuj Aggarwal 339*f2933d33SAxel Lin switch (rid) { 3403fa5b8e0SAnuj Aggarwal case TPS6507X_DCDC_1: 3413fa5b8e0SAnuj Aggarwal reg = TPS6507X_REG_DEFDCDC1; 342*f2933d33SAxel Lin mask = TPS6507X_DEFDCDCX_DCDC_MASK; 3433fa5b8e0SAnuj Aggarwal break; 3443fa5b8e0SAnuj Aggarwal case TPS6507X_DCDC_2: 345*f2933d33SAxel Lin if (tps->info[rid]->defdcdc_default) 3467d14831eSAnuj Aggarwal reg = TPS6507X_REG_DEFDCDC2_HIGH; 3477d14831eSAnuj Aggarwal else 3483fa5b8e0SAnuj Aggarwal reg = TPS6507X_REG_DEFDCDC2_LOW; 349*f2933d33SAxel Lin mask = TPS6507X_DEFDCDCX_DCDC_MASK; 3503fa5b8e0SAnuj Aggarwal break; 3513fa5b8e0SAnuj Aggarwal case TPS6507X_DCDC_3: 352*f2933d33SAxel Lin if (tps->info[rid]->defdcdc_default) 3537d14831eSAnuj Aggarwal reg = TPS6507X_REG_DEFDCDC3_HIGH; 3547d14831eSAnuj Aggarwal else 3553fa5b8e0SAnuj Aggarwal reg = TPS6507X_REG_DEFDCDC3_LOW; 356*f2933d33SAxel Lin mask = TPS6507X_DEFDCDCX_DCDC_MASK; 357*f2933d33SAxel Lin break; 358*f2933d33SAxel Lin case TPS6507X_LDO_1: 359*f2933d33SAxel Lin reg = TPS6507X_REG_LDO_CTRL1; 360*f2933d33SAxel Lin mask = TPS6507X_REG_LDO_CTRL1_LDO1_MASK; 361*f2933d33SAxel Lin break; 362*f2933d33SAxel Lin case TPS6507X_LDO_2: 363*f2933d33SAxel Lin reg = TPS6507X_REG_DEFLDO2; 364*f2933d33SAxel Lin mask = TPS6507X_REG_DEFLDO2_LDO2_MASK; 3653fa5b8e0SAnuj Aggarwal break; 3663fa5b8e0SAnuj Aggarwal default: 3673fa5b8e0SAnuj Aggarwal return -EINVAL; 3683fa5b8e0SAnuj Aggarwal } 3693fa5b8e0SAnuj Aggarwal 370*f2933d33SAxel Lin if (min_uV < tps->info[rid]->min_uV || min_uV > tps->info[rid]->max_uV) 3713fa5b8e0SAnuj Aggarwal return -EINVAL; 372*f2933d33SAxel Lin if (max_uV < tps->info[rid]->min_uV || max_uV > tps->info[rid]->max_uV) 3733fa5b8e0SAnuj Aggarwal return -EINVAL; 3743fa5b8e0SAnuj Aggarwal 375*f2933d33SAxel Lin for (vsel = 0; vsel < tps->info[rid]->table_len; vsel++) { 376*f2933d33SAxel Lin int mV = tps->info[rid]->table[vsel]; 3773fa5b8e0SAnuj Aggarwal int uV = mV * 1000; 3783fa5b8e0SAnuj Aggarwal 3793fa5b8e0SAnuj Aggarwal /* Break at the first in-range value */ 3803fa5b8e0SAnuj Aggarwal if (min_uV <= uV && uV <= max_uV) 3813fa5b8e0SAnuj Aggarwal break; 3823fa5b8e0SAnuj Aggarwal } 3833fa5b8e0SAnuj Aggarwal 3843fa5b8e0SAnuj Aggarwal /* write to the register in case we found a match */ 385*f2933d33SAxel Lin if (vsel == tps->info[rid]->table_len) 3863fa5b8e0SAnuj Aggarwal return -EINVAL; 3873fa5b8e0SAnuj Aggarwal 3883a93f2a9SMark Brown *selector = vsel; 3893a93f2a9SMark Brown 3904ce5ba5bSTodd Fischer data = tps6507x_pmic_reg_read(tps, reg); 3913fa5b8e0SAnuj Aggarwal if (data < 0) 3923fa5b8e0SAnuj Aggarwal return data; 3933fa5b8e0SAnuj Aggarwal 3943fa5b8e0SAnuj Aggarwal data &= ~mask; 3953fa5b8e0SAnuj Aggarwal data |= vsel; 3963fa5b8e0SAnuj Aggarwal 3974ce5ba5bSTodd Fischer return tps6507x_pmic_reg_write(tps, reg, data); 3983fa5b8e0SAnuj Aggarwal } 3993fa5b8e0SAnuj Aggarwal 400*f2933d33SAxel Lin static int tps6507x_pmic_list_voltage(struct regulator_dev *dev, 4013fa5b8e0SAnuj Aggarwal unsigned selector) 4023fa5b8e0SAnuj Aggarwal { 4034ce5ba5bSTodd Fischer struct tps6507x_pmic *tps = rdev_get_drvdata(dev); 404*f2933d33SAxel Lin int rid = rdev_get_id(dev); 4053fa5b8e0SAnuj Aggarwal 406*f2933d33SAxel Lin if (rid < TPS6507X_DCDC_1 || rid > TPS6507X_LDO_2) 4073fa5b8e0SAnuj Aggarwal return -EINVAL; 4083fa5b8e0SAnuj Aggarwal 409*f2933d33SAxel Lin if (selector >= tps->info[rid]->table_len) 4103fa5b8e0SAnuj Aggarwal return -EINVAL; 4113fa5b8e0SAnuj Aggarwal else 412*f2933d33SAxel Lin return tps->info[rid]->table[selector] * 1000; 4133fa5b8e0SAnuj Aggarwal } 4143fa5b8e0SAnuj Aggarwal 415*f2933d33SAxel Lin static struct regulator_ops tps6507x_pmic_ops = { 416*f2933d33SAxel Lin .is_enabled = tps6507x_pmic_is_enabled, 417*f2933d33SAxel Lin .enable = tps6507x_pmic_enable, 418*f2933d33SAxel Lin .disable = tps6507x_pmic_disable, 419*f2933d33SAxel Lin .get_voltage = tps6507x_pmic_get_voltage, 420*f2933d33SAxel Lin .set_voltage = tps6507x_pmic_set_voltage, 421*f2933d33SAxel Lin .list_voltage = tps6507x_pmic_list_voltage, 4223fa5b8e0SAnuj Aggarwal }; 4233fa5b8e0SAnuj Aggarwal 424*f2933d33SAxel Lin static __devinit int tps6507x_pmic_probe(struct platform_device *pdev) 4253fa5b8e0SAnuj Aggarwal { 42631dd6a26STodd Fischer struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); 4277d14831eSAnuj Aggarwal struct tps_info *info = &tps6507x_pmic_regs[0]; 4283fa5b8e0SAnuj Aggarwal struct regulator_init_data *init_data; 4293fa5b8e0SAnuj Aggarwal struct regulator_dev *rdev; 4304ce5ba5bSTodd Fischer struct tps6507x_pmic *tps; 4310bc20bbaSTodd Fischer struct tps6507x_board *tps_board; 4323fa5b8e0SAnuj Aggarwal int i; 43356c23492SDmitry Torokhov int error; 4343fa5b8e0SAnuj Aggarwal 4353fa5b8e0SAnuj Aggarwal /** 4360bc20bbaSTodd Fischer * tps_board points to pmic related constants 4370bc20bbaSTodd Fischer * coming from the board-evm file. 4380bc20bbaSTodd Fischer */ 4390bc20bbaSTodd Fischer 44031dd6a26STodd Fischer tps_board = dev_get_platdata(tps6507x_dev->dev); 4410bc20bbaSTodd Fischer if (!tps_board) 4420bc20bbaSTodd Fischer return -EINVAL; 4430bc20bbaSTodd Fischer 4440bc20bbaSTodd Fischer /** 4453fa5b8e0SAnuj Aggarwal * init_data points to array of regulator_init structures 4463fa5b8e0SAnuj Aggarwal * coming from the board-evm file. 4473fa5b8e0SAnuj Aggarwal */ 4480bc20bbaSTodd Fischer init_data = tps_board->tps6507x_pmic_init_data; 4493fa5b8e0SAnuj Aggarwal if (!init_data) 4500bc20bbaSTodd Fischer return -EINVAL; 4513fa5b8e0SAnuj Aggarwal 4523fa5b8e0SAnuj Aggarwal tps = kzalloc(sizeof(*tps), GFP_KERNEL); 4533fa5b8e0SAnuj Aggarwal if (!tps) 4543fa5b8e0SAnuj Aggarwal return -ENOMEM; 4553fa5b8e0SAnuj Aggarwal 4563fa5b8e0SAnuj Aggarwal mutex_init(&tps->io_lock); 4573fa5b8e0SAnuj Aggarwal 4583fa5b8e0SAnuj Aggarwal /* common for all regulators */ 45931dd6a26STodd Fischer tps->mfd = tps6507x_dev; 4603fa5b8e0SAnuj Aggarwal 4613fa5b8e0SAnuj Aggarwal for (i = 0; i < TPS6507X_NUM_REGULATOR; i++, info++, init_data++) { 4623fa5b8e0SAnuj Aggarwal /* Register the regulators */ 4633fa5b8e0SAnuj Aggarwal tps->info[i] = info; 4647d14831eSAnuj Aggarwal if (init_data->driver_data) { 4657d14831eSAnuj Aggarwal struct tps6507x_reg_platform_data *data = 4667d14831eSAnuj Aggarwal init_data->driver_data; 4677d14831eSAnuj Aggarwal tps->info[i]->defdcdc_default = data->defdcdc_default; 4687d14831eSAnuj Aggarwal } 4697d14831eSAnuj Aggarwal 4703fa5b8e0SAnuj Aggarwal tps->desc[i].name = info->name; 47177fa44d0SAxel Lin tps->desc[i].id = i; 4720fcdb109SAxel Lin tps->desc[i].n_voltages = info->table_len; 473*f2933d33SAxel Lin tps->desc[i].ops = &tps6507x_pmic_ops; 4743fa5b8e0SAnuj Aggarwal tps->desc[i].type = REGULATOR_VOLTAGE; 4753fa5b8e0SAnuj Aggarwal tps->desc[i].owner = THIS_MODULE; 4763fa5b8e0SAnuj Aggarwal 4773fa5b8e0SAnuj Aggarwal rdev = regulator_register(&tps->desc[i], 4782c043bcbSRajendra Nayak tps6507x_dev->dev, init_data, tps, NULL); 4793fa5b8e0SAnuj Aggarwal if (IS_ERR(rdev)) { 48031dd6a26STodd Fischer dev_err(tps6507x_dev->dev, 48131dd6a26STodd Fischer "failed to register %s regulator\n", 48231dd6a26STodd Fischer pdev->name); 48356c23492SDmitry Torokhov error = PTR_ERR(rdev); 48456c23492SDmitry Torokhov goto fail; 4853fa5b8e0SAnuj Aggarwal } 4863fa5b8e0SAnuj Aggarwal 4873fa5b8e0SAnuj Aggarwal /* Save regulator for cleanup */ 4883fa5b8e0SAnuj Aggarwal tps->rdev[i] = rdev; 4893fa5b8e0SAnuj Aggarwal } 4903fa5b8e0SAnuj Aggarwal 49131dd6a26STodd Fischer tps6507x_dev->pmic = tps; 492d7399fa8SAxel Lin platform_set_drvdata(pdev, tps6507x_dev); 4933fa5b8e0SAnuj Aggarwal 4943fa5b8e0SAnuj Aggarwal return 0; 49556c23492SDmitry Torokhov 49656c23492SDmitry Torokhov fail: 49756c23492SDmitry Torokhov while (--i >= 0) 49856c23492SDmitry Torokhov regulator_unregister(tps->rdev[i]); 49956c23492SDmitry Torokhov 50056c23492SDmitry Torokhov kfree(tps); 50156c23492SDmitry Torokhov return error; 5023fa5b8e0SAnuj Aggarwal } 5033fa5b8e0SAnuj Aggarwal 50431dd6a26STodd Fischer static int __devexit tps6507x_pmic_remove(struct platform_device *pdev) 5053fa5b8e0SAnuj Aggarwal { 50631dd6a26STodd Fischer struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev); 50731dd6a26STodd Fischer struct tps6507x_pmic *tps = tps6507x_dev->pmic; 5083fa5b8e0SAnuj Aggarwal int i; 5093fa5b8e0SAnuj Aggarwal 5103fa5b8e0SAnuj Aggarwal for (i = 0; i < TPS6507X_NUM_REGULATOR; i++) 5113fa5b8e0SAnuj Aggarwal regulator_unregister(tps->rdev[i]); 5123fa5b8e0SAnuj Aggarwal 5133fa5b8e0SAnuj Aggarwal kfree(tps); 5143fa5b8e0SAnuj Aggarwal 5153fa5b8e0SAnuj Aggarwal return 0; 5163fa5b8e0SAnuj Aggarwal } 5173fa5b8e0SAnuj Aggarwal 51831dd6a26STodd Fischer static struct platform_driver tps6507x_pmic_driver = { 5193fa5b8e0SAnuj Aggarwal .driver = { 52031dd6a26STodd Fischer .name = "tps6507x-pmic", 5213fa5b8e0SAnuj Aggarwal .owner = THIS_MODULE, 5223fa5b8e0SAnuj Aggarwal }, 5234ce5ba5bSTodd Fischer .probe = tps6507x_pmic_probe, 5244ce5ba5bSTodd Fischer .remove = __devexit_p(tps6507x_pmic_remove), 5253fa5b8e0SAnuj Aggarwal }; 5263fa5b8e0SAnuj Aggarwal 5274ce5ba5bSTodd Fischer static int __init tps6507x_pmic_init(void) 5283fa5b8e0SAnuj Aggarwal { 52931dd6a26STodd Fischer return platform_driver_register(&tps6507x_pmic_driver); 5303fa5b8e0SAnuj Aggarwal } 5314ce5ba5bSTodd Fischer subsys_initcall(tps6507x_pmic_init); 5323fa5b8e0SAnuj Aggarwal 5334ce5ba5bSTodd Fischer static void __exit tps6507x_pmic_cleanup(void) 5343fa5b8e0SAnuj Aggarwal { 53531dd6a26STodd Fischer platform_driver_unregister(&tps6507x_pmic_driver); 5363fa5b8e0SAnuj Aggarwal } 5374ce5ba5bSTodd Fischer module_exit(tps6507x_pmic_cleanup); 5383fa5b8e0SAnuj Aggarwal 5393fa5b8e0SAnuj Aggarwal MODULE_AUTHOR("Texas Instruments"); 5403fa5b8e0SAnuj Aggarwal MODULE_DESCRIPTION("TPS6507x voltage regulator driver"); 5413fa5b8e0SAnuj Aggarwal MODULE_LICENSE("GPL v2"); 54231dd6a26STodd Fischer MODULE_ALIAS("platform:tps6507x-pmic"); 543