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 <afd@ti.com>
Acked-by: Nishanth Menon <nm@ti.com>
This commit is contained in:
Andrew F. Davis 2019-01-03 13:23:52 -06:00
parent 394977e7ef
commit 72f418e05f
2 changed files with 83 additions and 0 deletions

View File

@ -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
*

View File

@ -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