/* * driver/regulator/gpio-switch-regulator.c * GPIO based switch regulator to enable/disable power rails. * * Copyright (c) 2011, NVIDIA Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /*#define DEBUG 1*/ /*#define VERBOSE_DEBUG 1*/ #include #include #include #include #include #include #include #include #include struct gpio_switch_regulator { struct regulator_desc reg_desc; struct regulator_init_data reg_init_data; struct regulator *input_regulator; struct regulator_dev *rdev; struct device *dev; int gpio_nr; int pin_group; bool is_gpio_init; bool is_enable; bool active_low; bool is_init_success; int *voltages; unsigned curr_vol_sel; struct gpio_switch_regulator_subdev_data *psubdev_data; int (*enable_rail)(struct gpio_switch_regulator_subdev_data *sdata); int (*disable_rail)(struct gpio_switch_regulator_subdev_data *sdata); }; static int _gpio_regulator_enable(struct device *dev, struct gpio_switch_regulator *ri) { int init_val; int ret; if (ri->enable_rail) { ret = ri->enable_rail(ri->psubdev_data); if (ret < 0) dev_err(dev, "Unable to enable rail through board api" " error %d\n", ret); } else { init_val = (ri->active_low) ? 0 : 1; ret = gpio_direction_output(ri->gpio_nr, init_val); if (ret < 0) dev_err(dev, "Unable to set direction %d\n", ri->gpio_nr); } return ret; } static int _gpio_regulator_disable(struct device *dev, struct gpio_switch_regulator *ri) { int init_val; int ret; if (ri->disable_rail) { ret = ri->disable_rail(ri->psubdev_data); if (ret < 0) dev_err(dev, "Unable to disable rail through " "board api %d\n", ret); } else { init_val = (ri->active_low) ? 1 : 0; ret = gpio_direction_output(ri->gpio_nr, init_val); if (ret < 0) dev_err(dev, "Unable to set direction %d\n", ri->gpio_nr); } return ret; } static int gpio_switch_list_voltage(struct regulator_dev *rdev, unsigned selector) { struct gpio_switch_regulator *ri = rdev_get_drvdata(rdev); if (selector < ri->reg_desc.n_voltages) return ri->voltages[selector] * 1000; else return 0; } static int gpio_switch_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) { struct gpio_switch_regulator *ri = rdev_get_drvdata(rdev); int uV; bool found = false; unsigned val; for (val = 0; val < ri->reg_desc.n_voltages; val++) { uV = ri->voltages[val] * 1000; if (min_uV <= uV && uV <= max_uV) { found = true; ri->curr_vol_sel = val; break; } } if (found && ri->input_regulator) return regulator_set_voltage(ri->input_regulator, min_uV, max_uV); ri->curr_vol_sel = 0; return -EINVAL; } static int gpio_switch_get_voltage(struct regulator_dev *rdev) { struct gpio_switch_regulator *ri = rdev_get_drvdata(rdev); if (ri->input_regulator) return regulator_get_voltage(ri->input_regulator); if (ri->curr_vol_sel < ri->reg_desc.n_voltages) return ri->voltages[ri->curr_vol_sel] * 1000; return 0; } static int gpio_switch_regulator_enable(struct regulator_dev *rdev) { struct gpio_switch_regulator *ri = rdev_get_drvdata(rdev); int ret = 0; if (ri->is_enable) return 0; if (ri->input_regulator) { ret = regulator_enable(ri->input_regulator); if (ret < 0) { dev_err(&rdev->dev, "%s:Failed to enable regulator" " Error %d\n", __func__, ret); return ret; } } ret = _gpio_regulator_enable(&rdev->dev, ri); if (ret < 0) return ret; ri->is_enable = true; return 0; } static int gpio_switch_regulator_disable(struct regulator_dev *rdev) { struct gpio_switch_regulator *ri = rdev_get_drvdata(rdev); int ret = 0; if (!ri->is_enable) return 0; ret = _gpio_regulator_disable(&rdev->dev, ri); if (ret < 0) return ret; if (ri->input_regulator) { ret = regulator_disable(ri->input_regulator); if (ret < 0) { dev_err(&rdev->dev, "%s:Failed to disable regulator" " Error %d\n", __func__, ret); return ret; } } ri->is_enable = false; return 0; } static int gpio_switch_regulator_is_enabled(struct regulator_dev *rdev) { struct gpio_switch_regulator *ri = rdev_get_drvdata(rdev); int ret = 0; if (ri->input_regulator) { ret = regulator_is_enabled(ri->input_regulator); if (!ret) return ret; } return (ri->is_enable) ? 1 : 0; } static struct regulator_ops gpio_switch_regulator_ops = { .list_voltage = gpio_switch_list_voltage, .get_voltage = gpio_switch_get_voltage, .set_voltage = gpio_switch_set_voltage, .is_enabled = gpio_switch_regulator_is_enabled, .enable = gpio_switch_regulator_enable, .disable = gpio_switch_regulator_disable, }; static int __devinit gpio_switch_regulator_probe(struct platform_device *pdev) { struct gpio_switch_regulator *ri = NULL; struct gpio_switch_regulator *gswitch_reg = NULL; struct gpio_switch_regulator_platform_data *pdata; struct gpio_switch_regulator_subdev_data *sdata = NULL; int id = pdev->id; int ret = 0; int rcount; dev_dbg(&pdev->dev, "Probing regulator %d\n", id); pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "%s:No platform data Exiting\n", __func__); return -ENODEV; } BUG_ON(!pdata->num_subdevs); gswitch_reg = kzalloc(sizeof(struct gpio_switch_regulator) * pdata->num_subdevs, GFP_KERNEL); if (!gswitch_reg) { dev_err(&pdev->dev, "%s:Failed to allocate memory\n", __func__); return -ENOMEM; } for (rcount = 0; rcount < pdata->num_subdevs; ++rcount) { ri = &gswitch_reg[rcount]; sdata = pdata->subdevs[rcount]; /* Initialize the regulator parameter */ ri->reg_desc.name = sdata->regulator_name; ri->reg_desc.ops = &gpio_switch_regulator_ops; ri->reg_desc.type = REGULATOR_VOLTAGE; ri->reg_desc.id = sdata->id; ri->reg_desc.n_voltages = sdata->n_voltages; ri->reg_desc.owner = THIS_MODULE; ri->is_init_success = false; memcpy(&ri->reg_init_data.constraints, &sdata->constraints, sizeof(struct regulation_constraints)); /* Initialize min and maximum contraint voltage if it is not * define in platform device */ if (!sdata->constraints.min_uV) ri->reg_init_data.constraints.min_uV = 1000 * sdata->voltages[0]; if (!sdata->constraints.max_uV) ri->reg_init_data.constraints.max_uV = 1000 * sdata->voltages[sdata->n_voltages - 1]; ri->reg_init_data.num_consumer_supplies = sdata->num_consumer_supplies; ri->reg_init_data.consumer_supplies = sdata->consumer_supplies; ri->input_regulator = NULL; ri->is_gpio_init = false; ri->is_enable = (sdata->init_state) ? true : false; ri->voltages = sdata->voltages; ri->psubdev_data = sdata; ri->gpio_nr = sdata->gpio_nr; ri->active_low = sdata->active_low; ri->dev = &pdev->dev; ri->enable_rail = sdata->enable_rail; ri->disable_rail = sdata->disable_rail; ri->pin_group = sdata->pin_group; /* Checking for board APIs enable/disable rail */ if (ri->enable_rail || ri->disable_rail) BUG_ON(!(ri->enable_rail && ri->disable_rail)); /* Get the regulator structure if input supply is available */ if (sdata->input_supply) { ri->input_regulator = regulator_get(NULL, sdata->input_supply); if (IS_ERR_OR_NULL(ri->input_regulator)) { dev_err(&pdev->dev, "Unable to get regu" "lator %s\n", sdata->input_supply); ret = -ENODEV; goto reg_get_fail; } if (ri->is_enable) { ret = regulator_enable(ri->input_regulator); if (ret < 0) { dev_err(&pdev->dev, "Unable to enable " "regulator %s\n", sdata->input_supply); goto reg_en_fail; } } } /* Initialize gpios */ ret = gpio_request(ri->gpio_nr, sdata->regulator_name); if (ret < 0) { dev_err(&pdev->dev, "Unable to request gpio %d\n", ri->gpio_nr); goto gpio_req_fail; } if (ri->is_enable) ret = _gpio_regulator_enable(&pdev->dev, ri); else ret = _gpio_regulator_disable(&pdev->dev, ri); if (ret < 0) goto reg_cont_fail; ri->is_gpio_init = true; ri->rdev = regulator_register(&ri->reg_desc, &pdev->dev, &ri->reg_init_data, ri); if (IS_ERR_OR_NULL(ri->rdev)) { dev_err(&pdev->dev, "Failed to register regulator %s\n", ri->reg_desc.name); ret = PTR_ERR(ri->rdev); goto reg_reg_fail; } /* If everything success then continue for next registration */ ri->is_init_success = true; continue; /* Cleanup the current registration and continue for next * registration*/ reg_reg_fail: if (ri->is_enable) _gpio_regulator_disable(&pdev->dev, ri); reg_cont_fail: gpio_free(ri->gpio_nr); gpio_req_fail: if (ri->is_enable && ri->input_regulator) regulator_disable(ri->input_regulator); reg_en_fail: if (ri->input_regulator) { regulator_put(ri->input_regulator); ri->input_regulator = NULL; } reg_get_fail: dev_err(&pdev->dev, "Unable to register regulator %s\n", sdata->regulator_name); } platform_set_drvdata(pdev, gswitch_reg); return 0; } static int __devexit gpio_switch_regulator_remove(struct platform_device *pdev) { struct gpio_switch_regulator *ri = NULL; struct gpio_switch_regulator *gswitch_reg = platform_get_drvdata(pdev); int i; struct gpio_switch_regulator_platform_data *pdata; pdata = pdev->dev.platform_data; /* Unregister devices in reverse order */ for (i = pdata->num_subdevs; i; --i) { ri = &gswitch_reg[i - 1]; /* If registration was not success, then do not release */ if (!ri->is_init_success) continue; if (ri->is_enable) _gpio_regulator_disable(&pdev->dev, ri); if (ri->input_regulator) { if (ri->is_enable) regulator_disable(ri->input_regulator); regulator_put(ri->input_regulator); } regulator_unregister(ri->rdev); gpio_free(ri->gpio_nr); } kfree(gswitch_reg); platform_set_drvdata(pdev, NULL); return 0; } static struct platform_driver gpio_switch_regulator_driver = { .driver = { .name = "gpio-switch-regulator", .owner = THIS_MODULE, }, .probe = gpio_switch_regulator_probe, .remove = __devexit_p(gpio_switch_regulator_remove), }; static int __init gpio_switch_regulator_init(void) { return platform_driver_register(&gpio_switch_regulator_driver); } static void __exit gpio_switch_regulator_exit(void) { platform_driver_unregister(&gpio_switch_regulator_driver); } subsys_initcall_sync(gpio_switch_regulator_init); module_exit(gpio_switch_regulator_exit);