summaryrefslogtreecommitdiffstats
path: root/drivers/regulator
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2014-04-08 12:49:33 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2014-04-29 08:01:32 +0200
commit485788954c1c4be5f64be143228fc54ec41c00f0 (patch)
tree7f2f7034af557314ceced91bd6ed7a75af33456f /drivers/regulator
parent0af79fbb6779921d3f1962773adb7fb57d3c89d4 (diff)
downloadbarebox-485788954c1c4be5f64be143228fc54ec41c00f0.tar.gz
Add initial regulator support
Provide minimal regulator support. Only supported operations are enabling and disabling regulators. Association of devices with their regulators is limited to devicetree only. If regulator support is disabled the API expands to static inline stubs so consumers can still use the API. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/regulator')
-rw-r--r--drivers/regulator/Kconfig13
-rw-r--r--drivers/regulator/Makefile2
-rw-r--r--drivers/regulator/core.c262
-rw-r--r--drivers/regulator/fixed.c103
4 files changed, 380 insertions, 0 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
new file mode 100644
index 0000000..55f87a4
--- /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 0000000..65e65d8
--- /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 0000000..2808c27
--- /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, &regulator_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, &regulator_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, &regulator_list, list)
+ regulator_print_one(ri);
+}
diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c
new file mode 100644
index 0000000..7e64919
--- /dev/null
+++ b/drivers/regulator/fixed.c
@@ -0,0 +1,103 @@
+/*
+ * fixed 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 <malloc.h>
+#include <init.h>
+#include <regulator.h>
+#include <of.h>
+#include <of_gpio.h>
+#include <gpio.h>
+
+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);