The zynqmp power domain driver communicates the usage requirements
for logical power domains / devices to the platform FW.
FW is responsible for choosing appropriate power states,
taking Linux' usage information into account.
This patch series is based on top of Xilinx firmware patch set:
https://patchwork.kernel.org/cover/10555405/
v3:
- Changed binding to have FW node as a power controller as suggested by Rob
- Updated FW driver to register it as mfd child devices from firmware probe
- Move bindings location as suggested
v2:
- Rebased on top of latest firmware driver patch series
- Updated driver name from zynqmp-genpd to zynqmp-power-controller
- Updated device tree bindings to move power controller node under firmware node
Jolly Shah (1):
drivers: soc: xilinx: Add ZynqMP power domain driver
Rajan Vaja (3):
dt-bindings: power: Add ZynqMP power domain bindings
firmware: xilinx: Add APIs to control node status/power
firmware: xilinx: Add node IDs for zynqmp firmware
.../bindings/power/xlnx,zynqmp-genpd.txt | 34 ++
drivers/firmware/xilinx/Kconfig | 1 +
drivers/firmware/xilinx/zynqmp.c | 73 ++++
drivers/soc/xilinx/Kconfig | 9 +
drivers/soc/xilinx/Makefile | 2 +
drivers/soc/xilinx/zynqmp_pm_domains.c | 403 +++++++++++++++++++++
include/dt-bindings/power/xlnx-zynqmp-power.h | 39 ++
include/linux/firmware/xlnx-zynqmp.h | 98 +++++
8 files changed, 659 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/xlnx,zynqmp-genpd.txt
create mode 100644 drivers/soc/xilinx/zynqmp_pm_domains.c
create mode 100644 include/dt-bindings/power/xlnx-zynqmp-power.h
--
2.7.4
From: Rajan Vaja <[email protected]>
Xilinx ZynqMP firmware has different node IDs for different
peripherals. Add node IDs which can be used by any driver.
Signed-off-by: Rajan Vaja <[email protected]>
Signed-off-by: Jolly Shah <[email protected]>
---
include/linux/firmware/xlnx-zynqmp.h | 72 ++++++++++++++++++++++++++++++++++++
1 file changed, 72 insertions(+)
diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
index 6998f7d..14bb780 100644
--- a/include/linux/firmware/xlnx-zynqmp.h
+++ b/include/linux/firmware/xlnx-zynqmp.h
@@ -40,6 +40,78 @@
#define ZYNQMP_PM_CAPABILITY_WAKEUP 0x4U
#define ZYNQMP_PM_CAPABILITY_POWER 0x8U
+/* Node IDs */
+#define NODE_UNKNOWN 0
+#define NODE_APU 1
+#define NODE_APU_0 2
+#define NODE_APU_1 3
+#define NODE_APU_2 4
+#define NODE_APU_3 5
+#define NODE_RPU 6
+#define NODE_RPU_0 7
+#define NODE_RPU_1 8
+#define NODE_PLD 9
+#define NODE_FPD 10
+#define NODE_OCM_BANK_0 11
+#define NODE_OCM_BANK_1 12
+#define NODE_OCM_BANK_2 13
+#define NODE_OCM_BANK_3 14
+#define NODE_TCM_0_A 15
+#define NODE_TCM_0_B 16
+#define NODE_TCM_1_A 17
+#define NODE_TCM_1_B 18
+#define NODE_L2 19
+#define NODE_GPU_PP_0 20
+#define NODE_GPU_PP_1 21
+#define NODE_USB_0 22
+#define NODE_USB_1 23
+#define NODE_TTC_0 24
+#define NODE_TTC_1 25
+#define NODE_TTC_2 26
+#define NODE_TTC_3 27
+#define NODE_SATA 28
+#define NODE_ETH_0 29
+#define NODE_ETH_1 30
+#define NODE_ETH_2 31
+#define NODE_ETH_3 32
+#define NODE_UART_0 33
+#define NODE_UART_1 34
+#define NODE_SPI_0 35
+#define NODE_SPI_1 36
+#define NODE_I2C_0 37
+#define NODE_I2C_1 38
+#define NODE_SD_0 39
+#define NODE_SD_1 40
+#define NODE_DP 41
+#define NODE_GDMA 42
+#define NODE_ADMA 43
+#define NODE_NAND 44
+#define NODE_QSPI 45
+#define NODE_GPIO 46
+#define NODE_CAN_0 47
+#define NODE_CAN_1 48
+#define NODE_EXTERN 49
+#define NODE_APLL 50
+#define NODE_VPLL 51
+#define NODE_DPLL 52
+#define NODE_RPLL 53
+#define NODE_IOPLL 54
+#define NODE_DDR 55
+#define NODE_IPI_APU 56
+#define NODE_IPI_RPU_0 57
+#define NODE_GPU 58
+#define NODE_PCIE 59
+#define NODE_PCAP 60
+#define NODE_RTC 61
+#define NODE_LPD 62
+#define NODE_VCU 63
+#define NODE_IPI_RPU_1 64
+#define NODE_IPI_PL_0 65
+#define NODE_IPI_PL_1 66
+#define NODE_IPI_PL_2 67
+#define NODE_IPI_PL_3 68
+#define NODE_PL 69
+
enum pm_api_id {
PM_GET_API_VERSION = 1,
PM_REQUEST_NODE = 13,
--
2.7.4
From: Rajan Vaja <[email protected]>
Add documentation to describe ZynqMP power domain bindings.
Signed-off-by: Rajan Vaja <[email protected]>
Signed-off-by: Jolly Shah <[email protected]>
---
.../bindings/power/xlnx,zynqmp-genpd.txt | 34 +++++++++++++++++++
include/dt-bindings/power/xlnx-zynqmp-power.h | 39 ++++++++++++++++++++++
2 files changed, 73 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/xlnx,zynqmp-genpd.txt
create mode 100644 include/dt-bindings/power/xlnx-zynqmp-power.h
diff --git a/Documentation/devicetree/bindings/power/xlnx,zynqmp-genpd.txt b/Documentation/devicetree/bindings/power/xlnx,zynqmp-genpd.txt
new file mode 100644
index 0000000..3c7f237
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/xlnx,zynqmp-genpd.txt
@@ -0,0 +1,34 @@
+-----------------------------------------------------------
+Device Tree Bindings for the Xilinx Zynq MPSoC PM domains
+-----------------------------------------------------------
+The binding for zynqmp-power-controller follow the common
+generic PM domain binding[1].
+
+[1] Documentation/devicetree/bindings/power/power_domain.txt
+
+== Zynq MPSoC Generic PM Domain Node ==
+
+Required property:
+ - Below property should be in zynqmp-firmware node.
+ - #power-domain-cells: Number of cells in a PM domain specifier. Must be 1.
+
+Power domain ID indexes are mentioned in
+include/dt-bindings/power/xlnx-zynqmp-power.h.
+
+-------
+Example
+-------
+
+firmware {
+ zynqmp_firmware: zynqmp-firmware {
+ ...
+ #power-domain-cells = <1>;
+ ...
+ };
+};
+
+sata {
+ ...
+ power-domains = <&zynqmp_firmware 2>;
+ ...
+};
diff --git a/include/dt-bindings/power/xlnx-zynqmp-power.h b/include/dt-bindings/power/xlnx-zynqmp-power.h
new file mode 100644
index 0000000..1bc9636
--- /dev/null
+++ b/include/dt-bindings/power/xlnx-zynqmp-power.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Xilinx, Inc.
+ */
+
+#ifndef _DT_BINDINGS_ZYNQMP_POWER_H
+#define _DT_BINDINGS_ZYNQMP_POWER_H
+
+#define PD_USB_0 0
+#define PD_USB_1 1
+#define PD_SATA 2
+#define PD_SPI_0 3
+#define PD_SPI_1 4
+#define PD_UART_0 5
+#define PD_UART_1 6
+#define PD_ETH_0 7
+#define PD_ETH_1 8
+#define PD_ETH_2 9
+#define PD_ETH_3 10
+#define PD_I2C_0 11
+#define PD_I2C_1 12
+#define PD_DP 13
+#define PD_GDMA 14
+#define PD_ADMA 15
+#define PD_TTC_0 16
+#define PD_TTC_1 17
+#define PD_TTC_2 18
+#define PD_TTC_3 19
+#define PD_SD_0 20
+#define PD_SD_1 21
+#define PD_NAND 22
+#define PD_QSPI 23
+#define PD_GPIO 24
+#define PD_CAN_0 25
+#define PD_CAN_1 26
+#define PD_PCIE 27
+#define PD_GPU 28
+
+#endif
--
2.7.4
From: Rajan Vaja <[email protected]>
Add Xilinx ZynqMP firmware APIs to control node status
and power. These APIs allows turning on/off power domain
and setting capabilities of devices present in power domain.
Signed-off-by: Rajan Vaja <[email protected]>
Signed-off-by: Jolly Shah <[email protected]>
---
drivers/firmware/xilinx/zynqmp.c | 58 ++++++++++++++++++++++++++++++++++++
include/linux/firmware/xlnx-zynqmp.h | 26 ++++++++++++++++
2 files changed, 84 insertions(+)
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c
index 9a1c72a..faf6a52 100644
--- a/drivers/firmware/xilinx/zynqmp.c
+++ b/drivers/firmware/xilinx/zynqmp.c
@@ -469,6 +469,61 @@ static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2,
arg1, arg2, out);
}
+/**
+ * zynqmp_pm_request_node() - Request a node with specific capabilities
+ * @node: Node ID of the slave
+ * @capabilities: Requested capabilities of the slave
+ * @qos: Quality of service (not supported)
+ * @ack: Flag to specify whether acknowledge is requested
+ *
+ * This function is used by master to request particular node from firmware.
+ * Every master must request node before using it.
+ *
+ * Return: Returns status, either success or error+reason
+ */
+static int zynqmp_pm_request_node(const u32 node, const u32 capabilities,
+ const u32 qos,
+ const enum zynqmp_pm_request_ack ack)
+{
+ return zynqmp_pm_invoke_fn(PM_REQUEST_NODE, node, capabilities,
+ qos, ack, NULL);
+}
+
+/**
+ * zynqmp_pm_release_node() - Release a node
+ * @node: Node ID of the slave
+ *
+ * This function is used by master to inform firmware that master
+ * has released node. Once released, master must not use that node
+ * without re-request.
+ *
+ * Return: Returns status, either success or error+reason
+ */
+static int zynqmp_pm_release_node(const u32 node)
+{
+ return zynqmp_pm_invoke_fn(PM_RELEASE_NODE, node, 0, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_set_requirement() - PM call to set requirement for PM slaves
+ * @node: Node ID of the slave
+ * @capabilities: Requested capabilities of the slave
+ * @qos: Quality of service (not supported)
+ * @ack: Flag to specify whether acknowledge is requested
+ *
+ * This API function is to be used for slaves a PU already has requested
+ * to change its capabilities.
+ *
+ * Return: Returns status, either success or error+reason
+ */
+static int zynqmp_pm_set_requirement(const u32 node, const u32 capabilities,
+ const u32 qos,
+ const enum zynqmp_pm_request_ack ack)
+{
+ return zynqmp_pm_invoke_fn(PM_SET_REQUIREMENT, node, capabilities,
+ qos, ack, NULL);
+}
+
static const struct zynqmp_eemi_ops eemi_ops = {
.get_api_version = zynqmp_pm_get_api_version,
.query_data = zynqmp_pm_query_data,
@@ -482,6 +537,9 @@ static const struct zynqmp_eemi_ops eemi_ops = {
.clock_setparent = zynqmp_pm_clock_setparent,
.clock_getparent = zynqmp_pm_clock_getparent,
.ioctl = zynqmp_pm_ioctl,
+ .request_node = zynqmp_pm_request_node,
+ .release_node = zynqmp_pm_release_node,
+ .set_requirement = zynqmp_pm_set_requirement,
};
/**
diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
index 3c3c28e..6998f7d 100644
--- a/include/linux/firmware/xlnx-zynqmp.h
+++ b/include/linux/firmware/xlnx-zynqmp.h
@@ -32,8 +32,19 @@
/* Number of 32bits values in payload */
#define PAYLOAD_ARG_CNT 4U
+#define ZYNQMP_PM_MAX_QOS 100U
+
+/* Node capabilities */
+#define ZYNQMP_PM_CAPABILITY_ACCESS 0x1U
+#define ZYNQMP_PM_CAPABILITY_CONTEXT 0x2U
+#define ZYNQMP_PM_CAPABILITY_WAKEUP 0x4U
+#define ZYNQMP_PM_CAPABILITY_POWER 0x8U
+
enum pm_api_id {
PM_GET_API_VERSION = 1,
+ PM_REQUEST_NODE = 13,
+ PM_RELEASE_NODE,
+ PM_SET_REQUIREMENT,
PM_IOCTL = 34,
PM_QUERY_DATA,
PM_CLOCK_ENABLE,
@@ -75,6 +86,12 @@ enum pm_query_id {
PM_QID_CLOCK_GET_NUM_CLOCKS = 12,
};
+enum zynqmp_pm_request_ack {
+ ZYNQMP_PM_REQUEST_ACK_NO = 1,
+ ZYNQMP_PM_REQUEST_ACK_BLOCKING,
+ ZYNQMP_PM_REQUEST_ACK_NON_BLOCKING,
+};
+
/**
* struct zynqmp_pm_query_data - PM query data
* @qid: query ID
@@ -102,6 +119,15 @@ struct zynqmp_eemi_ops {
int (*clock_setparent)(u32 clock_id, u32 parent_id);
int (*clock_getparent)(u32 clock_id, u32 *parent_id);
int (*ioctl)(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, u32 *out);
+ int (*request_node)(const u32 node,
+ const u32 capabilities,
+ const u32 qos,
+ const enum zynqmp_pm_request_ack ack);
+ int (*release_node)(const u32 node);
+ int (*set_requirement)(const u32 node,
+ const u32 capabilities,
+ const u32 qos,
+ const enum zynqmp_pm_request_ack ack);
};
#if IS_REACHABLE(CONFIG_ARCH_ZYNQMP)
--
2.7.4
From: Jolly Shah <[email protected]>
The zynqmp-genpd driver communicates the usage requirements
for logical power domains / devices to the platform FW.
FW is responsible for choosing appropriate power states,
taking Linux' usage information into account.
Signed-off-by: Rajan Vaja <[email protected]>
Signed-off-by: Jolly Shah <[email protected]>
---
drivers/firmware/xilinx/Kconfig | 1 +
drivers/firmware/xilinx/zynqmp.c | 15 ++
drivers/soc/xilinx/Kconfig | 9 +
drivers/soc/xilinx/Makefile | 2 +
drivers/soc/xilinx/zynqmp_pm_domains.c | 403 +++++++++++++++++++++++++++++++++
5 files changed, 430 insertions(+)
create mode 100644 drivers/soc/xilinx/zynqmp_pm_domains.c
diff --git a/drivers/firmware/xilinx/Kconfig b/drivers/firmware/xilinx/Kconfig
index 8f44b9c..bd33bbf 100644
--- a/drivers/firmware/xilinx/Kconfig
+++ b/drivers/firmware/xilinx/Kconfig
@@ -6,6 +6,7 @@ menu "Zynq MPSoC Firmware Drivers"
config ZYNQMP_FIRMWARE
bool "Enable Xilinx Zynq MPSoC firmware interface"
+ select MFD_CORE
help
Firmware interface driver is used by different
drivers to communicate with the firmware for
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c
index faf6a52..6b365df 100644
--- a/drivers/firmware/xilinx/zynqmp.c
+++ b/drivers/firmware/xilinx/zynqmp.c
@@ -14,6 +14,7 @@
#include <linux/compiler.h>
#include <linux/device.h>
#include <linux/init.h>
+#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
@@ -23,6 +24,12 @@
#include <linux/firmware/xlnx-zynqmp.h>
#include "zynqmp-debug.h"
+static const struct mfd_cell firmware_devs[] = {
+ {
+ .name = "zynqmp_power_controller",
+ },
+};
+
/**
* zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes
* @ret_status: PMUFW return code
@@ -596,11 +603,19 @@ static int zynqmp_firmware_probe(struct platform_device *pdev)
zynqmp_pm_api_debugfs_init();
+ ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, firmware_devs,
+ ARRAY_SIZE(firmware_devs), NULL, 0, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add MFD devices %d\n", ret);
+ return ret;
+ }
+
return of_platform_populate(dev->of_node, NULL, NULL, dev);
}
static int zynqmp_firmware_remove(struct platform_device *pdev)
{
+ mfd_remove_devices(&pdev->dev);
zynqmp_pm_api_debugfs_exit();
return 0;
diff --git a/drivers/soc/xilinx/Kconfig b/drivers/soc/xilinx/Kconfig
index 687c8f3..81a345e 100644
--- a/drivers/soc/xilinx/Kconfig
+++ b/drivers/soc/xilinx/Kconfig
@@ -17,4 +17,13 @@ config XILINX_VCU
To compile this driver as a module, choose M here: the
module will be called xlnx_vcu.
+config ZYNQMP_PM_DOMAINS
+ bool "Enable Zynq MPSoC generic PM domains"
+ default y
+ depends on PM && ARCH_ZYNQMP && ZYNQMP_FIRMWARE
+ select PM_GENERIC_DOMAINS
+ help
+ Say yes to enable device power management through PM domains
+ If in doubt, say N.
+
endmenu
diff --git a/drivers/soc/xilinx/Makefile b/drivers/soc/xilinx/Makefile
index dee8fd5..f468d1b 100644
--- a/drivers/soc/xilinx/Makefile
+++ b/drivers/soc/xilinx/Makefile
@@ -1,2 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_XILINX_VCU) += xlnx_vcu.o
+
+obj-$(CONFIG_ZYNQMP_PM_DOMAINS) += zynqmp_pm_domains.o
diff --git a/drivers/soc/xilinx/zynqmp_pm_domains.c b/drivers/soc/xilinx/zynqmp_pm_domains.c
new file mode 100644
index 0000000..999fe67
--- /dev/null
+++ b/drivers/soc/xilinx/zynqmp_pm_domains.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ZynqMP Generic PM domain support
+ *
+ * Copyright (C) 2015-2018 Xilinx, Inc.
+ *
+ * Davorin Mista <[email protected]>
+ * Jolly Shah <[email protected]>
+ * Rajan Vaja <[email protected]>
+ */
+
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+
+#include <linux/firmware/xlnx-zynqmp.h>
+
+/* Flag stating if PM nodes mapped to the PM domain has been requested */
+#define ZYNQMP_PM_DOMAIN_REQUESTED BIT(0)
+
+/**
+ * struct zynqmp_pm_domain - Wrapper around struct generic_pm_domain
+ * @gpd: Generic power domain
+ * @dev_list: List of devices belong to power domain
+ * @node_ids: PM node IDs corresponding to device(s) inside PM domain
+ * @node_id_num: Number of PM node IDs
+ * @flags: ZynqMP PM domain flags
+ */
+struct zynqmp_pm_domain {
+ struct generic_pm_domain gpd;
+ struct list_head dev_list;
+ const u32 *node_ids;
+ int node_id_num;
+ u8 flags;
+};
+
+/*
+ * struct zynqmp_domain_device - Device node present in power domain
+ * @dev: Device
+ * &list: List member for the devices in domain list
+ */
+struct zynqmp_domain_device {
+ struct device *dev;
+ struct list_head list;
+};
+
+/*
+ * struct zynqmp_pd_info - PM domain info
+ * @id: Number of PM node IDs
+ * @ids: PM node IDs corresponding to device(s) inside PM domain
+ * @name: PM node name
+ */
+struct zynqmp_pd_info {
+ u32 num_id;
+ const u32 *ids;
+ const char *name;
+};
+
+/**
+ * zynqmp_gpd_is_active_wakeup_path() - Check if device is in wakeup source
+ * path
+ * @dev: Device to check for wakeup source path
+ * @not_used: Data member (not required)
+ *
+ * This function is checks device's child hierarchy and checks if any device is
+ * set as wakeup source.
+ *
+ * Return: 1 if device is in wakeup source path else 0
+ */
+static int zynqmp_gpd_is_active_wakeup_path(struct device *dev, void *not_used)
+{
+ int may_wakeup;
+
+ may_wakeup = device_may_wakeup(dev);
+ if (may_wakeup)
+ return may_wakeup;
+
+ return device_for_each_child(dev, NULL,
+ zynqmp_gpd_is_active_wakeup_path);
+}
+
+/**
+ * zynqmp_gpd_power_on() - Power on PM domain
+ * @domain: Generic PM domain
+ *
+ * This function is called before devices inside a PM domain are resumed, to
+ * power on PM domain.
+ *
+ * Return: 0 on success, error code otherwise
+ */
+static int zynqmp_gpd_power_on(struct generic_pm_domain *domain)
+{
+ int ret, i;
+ struct zynqmp_pm_domain *pd;
+ const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
+
+ if (!eemi_ops || !eemi_ops->set_requirement)
+ return -ENXIO;
+
+ pd = container_of(domain, struct zynqmp_pm_domain, gpd);
+ for (i = 0; i < pd->node_id_num; i++) {
+ ret = eemi_ops->set_requirement(pd->node_ids[i],
+ ZYNQMP_PM_CAPABILITY_ACCESS,
+ ZYNQMP_PM_MAX_QOS,
+ ZYNQMP_PM_REQUEST_ACK_BLOCKING);
+ if (ret) {
+ pr_err("%s() %s set requirement for node %d failed: %d\n",
+ __func__, domain->name, pd->node_ids[i], ret);
+ return ret;
+ }
+ }
+
+ pr_debug("%s() Powered on %s domain\n", __func__, domain->name);
+ return 0;
+}
+
+/**
+ * zynqmp_gpd_power_off() - Power off PM domain
+ * @domain: Generic PM domain
+ *
+ * This function is called after devices inside a PM domain are suspended, to
+ * power off PM domain.
+ *
+ * Return: 0 on success, error code otherwise
+ */
+static int zynqmp_gpd_power_off(struct generic_pm_domain *domain)
+{
+ int ret, i;
+ struct zynqmp_pm_domain *pd;
+ struct zynqmp_domain_device *zdev, *tmp;
+ u32 capabilities = 0;
+ bool may_wakeup;
+ const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
+
+ if (!eemi_ops || !eemi_ops->set_requirement)
+ return -ENXIO;
+
+ pd = container_of(domain, struct zynqmp_pm_domain, gpd);
+
+ /* If domain is already released there is nothing to be done */
+ if (!(pd->flags & ZYNQMP_PM_DOMAIN_REQUESTED))
+ return 0;
+
+ list_for_each_entry_safe(zdev, tmp, &pd->dev_list, list) {
+ /* If device is in wakeup path, set capability to WAKEUP */
+ may_wakeup = zynqmp_gpd_is_active_wakeup_path(zdev->dev, NULL);
+ if (may_wakeup) {
+ dev_dbg(zdev->dev, "device is in wakeup path in %s\n",
+ domain->name);
+ capabilities = ZYNQMP_PM_CAPABILITY_WAKEUP;
+ break;
+ }
+ }
+
+ for (i = pd->node_id_num - 1; i >= 0; i--) {
+ ret = eemi_ops->set_requirement(pd->node_ids[i],
+ capabilities, 0,
+ ZYNQMP_PM_REQUEST_ACK_NO);
+ /**
+ * If powering down of any node inside this domain fails,
+ * report and return the error
+ */
+ if (ret) {
+ pr_err("%s() %s set requirement for node %d failed: %d\n",
+ __func__, domain->name, pd->node_ids[i], ret);
+ return ret;
+ }
+ }
+
+ pr_debug("%s() Powered off %s domain\n", __func__, domain->name);
+ return 0;
+}
+
+/**
+ * zynqmp_gpd_attach_dev() - Attach device to the PM domain
+ * @domain: Generic PM domain
+ * @dev: Device to attach
+ *
+ * Return: 0 on success, error code otherwise
+ */
+static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain,
+ struct device *dev)
+{
+ int ret, i;
+ struct zynqmp_pm_domain *pd;
+ struct zynqmp_domain_device *zdev;
+ const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
+
+ if (!eemi_ops || !eemi_ops->request_node)
+ return -ENXIO;
+
+ pd = container_of(domain, struct zynqmp_pm_domain, gpd);
+
+ zdev = devm_kzalloc(dev, sizeof(*zdev), GFP_KERNEL);
+ if (!zdev)
+ return -ENOMEM;
+
+ zdev->dev = dev;
+ list_add(&zdev->list, &pd->dev_list);
+
+ /* If this is not the first device to attach there is nothing to do */
+ if (domain->device_count)
+ return 0;
+
+ for (i = 0; i < pd->node_id_num; i++) {
+ ret = eemi_ops->request_node(pd->node_ids[i], 0, 0,
+ ZYNQMP_PM_REQUEST_ACK_BLOCKING);
+ /* If requesting a node fails print and return the error */
+ if (ret) {
+ pr_err("%s() %s request failed for node %d: %d\n",
+ __func__, domain->name, pd->node_ids[i], ret);
+ list_del(&zdev->list);
+ zdev->dev = NULL;
+ devm_kfree(dev, zdev);
+ return ret;
+ }
+ }
+
+ pd->flags |= ZYNQMP_PM_DOMAIN_REQUESTED;
+
+ pr_debug("%s() %s attached to %s domain\n", __func__,
+ dev_name(dev), domain->name);
+ return 0;
+}
+
+/**
+ * zynqmp_gpd_detach_dev() - Detach device from the PM domain
+ * @domain: Generic PM domain
+ * @dev: Device to detach
+ */
+static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain,
+ struct device *dev)
+{
+ int ret, i;
+ struct zynqmp_pm_domain *pd;
+ struct zynqmp_domain_device *zdev, *tmp;
+ const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
+
+ if (!eemi_ops || !eemi_ops->release_node)
+ return;
+
+ pd = container_of(domain, struct zynqmp_pm_domain, gpd);
+
+ list_for_each_entry_safe(zdev, tmp, &pd->dev_list, list)
+ if (zdev->dev == dev) {
+ list_del(&zdev->list);
+ zdev->dev = NULL;
+ devm_kfree(dev, zdev);
+ }
+
+ /* If this is not the last device to detach there is nothing to do */
+ if (domain->device_count)
+ return;
+
+ for (i = 0; i < pd->node_id_num; i++) {
+ ret = eemi_ops->release_node(pd->node_ids[i]);
+ /* If releasing a node fails print the error and return */
+ if (ret) {
+ pr_err("%s() %s release failed for node %d: %d\n",
+ __func__, domain->name, pd->node_ids[i], ret);
+ return;
+ }
+ }
+
+ pd->flags &= ~ZYNQMP_PM_DOMAIN_REQUESTED;
+
+ pr_debug("%s() %s detached from %s domain\n", __func__,
+ dev_name(dev), domain->name);
+}
+
+static const u32 usb0_ids[] = {NODE_USB_0};
+static const u32 usb1_ids[] = {NODE_USB_1};
+static const u32 sata_ids[] = {NODE_SATA};
+static const u32 spi0_ids[] = {NODE_SPI_0};
+static const u32 spi1_ids[] = {NODE_SPI_1};
+static const u32 uart0_ids[] = {NODE_UART_0};
+static const u32 uart1_ids[] = {NODE_UART_1};
+static const u32 eth0_ids[] = {NODE_ETH_0};
+static const u32 eth1_ids[] = {NODE_ETH_1};
+static const u32 eth2_ids[] = {NODE_ETH_2};
+static const u32 eth3_ids[] = {NODE_ETH_3};
+static const u32 i2c0_ids[] = {NODE_I2C_0};
+static const u32 i2c1_ids[] = {NODE_I2C_1};
+static const u32 dp_ids[] = {NODE_DP};
+static const u32 gdma_ids[] = {NODE_GDMA};
+static const u32 adma_ids[] = {NODE_ADMA};
+static const u32 ttc0_ids[] = {NODE_TTC_0};
+static const u32 ttc1_ids[] = {NODE_TTC_1};
+static const u32 ttc2_ids[] = {NODE_TTC_2};
+static const u32 ttc3_ids[] = {NODE_TTC_3};
+static const u32 sd0_ids[] = {NODE_SD_0};
+static const u32 sd1_ids[] = {NODE_SD_1};
+static const u32 nand_ids[] = {NODE_NAND};
+static const u32 qspi_ids[] = {NODE_QSPI};
+static const u32 gpio_ids[] = {NODE_GPIO};
+static const u32 can0_ids[] = {NODE_CAN_0};
+static const u32 can1_ids[] = {NODE_CAN_1};
+static const u32 pcie_ids[] = {NODE_PCIE};
+static const u32 gpu_ids[] = {NODE_GPU, NODE_GPU_PP_0, NODE_GPU_PP_1};
+
+#define ZYNQMP_PM_DOMAIN(nm) { \
+ .name = "pd-" #nm, \
+ .num_id = ARRAY_SIZE(nm ## _ids), \
+ .ids = nm ## _ids, \
+ }
+
+static const struct zynqmp_pd_info pm_ids[] = {
+ ZYNQMP_PM_DOMAIN(usb0),
+ ZYNQMP_PM_DOMAIN(usb1),
+ ZYNQMP_PM_DOMAIN(sata),
+ ZYNQMP_PM_DOMAIN(spi0),
+ ZYNQMP_PM_DOMAIN(spi1),
+ ZYNQMP_PM_DOMAIN(uart0),
+ ZYNQMP_PM_DOMAIN(uart1),
+ ZYNQMP_PM_DOMAIN(eth0),
+ ZYNQMP_PM_DOMAIN(eth1),
+ ZYNQMP_PM_DOMAIN(eth2),
+ ZYNQMP_PM_DOMAIN(eth3),
+ ZYNQMP_PM_DOMAIN(i2c0),
+ ZYNQMP_PM_DOMAIN(i2c1),
+ ZYNQMP_PM_DOMAIN(dp),
+ ZYNQMP_PM_DOMAIN(gdma),
+ ZYNQMP_PM_DOMAIN(adma),
+ ZYNQMP_PM_DOMAIN(ttc0),
+ ZYNQMP_PM_DOMAIN(ttc1),
+ ZYNQMP_PM_DOMAIN(ttc2),
+ ZYNQMP_PM_DOMAIN(ttc3),
+ ZYNQMP_PM_DOMAIN(sd0),
+ ZYNQMP_PM_DOMAIN(sd1),
+ ZYNQMP_PM_DOMAIN(nand),
+ ZYNQMP_PM_DOMAIN(qspi),
+ ZYNQMP_PM_DOMAIN(gpio),
+ ZYNQMP_PM_DOMAIN(can0),
+ ZYNQMP_PM_DOMAIN(can1),
+ ZYNQMP_PM_DOMAIN(pcie),
+ ZYNQMP_PM_DOMAIN(gpu),
+};
+
+static int zynqmp_gpd_probe(struct platform_device *pdev)
+{
+ int i;
+ struct genpd_onecell_data *zynqmp_pd_data;
+ struct generic_pm_domain **domains;
+ struct zynqmp_pm_domain *pd;
+ struct device *dev = &pdev->dev;
+
+ pd = devm_kcalloc(dev, ARRAY_SIZE(pm_ids), sizeof(*pd), GFP_KERNEL);
+ if (!pd)
+ return -ENOMEM;
+
+ zynqmp_pd_data = devm_kzalloc(dev, sizeof(*zynqmp_pd_data), GFP_KERNEL);
+ if (!zynqmp_pd_data)
+ return -ENOMEM;
+
+ domains = devm_kcalloc(dev, ARRAY_SIZE(pm_ids), sizeof(*domains),
+ GFP_KERNEL);
+ if (!domains)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(pm_ids); i++, pd++) {
+ pd->node_id_num = pm_ids[i].num_id;
+ pd->node_ids = pm_ids[i].ids;
+ pd->gpd.name = pm_ids[i].name;
+ pd->gpd.power_off = zynqmp_gpd_power_off;
+ pd->gpd.power_on = zynqmp_gpd_power_on;
+ pd->gpd.attach_dev = zynqmp_gpd_attach_dev;
+ pd->gpd.detach_dev = zynqmp_gpd_detach_dev;
+
+ domains[i] = &pd->gpd;
+
+ /* Mark all PM domains as initially powered off */
+ pm_genpd_init(&pd->gpd, NULL, true);
+ INIT_LIST_HEAD(&pd->dev_list);
+ }
+
+ zynqmp_pd_data->domains = domains;
+ zynqmp_pd_data->num_domains = ARRAY_SIZE(pm_ids);
+ of_genpd_add_provider_onecell(dev->parent->of_node, zynqmp_pd_data);
+
+ return 0;
+}
+
+static int zynqmp_gpd_remove(struct platform_device *pdev)
+{
+ of_genpd_del_provider(pdev->dev.parent->of_node);
+
+ return 0;
+}
+
+static struct platform_driver zynqmp_power_domain_driver = {
+ .driver = {
+ .name = "zynqmp_power_controller",
+ },
+ .probe = zynqmp_gpd_probe,
+ .remove = zynqmp_gpd_remove,
+};
+module_platform_driver(zynqmp_power_domain_driver);
+
+MODULE_ALIAS("platform:zynqmp_power_controller");
--
2.7.4
Ping for comments
Thanks,
Jolly Shah
> -----Original Message-----
> From: Jolly Shah [mailto:[email protected]]
> Sent: Thursday, October 04, 2018 2:24 PM
> To: [email protected]; [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; Michal Simek
> <[email protected]>; [email protected]; [email protected]
> Cc: Rajan Vaja <[email protected]>; [email protected]; linux-arm-
> [email protected]; [email protected]; Jolly Shah
> <[email protected]>
> Subject: [PATCH v3 0/4] drivers: soc: xilinx: Add support for ZynqMP power
> domain driver
>
> The zynqmp power domain driver communicates the usage requirements for
> logical power domains / devices to the platform FW.
> FW is responsible for choosing appropriate power states, taking Linux' usage
> information into account.
>
> This patch series is based on top of Xilinx firmware patch set:
> https://patchwork.kernel.org/cover/10555405/
>
> v3:
> - Changed binding to have FW node as a power controller as suggested by Rob
> - Updated FW driver to register it as mfd child devices from firmware probe
> - Move bindings location as suggested
>
> v2:
> - Rebased on top of latest firmware driver patch series
> - Updated driver name from zynqmp-genpd to zynqmp-power-controller
> - Updated device tree bindings to move power controller node under firmware
> node
>
> Jolly Shah (1):
> drivers: soc: xilinx: Add ZynqMP power domain driver
>
> Rajan Vaja (3):
> dt-bindings: power: Add ZynqMP power domain bindings
> firmware: xilinx: Add APIs to control node status/power
> firmware: xilinx: Add node IDs for zynqmp firmware
>
> .../bindings/power/xlnx,zynqmp-genpd.txt | 34 ++
> drivers/firmware/xilinx/Kconfig | 1 +
> drivers/firmware/xilinx/zynqmp.c | 73 ++++
> drivers/soc/xilinx/Kconfig | 9 +
> drivers/soc/xilinx/Makefile | 2 +
> drivers/soc/xilinx/zynqmp_pm_domains.c | 403
> +++++++++++++++++++++
> include/dt-bindings/power/xlnx-zynqmp-power.h | 39 ++
> include/linux/firmware/xlnx-zynqmp.h | 98 +++++
> 8 files changed, 659 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/power/xlnx,zynqmp-
> genpd.txt
> create mode 100644 drivers/soc/xilinx/zynqmp_pm_domains.c
> create mode 100644 include/dt-bindings/power/xlnx-zynqmp-power.h
>
> --
> 2.7.4
On Wed, Oct 10, 2018 at 06:41:44PM +0000, Jolly Shah wrote:
> Ping for comments
Please see my comments on other ZynqMP firmware patches. In summary, I
need to see a complete binding, not piecemeal additions.
Rob
>
> Thanks,
> Jolly Shah
>
> > -----Original Message-----
> > From: Jolly Shah [mailto:[email protected]]
> > Sent: Thursday, October 04, 2018 2:24 PM
> > To: [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected];
> > [email protected]; [email protected]; Michal Simek
> > <[email protected]>; [email protected]; [email protected]
> > Cc: Rajan Vaja <[email protected]>; [email protected]; linux-arm-
> > [email protected]; [email protected]; Jolly Shah
> > <[email protected]>
> > Subject: [PATCH v3 0/4] drivers: soc: xilinx: Add support for ZynqMP power
> > domain driver
> >
> > The zynqmp power domain driver communicates the usage requirements for
> > logical power domains / devices to the platform FW.
> > FW is responsible for choosing appropriate power states, taking Linux' usage
> > information into account.
> >
> > This patch series is based on top of Xilinx firmware patch set:
> > https://patchwork.kernel.org/cover/10555405/
> >
> > v3:
> > - Changed binding to have FW node as a power controller as suggested by Rob
> > - Updated FW driver to register it as mfd child devices from firmware probe
> > - Move bindings location as suggested
> >
> > v2:
> > - Rebased on top of latest firmware driver patch series
> > - Updated driver name from zynqmp-genpd to zynqmp-power-controller
> > - Updated device tree bindings to move power controller node under firmware
> > node
> >
> > Jolly Shah (1):
> > drivers: soc: xilinx: Add ZynqMP power domain driver
> >
> > Rajan Vaja (3):
> > dt-bindings: power: Add ZynqMP power domain bindings
> > firmware: xilinx: Add APIs to control node status/power
> > firmware: xilinx: Add node IDs for zynqmp firmware
> >
> > .../bindings/power/xlnx,zynqmp-genpd.txt | 34 ++
> > drivers/firmware/xilinx/Kconfig | 1 +
> > drivers/firmware/xilinx/zynqmp.c | 73 ++++
> > drivers/soc/xilinx/Kconfig | 9 +
> > drivers/soc/xilinx/Makefile | 2 +
> > drivers/soc/xilinx/zynqmp_pm_domains.c | 403
> > +++++++++++++++++++++
> > include/dt-bindings/power/xlnx-zynqmp-power.h | 39 ++
> > include/linux/firmware/xlnx-zynqmp.h | 98 +++++
> > 8 files changed, 659 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/power/xlnx,zynqmp-
> > genpd.txt
> > create mode 100644 drivers/soc/xilinx/zynqmp_pm_domains.c
> > create mode 100644 include/dt-bindings/power/xlnx-zynqmp-power.h
> >
> > --
> > 2.7.4
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Hi Rob,
> -----Original Message-----
> From: Rob Herring [mailto:[email protected]]
> Sent: 12 October 2018 08:23 PM
> To: Jolly Shah <[email protected]>
> Cc: [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; Michal Simek <[email protected]>;
> [email protected]; [email protected]; Rajan Vaja
> <[email protected]>; [email protected]; linux-arm-
> [email protected]
> Subject: Re: [PATCH v3 0/4] drivers: soc: xilinx: Add support for ZynqMP power
> domain driver
>
> On Wed, Oct 10, 2018 at 06:41:44PM +0000, Jolly Shah wrote:
> > Ping for comments
>
> Please see my comments on other ZynqMP firmware patches. In summary, I
> need to see a complete binding, not piecemeal additions.
[Rajan] Based on your suggestion to have complete zynqmp firmware binding, we can
have two options:
1. Use firmware node instead of having separate subnodes for dependent
drivers(genpd, reset, nvmem, etc). In this case, firmware driver
needs to take care of probing of depedent drivers. We have thought of
MFD approach to probe depedent drivers. Firmware driver would register
depedent drivers as MFD child devices from firmware probe.
firmware {
zynqmp_firmware: zynqmp-firmware {
compatible = "xlnx,zynqmp-firmware";
method = "smc";
#power-domain-cells = <1>;
};
};
sata {
...
power-domains = <&zynqmp_firmware 2>;
...
};
drivers/firmware/xilinx/zynqmp.c: zynqmp_firmware_probe(struct platform_device *pdev)
static const struct mfd_cell firmware_devs[] = {
{
.name = "zynqmp_power_controller",
},
};
ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, firmware_devs,
ARRAY_SIZE(firmware_devs), NULL, 0, NULL);
2. Keep separate node for each device outside (in parralel to) zynqmp-firmware node.
In this case, each firmware driver consumer is independet driver/binding.
Order of probe can be taken care by -EPROBE defer mechanism.
Example:
firmware {
zynqmp_firmware: zynqmp-firmware {
compatible = "xlnx,zynqmp-firmware";
method = "smc";
};
};
zynqmp_power_domains: zynqmp-power-domains {
...
compatible = "xlnx,zynqmp-genpd";
#power-domain-cells = <1>;
...
};
sata {
...
power-domains = <&zynqmp_power_domains 2>;
...
};
Please let us know your inputs and suggest better approach so we can submit
changes accordingly.
Thanks,
Rajan
>
> Rob
>
> >
> > Thanks,
> > Jolly Shah
> >
> > > -----Original Message-----
> > > From: Jolly Shah [mailto:[email protected]]
> > > Sent: Thursday, October 04, 2018 2:24 PM
> > > To: [email protected]; [email protected]; [email protected];
> > > [email protected]; [email protected];
> > > [email protected]; [email protected]; Michal Simek
> > > <[email protected]>; [email protected]; [email protected]
> > > Cc: Rajan Vaja <[email protected]>; [email protected]; linux-arm-
> > > [email protected]; [email protected]; Jolly Shah
> > > <[email protected]>
> > > Subject: [PATCH v3 0/4] drivers: soc: xilinx: Add support for ZynqMP power
> > > domain driver
> > >
> > > The zynqmp power domain driver communicates the usage requirements for
> > > logical power domains / devices to the platform FW.
> > > FW is responsible for choosing appropriate power states, taking Linux' usage
> > > information into account.
> > >
> > > This patch series is based on top of Xilinx firmware patch set:
> > > https://patchwork.kernel.org/cover/10555405/
> > >
> > > v3:
> > > - Changed binding to have FW node as a power controller as suggested by Rob
> > > - Updated FW driver to register it as mfd child devices from firmware probe
> > > - Move bindings location as suggested
> > >
> > > v2:
> > > - Rebased on top of latest firmware driver patch series
> > > - Updated driver name from zynqmp-genpd to zynqmp-power-controller
> > > - Updated device tree bindings to move power controller node under firmware
> > > node
> > >
> > > Jolly Shah (1):
> > > drivers: soc: xilinx: Add ZynqMP power domain driver
> > >
> > > Rajan Vaja (3):
> > > dt-bindings: power: Add ZynqMP power domain bindings
> > > firmware: xilinx: Add APIs to control node status/power
> > > firmware: xilinx: Add node IDs for zynqmp firmware
> > >
> > > .../bindings/power/xlnx,zynqmp-genpd.txt | 34 ++
> > > drivers/firmware/xilinx/Kconfig | 1 +
> > > drivers/firmware/xilinx/zynqmp.c | 73 ++++
> > > drivers/soc/xilinx/Kconfig | 9 +
> > > drivers/soc/xilinx/Makefile | 2 +
> > > drivers/soc/xilinx/zynqmp_pm_domains.c | 403
> > > +++++++++++++++++++++
> > > include/dt-bindings/power/xlnx-zynqmp-power.h | 39 ++
> > > include/linux/firmware/xlnx-zynqmp.h | 98 +++++
> > > 8 files changed, 659 insertions(+)
> > > create mode 100644 Documentation/devicetree/bindings/power/xlnx,zynqmp-
> > > genpd.txt
> > > create mode 100644 drivers/soc/xilinx/zynqmp_pm_domains.c
> > > create mode 100644 include/dt-bindings/power/xlnx-zynqmp-power.h
> > >
> > > --
> > > 2.7.4
> >
> >
> > _______________________________________________
> > linux-arm-kernel mailing list
> > [email protected]
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel