summaryrefslogtreecommitdiff
path: root/drivers/power/domain/power-domain-uclass.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power/domain/power-domain-uclass.c')
-rw-r--r--drivers/power/domain/power-domain-uclass.c90
1 files changed, 84 insertions, 6 deletions
diff --git a/drivers/power/domain/power-domain-uclass.c b/drivers/power/domain/power-domain-uclass.c
index 938bd8cbc9f..d9fa8ad4bd2 100644
--- a/drivers/power/domain/power-domain-uclass.c
+++ b/drivers/power/domain/power-domain-uclass.c
@@ -12,6 +12,10 @@
#include <power-domain-uclass.h>
#include <dm/device-internal.h>
+struct power_domain_priv {
+ int *on_count;
+};
+
static inline struct power_domain_ops *power_domain_dev_ops(struct udevice *dev)
{
return (struct power_domain_ops *)dev->driver->ops;
@@ -107,22 +111,67 @@ int power_domain_free(struct power_domain *power_domain)
return ops->rfree ? ops->rfree(power_domain) : 0;
}
-int power_domain_on(struct power_domain *power_domain)
+int power_domain_on_lowlevel(struct power_domain *power_domain)
{
+ struct power_domain_priv *priv = dev_get_uclass_priv(power_domain->dev);
+ struct power_domain_plat *plat = dev_get_uclass_plat(power_domain->dev);
struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
+ int *on_count = plat->subdomains ? &priv->on_count[power_domain->id] : NULL;
+ int ret;
- debug("%s(power_domain=%p)\n", __func__, power_domain);
+ /* Refcounting is not enabled on all drivers by default */
+ if (on_count) {
+ debug("Enable power domain %s.%ld: %d -> %d (%s)\n",
+ power_domain->dev->name, power_domain->id, *on_count, *on_count + 1,
+ (((*on_count + 1) > 1) ? "EALREADY" : "todo"));
+
+ (*on_count)++;
+ if (*on_count > 1)
+ return -EALREADY;
+ }
+
+ ret = ops->on ? ops->on(power_domain) : 0;
+ if (ret) {
+ if (on_count)
+ (*on_count)--;
+ return ret;
+ }
- return ops->on ? ops->on(power_domain) : 0;
+ return 0;
}
-int power_domain_off(struct power_domain *power_domain)
+int power_domain_off_lowlevel(struct power_domain *power_domain)
{
+ struct power_domain_priv *priv = dev_get_uclass_priv(power_domain->dev);
+ struct power_domain_plat *plat = dev_get_uclass_plat(power_domain->dev);
struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
+ int *on_count = plat->subdomains ? &priv->on_count[power_domain->id] : NULL;
+ int ret;
- debug("%s(power_domain=%p)\n", __func__, power_domain);
+ /* Refcounting is not enabled on all drivers by default */
+ if (on_count) {
+ debug("Disable power domain %s.%ld: %d -> %d (%s%s)\n",
+ power_domain->dev->name, power_domain->id, *on_count, *on_count - 1,
+ (((*on_count) <= 0) ? "EALREADY" : ""),
+ (((*on_count - 1) > 0) ? "BUSY" : "todo"));
+
+ if (*on_count <= 0)
+ return -EALREADY;
+
+ (*on_count)--;
+ if (*on_count > 0)
+ return -EBUSY;
+ }
+
+ ret = ops->off ? ops->off(power_domain) : 0;
+ if (ret) {
+ if (on_count)
+ (*on_count)++;
- return ops->off ? ops->off(power_domain) : 0;
+ return ret;
+ }
+
+ return 0;
}
#if CONFIG_IS_ENABLED(OF_REAL)
@@ -177,7 +226,36 @@ int dev_power_domain_off(struct udevice *dev)
}
#endif /* OF_REAL */
+static int power_domain_post_probe(struct udevice *dev)
+{
+ struct power_domain_priv *priv = dev_get_uclass_priv(dev);
+ struct power_domain_plat *plat = dev_get_uclass_plat(dev);
+
+ if (plat->subdomains) {
+ priv->on_count = calloc(sizeof(int), plat->subdomains);
+ if (!priv->on_count)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int power_domain_pre_remove(struct udevice *dev)
+{
+ struct power_domain_priv *priv = dev_get_uclass_priv(dev);
+ struct power_domain_plat *plat = dev_get_uclass_plat(dev);
+
+ if (plat->subdomains)
+ free(priv->on_count);
+
+ return 0;
+}
+
UCLASS_DRIVER(power_domain) = {
.id = UCLASS_POWER_DOMAIN,
.name = "power_domain",
+ .post_probe = power_domain_post_probe,
+ .pre_remove = power_domain_pre_remove,
+ .per_device_auto = sizeof(struct power_domain_priv),
+ .per_device_plat_auto = sizeof(struct power_domain_plat),
};