diff --git a/commands/Kconfig b/commands/Kconfig index cc014f30a..510cc910e 100644 --- a/commands/Kconfig +++ b/commands/Kconfig @@ -690,6 +690,14 @@ config CMD_GPIO include gpio_set_value, gpio_get_value, gpio_direction_input and gpio_direction_output commands to control gpios. +config CMD_REGULATOR + bool + depends on REGULATOR + prompt "regulator command" + help + the regulator command lists the currently registered regulators and + their current state. + config CMD_UNCOMPRESS bool select UNCOMPRESS diff --git a/commands/Makefile b/commands/Makefile index e46303145..7836515b3 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -95,3 +95,4 @@ obj-$(CONFIG_CMD_BOOT) += boot.o obj-$(CONFIG_CMD_DEVINFO) += devinfo.o obj-$(CONFIG_CMD_READF) += readf.o obj-$(CONFIG_CMD_MENUTREE) += menutree.o +obj-$(CONFIG_CMD_REGULATOR) += regulator.o diff --git a/commands/regulator.c b/commands/regulator.c new file mode 100644 index 000000000..42dcd0ab3 --- /dev/null +++ b/commands/regulator.c @@ -0,0 +1,33 @@ +/* + * regulator command + * + * Copyright (c) 2014 Sascha Hauer , 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 +#include +#include + +static int do_regulator(int argc, char *argv[]) +{ + regulators_print(); + + return 0; +} + +BAREBOX_CMD_START(regulator) + .cmd = do_regulator, + .usage = "list regulators", +BAREBOX_CMD_END diff --git a/drivers/Kconfig b/drivers/Kconfig index d34d2c744..7a2aa2810 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -25,5 +25,6 @@ source "drivers/gpio/Kconfig" source "drivers/w1/Kconfig" source "drivers/pinctrl/Kconfig" source "drivers/bus/Kconfig" +source "drivers/regulator/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index ba1dc6df7..f79da89e9 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_OFTREE) += of/ obj-$(CONFIG_W1) += w1/ obj-y += pinctrl/ obj-y += bus/ +obj-$(CONFIG_REGULATOR) += regulator/ diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig new file mode 100644 index 000000000..55f87a45b --- /dev/null +++ b/drivers/regulator/Kconfig @@ -0,0 +1,13 @@ +menuconfig REGULATOR + depends on OFDEVICE + bool "voltage regulator support" + +if REGULATOR + +config REGULATOR_FIXED + bool "fixed/gpio regulator" + help + This enables a simple fixed regulator. It is used for regulators + which are not software controllable or controllable via gpio. + +endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile new file mode 100644 index 000000000..65e65d815 --- /dev/null +++ b/drivers/regulator/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_REGULATOR) += core.o +obj-$(CONFIG_REGULATOR_FIXED) += fixed.o diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c new file mode 100644 index 000000000..2808c27eb --- /dev/null +++ b/drivers/regulator/core.c @@ -0,0 +1,262 @@ +/* + * barebox regulator support + * + * Copyright (c) 2014 Sascha Hauer , 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 +#include +#include +#include +#include + +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); +} diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c new file mode 100644 index 000000000..7e649199b --- /dev/null +++ b/drivers/regulator/fixed.c @@ -0,0 +1,103 @@ +/* + * fixed regulator support + * + * Copyright (c) 2014 Sascha Hauer , 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 +#include +#include +#include +#include +#include +#include + +struct regulator_fixed { + int gpio; + int active_low; + struct regulator_dev rdev; +}; + +static int regulator_fixed_enable(struct regulator_dev *rdev) +{ + struct regulator_fixed *fix = container_of(rdev, struct regulator_fixed, rdev); + + if (!gpio_is_valid(fix->gpio)) + return 0; + + return gpio_direction_output(fix->gpio, !fix->active_low); +} + +static int regulator_fixed_disable(struct regulator_dev *rdev) +{ + struct regulator_fixed *fix = container_of(rdev, struct regulator_fixed, rdev); + + if (!gpio_is_valid(fix->gpio)) + return 0; + + return gpio_direction_output(fix->gpio, fix->active_low); +} + +static struct regulator_ops fixed_ops = { + .enable = regulator_fixed_enable, + .disable = regulator_fixed_disable, +}; + +static int regulator_fixed_probe(struct device_d *dev) +{ + struct regulator_fixed *fix; + enum of_gpio_flags gpioflags; + int ret; + + if (!dev->device_node) + return -EINVAL; + + fix = xzalloc(sizeof(*fix)); + + if (of_get_property(dev->device_node, "gpio", NULL)) { + fix->gpio = of_get_named_gpio_flags(dev->device_node, "gpio", 0, &gpioflags); + if (fix->gpio < 0) { + ret = fix->gpio; + goto err; + } + + if (gpioflags & OF_GPIO_ACTIVE_LOW) + fix->active_low = 1; + } + + fix->rdev.ops = &fixed_ops; + + ret = of_regulator_register(&fix->rdev, dev->device_node); + if (ret) + return ret; + + return 0; +err: + free(fix); + + return ret; +} + +static struct of_device_id regulator_fixed_of_ids[] = { + { .compatible = "regulator-fixed", }, + { } +}; + +static struct driver_d regulator_fixed_driver = { + .name = "regulator-fixed", + .probe = regulator_fixed_probe, + .of_compatible = DRV_OF_COMPAT(regulator_fixed_of_ids), +}; +coredevice_platform_driver(regulator_fixed_driver); diff --git a/include/regulator.h b/include/regulator.h new file mode 100644 index 000000000..26a5e5604 --- /dev/null +++ b/include/regulator.h @@ -0,0 +1,47 @@ +#ifndef __REGULATOR_H +#define __REGULATOR_H + +/* struct regulator is an opaque object for consumers */ +struct regulator; + +struct regulator_dev { + struct regulator_ops *ops; +}; + +struct regulator_ops { + /* enable/disable regulator */ + int (*enable) (struct regulator_dev *); + int (*disable) (struct regulator_dev *); + int (*is_enabled) (struct regulator_dev *); +}; + +int of_regulator_register(struct regulator_dev *rd, struct device_node *node); + +void regulators_print(void); + +#ifdef CONFIG_REGULATOR + +struct regulator *regulator_get(struct device_d *, const char *); +int regulator_enable(struct regulator *); +int regulator_disable(struct regulator *); + +#else + +static inline struct regulator *regulator_get(struct device_d *dev, const char *id) +{ + return NULL; +} + +static inline int regulator_enable(struct regulator *r) +{ + return 0; +} + +static inline int regulator_disable(struct regulator *r) +{ + return 0; +} + +#endif + +#endif /* __REGULATOR_H */