1/* 2 * fixed.c 3 * 4 * Copyright 2008 Wolfson Microelectronics PLC. 5 * 6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 7 * 8 * Copyright (c) 2009 Nokia Corporation 9 * Roger Quadros <ext-roger.quadros@nokia.com> 10 * 11 * This program is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU General Public License as 13 * published by the Free Software Foundation; either version 2 of the 14 * License, or (at your option) any later version. 15 * 16 * This is useful for systems with mixed controllable and 17 * non-controllable regulators, as well as for allowing testing on 18 * systems with no controllable regulators. 19 */ 20 21#include <linux/err.h> 22#include <linux/mutex.h> 23#include <linux/platform_device.h> 24#include <linux/regulator/driver.h> 25#include <linux/regulator/fixed.h> 26#include <linux/gpio.h> 27#include <linux/delay.h> 28#include <linux/slab.h> 29 30struct fixed_voltage_data { 31 struct regulator_desc desc; 32 struct regulator_dev *dev; 33 int microvolts; 34 int gpio; 35 unsigned startup_delay; 36 bool enable_high; 37 bool is_enabled; 38}; 39 40static int fixed_voltage_is_enabled(struct regulator_dev *dev) 41{ 42 struct fixed_voltage_data *data = rdev_get_drvdata(dev); 43 44 return data->is_enabled; 45} 46 47static int fixed_voltage_enable(struct regulator_dev *dev) 48{ 49 struct fixed_voltage_data *data = rdev_get_drvdata(dev); 50 51 if (gpio_is_valid(data->gpio)) { 52 gpio_set_value_cansleep(data->gpio, data->enable_high); 53 data->is_enabled = true; 54 } 55 56 return 0; 57} 58 59static int fixed_voltage_disable(struct regulator_dev *dev) 60{ 61 struct fixed_voltage_data *data = rdev_get_drvdata(dev); 62 63 if (gpio_is_valid(data->gpio)) { 64 gpio_set_value_cansleep(data->gpio, !data->enable_high); 65 data->is_enabled = false; 66 } 67 68 return 0; 69} 70 71static int fixed_voltage_enable_time(struct regulator_dev *dev) 72{ 73 struct fixed_voltage_data *data = rdev_get_drvdata(dev); 74 75 return data->startup_delay; 76} 77 78static int fixed_voltage_get_voltage(struct regulator_dev *dev) 79{ 80 struct fixed_voltage_data *data = rdev_get_drvdata(dev); 81 82 return data->microvolts; 83} 84 85static int fixed_voltage_list_voltage(struct regulator_dev *dev, 86 unsigned selector) 87{ 88 struct fixed_voltage_data *data = rdev_get_drvdata(dev); 89 90 if (selector != 0) 91 return -EINVAL; 92 93 return data->microvolts; 94} 95 96static struct regulator_ops fixed_voltage_ops = { 97 .is_enabled = fixed_voltage_is_enabled, 98 .enable = fixed_voltage_enable, 99 .disable = fixed_voltage_disable, 100 .enable_time = fixed_voltage_enable_time, 101 .get_voltage = fixed_voltage_get_voltage, 102 .list_voltage = fixed_voltage_list_voltage, 103}; 104 105static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) 106{ 107 struct fixed_voltage_config *config = pdev->dev.platform_data; 108 struct fixed_voltage_data *drvdata; 109 int ret; 110 111 drvdata = kzalloc(sizeof(struct fixed_voltage_data), GFP_KERNEL); 112 if (drvdata == NULL) { 113 dev_err(&pdev->dev, "Failed to allocate device data\n"); 114 ret = -ENOMEM; 115 goto err; 116 } 117 118 drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL); 119 if (drvdata->desc.name == NULL) { 120 dev_err(&pdev->dev, "Failed to allocate supply name\n"); 121 ret = -ENOMEM; 122 goto err; 123 } 124 drvdata->desc.type = REGULATOR_VOLTAGE; 125 drvdata->desc.owner = THIS_MODULE; 126 drvdata->desc.ops = &fixed_voltage_ops; 127 drvdata->desc.n_voltages = 1; 128 129 drvdata->microvolts = config->microvolts; 130 drvdata->gpio = config->gpio; 131 drvdata->startup_delay = config->startup_delay; 132 133 if (gpio_is_valid(config->gpio)) { 134 drvdata->enable_high = config->enable_high; 135 136 if (!config->gpio) 137 dev_warn(&pdev->dev, 138 "using GPIO 0 for regulator enable control\n"); 139 140 ret = gpio_request(config->gpio, config->supply_name); 141 if (ret) { 142 dev_err(&pdev->dev, 143 "Could not obtain regulator enable GPIO %d: %d\n", 144 config->gpio, ret); 145 goto err_name; 146 } 147 148 /* set output direction without changing state 149 * to prevent glitch 150 */ 151 drvdata->is_enabled = config->enabled_at_boot; 152 ret = drvdata->is_enabled ? 153 config->enable_high : !config->enable_high; 154 155 ret = gpio_direction_output(config->gpio, ret); 156 if (ret) { 157 dev_err(&pdev->dev, 158 "Could not configure regulator enable GPIO %d direction: %d\n", 159 config->gpio, ret); 160 goto err_gpio; 161 } 162 163 } else { 164 /* Regulator without GPIO control is considered 165 * always enabled 166 */ 167 drvdata->is_enabled = true; 168 } 169 170 drvdata->dev = regulator_register(&drvdata->desc, &pdev->dev, 171 config->init_data, drvdata); 172 if (IS_ERR(drvdata->dev)) { 173 ret = PTR_ERR(drvdata->dev); 174 dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); 175 goto err_gpio; 176 } 177 178 platform_set_drvdata(pdev, drvdata); 179 180 dev_dbg(&pdev->dev, "%s supplying %duV\n", drvdata->desc.name, 181 drvdata->microvolts); 182 183 return 0; 184 185err_gpio: 186 if (gpio_is_valid(config->gpio)) 187 gpio_free(config->gpio); 188err_name: 189 kfree(drvdata->desc.name); 190err: 191 kfree(drvdata); 192 return ret; 193} 194 195static int __devexit reg_fixed_voltage_remove(struct platform_device *pdev) 196{ 197 struct fixed_voltage_data *drvdata = platform_get_drvdata(pdev); 198 199 regulator_unregister(drvdata->dev); 200 if (gpio_is_valid(drvdata->gpio)) 201 gpio_free(drvdata->gpio); 202 kfree(drvdata->desc.name); 203 kfree(drvdata); 204 205 return 0; 206} 207 208static struct platform_driver regulator_fixed_voltage_driver = { 209 .probe = reg_fixed_voltage_probe, 210 .remove = __devexit_p(reg_fixed_voltage_remove), 211 .driver = { 212 .name = "reg-fixed-voltage", 213 .owner = THIS_MODULE, 214 }, 215}; 216 217static int __init regulator_fixed_voltage_init(void) 218{ 219 return platform_driver_register(®ulator_fixed_voltage_driver); 220} 221subsys_initcall(regulator_fixed_voltage_init); 222 223static void __exit regulator_fixed_voltage_exit(void) 224{ 225 platform_driver_unregister(®ulator_fixed_voltage_driver); 226} 227module_exit(regulator_fixed_voltage_exit); 228 229MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 230MODULE_DESCRIPTION("Fixed voltage regulator"); 231MODULE_LICENSE("GPL"); 232MODULE_ALIAS("platform:reg-fixed-voltage"); 233