diff options
Diffstat (limited to 'drivers/regulator/fixed.c')
| -rw-r--r-- | drivers/regulator/fixed.c | 267 |
1 files changed, 127 insertions, 140 deletions
diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 2fe9d99c9f2..354105eff1f 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -20,208 +20,195 @@ #include <linux/err.h> #include <linux/mutex.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/fixed.h> #include <linux/gpio.h> -#include <linux/delay.h> #include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/regulator/of_regulator.h> +#include <linux/regulator/machine.h> struct fixed_voltage_data { struct regulator_desc desc; struct regulator_dev *dev; - int microvolts; - int gpio; - unsigned startup_delay; - bool enable_high; - bool is_enabled; }; -static int fixed_voltage_is_enabled(struct regulator_dev *dev) -{ - struct fixed_voltage_data *data = rdev_get_drvdata(dev); - - return data->is_enabled; -} -static int fixed_voltage_enable(struct regulator_dev *dev) +/** + * of_get_fixed_voltage_config - extract fixed_voltage_config structure info + * @dev: device requesting for fixed_voltage_config + * + * Populates fixed_voltage_config structure by extracting data from device + * tree node, returns a pointer to the populated structure of NULL if memory + * alloc fails. + */ +static struct fixed_voltage_config * +of_get_fixed_voltage_config(struct device *dev) { - struct fixed_voltage_data *data = rdev_get_drvdata(dev); + struct fixed_voltage_config *config; + struct device_node *np = dev->of_node; + struct regulator_init_data *init_data; - if (gpio_is_valid(data->gpio)) { - gpio_set_value_cansleep(data->gpio, data->enable_high); - data->is_enabled = true; - } + config = devm_kzalloc(dev, sizeof(struct fixed_voltage_config), + GFP_KERNEL); + if (!config) + return ERR_PTR(-ENOMEM); - return 0; -} + config->init_data = of_get_regulator_init_data(dev, dev->of_node); + if (!config->init_data) + return ERR_PTR(-EINVAL); -static int fixed_voltage_disable(struct regulator_dev *dev) -{ - struct fixed_voltage_data *data = rdev_get_drvdata(dev); + init_data = config->init_data; + init_data->constraints.apply_uV = 0; - if (gpio_is_valid(data->gpio)) { - gpio_set_value_cansleep(data->gpio, !data->enable_high); - data->is_enabled = false; + config->supply_name = init_data->constraints.name; + if (init_data->constraints.min_uV == init_data->constraints.max_uV) { + config->microvolts = init_data->constraints.min_uV; + } else { + dev_err(dev, + "Fixed regulator specified with variable voltages\n"); + return ERR_PTR(-EINVAL); } - return 0; -} - -static int fixed_voltage_enable_time(struct regulator_dev *dev) -{ - struct fixed_voltage_data *data = rdev_get_drvdata(dev); - - return data->startup_delay; -} - -static int fixed_voltage_get_voltage(struct regulator_dev *dev) -{ - struct fixed_voltage_data *data = rdev_get_drvdata(dev); - - return data->microvolts; -} - -static int fixed_voltage_list_voltage(struct regulator_dev *dev, - unsigned selector) -{ - struct fixed_voltage_data *data = rdev_get_drvdata(dev); - - if (selector != 0) - return -EINVAL; - - return data->microvolts; + if (init_data->constraints.boot_on) + config->enabled_at_boot = true; + + config->gpio = of_get_named_gpio(np, "gpio", 0); + /* + * of_get_named_gpio() currently returns ENODEV rather than + * EPROBE_DEFER. This code attempts to be compatible with both + * for now; the ENODEV check can be removed once the API is fixed. + * of_get_named_gpio() doesn't differentiate between a missing + * property (which would be fine here, since the GPIO is optional) + * and some other error. Patches have been posted for both issues. + * Once they are check in, we should replace this with: + * if (config->gpio < 0 && config->gpio != -ENOENT) + */ + if ((config->gpio == -ENODEV) || (config->gpio == -EPROBE_DEFER)) + return ERR_PTR(-EPROBE_DEFER); + + of_property_read_u32(np, "startup-delay-us", &config->startup_delay); + + config->enable_high = of_property_read_bool(np, "enable-active-high"); + config->gpio_is_open_drain = of_property_read_bool(np, + "gpio-open-drain"); + + if (of_find_property(np, "vin-supply", NULL)) + config->input_supply = "vin"; + + return config; } static struct regulator_ops fixed_voltage_ops = { - .is_enabled = fixed_voltage_is_enabled, - .enable = fixed_voltage_enable, - .disable = fixed_voltage_disable, - .enable_time = fixed_voltage_enable_time, - .get_voltage = fixed_voltage_get_voltage, - .list_voltage = fixed_voltage_list_voltage, }; -static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) +static int reg_fixed_voltage_probe(struct platform_device *pdev) { - struct fixed_voltage_config *config = pdev->dev.platform_data; + struct fixed_voltage_config *config; struct fixed_voltage_data *drvdata; + struct regulator_config cfg = { }; int ret; - drvdata = kzalloc(sizeof(struct fixed_voltage_data), GFP_KERNEL); - if (drvdata == NULL) { - dev_err(&pdev->dev, "Failed to allocate device data\n"); - ret = -ENOMEM; - goto err; + if (pdev->dev.of_node) { + config = of_get_fixed_voltage_config(&pdev->dev); + if (IS_ERR(config)) + return PTR_ERR(config); + } else { + config = dev_get_platdata(&pdev->dev); } - drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL); + if (!config) + return -ENOMEM; + + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct fixed_voltage_data), + GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->desc.name = devm_kstrdup(&pdev->dev, + config->supply_name, + GFP_KERNEL); if (drvdata->desc.name == NULL) { dev_err(&pdev->dev, "Failed to allocate supply name\n"); - ret = -ENOMEM; - goto err; + return -ENOMEM; } drvdata->desc.type = REGULATOR_VOLTAGE; drvdata->desc.owner = THIS_MODULE; drvdata->desc.ops = &fixed_voltage_ops; - drvdata->desc.n_voltages = 1; - - drvdata->microvolts = config->microvolts; - drvdata->gpio = config->gpio; - drvdata->startup_delay = config->startup_delay; - - if (gpio_is_valid(config->gpio)) { - drvdata->enable_high = config->enable_high; - - /* FIXME: Remove below print warning - * - * config->gpio must be set to -EINVAL by platform code if - * GPIO control is not required. However, early adopters - * not requiring GPIO control may forget to initialize - * config->gpio to -EINVAL. This will cause GPIO 0 to be used - * for GPIO control. - * - * This warning will be removed once there are a couple of users - * for this driver. - */ - if (!config->gpio) - dev_warn(&pdev->dev, - "using GPIO 0 for regulator enable control\n"); - - ret = gpio_request(config->gpio, config->supply_name); - if (ret) { - dev_err(&pdev->dev, - "Could not obtain regulator enable GPIO %d: %d\n", - config->gpio, ret); - goto err_name; - } - /* set output direction without changing state - * to prevent glitch - */ - drvdata->is_enabled = config->enabled_at_boot; - ret = drvdata->is_enabled ? - config->enable_high : !config->enable_high; + drvdata->desc.enable_time = config->startup_delay; - ret = gpio_direction_output(config->gpio, ret); - if (ret) { + if (config->input_supply) { + drvdata->desc.supply_name = devm_kstrdup(&pdev->dev, + config->input_supply, + GFP_KERNEL); + if (!drvdata->desc.supply_name) { dev_err(&pdev->dev, - "Could not configure regulator enable GPIO %d direction: %d\n", - config->gpio, ret); - goto err_gpio; + "Failed to allocate input supply\n"); + return -ENOMEM; } + } + if (config->microvolts) + drvdata->desc.n_voltages = 1; + + drvdata->desc.fixed_uV = config->microvolts; + + if (config->gpio >= 0) + cfg.ena_gpio = config->gpio; + cfg.ena_gpio_invert = !config->enable_high; + if (config->enabled_at_boot) { + if (config->enable_high) + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; + else + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_LOW; } else { - /* Regulator without GPIO control is considered - * always enabled - */ - drvdata->is_enabled = true; + if (config->enable_high) + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_LOW; + else + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; } + if (config->gpio_is_open_drain) + cfg.ena_gpio_flags |= GPIOF_OPEN_DRAIN; + + cfg.dev = &pdev->dev; + cfg.init_data = config->init_data; + cfg.driver_data = drvdata; + cfg.of_node = pdev->dev.of_node; - drvdata->dev = regulator_register(&drvdata->desc, &pdev->dev, - config->init_data, drvdata); + drvdata->dev = devm_regulator_register(&pdev->dev, &drvdata->desc, + &cfg); if (IS_ERR(drvdata->dev)) { ret = PTR_ERR(drvdata->dev); dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); - goto err_gpio; + return ret; } platform_set_drvdata(pdev, drvdata); dev_dbg(&pdev->dev, "%s supplying %duV\n", drvdata->desc.name, - drvdata->microvolts); + drvdata->desc.fixed_uV); return 0; - -err_gpio: - if (gpio_is_valid(config->gpio)) - gpio_free(config->gpio); -err_name: - kfree(drvdata->desc.name); -err: - kfree(drvdata); - return ret; } -static int __devexit reg_fixed_voltage_remove(struct platform_device *pdev) -{ - struct fixed_voltage_data *drvdata = platform_get_drvdata(pdev); - - regulator_unregister(drvdata->dev); - if (gpio_is_valid(drvdata->gpio)) - gpio_free(drvdata->gpio); - kfree(drvdata->desc.name); - kfree(drvdata); - - return 0; -} +#if defined(CONFIG_OF) +static const struct of_device_id fixed_of_match[] = { + { .compatible = "regulator-fixed", }, + {}, +}; +MODULE_DEVICE_TABLE(of, fixed_of_match); +#endif static struct platform_driver regulator_fixed_voltage_driver = { .probe = reg_fixed_voltage_probe, - .remove = __devexit_p(reg_fixed_voltage_remove), .driver = { .name = "reg-fixed-voltage", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(fixed_of_match), }, }; |
