Add a power domain framework/uclass

Many SoCs allow power to be applied to or removed from portions of the SoC
(power domains). This may be used to save power. This API provides the
means to control such power management hardware.

Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Stephen Warren
2016-07-13 13:45:31 -06:00
committed by Simon Glass
parent 1e2b3ef865
commit 61f5ddcb7a
15 changed files with 584 additions and 0 deletions

View File

@@ -1,5 +1,7 @@
menu "Power"
source "drivers/power/domain/Kconfig"
source "drivers/power/pmic/Kconfig"
source "drivers/power/regulator/Kconfig"

View File

@@ -0,0 +1,20 @@
menu "Power Domain Support"
config POWER_DOMAIN
bool "Enable power domain support using Driver Model"
depends on DM && OF_CONTROL
help
Enable support for the power domain driver class. Many SoCs allow
power to be applied to or removed from portions of the SoC (power
domains). This may be used to save power. This API provides the
means to control such power management hardware.
config SANDBOX_POWER_DOMAIN
bool "Enable the sandbox power domain test driver"
depends on POWER_DOMAIN && SANDBOX
help
Enable support for a test power domain driver implementation, which
simply accepts requests to power on/off various HW modules without
actually doing anything beyond a little error checking.
endmenu

View File

@@ -0,0 +1,7 @@
# Copyright (c) 2016, NVIDIA CORPORATION.
#
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_POWER_DOMAIN) += power-domain-uclass.o
obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain.o
obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o

View File

@@ -0,0 +1,112 @@
/*
* Copyright (c) 2016, NVIDIA CORPORATION.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <fdtdec.h>
#include <power-domain.h>
#include <power-domain-uclass.h>
DECLARE_GLOBAL_DATA_PTR;
static inline struct power_domain_ops *power_domain_dev_ops(struct udevice *dev)
{
return (struct power_domain_ops *)dev->driver->ops;
}
static int power_domain_of_xlate_default(struct power_domain *power_domain,
struct fdtdec_phandle_args *args)
{
debug("%s(power_domain=%p)\n", __func__, power_domain);
if (args->args_count != 1) {
debug("Invalid args_count: %d\n", args->args_count);
return -EINVAL;
}
power_domain->id = args->args[0];
return 0;
}
int power_domain_get(struct udevice *dev, struct power_domain *power_domain)
{
struct fdtdec_phandle_args args;
int ret;
struct udevice *dev_power_domain;
struct power_domain_ops *ops;
debug("%s(dev=%p, power_domain=%p)\n", __func__, dev, power_domain);
ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev->of_offset,
"power-domains",
"#power-domain-cells", 0, 0,
&args);
if (ret) {
debug("%s: fdtdec_parse_phandle_with_args failed: %d\n",
__func__, ret);
return ret;
}
ret = uclass_get_device_by_of_offset(UCLASS_POWER_DOMAIN, args.node,
&dev_power_domain);
if (ret) {
debug("%s: uclass_get_device_by_of_offset failed: %d\n",
__func__, ret);
return ret;
}
ops = power_domain_dev_ops(dev_power_domain);
power_domain->dev = dev_power_domain;
if (ops->of_xlate)
ret = ops->of_xlate(power_domain, &args);
else
ret = power_domain_of_xlate_default(power_domain, &args);
if (ret) {
debug("of_xlate() failed: %d\n", ret);
return ret;
}
ret = ops->request(power_domain);
if (ret) {
debug("ops->request() failed: %d\n", ret);
return ret;
}
return 0;
}
int power_domain_free(struct power_domain *power_domain)
{
struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
debug("%s(power_domain=%p)\n", __func__, power_domain);
return ops->free(power_domain);
}
int power_domain_on(struct power_domain *power_domain)
{
struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
debug("%s(power_domain=%p)\n", __func__, power_domain);
return ops->on(power_domain);
}
int power_domain_off(struct power_domain *power_domain)
{
struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
debug("%s(power_domain=%p)\n", __func__, power_domain);
return ops->off(power_domain);
}
UCLASS_DRIVER(power_domain) = {
.id = UCLASS_POWER_DOMAIN,
.name = "power_domain",
};

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2016, NVIDIA CORPORATION.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <power-domain.h>
#include <asm/io.h>
#include <asm/power-domain.h>
struct sandbox_power_domain_test {
struct power_domain pd;
};
int sandbox_power_domain_test_get(struct udevice *dev)
{
struct sandbox_power_domain_test *sbrt = dev_get_priv(dev);
return power_domain_get(dev, &sbrt->pd);
}
int sandbox_power_domain_test_on(struct udevice *dev)
{
struct sandbox_power_domain_test *sbrt = dev_get_priv(dev);
return power_domain_on(&sbrt->pd);
}
int sandbox_power_domain_test_off(struct udevice *dev)
{
struct sandbox_power_domain_test *sbrt = dev_get_priv(dev);
return power_domain_off(&sbrt->pd);
}
int sandbox_power_domain_test_free(struct udevice *dev)
{
struct sandbox_power_domain_test *sbrt = dev_get_priv(dev);
return power_domain_free(&sbrt->pd);
}
static const struct udevice_id sandbox_power_domain_test_ids[] = {
{ .compatible = "sandbox,power-domain-test" },
{ }
};
U_BOOT_DRIVER(sandbox_power_domain_test) = {
.name = "sandbox_power_domain_test",
.id = UCLASS_MISC,
.of_match = sandbox_power_domain_test_ids,
.priv_auto_alloc_size = sizeof(struct sandbox_power_domain_test),
};

View File

@@ -0,0 +1,104 @@
/*
* Copyright (c) 2016, NVIDIA CORPORATION.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <power-domain-uclass.h>
#include <asm/io.h>
#include <asm/power-domain.h>
#define SANDBOX_POWER_DOMAINS 3
struct sandbox_power_domain {
bool on[SANDBOX_POWER_DOMAINS];
};
static int sandbox_power_domain_request(struct power_domain *power_domain)
{
debug("%s(power_domain=%p)\n", __func__, power_domain);
if (power_domain->id >= SANDBOX_POWER_DOMAINS)
return -EINVAL;
return 0;
}
static int sandbox_power_domain_free(struct power_domain *power_domain)
{
debug("%s(power_domain=%p)\n", __func__, power_domain);
return 0;
}
static int sandbox_power_domain_on(struct power_domain *power_domain)
{
struct sandbox_power_domain *sbr = dev_get_priv(power_domain->dev);
debug("%s(power_domain=%p)\n", __func__, power_domain);
sbr->on[power_domain->id] = true;
return 0;
}
static int sandbox_power_domain_off(struct power_domain *power_domain)
{
struct sandbox_power_domain *sbr = dev_get_priv(power_domain->dev);
debug("%s(power_domain=%p)\n", __func__, power_domain);
sbr->on[power_domain->id] = false;
return 0;
}
static int sandbox_power_domain_bind(struct udevice *dev)
{
debug("%s(dev=%p)\n", __func__, dev);
return 0;
}
static int sandbox_power_domain_probe(struct udevice *dev)
{
debug("%s(dev=%p)\n", __func__, dev);
return 0;
}
static const struct udevice_id sandbox_power_domain_ids[] = {
{ .compatible = "sandbox,power-domain" },
{ }
};
struct power_domain_ops sandbox_power_domain_ops = {
.request = sandbox_power_domain_request,
.free = sandbox_power_domain_free,
.on = sandbox_power_domain_on,
.off = sandbox_power_domain_off,
};
U_BOOT_DRIVER(sandbox_power_domain) = {
.name = "sandbox_power_domain",
.id = UCLASS_POWER_DOMAIN,
.of_match = sandbox_power_domain_ids,
.bind = sandbox_power_domain_bind,
.probe = sandbox_power_domain_probe,
.priv_auto_alloc_size = sizeof(struct sandbox_power_domain),
.ops = &sandbox_power_domain_ops,
};
int sandbox_power_domain_query(struct udevice *dev, unsigned long id)
{
struct sandbox_power_domain *sbr = dev_get_priv(dev);
debug("%s(dev=%p, id=%ld)\n", __func__, dev, id);
if (id >= SANDBOX_POWER_DOMAINS)
return -EINVAL;
return sbr->on[id];
}