1452534e5SVenu Byravarasu /* 2452534e5SVenu Byravarasu * Regulator driver for tps65090 power management chip. 3452534e5SVenu Byravarasu * 4452534e5SVenu Byravarasu * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 5452534e5SVenu Byravarasu 6452534e5SVenu Byravarasu * This program is free software; you can redistribute it and/or modify it 7452534e5SVenu Byravarasu * under the terms and conditions of the GNU General Public License, 8452534e5SVenu Byravarasu * version 2, as published by the Free Software Foundation. 9452534e5SVenu Byravarasu 10452534e5SVenu Byravarasu * This program is distributed in the hope it will be useful, but WITHOUT 11452534e5SVenu Byravarasu * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12452534e5SVenu Byravarasu * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13452534e5SVenu Byravarasu * more details. 14452534e5SVenu Byravarasu 15452534e5SVenu Byravarasu * You should have received a copy of the GNU General Public License 16452534e5SVenu Byravarasu * along with this program. If not, see <http://www.gnu.org/licenses/> 17452534e5SVenu Byravarasu */ 18452534e5SVenu Byravarasu 19452534e5SVenu Byravarasu #include <linux/module.h> 20452534e5SVenu Byravarasu #include <linux/init.h> 21f329b175SLaxman Dewangan #include <linux/gpio.h> 22452534e5SVenu Byravarasu #include <linux/slab.h> 23452534e5SVenu Byravarasu #include <linux/err.h> 24452534e5SVenu Byravarasu #include <linux/platform_device.h> 25452534e5SVenu Byravarasu #include <linux/regulator/driver.h> 26452534e5SVenu Byravarasu #include <linux/regulator/machine.h> 27452534e5SVenu Byravarasu #include <linux/mfd/tps65090.h> 28452534e5SVenu Byravarasu 29452534e5SVenu Byravarasu struct tps65090_regulator { 30452534e5SVenu Byravarasu struct device *dev; 3124282a1cSLaxman Dewangan struct regulator_desc *desc; 3224282a1cSLaxman Dewangan struct regulator_dev *rdev; 33452534e5SVenu Byravarasu }; 34452534e5SVenu Byravarasu 35f329b175SLaxman Dewangan static struct regulator_ops tps65090_ext_control_ops = { 36f329b175SLaxman Dewangan }; 37f329b175SLaxman Dewangan 38f329b175SLaxman Dewangan static struct regulator_ops tps65090_reg_contol_ops = { 3906c4998bSAxel Lin .enable = regulator_enable_regmap, 4006c4998bSAxel Lin .disable = regulator_disable_regmap, 4106c4998bSAxel Lin .is_enabled = regulator_is_enabled_regmap, 42452534e5SVenu Byravarasu }; 43452534e5SVenu Byravarasu 443a81ef8cSLaxman Dewangan static struct regulator_ops tps65090_ldo_ops = { 453a81ef8cSLaxman Dewangan }; 463a81ef8cSLaxman Dewangan 4724282a1cSLaxman Dewangan #define tps65090_REG_DESC(_id, _sname, _en_reg, _ops) \ 48452534e5SVenu Byravarasu { \ 4924282a1cSLaxman Dewangan .name = "TPS65090_RAILS"#_id, \ 5024282a1cSLaxman Dewangan .supply_name = _sname, \ 518620ca9fSLaxman Dewangan .id = TPS65090_REGULATOR_##_id, \ 5224282a1cSLaxman Dewangan .ops = &_ops, \ 5324282a1cSLaxman Dewangan .enable_reg = _en_reg, \ 5424282a1cSLaxman Dewangan .enable_mask = BIT(0), \ 55452534e5SVenu Byravarasu .type = REGULATOR_VOLTAGE, \ 56452534e5SVenu Byravarasu .owner = THIS_MODULE, \ 57452534e5SVenu Byravarasu } 58452534e5SVenu Byravarasu 5924282a1cSLaxman Dewangan static struct regulator_desc tps65090_regulator_desc[] = { 60f329b175SLaxman Dewangan tps65090_REG_DESC(DCDC1, "vsys1", 0x0C, tps65090_reg_contol_ops), 61f329b175SLaxman Dewangan tps65090_REG_DESC(DCDC2, "vsys2", 0x0D, tps65090_reg_contol_ops), 62f329b175SLaxman Dewangan tps65090_REG_DESC(DCDC3, "vsys3", 0x0E, tps65090_reg_contol_ops), 63f329b175SLaxman Dewangan tps65090_REG_DESC(FET1, "infet1", 0x0F, tps65090_reg_contol_ops), 64f329b175SLaxman Dewangan tps65090_REG_DESC(FET2, "infet2", 0x10, tps65090_reg_contol_ops), 65f329b175SLaxman Dewangan tps65090_REG_DESC(FET3, "infet3", 0x11, tps65090_reg_contol_ops), 66f329b175SLaxman Dewangan tps65090_REG_DESC(FET4, "infet4", 0x12, tps65090_reg_contol_ops), 67f329b175SLaxman Dewangan tps65090_REG_DESC(FET5, "infet5", 0x13, tps65090_reg_contol_ops), 68f329b175SLaxman Dewangan tps65090_REG_DESC(FET6, "infet6", 0x14, tps65090_reg_contol_ops), 69f329b175SLaxman Dewangan tps65090_REG_DESC(FET7, "infet7", 0x15, tps65090_reg_contol_ops), 703a81ef8cSLaxman Dewangan tps65090_REG_DESC(LDO1, "vsys_l1", 0, tps65090_ldo_ops), 713a81ef8cSLaxman Dewangan tps65090_REG_DESC(LDO2, "vsys_l2", 0, tps65090_ldo_ops), 72452534e5SVenu Byravarasu }; 73452534e5SVenu Byravarasu 7424282a1cSLaxman Dewangan static inline bool is_dcdc(int id) 75452534e5SVenu Byravarasu { 7624282a1cSLaxman Dewangan switch (id) { 778620ca9fSLaxman Dewangan case TPS65090_REGULATOR_DCDC1: 788620ca9fSLaxman Dewangan case TPS65090_REGULATOR_DCDC2: 798620ca9fSLaxman Dewangan case TPS65090_REGULATOR_DCDC3: 8024282a1cSLaxman Dewangan return true; 8124282a1cSLaxman Dewangan default: 8224282a1cSLaxman Dewangan return false; 83452534e5SVenu Byravarasu } 8424282a1cSLaxman Dewangan } 8524282a1cSLaxman Dewangan 8624282a1cSLaxman Dewangan static int __devinit tps65090_config_ext_control( 8724282a1cSLaxman Dewangan struct tps65090_regulator *ri, bool enable) 8824282a1cSLaxman Dewangan { 8924282a1cSLaxman Dewangan int ret; 9024282a1cSLaxman Dewangan struct device *parent = ri->dev->parent; 9124282a1cSLaxman Dewangan unsigned int reg_en_reg = ri->desc->enable_reg; 9224282a1cSLaxman Dewangan 9324282a1cSLaxman Dewangan if (enable) 9424282a1cSLaxman Dewangan ret = tps65090_set_bits(parent, reg_en_reg, 1); 9524282a1cSLaxman Dewangan else 9624282a1cSLaxman Dewangan ret = tps65090_clr_bits(parent, reg_en_reg, 1); 9724282a1cSLaxman Dewangan if (ret < 0) 9824282a1cSLaxman Dewangan dev_err(ri->dev, "Error in updating reg 0x%x\n", reg_en_reg); 9924282a1cSLaxman Dewangan return ret; 10024282a1cSLaxman Dewangan } 10124282a1cSLaxman Dewangan 10224282a1cSLaxman Dewangan static int __devinit tps65090_regulator_disable_ext_control( 10324282a1cSLaxman Dewangan struct tps65090_regulator *ri, 10424282a1cSLaxman Dewangan struct tps65090_regulator_plat_data *tps_pdata) 10524282a1cSLaxman Dewangan { 10624282a1cSLaxman Dewangan int ret = 0; 10724282a1cSLaxman Dewangan struct device *parent = ri->dev->parent; 10824282a1cSLaxman Dewangan unsigned int reg_en_reg = ri->desc->enable_reg; 10924282a1cSLaxman Dewangan 11024282a1cSLaxman Dewangan /* 11124282a1cSLaxman Dewangan * First enable output for internal control if require. 11224282a1cSLaxman Dewangan * And then disable external control. 11324282a1cSLaxman Dewangan */ 11424282a1cSLaxman Dewangan if (tps_pdata->reg_init_data->constraints.always_on || 11524282a1cSLaxman Dewangan tps_pdata->reg_init_data->constraints.boot_on) { 11624282a1cSLaxman Dewangan ret = tps65090_set_bits(parent, reg_en_reg, 0); 11724282a1cSLaxman Dewangan if (ret < 0) { 11824282a1cSLaxman Dewangan dev_err(ri->dev, "Error in set reg 0x%x\n", reg_en_reg); 11924282a1cSLaxman Dewangan return ret; 12024282a1cSLaxman Dewangan } 12124282a1cSLaxman Dewangan } 12224282a1cSLaxman Dewangan return tps65090_config_ext_control(ri, false); 123452534e5SVenu Byravarasu } 124452534e5SVenu Byravarasu 125f329b175SLaxman Dewangan static void __devinit tps65090_configure_regulator_config( 126f329b175SLaxman Dewangan struct tps65090_regulator_plat_data *tps_pdata, 127f329b175SLaxman Dewangan struct regulator_config *config) 128f329b175SLaxman Dewangan { 129f329b175SLaxman Dewangan if (gpio_is_valid(tps_pdata->gpio)) { 130f329b175SLaxman Dewangan int gpio_flag = GPIOF_OUT_INIT_LOW; 131f329b175SLaxman Dewangan 132f329b175SLaxman Dewangan if (tps_pdata->reg_init_data->constraints.always_on || 133f329b175SLaxman Dewangan tps_pdata->reg_init_data->constraints.boot_on) 134f329b175SLaxman Dewangan gpio_flag = GPIOF_OUT_INIT_HIGH; 135f329b175SLaxman Dewangan 136f329b175SLaxman Dewangan config->ena_gpio = tps_pdata->gpio; 137f329b175SLaxman Dewangan config->ena_gpio_flags = gpio_flag; 138f329b175SLaxman Dewangan } 139f329b175SLaxman Dewangan } 140f329b175SLaxman Dewangan 141452534e5SVenu Byravarasu static int __devinit tps65090_regulator_probe(struct platform_device *pdev) 142452534e5SVenu Byravarasu { 14306c4998bSAxel Lin struct tps65090 *tps65090_mfd = dev_get_drvdata(pdev->dev.parent); 144452534e5SVenu Byravarasu struct tps65090_regulator *ri = NULL; 145c172708dSMark Brown struct regulator_config config = { }; 146452534e5SVenu Byravarasu struct regulator_dev *rdev; 14724282a1cSLaxman Dewangan struct tps65090_regulator_plat_data *tps_pdata; 14824282a1cSLaxman Dewangan struct tps65090_regulator *pmic; 14924282a1cSLaxman Dewangan struct tps65090_platform_data *tps65090_pdata; 15024282a1cSLaxman Dewangan int num; 15124282a1cSLaxman Dewangan int ret; 152452534e5SVenu Byravarasu 15324282a1cSLaxman Dewangan dev_dbg(&pdev->dev, "Probing regulator\n"); 154452534e5SVenu Byravarasu 15524282a1cSLaxman Dewangan tps65090_pdata = dev_get_platdata(pdev->dev.parent); 15624282a1cSLaxman Dewangan if (!tps65090_pdata) { 15724282a1cSLaxman Dewangan dev_err(&pdev->dev, "Platform data missing\n"); 158452534e5SVenu Byravarasu return -EINVAL; 159452534e5SVenu Byravarasu } 160452534e5SVenu Byravarasu 1618620ca9fSLaxman Dewangan pmic = devm_kzalloc(&pdev->dev, TPS65090_REGULATOR_MAX * sizeof(*pmic), 16224282a1cSLaxman Dewangan GFP_KERNEL); 16324282a1cSLaxman Dewangan if (!pmic) { 16424282a1cSLaxman Dewangan dev_err(&pdev->dev, "mem alloc for pmic failed\n"); 16524282a1cSLaxman Dewangan return -ENOMEM; 166452534e5SVenu Byravarasu } 167452534e5SVenu Byravarasu 1688620ca9fSLaxman Dewangan for (num = 0; num < TPS65090_REGULATOR_MAX; num++) { 16924282a1cSLaxman Dewangan tps_pdata = tps65090_pdata->reg_pdata[num]; 17024282a1cSLaxman Dewangan 17124282a1cSLaxman Dewangan ri = &pmic[num]; 17224282a1cSLaxman Dewangan ri->dev = &pdev->dev; 17324282a1cSLaxman Dewangan ri->desc = &tps65090_regulator_desc[num]; 17424282a1cSLaxman Dewangan 17524282a1cSLaxman Dewangan /* 17624282a1cSLaxman Dewangan * TPS5090 DCDC support the control from external digital input. 177f329b175SLaxman Dewangan * Configure it as per platform data. 17824282a1cSLaxman Dewangan */ 17924282a1cSLaxman Dewangan if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data) { 180f329b175SLaxman Dewangan if (tps_pdata->enable_ext_control) { 181f329b175SLaxman Dewangan tps65090_configure_regulator_config( 182f329b175SLaxman Dewangan tps_pdata, &config); 183f329b175SLaxman Dewangan ri->desc->ops = &tps65090_ext_control_ops; 184f329b175SLaxman Dewangan } else { 18524282a1cSLaxman Dewangan ret = tps65090_regulator_disable_ext_control( 18624282a1cSLaxman Dewangan ri, tps_pdata); 18724282a1cSLaxman Dewangan if (ret < 0) { 18824282a1cSLaxman Dewangan dev_err(&pdev->dev, 18924282a1cSLaxman Dewangan "failed disable ext control\n"); 19024282a1cSLaxman Dewangan goto scrub; 19124282a1cSLaxman Dewangan } 19224282a1cSLaxman Dewangan } 193f329b175SLaxman Dewangan } 194f329b175SLaxman Dewangan 19524282a1cSLaxman Dewangan config.dev = &pdev->dev; 19624282a1cSLaxman Dewangan config.driver_data = ri; 19724282a1cSLaxman Dewangan config.regmap = tps65090_mfd->rmap; 19824282a1cSLaxman Dewangan if (tps_pdata) 19924282a1cSLaxman Dewangan config.init_data = tps_pdata->reg_init_data; 20024282a1cSLaxman Dewangan else 20124282a1cSLaxman Dewangan config.init_data = NULL; 20224282a1cSLaxman Dewangan 20324282a1cSLaxman Dewangan rdev = regulator_register(ri->desc, &config); 20424282a1cSLaxman Dewangan if (IS_ERR(rdev)) { 20524282a1cSLaxman Dewangan dev_err(&pdev->dev, "failed to register regulator %s\n", 20624282a1cSLaxman Dewangan ri->desc->name); 20724282a1cSLaxman Dewangan ret = PTR_ERR(rdev); 20824282a1cSLaxman Dewangan goto scrub; 20924282a1cSLaxman Dewangan } 21024282a1cSLaxman Dewangan ri->rdev = rdev; 211f329b175SLaxman Dewangan 212f329b175SLaxman Dewangan /* Enable external control if it is require */ 213f329b175SLaxman Dewangan if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data && 214f329b175SLaxman Dewangan tps_pdata->enable_ext_control) { 215f329b175SLaxman Dewangan ret = tps65090_config_ext_control(ri, true); 216f329b175SLaxman Dewangan if (ret < 0) { 217f329b175SLaxman Dewangan /* Increment num to get unregister rdev */ 218f329b175SLaxman Dewangan num++; 219f329b175SLaxman Dewangan goto scrub; 220f329b175SLaxman Dewangan } 221f329b175SLaxman Dewangan } 22224282a1cSLaxman Dewangan } 22324282a1cSLaxman Dewangan 22424282a1cSLaxman Dewangan platform_set_drvdata(pdev, pmic); 225452534e5SVenu Byravarasu return 0; 22624282a1cSLaxman Dewangan 22724282a1cSLaxman Dewangan scrub: 22824282a1cSLaxman Dewangan while (--num >= 0) { 22924282a1cSLaxman Dewangan ri = &pmic[num]; 23024282a1cSLaxman Dewangan regulator_unregister(ri->rdev); 23124282a1cSLaxman Dewangan } 23224282a1cSLaxman Dewangan return ret; 233452534e5SVenu Byravarasu } 234452534e5SVenu Byravarasu 235452534e5SVenu Byravarasu static int __devexit tps65090_regulator_remove(struct platform_device *pdev) 236452534e5SVenu Byravarasu { 23724282a1cSLaxman Dewangan struct tps65090_regulator *pmic = platform_get_drvdata(pdev); 23824282a1cSLaxman Dewangan struct tps65090_regulator *ri; 23924282a1cSLaxman Dewangan int num; 240452534e5SVenu Byravarasu 2418620ca9fSLaxman Dewangan for (num = 0; num < TPS65090_REGULATOR_MAX; ++num) { 24224282a1cSLaxman Dewangan ri = &pmic[num]; 24324282a1cSLaxman Dewangan regulator_unregister(ri->rdev); 24424282a1cSLaxman Dewangan } 245452534e5SVenu Byravarasu return 0; 246452534e5SVenu Byravarasu } 247452534e5SVenu Byravarasu 248452534e5SVenu Byravarasu static struct platform_driver tps65090_regulator_driver = { 249452534e5SVenu Byravarasu .driver = { 2508620ca9fSLaxman Dewangan .name = "tps65090-pmic", 251452534e5SVenu Byravarasu .owner = THIS_MODULE, 252452534e5SVenu Byravarasu }, 253452534e5SVenu Byravarasu .probe = tps65090_regulator_probe, 254452534e5SVenu Byravarasu .remove = __devexit_p(tps65090_regulator_remove), 255452534e5SVenu Byravarasu }; 256452534e5SVenu Byravarasu 257452534e5SVenu Byravarasu static int __init tps65090_regulator_init(void) 258452534e5SVenu Byravarasu { 259452534e5SVenu Byravarasu return platform_driver_register(&tps65090_regulator_driver); 260452534e5SVenu Byravarasu } 261452534e5SVenu Byravarasu subsys_initcall(tps65090_regulator_init); 262452534e5SVenu Byravarasu 263452534e5SVenu Byravarasu static void __exit tps65090_regulator_exit(void) 264452534e5SVenu Byravarasu { 265452534e5SVenu Byravarasu platform_driver_unregister(&tps65090_regulator_driver); 266452534e5SVenu Byravarasu } 267452534e5SVenu Byravarasu module_exit(tps65090_regulator_exit); 268452534e5SVenu Byravarasu 269452534e5SVenu Byravarasu MODULE_DESCRIPTION("tps65090 regulator driver"); 270452534e5SVenu Byravarasu MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>"); 271452534e5SVenu Byravarasu MODULE_LICENSE("GPL v2"); 272