summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorMichal Simek <michal.simek@xilinx.com>2022-02-07 10:27:37 +0100
committerMichal Simek <michal.simek@xilinx.com>2022-02-15 13:01:42 +0100
commite0283cbdfd49a437ef3cfdd662b196d2f70b5ea0 (patch)
treed845cd6b699bc029289a76bda1d065234a24c1ba /drivers
parent2f9dd4bfc7a2d618c9c76448dd7b3ce67c6ec4ce (diff)
power: zynqmp: Add power domain driver for ZynqMP
Driver should be enabled by CONFIG_POWER_DOMAIN=y and CONFIG_ZYNQMP_POWER_DOMAIN=y. Power domain driver doesn't have own DT node but it uses zynqmp firmware DT node that's why there is a need to bind driver when firmware node is found. Driver itself is simple. It is sending pmufw config object overlay for enabling access to device which is done in ...domain_request(). In ...domain_on() capabilities are passed and node is requested. This should be bare minimum of required to get power domain driver working. Signed-off-by: Michal Simek <michal.simek@xilinx.com> Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com> Link: https://lore.kernel.org/r/f4b9433b91c0b18c375b061c7a4e29d428f70547.1644226055.git.michal.simek@xilinx.com
Diffstat (limited to 'drivers')
-rw-r--r--drivers/firmware/firmware-zynqmp.c20
-rw-r--r--drivers/power/domain/Kconfig9
-rw-r--r--drivers/power/domain/Makefile1
-rw-r--r--drivers/power/domain/zynqmp-power-domain.c89
4 files changed, 119 insertions, 0 deletions
diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c
index 839203ec82..8d78888eff 100644
--- a/drivers/firmware/firmware-zynqmp.c
+++ b/drivers/firmware/firmware-zynqmp.c
@@ -8,6 +8,7 @@
#include <common.h>
#include <cpu_func.h>
#include <dm.h>
+#include <dm/lists.h>
#include <log.h>
#include <zynqmp_firmware.h>
#include <asm/cache.h>
@@ -226,8 +227,27 @@ static const struct udevice_id zynqmp_firmware_ids[] = {
{ }
};
+static int zynqmp_firmware_bind(struct udevice *dev)
+{
+ int ret;
+ struct udevice *child;
+
+ if (IS_ENABLED(CONFIG_ZYNQMP_POWER_DOMAIN)) {
+ ret = device_bind_driver_to_node(dev, "zynqmp_power_domain",
+ "zynqmp_power_domain",
+ dev_ofnode(dev), &child);
+ if (ret) {
+ printf("zynqmp power domain driver is not bound: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return dm_scan_fdt_dev(dev);
+}
+
U_BOOT_DRIVER(zynqmp_firmware) = {
.id = UCLASS_FIRMWARE,
.name = "zynqmp_firmware",
.of_match = zynqmp_firmware_ids,
+ .bind = zynqmp_firmware_bind,
};
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig
index 9aea5fcdf0..93d2599d83 100644
--- a/drivers/power/domain/Kconfig
+++ b/drivers/power/domain/Kconfig
@@ -88,4 +88,13 @@ config TI_POWER_DOMAIN
help
Generic power domain implementation for TI K3 devices.
+config ZYNQMP_POWER_DOMAIN
+ bool "Enable the Xilinx ZynqMP Power domain driver"
+ depends on POWER_DOMAIN && ZYNQMP_FIRMWARE
+ help
+ Generic power domain implementation for Xilinx ZynqMP devices.
+ The driver should be enabled when system starts in very minimal
+ configuration and it is extended at run time. Then enabling
+ the driver will ensure that PMUFW enable access to requested IP.
+
endmenu
diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile
index 530ae35671..7c8af67dbd 100644
--- a/drivers/power/domain/Makefile
+++ b/drivers/power/domain/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o
obj-$(CONFIG_TEGRA186_POWER_DOMAIN) += tegra186-power-domain.o
obj-$(CONFIG_TI_SCI_POWER_DOMAIN) += ti-sci-power-domain.o
obj-$(CONFIG_TI_POWER_DOMAIN) += ti-power-domain.o
+obj-$(CONFIG_ZYNQMP_POWER_DOMAIN) += zynqmp-power-domain.o
diff --git a/drivers/power/domain/zynqmp-power-domain.c b/drivers/power/domain/zynqmp-power-domain.c
new file mode 100644
index 0000000000..5383d09896
--- /dev/null
+++ b/drivers/power/domain/zynqmp-power-domain.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021, Xilinx. Inc.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <misc.h>
+#include <power-domain-uclass.h>
+#include <linux/bitops.h>
+
+#include <zynqmp_firmware.h>
+
+#define NODE_ID_LOCATION 5
+
+static unsigned int xpm_configobject[] = {
+ /* HEADER */
+ 2, /* Number of remaining words in the header */
+ 1, /* Number of sections included in config object */
+ PM_CONFIG_OBJECT_TYPE_OVERLAY, /* Type of Config object as overlay */
+ /* SLAVE SECTION */
+
+ PM_CONFIG_SLAVE_SECTION_ID, /* Section ID */
+ 1, /* Number of slaves */
+
+ 0, /* Node ID which will be changed below */
+ PM_SLAVE_FLAG_IS_SHAREABLE,
+ PM_CONFIG_IPI_PSU_CORTEXA53_0_MASK |
+ PM_CONFIG_IPI_PSU_CORTEXR5_0_MASK |
+ PM_CONFIG_IPI_PSU_CORTEXR5_1_MASK, /* IPI Mask */
+};
+
+static int zynqmp_pm_request_node(const u32 node, const u32 capabilities,
+ const u32 qos, const enum zynqmp_pm_request_ack ack)
+{
+ return xilinx_pm_request(PM_REQUEST_NODE, node, capabilities,
+ qos, ack, NULL);
+}
+
+static int zynqmp_power_domain_request(struct power_domain *power_domain)
+{
+ /* Record power domain id */
+ xpm_configobject[NODE_ID_LOCATION] = power_domain->id;
+
+ zynqmp_pmufw_load_config_object(xpm_configobject, sizeof(xpm_configobject));
+
+ return 0;
+}
+
+static int zynqmp_power_domain_free(struct power_domain *power_domain)
+{
+ /* nop now */
+ return 0;
+}
+
+static int zynqmp_power_domain_on(struct power_domain *power_domain)
+{
+ return zynqmp_pm_request_node(power_domain->id,
+ ZYNQMP_PM_CAPABILITY_ACCESS,
+ ZYNQMP_PM_MAX_QOS,
+ ZYNQMP_PM_REQUEST_ACK_BLOCKING);
+}
+
+static int zynqmp_power_domain_off(struct power_domain *power_domain)
+{
+ /* nop now */
+ return 0;
+}
+
+struct power_domain_ops zynqmp_power_domain_ops = {
+ .request = zynqmp_power_domain_request,
+ .rfree = zynqmp_power_domain_free,
+ .on = zynqmp_power_domain_on,
+ .off = zynqmp_power_domain_off,
+};
+
+static int zynqmp_power_domain_probe(struct udevice *dev)
+{
+ return 0;
+}
+
+U_BOOT_DRIVER(zynqmp_power_domain) = {
+ .name = "zynqmp_power_domain",
+ .id = UCLASS_POWER_DOMAIN,
+ .probe = zynqmp_power_domain_probe,
+ .ops = &zynqmp_power_domain_ops,
+};