diff options
author | Filip Drazic <filip.drazic@aggios.com> | 2016-07-26 12:11:33 +0200 |
---|---|---|
committer | Soren Brinkmann <soren.brinkmann@xilinx.com> | 2016-09-13 09:19:03 -0700 |
commit | b516b7dc5dfeaf14f452f608ce9014c79153ce59 (patch) | |
tree | 1bdd5ff37fc03683e00b7a95b3b04bf7302fda30 | |
parent | 6aa4c533b815df625f960eb58c44e98d667e91ab (diff) |
zynqmp: pm: Call set_wakeup_source for all wake devices on sys-suspend
During system suspend, identify slaves which are configured
as wake sources and call pm_set_wakeup_source API for each of them.
Identifying if device may wake the system is done by checking if any
interrupt of that device is enabled in GICD_ISENABLER when the APU is
about to enter SUSPEND_TO_RAM state. If such interrupt is found,
pm_set_wakeup_source is called with corresponding PM node ID as
argument.
Signed-off-by: Filip Drazic <filip.drazic@aggios.com>
-rw-r--r-- | plat/xilinx/zynqmp/pm_service/pm_api_sys.c | 2 | ||||
-rw-r--r-- | plat/xilinx/zynqmp/pm_service/pm_client.c | 153 | ||||
-rw-r--r-- | plat/xilinx/zynqmp/pm_service/pm_client.h | 2 | ||||
-rw-r--r-- | plat/xilinx/zynqmp/pm_service/pm_defs.h | 1 |
4 files changed, 155 insertions, 3 deletions
diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index f78a3113..52d91711 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -97,7 +97,7 @@ enum pm_ret_status pm_self_suspend(enum pm_node_id nid, * Do client specific suspend operations * (e.g. set powerdown request bit) */ - pm_client_suspend(proc); + pm_client_suspend(proc, state); /* Send request to the PMU */ PM_PACK_PAYLOAD6(payload, PM_SELF_SUSPEND, proc->node_id, latency, state, address, (address >> 32)); diff --git a/plat/xilinx/zynqmp/pm_service/pm_client.c b/plat/xilinx/zynqmp/pm_service/pm_client.c index f0b34cbb..b77a1cf0 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_client.c +++ b/plat/xilinx/zynqmp/pm_service/pm_client.c @@ -33,16 +33,21 @@ * for getting information about and changing state of the APU. */ +#include <assert.h> #include <bakery_lock.h> #include <gicv2.h> +#include <gic_common.h> #include <bl_common.h> #include <mmio.h> +#include <string.h> #include <utils.h> #include "pm_api_sys.h" #include "pm_client.h" #include "pm_ipi.h" #include "../zynqmp_def.h" +#define IRQ_MAX 84 +#define NUM_GICD_ISENABLER ((IRQ_MAX >> 5) + 1) #define UNDEFINED_CPUID (~0) DEFINE_BAKERY_LOCK(pm_client_secure_lock); @@ -73,6 +78,148 @@ static const struct pm_proc const pm_procs_all[] = { }, }; +/* Interrupt to PM node ID map */ +static enum pm_node_id irq_node_map[IRQ_MAX + 1] = { + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, /* 3 */ + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, /* 7 */ + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, /* 11 */ + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_NAND, + NODE_QSPI, /* 15 */ + NODE_GPIO, + NODE_I2C_0, + NODE_I2C_1, + NODE_SPI_0, /* 19 */ + NODE_SPI_1, + NODE_UART_0, + NODE_UART_1, + NODE_CAN_0, /* 23 */ + NODE_CAN_1, + NODE_UNKNOWN, + NODE_RTC, + NODE_RTC, /* 27 */ + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, /* 31 */ + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, /* 35, NODE_IPI_APU */ + NODE_TTC_0, + NODE_TTC_0, + NODE_TTC_0, + NODE_TTC_1, /* 39 */ + NODE_TTC_1, + NODE_TTC_1, + NODE_TTC_2, + NODE_TTC_2, /* 43 */ + NODE_TTC_2, + NODE_TTC_3, + NODE_TTC_3, + NODE_TTC_3, /* 47 */ + NODE_SD_0, + NODE_SD_1, + NODE_SD_0, + NODE_SD_1, /* 51 */ + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, /* 55 */ + NODE_UNKNOWN, + NODE_ETH_0, + NODE_ETH_0, + NODE_ETH_1, /* 59 */ + NODE_ETH_1, + NODE_ETH_2, + NODE_ETH_2, + NODE_ETH_3, /* 63 */ + NODE_ETH_3, + NODE_USB_0, + NODE_USB_0, + NODE_USB_0, /* 67 */ + NODE_USB_0, + NODE_USB_0, + NODE_USB_1, + NODE_USB_1, /* 71 */ + NODE_USB_1, + NODE_USB_1, + NODE_USB_1, + NODE_USB_0, /* 75 */ + NODE_USB_0, + NODE_ADMA, + NODE_ADMA, + NODE_ADMA, /* 79 */ + NODE_ADMA, + NODE_ADMA, + NODE_ADMA, + NODE_ADMA, /* 83 */ + NODE_ADMA, +}; + +/** + * irq_to_pm_node - Get PM node ID corresponding to the interrupt number + * @irq: Interrupt number + * + * Return: PM node ID corresponding to the specified interrupt + */ +static enum pm_node_id irq_to_pm_node(unsigned int irq) +{ + assert(irq <= IRQ_MAX); + return irq_node_map[irq]; +} + +/** + * pm_client_set_wakeup_sources - Set all slaves with enabled interrupts as wake + * sources in the PMU firmware + */ +static void pm_client_set_wakeup_sources(void) +{ + uint32_t reg_num; + uint8_t pm_wakeup_nodes_set[NODE_MAX]; + uintptr_t isenabler1 = BASE_GICD_BASE + GICD_ISENABLER + 4; + + memset(&pm_wakeup_nodes_set, 0, sizeof(pm_wakeup_nodes_set)); + + for (reg_num = 0; reg_num < NUM_GICD_ISENABLER; reg_num++) { + uint32_t base_irq = reg_num << ISENABLER_SHIFT; + uint32_t reg = mmio_read_32(isenabler1 + (reg_num << 2)); + + if (!reg) + continue; + + while (reg) { + enum pm_node_id node; + uint32_t idx, ret, irq, lowest_set = reg & (-reg); + + idx = __builtin_ctz(lowest_set); + irq = base_irq + idx; + + if (irq > IRQ_MAX) + break; + + node = irq_to_pm_node(irq); + reg &= ~lowest_set; + + if ((node != NODE_UNKNOWN) && + (!pm_wakeup_nodes_set[node])) { + ret = pm_set_wakeup_source(NODE_APU, node, 1); + pm_wakeup_nodes_set[node] = !ret; + } + } + } +} + /** * pm_get_proc() - returns pointer to the proc structure * @cpuid: id of the cpu whose proc struct pointer should be returned @@ -124,11 +271,15 @@ const struct pm_proc *primary_proc = &pm_procs_all[0]; * * This function should contain any PU-specific actions * required prior to sending suspend request to PMU + * Actions taken depend on the state system is suspending to. */ -void pm_client_suspend(const struct pm_proc *proc) +void pm_client_suspend(const struct pm_proc *proc, unsigned int state) { bakery_lock_get(&pm_client_secure_lock); + if (state == PM_STATE_SUSPEND_TO_RAM) + pm_client_set_wakeup_sources(); + /* Set powerdown request */ mmio_write_32(APU_PWRCTL, mmio_read_32(APU_PWRCTL) | proc->pwrdn_mask); diff --git a/plat/xilinx/zynqmp/pm_service/pm_client.h b/plat/xilinx/zynqmp/pm_service/pm_client.h index 9483b0d1..7f80d5b4 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_client.h +++ b/plat/xilinx/zynqmp/pm_service/pm_client.h @@ -40,7 +40,7 @@ #include "pm_common.h" /* Functions to be implemented by each PU */ -void pm_client_suspend(const struct pm_proc *proc); +void pm_client_suspend(const struct pm_proc *proc, unsigned int state); void pm_client_abort_suspend(void); void pm_client_wakeup(const struct pm_proc *proc); enum pm_ret_status set_ocm_retention(void); diff --git a/plat/xilinx/zynqmp/pm_service/pm_defs.h b/plat/xilinx/zynqmp/pm_service/pm_defs.h index 90d7d3ad..db44785d 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_defs.h +++ b/plat/xilinx/zynqmp/pm_service/pm_defs.h @@ -152,6 +152,7 @@ enum pm_node_id { NODE_PCIE, NODE_PCAP, NODE_RTC, + NODE_MAX }; enum pm_request_ack { |