162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020-21 Intel Corporation. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include "iosm_ipc_imem.h" 762306a36Sopenharmony_ci#include "iosm_ipc_protocol.h" 862306a36Sopenharmony_ci#include "iosm_ipc_protocol_ops.h" 962306a36Sopenharmony_ci#include "iosm_ipc_pm.h" 1062306a36Sopenharmony_ci#include "iosm_ipc_task_queue.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ciint ipc_protocol_tq_msg_send(struct iosm_protocol *ipc_protocol, 1362306a36Sopenharmony_ci enum ipc_msg_prep_type msg_type, 1462306a36Sopenharmony_ci union ipc_msg_prep_args *prep_args, 1562306a36Sopenharmony_ci struct ipc_rsp *response) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci int index = ipc_protocol_msg_prep(ipc_protocol->imem, msg_type, 1862306a36Sopenharmony_ci prep_args); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci /* Store reference towards caller specified response in response ring 2162306a36Sopenharmony_ci * and signal CP 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci if (index >= 0 && index < IPC_MEM_MSG_ENTRIES) { 2462306a36Sopenharmony_ci ipc_protocol->rsp_ring[index] = response; 2562306a36Sopenharmony_ci ipc_protocol_msg_hp_update(ipc_protocol->imem); 2662306a36Sopenharmony_ci } 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci return index; 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Callback for message send */ 3262306a36Sopenharmony_cistatic int ipc_protocol_tq_msg_send_cb(struct iosm_imem *ipc_imem, int arg, 3362306a36Sopenharmony_ci void *msg, size_t size) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct ipc_call_msg_send_args *send_args = msg; 3662306a36Sopenharmony_ci struct iosm_protocol *ipc_protocol = ipc_imem->ipc_protocol; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci return ipc_protocol_tq_msg_send(ipc_protocol, send_args->msg_type, 3962306a36Sopenharmony_ci send_args->prep_args, 4062306a36Sopenharmony_ci send_args->response); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Remove reference to a response. This is typically used when a requestor timed 4462306a36Sopenharmony_ci * out and is no longer interested in the response. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistatic int ipc_protocol_tq_msg_remove(struct iosm_imem *ipc_imem, int arg, 4762306a36Sopenharmony_ci void *msg, size_t size) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct iosm_protocol *ipc_protocol = ipc_imem->ipc_protocol; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci ipc_protocol->rsp_ring[arg] = NULL; 5262306a36Sopenharmony_ci return 0; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ciint ipc_protocol_msg_send(struct iosm_protocol *ipc_protocol, 5662306a36Sopenharmony_ci enum ipc_msg_prep_type prep, 5762306a36Sopenharmony_ci union ipc_msg_prep_args *prep_args) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct ipc_call_msg_send_args send_args; 6062306a36Sopenharmony_ci unsigned int exec_timeout; 6162306a36Sopenharmony_ci struct ipc_rsp response; 6262306a36Sopenharmony_ci int index; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci exec_timeout = (ipc_protocol_get_ap_exec_stage(ipc_protocol) == 6562306a36Sopenharmony_ci IPC_MEM_EXEC_STAGE_RUN ? 6662306a36Sopenharmony_ci IPC_MSG_COMPLETE_RUN_DEFAULT_TIMEOUT : 6762306a36Sopenharmony_ci IPC_MSG_COMPLETE_BOOT_DEFAULT_TIMEOUT); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* Trap if called from non-preemptible context */ 7062306a36Sopenharmony_ci might_sleep(); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci response.status = IPC_MEM_MSG_CS_INVALID; 7362306a36Sopenharmony_ci init_completion(&response.completion); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci send_args.msg_type = prep; 7662306a36Sopenharmony_ci send_args.prep_args = prep_args; 7762306a36Sopenharmony_ci send_args.response = &response; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Allocate and prepare message to be sent in tasklet context. 8062306a36Sopenharmony_ci * A positive index returned form tasklet_call references the message 8162306a36Sopenharmony_ci * in case it needs to be cancelled when there is a timeout. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci index = ipc_task_queue_send_task(ipc_protocol->imem, 8462306a36Sopenharmony_ci ipc_protocol_tq_msg_send_cb, 0, 8562306a36Sopenharmony_ci &send_args, 0, true); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (index < 0) { 8862306a36Sopenharmony_ci dev_err(ipc_protocol->dev, "msg %d failed", prep); 8962306a36Sopenharmony_ci return index; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* Wait for the device to respond to the message */ 9362306a36Sopenharmony_ci switch (wait_for_completion_timeout(&response.completion, 9462306a36Sopenharmony_ci msecs_to_jiffies(exec_timeout))) { 9562306a36Sopenharmony_ci case 0: 9662306a36Sopenharmony_ci /* Timeout, there was no response from the device. 9762306a36Sopenharmony_ci * Remove the reference to the local response completion 9862306a36Sopenharmony_ci * object as we are no longer interested in the response. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci ipc_task_queue_send_task(ipc_protocol->imem, 10162306a36Sopenharmony_ci ipc_protocol_tq_msg_remove, index, 10262306a36Sopenharmony_ci NULL, 0, true); 10362306a36Sopenharmony_ci dev_err(ipc_protocol->dev, "msg timeout"); 10462306a36Sopenharmony_ci ipc_uevent_send(ipc_protocol->pcie->dev, UEVENT_MDM_TIMEOUT); 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci default: 10762306a36Sopenharmony_ci /* We got a response in time; check completion status: */ 10862306a36Sopenharmony_ci if (response.status != IPC_MEM_MSG_CS_SUCCESS) { 10962306a36Sopenharmony_ci dev_err(ipc_protocol->dev, 11062306a36Sopenharmony_ci "msg completion status error %d", 11162306a36Sopenharmony_ci response.status); 11262306a36Sopenharmony_ci return -EIO; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int ipc_protocol_msg_send_host_sleep(struct iosm_protocol *ipc_protocol, 12062306a36Sopenharmony_ci u32 state) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci union ipc_msg_prep_args prep_args = { 12362306a36Sopenharmony_ci .sleep.target = 0, 12462306a36Sopenharmony_ci .sleep.state = state, 12562306a36Sopenharmony_ci }; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return ipc_protocol_msg_send(ipc_protocol, IPC_MSG_PREP_SLEEP, 12862306a36Sopenharmony_ci &prep_args); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_civoid ipc_protocol_doorbell_trigger(struct iosm_protocol *ipc_protocol, 13262306a36Sopenharmony_ci u32 identifier) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci ipc_pm_signal_hpda_doorbell(&ipc_protocol->pm, identifier, true); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cibool ipc_protocol_pm_dev_sleep_handle(struct iosm_protocol *ipc_protocol) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci u32 ipc_status = ipc_protocol_get_ipc_status(ipc_protocol); 14062306a36Sopenharmony_ci u32 requested; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (ipc_status != IPC_MEM_DEVICE_IPC_RUNNING) { 14362306a36Sopenharmony_ci dev_err(ipc_protocol->dev, 14462306a36Sopenharmony_ci "irq ignored, CP IPC state is %d, should be RUNNING", 14562306a36Sopenharmony_ci ipc_status); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* Stop further processing. */ 14862306a36Sopenharmony_ci return false; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Get a copy of the requested PM state by the device and the local 15262306a36Sopenharmony_ci * device PM state. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ci requested = ipc_protocol_pm_dev_get_sleep_notification(ipc_protocol); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return ipc_pm_dev_slp_notification(&ipc_protocol->pm, requested); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int ipc_protocol_tq_wakeup_dev_slp(struct iosm_imem *ipc_imem, int arg, 16062306a36Sopenharmony_ci void *msg, size_t size) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct iosm_pm *ipc_pm = &ipc_imem->ipc_protocol->pm; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* Wakeup from device sleep if it is not ACTIVE */ 16562306a36Sopenharmony_ci ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_HS, true); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_HS, false); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_civoid ipc_protocol_s2idle_sleep(struct iosm_protocol *ipc_protocol, bool sleep) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci ipc_pm_set_s2idle_sleep(&ipc_protocol->pm, sleep); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cibool ipc_protocol_suspend(struct iosm_protocol *ipc_protocol) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci if (!ipc_pm_prepare_host_sleep(&ipc_protocol->pm)) 18062306a36Sopenharmony_ci goto err; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci ipc_task_queue_send_task(ipc_protocol->imem, 18362306a36Sopenharmony_ci ipc_protocol_tq_wakeup_dev_slp, 0, NULL, 0, 18462306a36Sopenharmony_ci true); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (!ipc_pm_wait_for_device_active(&ipc_protocol->pm)) { 18762306a36Sopenharmony_ci ipc_uevent_send(ipc_protocol->pcie->dev, UEVENT_MDM_TIMEOUT); 18862306a36Sopenharmony_ci goto err; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Send the sleep message for sync sys calls. */ 19262306a36Sopenharmony_ci dev_dbg(ipc_protocol->dev, "send TARGET_HOST, ENTER_SLEEP"); 19362306a36Sopenharmony_ci if (ipc_protocol_msg_send_host_sleep(ipc_protocol, 19462306a36Sopenharmony_ci IPC_HOST_SLEEP_ENTER_SLEEP)) { 19562306a36Sopenharmony_ci /* Sending ENTER_SLEEP message failed, we are still active */ 19662306a36Sopenharmony_ci ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_ACTIVE; 19762306a36Sopenharmony_ci goto err; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_SLEEP; 20162306a36Sopenharmony_ci return true; 20262306a36Sopenharmony_cierr: 20362306a36Sopenharmony_ci return false; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cibool ipc_protocol_resume(struct iosm_protocol *ipc_protocol) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci if (!ipc_pm_prepare_host_active(&ipc_protocol->pm)) 20962306a36Sopenharmony_ci return false; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci dev_dbg(ipc_protocol->dev, "send TARGET_HOST, EXIT_SLEEP"); 21262306a36Sopenharmony_ci if (ipc_protocol_msg_send_host_sleep(ipc_protocol, 21362306a36Sopenharmony_ci IPC_HOST_SLEEP_EXIT_SLEEP)) { 21462306a36Sopenharmony_ci ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_SLEEP; 21562306a36Sopenharmony_ci return false; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_ACTIVE; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return true; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistruct iosm_protocol *ipc_protocol_init(struct iosm_imem *ipc_imem) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct iosm_protocol *ipc_protocol = 22662306a36Sopenharmony_ci kzalloc(sizeof(*ipc_protocol), GFP_KERNEL); 22762306a36Sopenharmony_ci struct ipc_protocol_context_info *p_ci; 22862306a36Sopenharmony_ci u64 addr; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (!ipc_protocol) 23162306a36Sopenharmony_ci return NULL; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci ipc_protocol->dev = ipc_imem->dev; 23462306a36Sopenharmony_ci ipc_protocol->pcie = ipc_imem->pcie; 23562306a36Sopenharmony_ci ipc_protocol->imem = ipc_imem; 23662306a36Sopenharmony_ci ipc_protocol->p_ap_shm = NULL; 23762306a36Sopenharmony_ci ipc_protocol->phy_ap_shm = 0; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci ipc_protocol->old_msg_tail = 0; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ipc_protocol->p_ap_shm = 24262306a36Sopenharmony_ci dma_alloc_coherent(&ipc_protocol->pcie->pci->dev, 24362306a36Sopenharmony_ci sizeof(*ipc_protocol->p_ap_shm), 24462306a36Sopenharmony_ci &ipc_protocol->phy_ap_shm, GFP_KERNEL); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (!ipc_protocol->p_ap_shm) { 24762306a36Sopenharmony_ci dev_err(ipc_protocol->dev, "pci shm alloc error"); 24862306a36Sopenharmony_ci kfree(ipc_protocol); 24962306a36Sopenharmony_ci return NULL; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Prepare the context info for CP. */ 25362306a36Sopenharmony_ci addr = ipc_protocol->phy_ap_shm; 25462306a36Sopenharmony_ci p_ci = &ipc_protocol->p_ap_shm->ci; 25562306a36Sopenharmony_ci p_ci->device_info_addr = 25662306a36Sopenharmony_ci addr + offsetof(struct ipc_protocol_ap_shm, device_info); 25762306a36Sopenharmony_ci p_ci->head_array = 25862306a36Sopenharmony_ci addr + offsetof(struct ipc_protocol_ap_shm, head_array); 25962306a36Sopenharmony_ci p_ci->tail_array = 26062306a36Sopenharmony_ci addr + offsetof(struct ipc_protocol_ap_shm, tail_array); 26162306a36Sopenharmony_ci p_ci->msg_head = addr + offsetof(struct ipc_protocol_ap_shm, msg_head); 26262306a36Sopenharmony_ci p_ci->msg_tail = addr + offsetof(struct ipc_protocol_ap_shm, msg_tail); 26362306a36Sopenharmony_ci p_ci->msg_ring_addr = 26462306a36Sopenharmony_ci addr + offsetof(struct ipc_protocol_ap_shm, msg_ring); 26562306a36Sopenharmony_ci p_ci->msg_ring_entries = cpu_to_le16(IPC_MEM_MSG_ENTRIES); 26662306a36Sopenharmony_ci p_ci->msg_irq_vector = IPC_MSG_IRQ_VECTOR; 26762306a36Sopenharmony_ci p_ci->device_info_irq_vector = IPC_DEVICE_IRQ_VECTOR; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci ipc_mmio_set_contex_info_addr(ipc_imem->mmio, addr); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci ipc_pm_init(ipc_protocol); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return ipc_protocol; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_civoid ipc_protocol_deinit(struct iosm_protocol *proto) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci dma_free_coherent(&proto->pcie->pci->dev, sizeof(*proto->p_ap_shm), 27962306a36Sopenharmony_ci proto->p_ap_shm, proto->phy_ap_shm); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci ipc_pm_deinit(proto); 28262306a36Sopenharmony_ci kfree(proto); 28362306a36Sopenharmony_ci} 284