From 394977e7ef2f85e112cffc66a42343d833ba9914 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Tue, 18 Dec 2018 13:21:12 -0600 Subject: [PATCH 1/3] ti: k3: drivers: ti_sci: Add processor status wait API This TI-SCI API can be used wait for a set of processor status flags to be set or cleared. The flags are processor type specific. This command will not return ACK until the specified status is met. NACK will be returned after the timeout elapses or on error. Signed-off-by: Andrew F. Davis Acked-by: Nishanth Menon --- plat/ti/k3/common/drivers/ti_sci/ti_sci.c | 81 +++++++++++++++++++ plat/ti/k3/common/drivers/ti_sci/ti_sci.h | 9 +++ .../common/drivers/ti_sci/ti_sci_protocol.h | 49 +++++++++++ 3 files changed, 139 insertions(+) diff --git a/plat/ti/k3/common/drivers/ti_sci/ti_sci.c b/plat/ti/k3/common/drivers/ti_sci/ti_sci.c index b211bdf60..f6d71a0a4 100644 --- a/plat/ti/k3/common/drivers/ti_sci/ti_sci.c +++ b/plat/ti/k3/common/drivers/ti_sci/ti_sci.c @@ -1458,6 +1458,87 @@ int ti_sci_proc_get_boot_status(uint8_t proc_id, uint64_t *bv, return 0; } +/** + * ti_sci_proc_wait_boot_status() - Wait for a processor boot status + * + * @proc_id: Processor ID this request is for + * @num_wait_iterations Total number of iterations we will check before + * we will timeout and give up + * @num_match_iterations How many iterations should we have continued + * status to account for status bits glitching. + * This is to make sure that match occurs for + * consecutive checks. This implies that the + * worst case should consider that the stable + * time should at the worst be num_wait_iterations + * num_match_iterations to prevent timeout. + * @delay_per_iteration_us Specifies how long to wait (in micro seconds) + * between each status checks. This is the minimum + * duration, and overhead of register reads and + * checks are on top of this and can vary based on + * varied conditions. + * @delay_before_iterations_us Specifies how long to wait (in micro seconds) + * before the very first check in the first + * iteration of status check loop. This is the + * minimum duration, and overhead of register + * reads and checks are. + * @status_flags_1_set_all_wait If non-zero, Specifies that all bits of the + * status matching this field requested MUST be 1. + * @status_flags_1_set_any_wait If non-zero, Specifies that at least one of the + * bits matching this field requested MUST be 1. + * @status_flags_1_clr_all_wait If non-zero, Specifies that all bits of the + * status matching this field requested MUST be 0. + * @status_flags_1_clr_any_wait If non-zero, Specifies that at least one of the + * bits matching this field requested MUST be 0. + * + * Return: 0 if all goes well, else appropriate error message + */ +int ti_sci_proc_wait_boot_status(uint8_t proc_id, uint8_t num_wait_iterations, + uint8_t num_match_iterations, + uint8_t delay_per_iteration_us, + uint8_t delay_before_iterations_us, + uint32_t status_flags_1_set_all_wait, + uint32_t status_flags_1_set_any_wait, + uint32_t status_flags_1_clr_all_wait, + uint32_t status_flags_1_clr_any_wait) +{ + struct ti_sci_msg_req_wait_proc_boot_status req; + struct ti_sci_msg_hdr resp; + + struct ti_sci_xfer xfer; + int ret; + + ret = ti_sci_setup_one_xfer(TISCI_MSG_WAIT_PROC_BOOT_STATUS, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + &req, sizeof(req), + &resp, sizeof(resp), + &xfer); + if (ret) { + ERROR("Message alloc failed (%d)\n", ret); + return ret; + } + + req.processor_id = proc_id; + req.num_wait_iterations = num_wait_iterations; + req.num_match_iterations = num_match_iterations; + req.delay_per_iteration_us = delay_per_iteration_us; + req.delay_before_iterations_us = delay_before_iterations_us; + req.status_flags_1_set_all_wait = status_flags_1_set_all_wait; + req.status_flags_1_set_any_wait = status_flags_1_set_any_wait; + req.status_flags_1_clr_all_wait = status_flags_1_clr_all_wait; + req.status_flags_1_clr_any_wait = status_flags_1_clr_any_wait; + + ret = ti_sci_do_xfer(&xfer); + if (ret) { + ERROR("Transfer send failed (%d)\n", ret); + return ret; + } + + if (!ti_sci_is_response_ack(&resp)) + return -ENODEV; + + return 0; +} + /** * ti_sci_init() - Basic initialization * diff --git a/plat/ti/k3/common/drivers/ti_sci/ti_sci.h b/plat/ti/k3/common/drivers/ti_sci/ti_sci.h index 1176b00f4..91f864573 100644 --- a/plat/ti/k3/common/drivers/ti_sci/ti_sci.h +++ b/plat/ti/k3/common/drivers/ti_sci/ti_sci.h @@ -179,6 +179,7 @@ int ti_sci_core_reboot(void); * and then set the processor configuration flags. * @cert_addr: Memory address at which payload image certificate is located. * - ti_sci_proc_get_boot_status - Command to get the processor boot status + * - ti_sci_proc_wait_boot_status - Command to wait for a processor boot status * * NOTE: for all these functions, the following are generic in nature: * @proc_id: Processor ID @@ -197,6 +198,14 @@ int ti_sci_proc_get_boot_status(uint8_t proc_id, uint64_t *bv, uint32_t *cfg_flags, uint32_t *ctrl_flags, uint32_t *sts_flags); +int ti_sci_proc_wait_boot_status(uint8_t proc_id, uint8_t num_wait_iterations, + uint8_t num_match_iterations, + uint8_t delay_per_iteration_us, + uint8_t delay_before_iterations_us, + uint32_t status_flags_1_set_all_wait, + uint32_t status_flags_1_set_any_wait, + uint32_t status_flags_1_clr_all_wait, + uint32_t status_flags_1_clr_any_wait); /** * ti_sci_init() - Basic initialization diff --git a/plat/ti/k3/common/drivers/ti_sci/ti_sci_protocol.h b/plat/ti/k3/common/drivers/ti_sci/ti_sci_protocol.h index c6d76d7ea..a921e512a 100644 --- a/plat/ti/k3/common/drivers/ti_sci/ti_sci_protocol.h +++ b/plat/ti/k3/common/drivers/ti_sci/ti_sci_protocol.h @@ -46,6 +46,7 @@ #define TISCI_MSG_SET_PROC_BOOT_CTRL 0xc101 #define TISCI_MSG_PROC_AUTH_BOOT_IMIAGE 0xc120 #define TISCI_MSG_GET_PROC_BOOT_STATUS 0xc400 +#define TISCI_MSG_WAIT_PROC_BOOT_STATUS 0xc401 /** * struct ti_sci_msg_hdr - Generic Message Header for All messages and responses @@ -647,4 +648,52 @@ struct ti_sci_msg_resp_get_proc_boot_status { uint32_t status_flags; } __packed; +/** + * struct ti_sci_msg_req_wait_proc_boot_status - Wait for a processor boot status + * @hdr: Generic Header + * @processor_id: ID of processor + * @num_wait_iterations Total number of iterations we will check before + * we will timeout and give up + * @num_match_iterations How many iterations should we have continued + * status to account for status bits glitching. + * This is to make sure that match occurs for + * consecutive checks. This implies that the + * worst case should consider that the stable + * time should at the worst be num_wait_iterations + * num_match_iterations to prevent timeout. + * @delay_per_iteration_us Specifies how long to wait (in micro seconds) + * between each status checks. This is the minimum + * duration, and overhead of register reads and + * checks are on top of this and can vary based on + * varied conditions. + * @delay_before_iterations_us Specifies how long to wait (in micro seconds) + * before the very first check in the first + * iteration of status check loop. This is the + * minimum duration, and overhead of register + * reads and checks are. + * @status_flags_1_set_all_wait If non-zero, Specifies that all bits of the + * status matching this field requested MUST be 1. + * @status_flags_1_set_any_wait If non-zero, Specifies that at least one of the + * bits matching this field requested MUST be 1. + * @status_flags_1_clr_all_wait If non-zero, Specifies that all bits of the + * status matching this field requested MUST be 0. + * @status_flags_1_clr_any_wait If non-zero, Specifies that at least one of the + * bits matching this field requested MUST be 0. + * + * Request type is TISCI_MSG_WAIT_PROC_BOOT_STATUS, response is appropriate + * message, or NACK in case of inability to satisfy request. + */ +struct ti_sci_msg_req_wait_proc_boot_status { + struct ti_sci_msg_hdr hdr; + uint8_t processor_id; + uint8_t num_wait_iterations; + uint8_t num_match_iterations; + uint8_t delay_per_iteration_us; + uint8_t delay_before_iterations_us; + uint32_t status_flags_1_set_all_wait; + uint32_t status_flags_1_set_any_wait; + uint32_t status_flags_1_clr_all_wait; + uint32_t status_flags_1_clr_any_wait; +} __packed; + #endif /* TI_SCI_PROTOCOL_H */ From 72f418e05f68d27662fb840e66fb2bb43eb97c99 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Thu, 3 Jan 2019 13:23:52 -0600 Subject: [PATCH 2/3] ti: k3: drivers: ti_sci: Add processor shutdown API This is a pseudo-API command consisting of a wait processor status command and a set device state command queued back-to-back without waiting for the System Firmware to ACK either message. This is needed as the K3 power down specification states the System Firmware must wait for a processor to be in WFI/WFE before powering it down. The current implementation of System Firmware does not provide such a command. Also given that with PSCI the core to be shutdown is the core that is processing the shutdown request, the core cannot itself wait for its own WFI/WFE status. To workaround this limitation, we submit a wait processor status command followed by the actual shutdown command. The shutdown command will not be processed until the wait command has finished. In this way we can continue to WFI before the wait command status has been met or timed-out and the shutdown command is processed. Signed-off-by: Andrew F. Davis Acked-by: Nishanth Menon --- plat/ti/k3/common/drivers/ti_sci/ti_sci.c | 82 +++++++++++++++++++++++ plat/ti/k3/common/drivers/ti_sci/ti_sci.h | 1 + 2 files changed, 83 insertions(+) diff --git a/plat/ti/k3/common/drivers/ti_sci/ti_sci.c b/plat/ti/k3/common/drivers/ti_sci/ti_sci.c index f6d71a0a4..81488a155 100644 --- a/plat/ti/k3/common/drivers/ti_sci/ti_sci.c +++ b/plat/ti/k3/common/drivers/ti_sci/ti_sci.c @@ -1539,6 +1539,88 @@ int ti_sci_proc_wait_boot_status(uint8_t proc_id, uint8_t num_wait_iterations, return 0; } +/** + * ti_sci_proc_shutdown() - Shutdown Processor without waiting for ACKs + * + * @proc_id: Processor ID this request is for + * @dev_id: Device identifier this request is for + * + * Return: 0 if all goes well, else appropriate error message + */ +int ti_sci_proc_shutdown(uint8_t proc_id, uint32_t dev_id) +{ + struct ti_sci_msg_req_wait_proc_boot_status wait_req; + struct ti_sci_msg_req_set_device_state set_req; + /* + * We will not be waiting for this response, but declare one anyway + * to pass to the setup function so the checks will still pass + */ + struct ti_sci_msg_hdr resp; + + struct ti_sci_xfer xfer; + int ret; + + /* Start by sending wait command */ + + /* Setup with NORESPONSE flag to keep response queue clean */ + ret = ti_sci_setup_one_xfer(TISCI_MSG_WAIT_PROC_BOOT_STATUS, + TI_SCI_FLAG_REQ_GENERIC_NORESPONSE, + &wait_req, sizeof(wait_req), + &resp, sizeof(resp), + &xfer); + if (ret) { + ERROR("Message alloc failed (%d)\n", ret); + return ret; + } + + wait_req.processor_id = proc_id; + /* + * Wait maximum time to give us the best chance to get + * to WFI before this command timeouts + */ + wait_req.delay_before_iterations_us = UINT8_MAX; + wait_req.num_wait_iterations = UINT8_MAX; + wait_req.delay_per_iteration_us = UINT8_MAX; /* TODO: optimize time */ + wait_req.num_match_iterations = 2; + wait_req.status_flags_1_set_all_wait = 0; + /* Wait for either WFE or WFI */ + wait_req.status_flags_1_set_any_wait = PROC_BOOT_STATUS_FLAG_ARMV8_WFE | + PROC_BOOT_STATUS_FLAG_ARMV8_WFI; + wait_req.status_flags_1_clr_all_wait = 0; + wait_req.status_flags_1_clr_any_wait = 0; + + /* Send wait message */ + ret = k3_sec_proxy_send(SP_HIGH_PRIORITY, &xfer.tx_message); + if (ret) { + ERROR("Message sending failed (%d)\n", ret); + return ret; + } + + /* Now queue up the shutdown request */ + ret = ti_sci_setup_one_xfer(TI_SCI_MSG_SET_DEVICE_STATE, + TI_SCI_FLAG_REQ_GENERIC_NORESPONSE, + &set_req, sizeof(set_req), + &resp, sizeof(resp), + &xfer); + if (ret) { + ERROR("Message alloc failed (%d)\n", ret); + return ret; + } + + set_req.id = dev_id; + set_req.state = MSG_DEVICE_SW_STATE_AUTO_OFF; + + /* Send shutdown message */ + ret = k3_sec_proxy_send(SP_HIGH_PRIORITY, &xfer.tx_message); + if (ret) { + ERROR("Message sending failed (%d)\n", ret); + return ret; + } + + /* Return without waiting for responses */ + return 0; +} + /** * ti_sci_init() - Basic initialization * diff --git a/plat/ti/k3/common/drivers/ti_sci/ti_sci.h b/plat/ti/k3/common/drivers/ti_sci/ti_sci.h index 91f864573..d07ee61df 100644 --- a/plat/ti/k3/common/drivers/ti_sci/ti_sci.h +++ b/plat/ti/k3/common/drivers/ti_sci/ti_sci.h @@ -206,6 +206,7 @@ int ti_sci_proc_wait_boot_status(uint8_t proc_id, uint8_t num_wait_iterations, uint32_t status_flags_1_set_any_wait, uint32_t status_flags_1_clr_all_wait, uint32_t status_flags_1_clr_any_wait); +int ti_sci_proc_shutdown(uint8_t proc_id, uint32_t dev_id); /** * ti_sci_init() - Basic initialization From ca3d3414a8d3860233119c974f7227f6885f78c3 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Thu, 3 Jan 2019 13:24:25 -0600 Subject: [PATCH 3/3] ti: k3: common: Use shutdown API for PSCI core poweroff To ensure WFI is reached before the PSC is trigger to power-down a processor, the shutdonw API must be used. Signed-off-by: Andrew F. Davis Acked-by: Nishanth Menon --- plat/ti/k3/common/k3_psci.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plat/ti/k3/common/k3_psci.c b/plat/ti/k3/common/k3_psci.c index cb75bf654..235e6396b 100644 --- a/plat/ti/k3/common/k3_psci.c +++ b/plat/ti/k3/common/k3_psci.c @@ -81,15 +81,16 @@ static int k3_pwr_domain_on(u_register_t mpidr) void k3_pwr_domain_off(const psci_power_state_t *target_state) { - int core_id, device, ret; + int core_id, proc, device, ret; /* Prevent interrupts from spuriously waking up this cpu */ k3_gic_cpuif_disable(); core_id = plat_my_core_pos(); + proc = PLAT_PROC_START_ID + core_id; device = PLAT_PROC_DEVICE_START_ID + core_id; - ret = ti_sci_device_put(device); + ret = ti_sci_proc_shutdown(proc, device); if (ret) { ERROR("Request to stop core failed: %d\n", ret); return;