dm: regulator: add implementation of driver model regulator uclass

This commit introduces the implementation of dm regulator API.
Device tree support allows for auto binding. And by the basic
uclass operations, it allows to driving the devices in a common
way. For detailed informations, please look into the header file.

Core files:
- drivers/power/regulator-uclass.c - provides regulator common functions api
- include/power/regulator.h - define all structures required by the regulator

Changes:
- new uclass-id: UCLASS_REGULATOR
- new config: CONFIG_DM_REGULATOR

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
Acked-by: Simon Glass <sjg@chromium.org>
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index d03626e..23cdd71 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -2,6 +2,8 @@
 
 source "drivers/power/pmic/Kconfig"
 
+source "drivers/power/regulator/Kconfig"
+
 config AXP221_POWER
 	boolean "axp221 / axp223 pmic support"
 	depends on MACH_SUN6I || MACH_SUN8I
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
new file mode 100644
index 0000000..cb15162
--- /dev/null
+++ b/drivers/power/regulator/Kconfig
@@ -0,0 +1,17 @@
+config DM_REGULATOR
+	bool "Enable Driver Model for REGULATOR drivers (UCLASS_REGULATOR)"
+	depends on DM
+	---help---
+	This config enables the driver model regulator support.
+	UCLASS_REGULATOR - designed to provide a common API for basic regulator's
+	functions, like get/set Voltage or Current value, enable state, etc...
+	Note:
+	When enabling this, please read the description, found in the files:
+	- 'include/power/pmic.h'
+	- 'include/power/regulator.h'
+	- 'drivers/power/pmic/pmic-uclass.c'
+	- 'drivers/power/pmic/regulator-uclass.c'
+	It's important to call the device_bind() with the proper node offset,
+	when binding the regulator devices. The pmic_bind_childs() can be used
+	for this purpose if PMIC I/O driver is implemented or dm_scan_fdt_node()
+	otherwise. Detailed informations can be found in the header file.
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
new file mode 100644
index 0000000..27c9006
--- /dev/null
+++ b/drivers/power/regulator/Makefile
@@ -0,0 +1,8 @@
+#
+# Copyright (C) 2015 Samsung Electronics
+# Przemyslaw Marczak <p.marczak@samsung.com>
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+
+obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c
new file mode 100644
index 0000000..07ce286
--- /dev/null
+++ b/drivers/power/regulator/regulator-uclass.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/uclass-internal.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+
+	*modep = NULL;
+
+	uc_pdata = dev_get_uclass_platdata(dev);
+	if (!uc_pdata)
+		return -ENXIO;
+
+	*modep = uc_pdata->mode;
+	return uc_pdata->mode_count;
+}
+
+int regulator_get_value(struct udevice *dev)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->get_value)
+		return -ENOSYS;
+
+	return ops->get_value(dev);
+}
+
+int regulator_set_value(struct udevice *dev, int uV)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->set_value)
+		return -ENOSYS;
+
+	return ops->set_value(dev, uV);
+}
+
+int regulator_get_current(struct udevice *dev)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->get_current)
+		return -ENOSYS;
+
+	return ops->get_current(dev);
+}
+
+int regulator_set_current(struct udevice *dev, int uA)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->set_current)
+		return -ENOSYS;
+
+	return ops->set_current(dev, uA);
+}
+
+bool regulator_get_enable(struct udevice *dev)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->get_enable)
+		return -ENOSYS;
+
+	return ops->get_enable(dev);
+}
+
+int regulator_set_enable(struct udevice *dev, bool enable)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->set_enable)
+		return -ENOSYS;
+
+	return ops->set_enable(dev, enable);
+}
+
+int regulator_get_mode(struct udevice *dev)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->get_mode)
+		return -ENOSYS;
+
+	return ops->get_mode(dev);
+}
+
+int regulator_set_mode(struct udevice *dev, int mode)
+{
+	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+	if (!ops || !ops->set_mode)
+		return -ENOSYS;
+
+	return ops->set_mode(dev, mode);
+}
+
+int regulator_by_platname(const char *plat_name, struct udevice **devp)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	struct udevice *dev;
+
+	*devp = NULL;
+
+	for (uclass_find_first_device(UCLASS_REGULATOR, &dev);
+	     dev;
+	     uclass_find_next_device(&dev)) {
+		uc_pdata = dev_get_uclass_platdata(dev);
+		if (!uc_pdata || strcmp(plat_name, uc_pdata->name))
+			continue;
+
+		return uclass_get_device_tail(dev, 0, devp);
+	}
+
+	debug("%s: can't find: %s\n", __func__, plat_name);
+
+	return -ENODEV;
+}
+
+int regulator_by_devname(const char *devname, struct udevice **devp)
+{
+	return uclass_get_device_by_name(UCLASS_REGULATOR, devname, devp);
+}
+
+static int setting_failed(int ret, bool verbose, const char *fmt, ...)
+{
+	va_list args;
+	char buf[64];
+
+	if (verbose == false)
+		return ret;
+
+	va_start(args, fmt);
+	vscnprintf(buf, sizeof(buf), fmt, args);
+	va_end(args);
+
+	printf(buf);
+
+	if (!ret)
+		return 0;
+
+	printf(" (ret: %d)", ret);
+
+	return ret;
+}
+
+int regulator_by_platname_autoset_and_enable(const char *platname,
+					     struct udevice **devp,
+					     bool verbose)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	struct udevice *dev;
+	bool v = verbose;
+	int ret;
+
+	if (devp)
+		*devp = NULL;
+
+	ret = regulator_by_platname(platname, &dev);
+	if (ret) {
+		error("Can get the regulator: %s!", platname);
+		return ret;
+	}
+
+	uc_pdata = dev_get_uclass_platdata(dev);
+	if (!uc_pdata) {
+		error("Can get the regulator %s uclass platdata!", platname);
+		return -ENXIO;
+	}
+
+	if (v)
+		printf("%s@%s: ", dev->name, uc_pdata->name);
+
+	/* Those values are optional (-ENODATA if unset) */
+	if ((uc_pdata->min_uV != -ENODATA) &&
+	    (uc_pdata->max_uV != -ENODATA) &&
+	    (uc_pdata->min_uV == uc_pdata->max_uV)) {
+		ret = regulator_set_value(dev, uc_pdata->min_uV);
+		if (setting_failed(ret, v, "set %d uV", uc_pdata->min_uV))
+			goto exit;
+	}
+
+	/* Those values are optional (-ENODATA if unset) */
+	if ((uc_pdata->min_uA != -ENODATA) &&
+	    (uc_pdata->max_uA != -ENODATA) &&
+	    (uc_pdata->min_uA == uc_pdata->max_uA)) {
+		ret = regulator_set_current(dev, uc_pdata->min_uA);
+		if (setting_failed(ret, v, "; set %d uA", uc_pdata->min_uA))
+			goto exit;
+	}
+
+	if (!uc_pdata->always_on && !uc_pdata->boot_on)
+		goto retdev;
+
+	ret = regulator_set_enable(dev, true);
+	if (setting_failed(ret, v, "; enabling", uc_pdata->min_uA))
+		goto exit;
+
+retdev:
+	if (devp)
+		*devp = dev;
+exit:
+	if (v)
+		printf("\n");
+	return ret;
+}
+
+int regulator_by_platname_list_autoset_and_enable(const char *list_platname[],
+						  int list_entries,
+						  struct udevice *list_devp[],
+						  bool verbose)
+{
+	struct udevice *dev;
+	int i, ret, success = 0;
+
+	for (i = 0; i < list_entries; i++) {
+		ret = regulator_autoset(list_platname[i], &dev, verbose);
+		if (!ret)
+			success++;
+
+		if (!list_devp)
+			continue;
+
+		if (ret)
+			list_devp[i] = NULL;
+		else
+			list_devp[i] = dev;
+	}
+
+	return (success != list_entries);
+}
+
+static int regulator_post_bind(struct udevice *dev)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	int offset = dev->of_offset;
+	const void *blob = gd->fdt_blob;
+
+	uc_pdata = dev_get_uclass_platdata(dev);
+	if (!uc_pdata)
+		return -ENXIO;
+
+	/* Regulator's mandatory constraint */
+	uc_pdata->name = fdt_getprop(blob, offset, "regulator-name", NULL);
+	if (!uc_pdata->name) {
+		debug("%s: dev: %s has no property 'regulator-name'\n",
+		      __func__, dev->name);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static int regulator_pre_probe(struct udevice *dev)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	int offset = dev->of_offset;
+
+	uc_pdata = dev_get_uclass_platdata(dev);
+	if (!uc_pdata)
+		return -ENXIO;
+
+	/* Regulator's optional constraints */
+	uc_pdata->min_uV = fdtdec_get_int(gd->fdt_blob, offset,
+					  "regulator-min-microvolt", -ENODATA);
+	uc_pdata->max_uV = fdtdec_get_int(gd->fdt_blob, offset,
+					  "regulator-max-microvolt", -ENODATA);
+	uc_pdata->min_uA = fdtdec_get_int(gd->fdt_blob, offset,
+					  "regulator-min-microamp", -ENODATA);
+	uc_pdata->max_uA = fdtdec_get_int(gd->fdt_blob, offset,
+					  "regulator-max-microamp", -ENODATA);
+	uc_pdata->always_on = fdtdec_get_bool(gd->fdt_blob, offset,
+					      "regulator-always-on");
+	uc_pdata->boot_on = fdtdec_get_bool(gd->fdt_blob, offset,
+					    "regulator-boot-on");
+
+	return 0;
+}
+
+UCLASS_DRIVER(regulator) = {
+	.id		= UCLASS_REGULATOR,
+	.name		= "regulator",
+	.post_bind	= regulator_post_bind,
+	.pre_probe	= regulator_pre_probe,
+	.per_device_platdata_auto_alloc_size =
+				sizeof(struct dm_regulator_uclass_platdata),
+};