firmware: ti_sci: Add processor shutdown API method
authorAndreas Dannenberg <dannenberg@ti.com>
Fri, 7 Jun 2019 13:54:40 +0000 (19:24 +0530)
committerTom Rini <trini@konsulko.com>
Sat, 27 Jul 2019 01:49:21 +0000 (21:49 -0400)
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>
drivers/firmware/ti_sci.c
drivers/firmware/ti_sci.h
include/linux/soc/ti/ti_sci_protocol.h

index eadb91e1076beea8cfcdd136cdc711c4d1cf5cf0..8c68f987882b576e8a017ee1d8ce491db8a32a84 100644 (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;
index a484b1fa408f1639a4a3b80fd7eea1a727cde8f6..69ff74d6a95476b75fd84e531f0246fc38655e02 100644 (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
  *
index 842fb596f70938d16cd9326026da43ced00982e8..cd6e5853b43f11c0b781b0716ba896c5645bb357 100644 (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)