diff options
Diffstat (limited to 'drivers/regulator/core.c')
-rw-r--r-- | drivers/regulator/core.c | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c new file mode 100644 index 0000000000..2808c27eba --- /dev/null +++ b/drivers/regulator/core.c @@ -0,0 +1,262 @@ +/* + * barebox regulator support + * + * Copyright (c) 2014 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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. + * + */ +#include <common.h> +#include <regulator.h> +#include <of.h> +#include <malloc.h> +#include <linux/err.h> + +static LIST_HEAD(regulator_list); + +struct regulator_internal { + struct list_head list; + struct device_node *node; + struct regulator_dev *rdev; + int enable_count; + int enable_time_us; + int min_uv; + int max_uv; + char *name; + struct list_head consumer_list; +}; + +struct regulator { + struct regulator_internal *ri; + struct list_head list; + struct device_d *dev; +}; + +/* + * of_regulator_register - register a regulator corresponding to a device_node + * @rd: the regulator device providing the ops + * @node: the device_node this regulator corresponds to + * + * Return: 0 for success or a negative error code + */ +int of_regulator_register(struct regulator_dev *rd, struct device_node *node) +{ + struct regulator_internal *ri; + const char *name; + + ri = xzalloc(sizeof(*ri)); + ri->rdev = rd; + ri->node = node; + + INIT_LIST_HEAD(&ri->consumer_list); + + list_add_tail(&ri->list, ®ulator_list); + + name = of_get_property(node, "regulator-name", NULL); + + if (name) + ri->name = xstrdup(name); + + of_property_read_u32(node, "regulator-enable-ramp-delay", + &ri->enable_time_us); + of_property_read_u32(node, "regulator-min-microvolt", + &ri->min_uv); + of_property_read_u32(node, "regulator-max-microvolt", + &ri->max_uv); + + return 0; +} + +static struct regulator_internal *of_regulator_get(struct device_d *dev, const char *supply) +{ + char *propname; + struct regulator_internal *ri; + struct device_node *node; + + propname = asprintf("%s-supply", supply); + + /* + * If the device does have a device node return the dummy regulator. + */ + if (!dev->device_node) + return NULL; + + /* + * If the device node does not contain a supply property, this device doesn't + * need a regulator. Return the dummy regulator in this case. + */ + if (!of_get_property(dev->device_node, propname, NULL)) { + dev_dbg(dev, "No %s-supply node found, using dummy regulator\n", + supply); + ri = NULL; + goto out; + } + + /* + * The device node specifies a supply, so it's mandatory. Return an error when + * something goes wrong below. + */ + node = of_parse_phandle(dev->device_node, propname, 0); + if (!node) { + dev_dbg(dev, "No %s node found\n", propname); + ri = ERR_PTR(-EINVAL); + goto out; + } + + list_for_each_entry(ri, ®ulator_list, list) { + if (ri->node == node) { + dev_dbg(dev, "Using %s regulator from %s\n", + propname, node->full_name); + goto out; + } + } + + ri = ERR_PTR(-ENODEV); +out: + free(propname); + + return ri; +} + +/* + * regulator_get - get the supply for a device. + * @dev: the device a supply is requested for + * @supply: the supply name + * + * This returns a supply for a device. Check the result with IS_ERR(). + * NULL is a valid regulator, the dummy regulator. + * + * Return: a regulator object or an error pointer + */ +struct regulator *regulator_get(struct device_d *dev, const char *supply) +{ + struct regulator_internal *ri; + struct regulator *r; + + if (!dev->device_node) + return NULL; + + ri = of_regulator_get(dev, supply); + if (IS_ERR(ri)) + return ERR_CAST(ri); + + if (!ri) + return NULL; + + r = xzalloc(sizeof(*r)); + r->ri = ri; + r->dev = dev; + + list_add_tail(&r->list, &ri->consumer_list); + + return r; +} + +/* + * regulator_enable - enable a regulator. + * @r: the regulator to enable + * + * This enables a regulator. Regulators are reference counted, only the + * first enable operation will enable the regulator. + * + * Return: 0 for success or a negative error code + */ +int regulator_enable(struct regulator *r) +{ + struct regulator_internal *ri; + int ret; + + if (!r) + return 0; + + ri = r->ri; + + if (ri->enable_count) { + ri->enable_count++; + return 0; + } + + if (!ri->rdev->ops->enable) + return -ENOSYS; + + ret = ri->rdev->ops->enable(ri->rdev); + if (ret) + return ret; + + if (ri->enable_time_us) + udelay(ri->enable_time_us); + + ri->enable_count++; + + return 0; +} + +/* + * regulator_disable - disable a regulator. + * @r: the regulator to disable + * + * This disables a regulator. Regulators are reference counted, only the + * when balanced with regulator_enable the regulator will be disabled. + * + * Return: 0 for success or a negative error code + */ +int regulator_disable(struct regulator *r) +{ + struct regulator_internal *ri; + int ret; + + if (!r) + return 0; + + ri = r->ri; + + if (!ri->enable_count) + return -EINVAL; + + if (!ri->rdev->ops->disable) + return -ENOSYS; + + ret = ri->rdev->ops->disable(ri->rdev); + if (ret) + return ret; + + ri->enable_count--; + + return 0; +} + +static void regulator_print_one(struct regulator_internal *ri) +{ + struct regulator *r; + + printf("%-20s %6d %10d %10d\n", ri->name, ri->enable_count, ri->min_uv, ri->max_uv); + + if (!list_empty(&ri->consumer_list)) { + printf(" consumers:\n"); + + list_for_each_entry(r, &ri->consumer_list, list) + printf(" %s\n", dev_name(r->dev)); + } +} + +/* + * regulators_print - print informations about all regulators + */ +void regulators_print(void) +{ + struct regulator_internal *ri; + + printf("%-20s %6s %10s %10s\n", "name", "enable", "min_uv", "max_uv"); + list_for_each_entry(ri, ®ulator_list, list) + regulator_print_one(ri); +} |