firmware: ti_sci: Add processor shutdown API method

Add and expose a new processor shutdown API that wraps the two TISCI
messages involved in initiating a core shutdown. The API will first
queue a message to have the DMSC wait for a certain processor boot
status to happen followed by a message to trigger the actual shutdown-
with both messages being sent without waiting or requesting for a
response. Note that the processor shutdown API call will need to be
followed up by user software placing the respective core into either
WFE or WFI mode.

Signed-off-by: Andreas Dannenberg <dannenberg@ti.com>
This commit is contained in:
Andreas Dannenberg 2019-06-07 19:24:40 +05:30 committed by Tom Rini
parent ae0b8a2bc8
commit 410adcc9e2
3 changed files with 241 additions and 4 deletions

View file

@ -101,7 +101,8 @@ struct ti_sci_info {
* @msg_flags: Flag to set for the message
* @buf: Buffer to be send to mailbox channel
* @tx_message_size: transmit message size
* @rx_message_size: receive message size
* @rx_message_size: receive message size. may be set to zero for send-only
* transactions.
*
* Helper function which is used by various command functions that are
* exposed to clients of this driver for allocating a message traffic event.
@ -121,7 +122,8 @@ static struct ti_sci_xfer *ti_sci_setup_one_xfer(struct ti_sci_info *info,
/* Ensure we have sane transfer sizes */
if (rx_message_size > info->desc->max_msg_size ||
tx_message_size > info->desc->max_msg_size ||
rx_message_size < sizeof(*hdr) || tx_message_size < sizeof(*hdr))
(rx_message_size > 0 && rx_message_size < sizeof(*hdr)) ||
tx_message_size < sizeof(*hdr))
return ERR_PTR(-ERANGE);
info->seq = ~info->seq;
@ -219,7 +221,9 @@ static inline int ti_sci_do_xfer(struct ti_sci_info *info,
xfer->tx_message.buf = (u32 *)secure_buf;
xfer->tx_message.len += sizeof(secure_hdr);
xfer->rx_len += sizeof(secure_hdr);
if (xfer->rx_len)
xfer->rx_len += sizeof(secure_hdr);
}
/* Send the message */
@ -230,7 +234,11 @@ static inline int ti_sci_do_xfer(struct ti_sci_info *info,
return ret;
}
return ti_sci_get_response(info, xfer, &info->chan_rx);
/* Get response if requested */
if (xfer->rx_len)
ret = ti_sci_get_response(info, xfer, &info->chan_rx);
return ret;
}
/**
@ -469,6 +477,49 @@ static int ti_sci_set_device_state(const struct ti_sci_handle *handle,
return ret;
}
/**
* ti_sci_set_device_state_no_wait() - Set device state helper without
* requesting or waiting for a response.
* @handle: pointer to TI SCI handle
* @id: Device identifier
* @flags: flags to setup for the device
* @state: State to move the device to
*
* Return: 0 if all went well, else returns appropriate error value.
*/
static int ti_sci_set_device_state_no_wait(const struct ti_sci_handle *handle,
u32 id, u32 flags, u8 state)
{
struct ti_sci_msg_req_set_device_state req;
struct ti_sci_info *info;
struct ti_sci_xfer *xfer;
int ret = 0;
if (IS_ERR(handle))
return PTR_ERR(handle);
if (!handle)
return -EINVAL;
info = handle_to_ti_sci_info(handle);
xfer = ti_sci_setup_one_xfer(info, TI_SCI_MSG_SET_DEVICE_STATE,
flags | TI_SCI_FLAG_REQ_GENERIC_NORESPONSE,
(u32 *)&req, sizeof(req), 0);
if (IS_ERR(xfer)) {
ret = PTR_ERR(xfer);
dev_err(info->dev, "Message alloc failed(%d)\n", ret);
return ret;
}
req.id = id;
req.state = state;
ret = ti_sci_do_xfer(info, xfer);
if (ret)
dev_err(info->dev, "Mbox send fail %d\n", ret);
return ret;
}
/**
* ti_sci_get_device_state() - Get device state helper
* @handle: Handle to the device
@ -2039,6 +2090,137 @@ static int ti_sci_cmd_get_proc_boot_status(const struct ti_sci_handle *handle,
return ret;
}
/**
* ti_sci_proc_wait_boot_status_no_wait() - Helper function to wait for a
* processor boot status without requesting or
* waiting for a response.
* @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
*/
static int
ti_sci_proc_wait_boot_status_no_wait(const struct ti_sci_handle *handle,
u8 proc_id,
u8 num_wait_iterations,
u8 num_match_iterations,
u8 delay_per_iteration_us,
u8 delay_before_iterations_us,
u32 status_flags_1_set_all_wait,
u32 status_flags_1_set_any_wait,
u32 status_flags_1_clr_all_wait,
u32 status_flags_1_clr_any_wait)
{
struct ti_sci_msg_req_wait_proc_boot_status req;
struct ti_sci_info *info;
struct ti_sci_xfer *xfer;
int ret = 0;
if (IS_ERR(handle))
return PTR_ERR(handle);
if (!handle)
return -EINVAL;
info = handle_to_ti_sci_info(handle);
xfer = ti_sci_setup_one_xfer(info, TISCI_MSG_WAIT_PROC_BOOT_STATUS,
TI_SCI_FLAG_REQ_GENERIC_NORESPONSE,
(u32 *)&req, sizeof(req), 0);
if (IS_ERR(xfer)) {
ret = PTR_ERR(xfer);
dev_err(info->dev, "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(info, xfer);
if (ret)
dev_err(info->dev, "Mbox send fail %d\n", ret);
return ret;
}
/**
* ti_sci_cmd_proc_shutdown_no_wait() - Command to shutdown a core without
* requesting or waiting for a response. Note that this API call
* should be followed by placing the respective processor into
* either WFE or WFI mode.
* @handle: Pointer to TI SCI handle
* @proc_id: Processor ID this request is for
*
* Return: 0 if all went well, else returns appropriate error value.
*/
static int ti_sci_cmd_proc_shutdown_no_wait(const struct ti_sci_handle *handle,
u8 proc_id)
{
int ret;
/*
* Send the core boot status wait message waiting for either WFE or
* WFI without requesting or waiting for a TISCI response with the
* maximum wait time to give us the best chance to get to the WFE/WFI
* command that should follow the invocation of this API before the
* DMSC-internal processing of this command times out. Note that
* waiting for the R5 WFE/WFI flags will also work on an ARMV8 type
* core as the related flag bit positions are the same.
*/
ret = ti_sci_proc_wait_boot_status_no_wait(handle, proc_id,
U8_MAX, 100, U8_MAX, U8_MAX,
0, PROC_BOOT_STATUS_FLAG_R5_WFE | PROC_BOOT_STATUS_FLAG_R5_WFI,
0, 0);
if (ret) {
dev_err(info->dev, "Sending core %u wait message fail %d\n",
proc_id, ret);
return ret;
}
/*
* Release a processor managed by TISCI without requesting or waiting
* for a response.
*/
ret = ti_sci_set_device_state_no_wait(handle, proc_id, 0,
MSG_DEVICE_SW_STATE_AUTO_OFF);
if (ret)
dev_err(info->dev, "Sending core %u shutdown message fail %d\n",
proc_id, ret);
return ret;
}
/**
* ti_sci_cmd_ring_config() - configure RA ring
* @handle: pointer to TI SCI handle
@ -2687,6 +2869,7 @@ static void ti_sci_setup_ops(struct ti_sci_info *info)
pops->set_proc_boot_ctrl = ti_sci_cmd_set_proc_boot_ctrl;
pops->proc_auth_boot_image = ti_sci_cmd_proc_auth_boot_image;
pops->get_proc_boot_status = ti_sci_cmd_get_proc_boot_status;
pops->proc_shutdown_no_wait = ti_sci_cmd_proc_shutdown_no_wait;
rops->config = ti_sci_cmd_ring_config;
rops->get_config = ti_sci_cmd_ring_get_config;

View file

@ -50,6 +50,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
/* Resource Management Requests */
#define TI_SCI_MSG_GET_RESOURCE_RANGE 0x1500
@ -772,6 +773,55 @@ struct ti_sci_msg_resp_get_proc_boot_status {
u32 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;
u8 processor_id;
u8 num_wait_iterations;
u8 num_match_iterations;
u8 delay_per_iteration_us;
u8 delay_before_iterations_us;
u32 status_flags_1_set_all_wait;
u32 status_flags_1_set_any_wait;
u32 status_flags_1_clr_all_wait;
u32 status_flags_1_clr_any_wait;
} __packed;
/**
* struct ti_sci_msg_rm_ring_cfg_req - Configure a Navigator Subsystem ring
*

View file

@ -266,6 +266,8 @@ struct ti_sci_core_ops {
* @set_proc_boot_ctrl: Setup limited control flags in specific cases.
* @proc_auth_boot_image:
* @get_proc_boot_status: Get the state of physical processor
* @proc_shutdown_no_wait: Shutdown a core without requesting or waiting for a
* response.
*
* NOTE: for all these functions, the following parameters are generic in
* nature:
@ -287,6 +289,8 @@ struct ti_sci_proc_ops {
int (*get_proc_boot_status)(const struct ti_sci_handle *handle, u8 pid,
u64 *bv, u32 *cfg_flags, u32 *ctrl_flags,
u32 *sts_flags);
int (*proc_shutdown_no_wait)(const struct ti_sci_handle *handle,
u8 pid);
};
#define TI_SCI_RING_MODE_RING (0)