Merge branch '2020-05-18-reduce-size-of-common.h'
[oweals/u-boot.git] / drivers / power / regulator / regulator-uclass.c
index 2e0b5ed307f9e8447781336484258762a82b0905..7dcb3171928c4d6b515f005e38f029848a73ac83 100644 (file)
@@ -1,19 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * 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 <log.h>
 #include <dm/uclass-internal.h>
+#include <linux/delay.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;
@@ -38,10 +37,22 @@ int regulator_get_value(struct udevice *dev)
        return ops->get_value(dev);
 }
 
+static void regulator_set_value_ramp_delay(struct udevice *dev, int old_uV,
+                                          int new_uV, unsigned int ramp_delay)
+{
+       int delay = DIV_ROUND_UP(abs(new_uV - old_uV), ramp_delay);
+
+       debug("regulator %s: delay %u us (%d uV -> %d uV)\n", dev->name, delay,
+             old_uV, new_uV);
+
+       udelay(delay);
+}
+
 int regulator_set_value(struct udevice *dev, int uV)
 {
        const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
        struct dm_regulator_uclass_platdata *uc_pdata;
+       int ret, old_uV = uV, is_enabled = 0;
 
        uc_pdata = dev_get_uclass_platdata(dev);
        if (uc_pdata->min_uV != -ENODATA && uV < uc_pdata->min_uV)
@@ -52,7 +63,47 @@ int regulator_set_value(struct udevice *dev, int uV)
        if (!ops || !ops->set_value)
                return -ENOSYS;
 
-       return ops->set_value(dev, uV);
+       if (uc_pdata->ramp_delay) {
+               is_enabled = regulator_get_enable(dev);
+               old_uV = regulator_get_value(dev);
+       }
+
+       ret = ops->set_value(dev, uV);
+
+       if (!ret) {
+               if (uc_pdata->ramp_delay && old_uV > 0 && is_enabled)
+                       regulator_set_value_ramp_delay(dev, old_uV, uV,
+                                                      uc_pdata->ramp_delay);
+       }
+
+       return ret;
+}
+
+int regulator_set_suspend_value(struct udevice *dev, int uV)
+{
+       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+       struct dm_regulator_uclass_platdata *uc_pdata;
+
+       uc_pdata = dev_get_uclass_platdata(dev);
+       if (uc_pdata->min_uV != -ENODATA && uV < uc_pdata->min_uV)
+               return -EINVAL;
+       if (uc_pdata->max_uV != -ENODATA && uV > uc_pdata->max_uV)
+               return -EINVAL;
+
+       if (!ops->set_suspend_value)
+               return -ENOSYS;
+
+       return ops->set_suspend_value(dev, uV);
+}
+
+int regulator_get_suspend_value(struct udevice *dev)
+{
+       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+       if (!ops->get_suspend_value)
+               return -ENOSYS;
+
+       return ops->get_suspend_value(dev);
 }
 
 /*
@@ -96,7 +147,7 @@ int regulator_set_current(struct udevice *dev, int uA)
        return ops->set_current(dev, uA);
 }
 
-bool regulator_get_enable(struct udevice *dev)
+int regulator_get_enable(struct udevice *dev)
 {
        const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
 
@@ -109,11 +160,63 @@ bool regulator_get_enable(struct udevice *dev)
 int regulator_set_enable(struct udevice *dev, bool enable)
 {
        const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+       struct dm_regulator_uclass_platdata *uc_pdata;
+       int ret, old_enable = 0;
 
        if (!ops || !ops->set_enable)
                return -ENOSYS;
 
-       return ops->set_enable(dev, enable);
+       uc_pdata = dev_get_uclass_platdata(dev);
+       if (!enable && uc_pdata->always_on)
+               return -EACCES;
+
+       if (uc_pdata->ramp_delay)
+               old_enable = regulator_get_enable(dev);
+
+       ret = ops->set_enable(dev, enable);
+       if (!ret) {
+               if (uc_pdata->ramp_delay && !old_enable && enable) {
+                       int uV = regulator_get_value(dev);
+
+                       if (uV > 0) {
+                               regulator_set_value_ramp_delay(dev, 0, uV,
+                                                              uc_pdata->ramp_delay);
+                       }
+               }
+       }
+
+       return ret;
+}
+
+int regulator_set_enable_if_allowed(struct udevice *dev, bool enable)
+{
+       int ret;
+
+       ret = regulator_set_enable(dev, enable);
+       if (ret == -ENOSYS || ret == -EACCES)
+               return 0;
+
+       return ret;
+}
+
+int regulator_set_suspend_enable(struct udevice *dev, bool enable)
+{
+       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+       if (!ops->set_suspend_enable)
+               return -ENOSYS;
+
+       return ops->set_suspend_enable(dev, enable);
+}
+
+int regulator_get_suspend_enable(struct udevice *dev)
+{
+       const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+       if (!ops->get_suspend_enable)
+               return -ENOSYS;
+
+       return ops->get_suspend_enable(dev);
 }
 
 int regulator_get_mode(struct udevice *dev)
@@ -146,8 +249,10 @@ int regulator_get_by_platname(const char *plat_name, struct udevice **devp)
 
        for (ret = uclass_find_first_device(UCLASS_REGULATOR, &dev); dev;
             ret = uclass_find_next_device(&dev)) {
-               if (ret)
+               if (ret) {
+                       debug("regulator %s, ret=%d\n", dev->name, ret);
                        continue;
+               }
 
                uc_pdata = dev_get_uclass_platdata(dev);
                if (!uc_pdata || strcmp(plat_name, uc_pdata->name))
@@ -156,7 +261,7 @@ int regulator_get_by_platname(const char *plat_name, struct udevice **devp)
                return uclass_get_device_tail(dev, 0, devp);
        }
 
-       debug("%s: can't find: %s\n", __func__, plat_name);
+       debug("%s: can't find: %s, ret=%d\n", __func__, plat_name, ret);
 
        return -ENODEV;
 }
@@ -179,11 +284,24 @@ int regulator_autoset(struct udevice *dev)
        int ret = 0;
 
        uc_pdata = dev_get_uclass_platdata(dev);
+
+       ret = regulator_set_suspend_enable(dev, uc_pdata->suspend_on);
+       if (!ret && uc_pdata->suspend_on) {
+               ret = regulator_set_suspend_value(dev, uc_pdata->suspend_uV);
+               if (!ret)
+                       return ret;
+       }
+
        if (!uc_pdata->always_on && !uc_pdata->boot_on)
                return -EMEDIUMTYPE;
 
+       if (uc_pdata->type == REGULATOR_TYPE_FIXED)
+               return regulator_set_enable(dev, true);
+
        if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV)
                ret = regulator_set_value(dev, uc_pdata->min_uV);
+       if (uc_pdata->init_uV > 0)
+               ret = regulator_set_value(dev, uc_pdata->init_uV);
        if (!ret && (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA))
                ret = regulator_set_current(dev, uc_pdata->min_uA);
 
@@ -219,7 +337,7 @@ int regulator_autoset_by_name(const char *platname, struct udevice **devp)
        if (devp)
                *devp = dev;
        if (ret) {
-               debug("Can get the regulator: %s!", platname);
+               debug("Can get the regulator: %s (err=%d)\n", platname, ret);
                return ret;
        }
 
@@ -278,20 +396,16 @@ static bool regulator_name_is_unique(struct udevice *check_dev,
 static int regulator_post_bind(struct udevice *dev)
 {
        struct dm_regulator_uclass_platdata *uc_pdata;
-       int offset = dev_of_offset(dev);
-       const void *blob = gd->fdt_blob;
        const char *property = "regulator-name";
 
        uc_pdata = dev_get_uclass_platdata(dev);
-       if (!uc_pdata)
-               return -ENXIO;
 
        /* Regulator's mandatory constraint */
-       uc_pdata->name = fdt_getprop(blob, offset, property, NULL);
+       uc_pdata->name = dev_read_string(dev, property);
        if (!uc_pdata->name) {
-               debug("%s: dev: %s has no property 'regulator-name'\n",
-                     __func__, dev->name);
-               uc_pdata->name = fdt_get_name(blob, offset, NULL);
+               debug("%s: dev '%s' has no property '%s'\n",
+                     __func__, dev->name, property);
+               uc_pdata->name = dev_read_name(dev);
                if (!uc_pdata->name)
                        return -EINVAL;
        }
@@ -299,7 +413,7 @@ static int regulator_post_bind(struct udevice *dev)
        if (regulator_name_is_unique(dev, uc_pdata->name))
                return 0;
 
-       debug("\"%s\" of dev: \"%s\", has nonunique value: \"%s\"",
+       debug("'%s' of dev: '%s', has nonunique value: '%s\n",
              property, dev->name, uc_pdata->name);
 
        return -EINVAL;
@@ -308,25 +422,37 @@ static int regulator_post_bind(struct udevice *dev)
 static int regulator_pre_probe(struct udevice *dev)
 {
        struct dm_regulator_uclass_platdata *uc_pdata;
-       int offset = dev_of_offset(dev);
+       ofnode node;
 
        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");
+       uc_pdata->min_uV = dev_read_u32_default(dev, "regulator-min-microvolt",
+                                               -ENODATA);
+       uc_pdata->max_uV = dev_read_u32_default(dev, "regulator-max-microvolt",
+                                               -ENODATA);
+       uc_pdata->init_uV = dev_read_u32_default(dev, "regulator-init-microvolt",
+                                                -ENODATA);
+       uc_pdata->min_uA = dev_read_u32_default(dev, "regulator-min-microamp",
+                                               -ENODATA);
+       uc_pdata->max_uA = dev_read_u32_default(dev, "regulator-max-microamp",
+                                               -ENODATA);
+       uc_pdata->always_on = dev_read_bool(dev, "regulator-always-on");
+       uc_pdata->boot_on = dev_read_bool(dev, "regulator-boot-on");
+       uc_pdata->ramp_delay = dev_read_u32_default(dev, "regulator-ramp-delay",
+                                                   0);
+
+       node = dev_read_subnode(dev, "regulator-state-mem");
+       if (ofnode_valid(node)) {
+               uc_pdata->suspend_on = !ofnode_read_bool(node, "regulator-off-in-suspend");
+               if (ofnode_read_u32(node, "regulator-suspend-microvolt", &uc_pdata->suspend_uV))
+                       uc_pdata->suspend_uV = uc_pdata->max_uV;
+       } else {
+               uc_pdata->suspend_on = true;
+               uc_pdata->suspend_uV = uc_pdata->max_uV;
+       }
 
        /* Those values are optional (-ENODATA if unset) */
        if ((uc_pdata->min_uV != -ENODATA) &&
@@ -340,6 +466,9 @@ static int regulator_pre_probe(struct udevice *dev)
            (uc_pdata->min_uA == uc_pdata->max_uA))
                uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UA;
 
+       if (uc_pdata->boot_on)
+               regulator_set_enable(dev, uc_pdata->boot_on);
+
        return 0;
 }