diff options
author | Stephen Warren <swarren@nvidia.com> | 2016-07-13 13:45:31 -0600 |
---|---|---|
committer | Simon Glass <sjg@chromium.org> | 2016-07-27 16:29:56 -0600 |
commit | 61f5ddcb7a997f7b7bca3680cd6f67e60e616841 (patch) | |
tree | ca5dcfb1d2335fa819cc7e36ea6295a6d12a4898 | |
parent | 1e2b3ef8653417f296ee23f32c28abfc086529dd (diff) | |
download | u-boot-61f5ddcb7a997f7b7bca3680cd6f67e60e616841.tar.gz u-boot-61f5ddcb7a997f7b7bca3680cd6f67e60e616841.tar.bz2 u-boot-61f5ddcb7a997f7b7bca3680cd6f67e60e616841.zip |
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>
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | arch/sandbox/dts/test.dts | 10 | ||||
-rw-r--r-- | arch/sandbox/include/asm/power-domain.h | 21 | ||||
-rw-r--r-- | configs/sandbox_defconfig | 2 | ||||
-rw-r--r-- | drivers/power/Kconfig | 2 | ||||
-rw-r--r-- | drivers/power/domain/Kconfig | 20 | ||||
-rw-r--r-- | drivers/power/domain/Makefile | 7 | ||||
-rw-r--r-- | drivers/power/domain/power-domain-uclass.c | 112 | ||||
-rw-r--r-- | drivers/power/domain/sandbox-power-domain-test.c | 55 | ||||
-rw-r--r-- | drivers/power/domain/sandbox-power-domain.c | 104 | ||||
-rw-r--r-- | include/dm/uclass-id.h | 1 | ||||
-rw-r--r-- | include/power-domain-uclass.h | 82 | ||||
-rw-r--r-- | include/power-domain.h | 120 | ||||
-rw-r--r-- | test/dm/Makefile | 1 | ||||
-rw-r--r-- | test/dm/power-domain.c | 46 |
15 files changed, 584 insertions, 0 deletions
@@ -638,6 +638,7 @@ libs-y += drivers/net/ libs-y += drivers/net/phy/ libs-y += drivers/pci/ libs-y += drivers/power/ \ + drivers/power/domain/ \ drivers/power/fuel_gauge/ \ drivers/power/mfd/ \ drivers/power/pmic/ \ diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 9e46f9e815..fff175d1b7 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -262,6 +262,16 @@ }; }; + pwrdom: power-domain { + compatible = "sandbox,power-domain"; + #power-domain-cells = <1>; + }; + + power-domain-test { + compatible = "sandbox,power-domain-test"; + power-domains = <&pwrdom 2>; + }; + ram { compatible = "sandbox,ram"; }; diff --git a/arch/sandbox/include/asm/power-domain.h b/arch/sandbox/include/asm/power-domain.h new file mode 100644 index 0000000000..cad388549e --- /dev/null +++ b/arch/sandbox/include/asm/power-domain.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __SANDBOX_POWER_DOMAIN_H +#define __SANDBOX_POWER_DOMAIN_H + +#include <common.h> + +struct udevice; + +int sandbox_power_domain_query(struct udevice *dev, unsigned long id); + +int sandbox_power_domain_test_get(struct udevice *dev); +int sandbox_power_domain_test_on(struct udevice *dev); +int sandbox_power_domain_test_off(struct udevice *dev); +int sandbox_power_domain_test_free(struct udevice *dev); + +#endif diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 29e6d85bd4..887d83acfd 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -176,3 +176,5 @@ CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y CONFIG_UT_ENV=y +CONFIG_POWER_DOMAIN=y +CONFIG_SANDBOX_POWER_DOMAIN=y diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 3c44167805..b422703126 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -1,5 +1,7 @@ menu "Power" +source "drivers/power/domain/Kconfig" + source "drivers/power/pmic/Kconfig" source "drivers/power/regulator/Kconfig" diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig new file mode 100644 index 0000000000..b904097433 --- /dev/null +++ b/drivers/power/domain/Kconfig @@ -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 diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile new file mode 100644 index 0000000000..c18292f0ec --- /dev/null +++ b/drivers/power/domain/Makefile @@ -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 diff --git a/drivers/power/domain/power-domain-uclass.c b/drivers/power/domain/power-domain-uclass.c new file mode 100644 index 0000000000..1bb6262fa1 --- /dev/null +++ b/drivers/power/domain/power-domain-uclass.c @@ -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", +}; diff --git a/drivers/power/domain/sandbox-power-domain-test.c b/drivers/power/domain/sandbox-power-domain-test.c new file mode 100644 index 0000000000..92a3a2a527 --- /dev/null +++ b/drivers/power/domain/sandbox-power-domain-test.c @@ -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), +}; diff --git a/drivers/power/domain/sandbox-power-domain.c b/drivers/power/domain/sandbox-power-domain.c new file mode 100644 index 0000000000..9071346f98 --- /dev/null +++ b/drivers/power/domain/sandbox-power-domain.c @@ -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]; +} diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index c5cdfc79d1..eb78c4dac4 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -59,6 +59,7 @@ enum uclass_id { UCLASS_PINCTRL, /* Pinctrl (pin muxing/configuration) device */ UCLASS_PMIC, /* PMIC I/O device */ UCLASS_PWM, /* Pulse-width modulator */ + UCLASS_POWER_DOMAIN, /* (SoC) Power domains */ UCLASS_PWRSEQ, /* Power sequence device */ UCLASS_RAM, /* RAM controller */ UCLASS_REGULATOR, /* Regulator device */ diff --git a/include/power-domain-uclass.h b/include/power-domain-uclass.h new file mode 100644 index 0000000000..5878021e32 --- /dev/null +++ b/include/power-domain-uclass.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _POWER_DOMAIN_UCLASS_H +#define _POWER_DOMAIN_UCLASS_H + +/* See power-domain.h for background documentation. */ + +#include <power-domain.h> + +struct udevice; + +/** + * struct power_domain_ops - The functions that a power domain controller driver + * must implement. + */ +struct power_domain_ops { + /** + * of_xlate - Translate a client's device-tree (OF) power domain + * specifier. + * + * The power domain core calls this function as the first step in + * implementing a client's power_domain_get() call. + * + * If this function pointer is set to NULL, the power domain core will + * use a default implementation, which assumes #power-domain-cells = + * <1>, and that the DT cell contains a simple integer power domain ID. + * + * At present, the power domain API solely supports device-tree. If + * this changes, other xxx_xlate() functions may be added to support + * those other mechanisms. + * + * @power_domain: The power domain struct to hold the + * translation result. + * @args: The power domain specifier values from device + * tree. + * @return 0 if OK, or a negative error code. + */ + int (*of_xlate)(struct power_domain *power_domain, + struct fdtdec_phandle_args *args); + /** + * request - Request a translated power domain. + * + * The power domain core calls this function as the second step in + * implementing a client's power_domain_get() call, following a + * successful xxx_xlate() call. + * + * @power_domain: The power domain to request; this has been + * filled in by a previous xxx_xlate() function + * call. + * @return 0 if OK, or a negative error code. + */ + int (*request)(struct power_domain *power_domain); + /** + * free - Free a previously requested power domain. + * + * This is the implementation of the client power_domain_free() API. + * + * @power_domain: The power domain to free. + * @return 0 if OK, or a negative error code. + */ + int (*free)(struct power_domain *power_domain); + /** + * on - Power on a power domain. + * + * @power_domain: The power domain to turn on. + * @return 0 if OK, or a negative error code. + */ + int (*on)(struct power_domain *power_domain); + /** + * off - Power off a power domain. + * + * @power_domain: The power domain to turn off. + * @return 0 if OK, or a negative error code. + */ + int (*off)(struct power_domain *power_domain); +}; + +#endif diff --git a/include/power-domain.h b/include/power-domain.h new file mode 100644 index 0000000000..10999790b5 --- /dev/null +++ b/include/power-domain.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _POWER_DOMAIN_H +#define _POWER_DOMAIN_H + +/** + * A power domain is a portion of an SoC or chip that is powered by a + * switchable source of power. In many cases, software has control over the + * power domain, and can turn the power source on or off. This is typically + * done to save power by powering off unused devices, or to enable software + * sequencing of initial powerup at boot. This API provides a means for + * drivers to turn power domains on and off. + * + * A driver that implements UCLASS_POWER_DOMAIN is a power domain controller or + * provider. A controller will often implement multiple separate power domains, + * since the hardware it manages often has this capability. + * power-domain-uclass.h describes the interface which power domain controllers + * must implement. + * + * Depending on the power domain controller hardware, changing the state of a + * power domain may require performing related operations on other resources. + * For example, some power domains may require certain clocks to be enabled + * whenever the power domain is powered on, or during the time when the power + * domain is transitioning state. These details are implementation-specific + * and should ideally be encapsulated entirely within the provider driver, or + * configured through mechanisms (e.g. device tree) that do not require client + * drivers to provide extra configuration information. + * + * Power domain consumers/clients are the drivers for HW modules within the + * power domain. This header file describes the API used by those drivers. + * + * In many cases, a single complex IO controller (e.g. a PCIe controller) will + * be the sole logic contained within a power domain. In such cases, it is + * logical for the relevant device driver to directly control that power + * domain. In other cases, multiple controllers, each with their own driver, + * may be contained in a single power domain. Any logic require to co-ordinate + * between drivers for these multiple controllers is beyond the scope of this + * API at present. Equally, this API does not define or implement any policy + * by which power domains are managed. + */ + +struct udevice; + +/** + * struct power_domain - A handle to (allowing control of) a single power domain. + * + * Clients provide storage for power domain handles. The content of the + * structure is managed solely by the power domain API and power domain + * drivers. A power domain struct is initialized by "get"ing the power domain + * struct. The power domain struct is passed to all other power domain APIs to + * identify which power domain to operate upon. + * + * @dev: The device which implements the power domain. + * @id: The power domain ID within the provider. + * + * Currently, the power domain API assumes that a single integer ID is enough + * to identify and configure any power domain for any power domain provider. If + * this assumption becomes invalid in the future, the struct could be expanded + * to either (a) add more fields to allow power domain providers to store + * additional information, or (b) replace the id field with an opaque pointer, + * which the provider would dynamically allocate during its .of_xlate op, and + * process during is .request op. This may require the addition of an extra op + * to clean up the allocation. + */ +struct power_domain { + struct udevice *dev; + /* + * Written by of_xlate. We assume a single id is enough for now. In the + * future, we might add more fields here. + */ + unsigned long id; +}; + +/** + * power_domain_get - Get/request the power domain for a device. + * + * This looks up and requests a power domain. Each device is assumed to have + * a single (or, at least one) power domain associated with it somehow, and + * that domain, or the first/default domain. The mapping of client device to + * provider power domain may be via device-tree properties, board-provided + * mapping tables, or some other mechanism. + * + * @dev: The client device. + * @power_domain A pointer to a power domain struct to initialize. + * @return 0 if OK, or a negative error code. + */ +int power_domain_get(struct udevice *dev, struct power_domain *power_domain); + +/** + * power_domain_free - Free a previously requested power domain. + * + * @power_domain: A power domain struct that was previously successfully + * requested by power_domain_get(). + * @return 0 if OK, or a negative error code. + */ +int power_domain_free(struct power_domain *power_domain); + +/** + * power_domain_on - Enable power to a power domain. + * + * @power_domain: A power domain struct that was previously successfully + * requested by power_domain_get(). + * @return 0 if OK, or a negative error code. + */ +int power_domain_on(struct power_domain *power_domain); + +/** + * power_domain_off - Disable power ot a power domain. + * + * @power_domain: A power domain struct that was previously successfully + * requested by power_domain_get(). + * @return 0 if OK, or a negative error code. + */ +int power_domain_off(struct power_domain *power_domain); + +#endif diff --git a/test/dm/Makefile b/test/dm/Makefile index cad3374e43..1885e17c38 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_LED) += led.o obj-$(CONFIG_DM_MAILBOX) += mailbox.o obj-$(CONFIG_DM_MMC) += mmc.o obj-$(CONFIG_DM_PCI) += pci.o +obj-$(CONFIG_POWER_DOMAIN) += power-domain.o obj-$(CONFIG_RAM) += ram.o obj-y += regmap.o obj-$(CONFIG_REMOTEPROC) += remoteproc.o diff --git a/test/dm/power-domain.c b/test/dm/power-domain.c new file mode 100644 index 0000000000..379a8fa3d6 --- /dev/null +++ b/test/dm/power-domain.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <dm/test.h> +#include <asm/power-domain.h> +#include <test/ut.h> + +/* This must match the specifier for power-domains in the DT node */ +#define TEST_POWER_DOMAIN 2 + +static int dm_test_power_domain(struct unit_test_state *uts) +{ + struct udevice *dev_power_domain; + struct udevice *dev_test; + + ut_assertok(uclass_get_device_by_name(UCLASS_POWER_DOMAIN, + "power-domain", + &dev_power_domain)); + ut_asserteq(0, sandbox_power_domain_query(dev_power_domain, 0)); + ut_asserteq(0, sandbox_power_domain_query(dev_power_domain, + TEST_POWER_DOMAIN)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "power-domain-test", + &dev_test)); + ut_assertok(sandbox_power_domain_test_get(dev_test)); + + ut_assertok(sandbox_power_domain_test_on(dev_test)); + ut_asserteq(0, sandbox_power_domain_query(dev_power_domain, 0)); + ut_asserteq(1, sandbox_power_domain_query(dev_power_domain, + TEST_POWER_DOMAIN)); + + ut_assertok(sandbox_power_domain_test_off(dev_test)); + ut_asserteq(0, sandbox_power_domain_query(dev_power_domain, 0)); + ut_asserteq(0, sandbox_power_domain_query(dev_power_domain, + TEST_POWER_DOMAIN)); + + ut_assertok(sandbox_power_domain_test_free(dev_test)); + + return 0; +} +DM_TEST(dm_test_power_domain, DM_TESTF_SCAN_FDT); |