diff options
Diffstat (limited to 'drivers/firmware/ti_sci.c')
-rw-r--r-- | drivers/firmware/ti_sci.c | 291 |
1 files changed, 282 insertions, 9 deletions
diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 303aa6a631..1fd29f2cdf 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -87,11 +87,18 @@ struct ti_sci_info { struct mbox_chan chan_notify; struct ti_sci_xfer xfer; struct list_head list; + struct list_head dev_list; bool is_secure; u8 host_id; u8 seq; }; +struct ti_sci_exclusive_dev { + u32 id; + u32 count; + struct list_head list; +}; + #define handle_to_ti_sci_info(h) container_of(h, struct ti_sci_info, handle) /** @@ -101,7 +108,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 +129,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 +228,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 +241,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; } /** @@ -419,6 +434,47 @@ static int ti_sci_cmd_set_board_config_pm(const struct ti_sci_handle *handle, addr, size); } +static struct ti_sci_exclusive_dev +*ti_sci_get_exclusive_dev(struct list_head *dev_list, u32 id) +{ + struct ti_sci_exclusive_dev *dev; + + list_for_each_entry(dev, dev_list, list) + if (dev->id == id) + return dev; + + return NULL; +} + +static void ti_sci_add_exclusive_dev(struct ti_sci_info *info, u32 id) +{ + struct ti_sci_exclusive_dev *dev; + + dev = ti_sci_get_exclusive_dev(&info->dev_list, id); + if (dev) { + dev->count++; + return; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev->id = id; + dev->count = 1; + INIT_LIST_HEAD(&dev->list); + list_add_tail(&dev->list, &info->dev_list); +} + +static void ti_sci_delete_exclusive_dev(struct ti_sci_info *info, u32 id) +{ + struct ti_sci_exclusive_dev *dev; + + dev = ti_sci_get_exclusive_dev(&info->dev_list, id); + if (!dev) + return; + + if (dev->count > 0) + dev->count--; +} + /** * ti_sci_set_device_state() - Set device state helper * @handle: pointer to TI SCI handle @@ -466,6 +522,54 @@ static int ti_sci_set_device_state(const struct ti_sci_handle *handle, if (!ti_sci_is_response_ack(resp)) return -ENODEV; + if (state == MSG_DEVICE_SW_STATE_AUTO_OFF) + ti_sci_delete_exclusive_dev(info, id); + else if (flags & MSG_FLAG_DEVICE_EXCLUSIVE) + ti_sci_add_exclusive_dev(info, id); + + 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; } @@ -547,8 +651,14 @@ static int ti_sci_get_device_state(const struct ti_sci_handle *handle, */ static int ti_sci_cmd_get_device(const struct ti_sci_handle *handle, u32 id) { - return ti_sci_set_device_state(handle, id, - MSG_FLAG_DEVICE_EXCLUSIVE, + return ti_sci_set_device_state(handle, id, 0, + MSG_DEVICE_SW_STATE_ON); +} + +static int ti_sci_cmd_get_device_exclusive(const struct ti_sci_handle *handle, + u32 id) +{ + return ti_sci_set_device_state(handle, id, MSG_FLAG_DEVICE_EXCLUSIVE, MSG_DEVICE_SW_STATE_ON); } @@ -566,7 +676,14 @@ static int ti_sci_cmd_get_device(const struct ti_sci_handle *handle, u32 id) static int ti_sci_cmd_idle_device(const struct ti_sci_handle *handle, u32 id) { return ti_sci_set_device_state(handle, id, - MSG_FLAG_DEVICE_EXCLUSIVE, + 0, + MSG_DEVICE_SW_STATE_RETENTION); +} + +static int ti_sci_cmd_idle_device_exclusive(const struct ti_sci_handle *handle, + u32 id) +{ + return ti_sci_set_device_state(handle, id, MSG_FLAG_DEVICE_EXCLUSIVE, MSG_DEVICE_SW_STATE_RETENTION); } @@ -583,8 +700,27 @@ static int ti_sci_cmd_idle_device(const struct ti_sci_handle *handle, u32 id) */ static int ti_sci_cmd_put_device(const struct ti_sci_handle *handle, u32 id) { - return ti_sci_set_device_state(handle, id, - 0, MSG_DEVICE_SW_STATE_AUTO_OFF); + return ti_sci_set_device_state(handle, id, 0, + MSG_DEVICE_SW_STATE_AUTO_OFF); +} + +static +int ti_sci_cmd_release_exclusive_devices(const struct ti_sci_handle *handle) +{ + struct ti_sci_exclusive_dev *dev, *tmp; + struct ti_sci_info *info; + int i, cnt; + + info = handle_to_ti_sci_info(handle); + + list_for_each_entry_safe(dev, tmp, &info->dev_list, list) { + cnt = dev->count; + debug("%s: id = %d, cnt = %d\n", __func__, dev->id, cnt); + for (i = 0; i < cnt; i++) + ti_sci_cmd_put_device(handle, dev->id); + } + + return 0; } /** @@ -2027,6 +2163,137 @@ static int ti_sci_cmd_get_proc_boot_status(const struct ti_sci_handle *handle, } /** + * 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 * @valid_params: Bitfield defining validity of ring configuration parameters. @@ -2632,7 +2899,9 @@ static void ti_sci_setup_ops(struct ti_sci_info *info) bops->board_config_pm = ti_sci_cmd_set_board_config_pm; dops->get_device = ti_sci_cmd_get_device; + dops->get_device_exclusive = ti_sci_cmd_get_device_exclusive; dops->idle_device = ti_sci_cmd_idle_device; + dops->idle_device_exclusive = ti_sci_cmd_idle_device_exclusive; dops->put_device = ti_sci_cmd_put_device; dops->is_valid = ti_sci_cmd_dev_is_valid; dops->get_context_loss_count = ti_sci_cmd_dev_get_clcnt; @@ -2642,6 +2911,7 @@ static void ti_sci_setup_ops(struct ti_sci_info *info) dops->is_transitioning = ti_sci_cmd_dev_is_trans; dops->set_device_resets = ti_sci_cmd_set_device_resets; dops->get_device_resets = ti_sci_cmd_get_device_resets; + dops->release_exclusive_devices = ti_sci_cmd_release_exclusive_devices; cops->get_clock = ti_sci_cmd_get_clock; cops->idle_clock = ti_sci_cmd_idle_clock; @@ -2672,6 +2942,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; @@ -2835,6 +3106,8 @@ static int ti_sci_probe(struct udevice *dev) ret = ti_sci_cmd_get_revision(&info->handle); + INIT_LIST_HEAD(&info->dev_list); + return ret; } |