Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2020-21 Intel Corporation.
0004  */
0005 
0006 #include "iosm_ipc_imem.h"
0007 #include "iosm_ipc_protocol.h"
0008 #include "iosm_ipc_protocol_ops.h"
0009 #include "iosm_ipc_pm.h"
0010 #include "iosm_ipc_task_queue.h"
0011 
0012 int ipc_protocol_tq_msg_send(struct iosm_protocol *ipc_protocol,
0013                  enum ipc_msg_prep_type msg_type,
0014                  union ipc_msg_prep_args *prep_args,
0015                  struct ipc_rsp *response)
0016 {
0017     int index = ipc_protocol_msg_prep(ipc_protocol->imem, msg_type,
0018                       prep_args);
0019 
0020     /* Store reference towards caller specified response in response ring
0021      * and signal CP
0022      */
0023     if (index >= 0 && index < IPC_MEM_MSG_ENTRIES) {
0024         ipc_protocol->rsp_ring[index] = response;
0025         ipc_protocol_msg_hp_update(ipc_protocol->imem);
0026     }
0027 
0028     return index;
0029 }
0030 
0031 /* Callback for message send */
0032 static int ipc_protocol_tq_msg_send_cb(struct iosm_imem *ipc_imem, int arg,
0033                        void *msg, size_t size)
0034 {
0035     struct ipc_call_msg_send_args *send_args = msg;
0036     struct iosm_protocol *ipc_protocol = ipc_imem->ipc_protocol;
0037 
0038     return ipc_protocol_tq_msg_send(ipc_protocol, send_args->msg_type,
0039                     send_args->prep_args,
0040                     send_args->response);
0041 }
0042 
0043 /* Remove reference to a response. This is typically used when a requestor timed
0044  * out and is no longer interested in the response.
0045  */
0046 static int ipc_protocol_tq_msg_remove(struct iosm_imem *ipc_imem, int arg,
0047                       void *msg, size_t size)
0048 {
0049     struct iosm_protocol *ipc_protocol = ipc_imem->ipc_protocol;
0050 
0051     ipc_protocol->rsp_ring[arg] = NULL;
0052     return 0;
0053 }
0054 
0055 int ipc_protocol_msg_send(struct iosm_protocol *ipc_protocol,
0056               enum ipc_msg_prep_type prep,
0057               union ipc_msg_prep_args *prep_args)
0058 {
0059     struct ipc_call_msg_send_args send_args;
0060     unsigned int exec_timeout;
0061     struct ipc_rsp response;
0062     int index;
0063 
0064     exec_timeout = (ipc_protocol_get_ap_exec_stage(ipc_protocol) ==
0065                     IPC_MEM_EXEC_STAGE_RUN ?
0066                 IPC_MSG_COMPLETE_RUN_DEFAULT_TIMEOUT :
0067                 IPC_MSG_COMPLETE_BOOT_DEFAULT_TIMEOUT);
0068 
0069     /* Trap if called from non-preemptible context */
0070     might_sleep();
0071 
0072     response.status = IPC_MEM_MSG_CS_INVALID;
0073     init_completion(&response.completion);
0074 
0075     send_args.msg_type = prep;
0076     send_args.prep_args = prep_args;
0077     send_args.response = &response;
0078 
0079     /* Allocate and prepare message to be sent in tasklet context.
0080      * A positive index returned form tasklet_call references the message
0081      * in case it needs to be cancelled when there is a timeout.
0082      */
0083     index = ipc_task_queue_send_task(ipc_protocol->imem,
0084                      ipc_protocol_tq_msg_send_cb, 0,
0085                      &send_args, 0, true);
0086 
0087     if (index < 0) {
0088         dev_err(ipc_protocol->dev, "msg %d failed", prep);
0089         return index;
0090     }
0091 
0092     /* Wait for the device to respond to the message */
0093     switch (wait_for_completion_timeout(&response.completion,
0094                         msecs_to_jiffies(exec_timeout))) {
0095     case 0:
0096         /* Timeout, there was no response from the device.
0097          * Remove the reference to the local response completion
0098          * object as we are no longer interested in the response.
0099          */
0100         ipc_task_queue_send_task(ipc_protocol->imem,
0101                      ipc_protocol_tq_msg_remove, index,
0102                      NULL, 0, true);
0103         dev_err(ipc_protocol->dev, "msg timeout");
0104         ipc_uevent_send(ipc_protocol->pcie->dev, UEVENT_MDM_TIMEOUT);
0105         break;
0106     default:
0107         /* We got a response in time; check completion status: */
0108         if (response.status != IPC_MEM_MSG_CS_SUCCESS) {
0109             dev_err(ipc_protocol->dev,
0110                 "msg completion status error %d",
0111                 response.status);
0112             return -EIO;
0113         }
0114     }
0115 
0116     return 0;
0117 }
0118 
0119 static int ipc_protocol_msg_send_host_sleep(struct iosm_protocol *ipc_protocol,
0120                         u32 state)
0121 {
0122     union ipc_msg_prep_args prep_args = {
0123         .sleep.target = 0,
0124         .sleep.state = state,
0125     };
0126 
0127     return ipc_protocol_msg_send(ipc_protocol, IPC_MSG_PREP_SLEEP,
0128                      &prep_args);
0129 }
0130 
0131 void ipc_protocol_doorbell_trigger(struct iosm_protocol *ipc_protocol,
0132                    u32 identifier)
0133 {
0134     ipc_pm_signal_hpda_doorbell(&ipc_protocol->pm, identifier, true);
0135 }
0136 
0137 bool ipc_protocol_pm_dev_sleep_handle(struct iosm_protocol *ipc_protocol)
0138 {
0139     u32 ipc_status = ipc_protocol_get_ipc_status(ipc_protocol);
0140     u32 requested;
0141 
0142     if (ipc_status != IPC_MEM_DEVICE_IPC_RUNNING) {
0143         dev_err(ipc_protocol->dev,
0144             "irq ignored, CP IPC state is %d, should be RUNNING",
0145             ipc_status);
0146 
0147         /* Stop further processing. */
0148         return false;
0149     }
0150 
0151     /* Get a copy of the requested PM state by the device and the local
0152      * device PM state.
0153      */
0154     requested = ipc_protocol_pm_dev_get_sleep_notification(ipc_protocol);
0155 
0156     return ipc_pm_dev_slp_notification(&ipc_protocol->pm, requested);
0157 }
0158 
0159 static int ipc_protocol_tq_wakeup_dev_slp(struct iosm_imem *ipc_imem, int arg,
0160                       void *msg, size_t size)
0161 {
0162     struct iosm_pm *ipc_pm = &ipc_imem->ipc_protocol->pm;
0163 
0164     /* Wakeup from device sleep if it is not ACTIVE */
0165     ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_HS, true);
0166 
0167     ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_HS, false);
0168 
0169     return 0;
0170 }
0171 
0172 void ipc_protocol_s2idle_sleep(struct iosm_protocol *ipc_protocol, bool sleep)
0173 {
0174     ipc_pm_set_s2idle_sleep(&ipc_protocol->pm, sleep);
0175 }
0176 
0177 bool ipc_protocol_suspend(struct iosm_protocol *ipc_protocol)
0178 {
0179     if (!ipc_pm_prepare_host_sleep(&ipc_protocol->pm))
0180         goto err;
0181 
0182     ipc_task_queue_send_task(ipc_protocol->imem,
0183                  ipc_protocol_tq_wakeup_dev_slp, 0, NULL, 0,
0184                  true);
0185 
0186     if (!ipc_pm_wait_for_device_active(&ipc_protocol->pm)) {
0187         ipc_uevent_send(ipc_protocol->pcie->dev, UEVENT_MDM_TIMEOUT);
0188         goto err;
0189     }
0190 
0191     /* Send the sleep message for sync sys calls. */
0192     dev_dbg(ipc_protocol->dev, "send TARGET_HOST, ENTER_SLEEP");
0193     if (ipc_protocol_msg_send_host_sleep(ipc_protocol,
0194                          IPC_HOST_SLEEP_ENTER_SLEEP)) {
0195         /* Sending ENTER_SLEEP message failed, we are still active */
0196         ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_ACTIVE;
0197         goto err;
0198     }
0199 
0200     ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_SLEEP;
0201     return true;
0202 err:
0203     return false;
0204 }
0205 
0206 bool ipc_protocol_resume(struct iosm_protocol *ipc_protocol)
0207 {
0208     if (!ipc_pm_prepare_host_active(&ipc_protocol->pm))
0209         return false;
0210 
0211     dev_dbg(ipc_protocol->dev, "send TARGET_HOST, EXIT_SLEEP");
0212     if (ipc_protocol_msg_send_host_sleep(ipc_protocol,
0213                          IPC_HOST_SLEEP_EXIT_SLEEP)) {
0214         ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_SLEEP;
0215         return false;
0216     }
0217 
0218     ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_ACTIVE;
0219 
0220     return true;
0221 }
0222 
0223 struct iosm_protocol *ipc_protocol_init(struct iosm_imem *ipc_imem)
0224 {
0225     struct iosm_protocol *ipc_protocol =
0226         kzalloc(sizeof(*ipc_protocol), GFP_KERNEL);
0227     struct ipc_protocol_context_info *p_ci;
0228     u64 addr;
0229 
0230     if (!ipc_protocol)
0231         return NULL;
0232 
0233     ipc_protocol->dev = ipc_imem->dev;
0234     ipc_protocol->pcie = ipc_imem->pcie;
0235     ipc_protocol->imem = ipc_imem;
0236     ipc_protocol->p_ap_shm = NULL;
0237     ipc_protocol->phy_ap_shm = 0;
0238 
0239     ipc_protocol->old_msg_tail = 0;
0240 
0241     ipc_protocol->p_ap_shm =
0242         dma_alloc_coherent(&ipc_protocol->pcie->pci->dev,
0243                    sizeof(*ipc_protocol->p_ap_shm),
0244                    &ipc_protocol->phy_ap_shm, GFP_KERNEL);
0245 
0246     if (!ipc_protocol->p_ap_shm) {
0247         dev_err(ipc_protocol->dev, "pci shm alloc error");
0248         kfree(ipc_protocol);
0249         return NULL;
0250     }
0251 
0252     /* Prepare the context info for CP. */
0253     addr = ipc_protocol->phy_ap_shm;
0254     p_ci = &ipc_protocol->p_ap_shm->ci;
0255     p_ci->device_info_addr =
0256         addr + offsetof(struct ipc_protocol_ap_shm, device_info);
0257     p_ci->head_array =
0258         addr + offsetof(struct ipc_protocol_ap_shm, head_array);
0259     p_ci->tail_array =
0260         addr + offsetof(struct ipc_protocol_ap_shm, tail_array);
0261     p_ci->msg_head = addr + offsetof(struct ipc_protocol_ap_shm, msg_head);
0262     p_ci->msg_tail = addr + offsetof(struct ipc_protocol_ap_shm, msg_tail);
0263     p_ci->msg_ring_addr =
0264         addr + offsetof(struct ipc_protocol_ap_shm, msg_ring);
0265     p_ci->msg_ring_entries = cpu_to_le16(IPC_MEM_MSG_ENTRIES);
0266     p_ci->msg_irq_vector = IPC_MSG_IRQ_VECTOR;
0267     p_ci->device_info_irq_vector = IPC_DEVICE_IRQ_VECTOR;
0268 
0269     ipc_mmio_set_contex_info_addr(ipc_imem->mmio, addr);
0270 
0271     ipc_pm_init(ipc_protocol);
0272 
0273     return ipc_protocol;
0274 }
0275 
0276 void ipc_protocol_deinit(struct iosm_protocol *proto)
0277 {
0278     dma_free_coherent(&proto->pcie->pci->dev, sizeof(*proto->p_ap_shm),
0279               proto->p_ap_shm, proto->phy_ap_shm);
0280 
0281     ipc_pm_deinit(proto);
0282     kfree(proto);
0283 }