Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: iosm: shared memory protocol

1) Defines messaging protocol for handling Transfer Descriptor
in both UL/DL direction.
2) Ring buffer management.

Signed-off-by: M Chetan Kumar <m.chetan.kumar@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

M Chetan Kumar and committed by
David S. Miller
faed4c6f be8c936e

+520
+283
drivers/net/wwan/iosm/iosm_ipc_protocol.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2020-21 Intel Corporation. 4 + */ 5 + 6 + #include "iosm_ipc_imem.h" 7 + #include "iosm_ipc_protocol.h" 8 + #include "iosm_ipc_protocol_ops.h" 9 + #include "iosm_ipc_pm.h" 10 + #include "iosm_ipc_task_queue.h" 11 + 12 + int ipc_protocol_tq_msg_send(struct iosm_protocol *ipc_protocol, 13 + enum ipc_msg_prep_type msg_type, 14 + union ipc_msg_prep_args *prep_args, 15 + struct ipc_rsp *response) 16 + { 17 + int index = ipc_protocol_msg_prep(ipc_protocol->imem, msg_type, 18 + prep_args); 19 + 20 + /* Store reference towards caller specified response in response ring 21 + * and signal CP 22 + */ 23 + if (index >= 0 && index < IPC_MEM_MSG_ENTRIES) { 24 + ipc_protocol->rsp_ring[index] = response; 25 + ipc_protocol_msg_hp_update(ipc_protocol->imem); 26 + } 27 + 28 + return index; 29 + } 30 + 31 + /* Callback for message send */ 32 + static int ipc_protocol_tq_msg_send_cb(struct iosm_imem *ipc_imem, int arg, 33 + void *msg, size_t size) 34 + { 35 + struct ipc_call_msg_send_args *send_args = msg; 36 + struct iosm_protocol *ipc_protocol = ipc_imem->ipc_protocol; 37 + 38 + return ipc_protocol_tq_msg_send(ipc_protocol, send_args->msg_type, 39 + send_args->prep_args, 40 + send_args->response); 41 + } 42 + 43 + /* Remove reference to a response. This is typically used when a requestor timed 44 + * out and is no longer interested in the response. 45 + */ 46 + static int ipc_protocol_tq_msg_remove(struct iosm_imem *ipc_imem, int arg, 47 + void *msg, size_t size) 48 + { 49 + struct iosm_protocol *ipc_protocol = ipc_imem->ipc_protocol; 50 + 51 + ipc_protocol->rsp_ring[arg] = NULL; 52 + return 0; 53 + } 54 + 55 + int ipc_protocol_msg_send(struct iosm_protocol *ipc_protocol, 56 + enum ipc_msg_prep_type prep, 57 + union ipc_msg_prep_args *prep_args) 58 + { 59 + struct ipc_call_msg_send_args send_args; 60 + unsigned int exec_timeout; 61 + struct ipc_rsp response; 62 + int index; 63 + 64 + exec_timeout = (ipc_protocol_get_ap_exec_stage(ipc_protocol) == 65 + IPC_MEM_EXEC_STAGE_RUN ? 66 + IPC_MSG_COMPLETE_RUN_DEFAULT_TIMEOUT : 67 + IPC_MSG_COMPLETE_BOOT_DEFAULT_TIMEOUT); 68 + 69 + /* Trap if called from non-preemptible context */ 70 + might_sleep(); 71 + 72 + response.status = IPC_MEM_MSG_CS_INVALID; 73 + init_completion(&response.completion); 74 + 75 + send_args.msg_type = prep; 76 + send_args.prep_args = prep_args; 77 + send_args.response = &response; 78 + 79 + /* Allocate and prepare message to be sent in tasklet context. 80 + * A positive index returned form tasklet_call references the message 81 + * in case it needs to be cancelled when there is a timeout. 82 + */ 83 + index = ipc_task_queue_send_task(ipc_protocol->imem, 84 + ipc_protocol_tq_msg_send_cb, 0, 85 + &send_args, 0, true); 86 + 87 + if (index < 0) { 88 + dev_err(ipc_protocol->dev, "msg %d failed", prep); 89 + return index; 90 + } 91 + 92 + /* Wait for the device to respond to the message */ 93 + switch (wait_for_completion_timeout(&response.completion, 94 + msecs_to_jiffies(exec_timeout))) { 95 + case 0: 96 + /* Timeout, there was no response from the device. 97 + * Remove the reference to the local response completion 98 + * object as we are no longer interested in the response. 99 + */ 100 + ipc_task_queue_send_task(ipc_protocol->imem, 101 + ipc_protocol_tq_msg_remove, index, 102 + NULL, 0, true); 103 + dev_err(ipc_protocol->dev, "msg timeout"); 104 + ipc_uevent_send(ipc_protocol->pcie->dev, UEVENT_MDM_TIMEOUT); 105 + break; 106 + default: 107 + /* We got a response in time; check completion status: */ 108 + if (response.status != IPC_MEM_MSG_CS_SUCCESS) { 109 + dev_err(ipc_protocol->dev, 110 + "msg completion status error %d", 111 + response.status); 112 + return -EIO; 113 + } 114 + } 115 + 116 + return 0; 117 + } 118 + 119 + static int ipc_protocol_msg_send_host_sleep(struct iosm_protocol *ipc_protocol, 120 + u32 state) 121 + { 122 + union ipc_msg_prep_args prep_args = { 123 + .sleep.target = 0, 124 + .sleep.state = state, 125 + }; 126 + 127 + return ipc_protocol_msg_send(ipc_protocol, IPC_MSG_PREP_SLEEP, 128 + &prep_args); 129 + } 130 + 131 + void ipc_protocol_doorbell_trigger(struct iosm_protocol *ipc_protocol, 132 + u32 identifier) 133 + { 134 + ipc_pm_signal_hpda_doorbell(&ipc_protocol->pm, identifier, true); 135 + } 136 + 137 + bool ipc_protocol_pm_dev_sleep_handle(struct iosm_protocol *ipc_protocol) 138 + { 139 + u32 ipc_status = ipc_protocol_get_ipc_status(ipc_protocol); 140 + u32 requested; 141 + 142 + if (ipc_status != IPC_MEM_DEVICE_IPC_RUNNING) { 143 + dev_err(ipc_protocol->dev, 144 + "irq ignored, CP IPC state is %d, should be RUNNING", 145 + ipc_status); 146 + 147 + /* Stop further processing. */ 148 + return false; 149 + } 150 + 151 + /* Get a copy of the requested PM state by the device and the local 152 + * device PM state. 153 + */ 154 + requested = ipc_protocol_pm_dev_get_sleep_notification(ipc_protocol); 155 + 156 + return ipc_pm_dev_slp_notification(&ipc_protocol->pm, requested); 157 + } 158 + 159 + static int ipc_protocol_tq_wakeup_dev_slp(struct iosm_imem *ipc_imem, int arg, 160 + void *msg, size_t size) 161 + { 162 + struct iosm_pm *ipc_pm = &ipc_imem->ipc_protocol->pm; 163 + 164 + /* Wakeup from device sleep if it is not ACTIVE */ 165 + ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_HS, true); 166 + 167 + ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_HS, false); 168 + 169 + return 0; 170 + } 171 + 172 + void ipc_protocol_s2idle_sleep(struct iosm_protocol *ipc_protocol, bool sleep) 173 + { 174 + ipc_pm_set_s2idle_sleep(&ipc_protocol->pm, sleep); 175 + } 176 + 177 + bool ipc_protocol_suspend(struct iosm_protocol *ipc_protocol) 178 + { 179 + if (!ipc_pm_prepare_host_sleep(&ipc_protocol->pm)) 180 + goto err; 181 + 182 + ipc_task_queue_send_task(ipc_protocol->imem, 183 + ipc_protocol_tq_wakeup_dev_slp, 0, NULL, 0, 184 + true); 185 + 186 + if (!ipc_pm_wait_for_device_active(&ipc_protocol->pm)) { 187 + ipc_uevent_send(ipc_protocol->pcie->dev, UEVENT_MDM_TIMEOUT); 188 + goto err; 189 + } 190 + 191 + /* Send the sleep message for sync sys calls. */ 192 + dev_dbg(ipc_protocol->dev, "send TARGET_HOST, ENTER_SLEEP"); 193 + if (ipc_protocol_msg_send_host_sleep(ipc_protocol, 194 + IPC_HOST_SLEEP_ENTER_SLEEP)) { 195 + /* Sending ENTER_SLEEP message failed, we are still active */ 196 + ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_ACTIVE; 197 + goto err; 198 + } 199 + 200 + ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_SLEEP; 201 + return true; 202 + err: 203 + return false; 204 + } 205 + 206 + bool ipc_protocol_resume(struct iosm_protocol *ipc_protocol) 207 + { 208 + if (!ipc_pm_prepare_host_active(&ipc_protocol->pm)) 209 + return false; 210 + 211 + dev_dbg(ipc_protocol->dev, "send TARGET_HOST, EXIT_SLEEP"); 212 + if (ipc_protocol_msg_send_host_sleep(ipc_protocol, 213 + IPC_HOST_SLEEP_EXIT_SLEEP)) { 214 + ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_SLEEP; 215 + return false; 216 + } 217 + 218 + ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_ACTIVE; 219 + 220 + return true; 221 + } 222 + 223 + struct iosm_protocol *ipc_protocol_init(struct iosm_imem *ipc_imem) 224 + { 225 + struct iosm_protocol *ipc_protocol = 226 + kzalloc(sizeof(*ipc_protocol), GFP_KERNEL); 227 + struct ipc_protocol_context_info *p_ci; 228 + u64 addr; 229 + 230 + if (!ipc_protocol) 231 + return NULL; 232 + 233 + ipc_protocol->dev = ipc_imem->dev; 234 + ipc_protocol->pcie = ipc_imem->pcie; 235 + ipc_protocol->imem = ipc_imem; 236 + ipc_protocol->p_ap_shm = NULL; 237 + ipc_protocol->phy_ap_shm = 0; 238 + 239 + ipc_protocol->old_msg_tail = 0; 240 + 241 + ipc_protocol->p_ap_shm = 242 + pci_alloc_consistent(ipc_protocol->pcie->pci, 243 + sizeof(*ipc_protocol->p_ap_shm), 244 + &ipc_protocol->phy_ap_shm); 245 + 246 + if (!ipc_protocol->p_ap_shm) { 247 + dev_err(ipc_protocol->dev, "pci shm alloc error"); 248 + kfree(ipc_protocol); 249 + return NULL; 250 + } 251 + 252 + /* Prepare the context info for CP. */ 253 + addr = ipc_protocol->phy_ap_shm; 254 + p_ci = &ipc_protocol->p_ap_shm->ci; 255 + p_ci->device_info_addr = 256 + addr + offsetof(struct ipc_protocol_ap_shm, device_info); 257 + p_ci->head_array = 258 + addr + offsetof(struct ipc_protocol_ap_shm, head_array); 259 + p_ci->tail_array = 260 + addr + offsetof(struct ipc_protocol_ap_shm, tail_array); 261 + p_ci->msg_head = addr + offsetof(struct ipc_protocol_ap_shm, msg_head); 262 + p_ci->msg_tail = addr + offsetof(struct ipc_protocol_ap_shm, msg_tail); 263 + p_ci->msg_ring_addr = 264 + addr + offsetof(struct ipc_protocol_ap_shm, msg_ring); 265 + p_ci->msg_ring_entries = cpu_to_le16(IPC_MEM_MSG_ENTRIES); 266 + p_ci->msg_irq_vector = IPC_MSG_IRQ_VECTOR; 267 + p_ci->device_info_irq_vector = IPC_DEVICE_IRQ_VECTOR; 268 + 269 + ipc_mmio_set_contex_info_addr(ipc_imem->mmio, addr); 270 + 271 + ipc_pm_init(ipc_protocol); 272 + 273 + return ipc_protocol; 274 + } 275 + 276 + void ipc_protocol_deinit(struct iosm_protocol *proto) 277 + { 278 + pci_free_consistent(proto->pcie->pci, sizeof(*proto->p_ap_shm), 279 + proto->p_ap_shm, proto->phy_ap_shm); 280 + 281 + ipc_pm_deinit(proto); 282 + kfree(proto); 283 + }
+237
drivers/net/wwan/iosm/iosm_ipc_protocol.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only 2 + * 3 + * Copyright (C) 2020-21 Intel Corporation. 4 + */ 5 + 6 + #ifndef IOSM_IPC_PROTOCOL_H 7 + #define IOSM_IPC_PROTOCOL_H 8 + 9 + #include "iosm_ipc_imem.h" 10 + #include "iosm_ipc_pm.h" 11 + #include "iosm_ipc_protocol_ops.h" 12 + 13 + /* Trigger the doorbell interrupt on CP. */ 14 + #define IPC_DOORBELL_IRQ_HPDA 0 15 + #define IPC_DOORBELL_IRQ_IPC 1 16 + #define IPC_DOORBELL_IRQ_SLEEP 2 17 + 18 + /* IRQ vector number. */ 19 + #define IPC_DEVICE_IRQ_VECTOR 0 20 + #define IPC_MSG_IRQ_VECTOR 0 21 + #define IPC_UL_PIPE_IRQ_VECTOR 0 22 + #define IPC_DL_PIPE_IRQ_VECTOR 0 23 + 24 + #define IPC_MEM_MSG_ENTRIES 128 25 + 26 + /* Default time out for sending IPC messages like open pipe, close pipe etc. 27 + * during run mode. 28 + * 29 + * If the message interface lock to CP times out, the link to CP is broken. 30 + * mode : run mode (IPC_MEM_EXEC_STAGE_RUN) 31 + * unit : milliseconds 32 + */ 33 + #define IPC_MSG_COMPLETE_RUN_DEFAULT_TIMEOUT 500 /* 0.5 seconds */ 34 + 35 + /* Default time out for sending IPC messages like open pipe, close pipe etc. 36 + * during boot mode. 37 + * 38 + * If the message interface lock to CP times out, the link to CP is broken. 39 + * mode : boot mode 40 + * (IPC_MEM_EXEC_STAGE_BOOT | IPC_MEM_EXEC_STAGE_PSI | IPC_MEM_EXEC_STAGE_EBL) 41 + * unit : milliseconds 42 + */ 43 + #define IPC_MSG_COMPLETE_BOOT_DEFAULT_TIMEOUT 500 /* 0.5 seconds */ 44 + 45 + /** 46 + * struct ipc_protocol_context_info - Structure of the context info 47 + * @device_info_addr: 64 bit address to device info 48 + * @head_array: 64 bit address to head pointer arr for the pipes 49 + * @tail_array: 64 bit address to tail pointer arr for the pipes 50 + * @msg_head: 64 bit address to message head pointer 51 + * @msg_tail: 64 bit address to message tail pointer 52 + * @msg_ring_addr: 64 bit pointer to the message ring buffer 53 + * @msg_ring_entries: This field provides the number of entries which 54 + * the MR can hold 55 + * @msg_irq_vector: This field provides the IRQ which shall be 56 + * generated by the EP device when generating 57 + * completion for Messages. 58 + * @device_info_irq_vector: This field provides the IRQ which shall be 59 + * generated by the EP dev after updating Dev. Info 60 + */ 61 + struct ipc_protocol_context_info { 62 + phys_addr_t device_info_addr; 63 + phys_addr_t head_array; 64 + phys_addr_t tail_array; 65 + phys_addr_t msg_head; 66 + phys_addr_t msg_tail; 67 + phys_addr_t msg_ring_addr; 68 + __le16 msg_ring_entries; 69 + u8 msg_irq_vector; 70 + u8 device_info_irq_vector; 71 + }; 72 + 73 + /** 74 + * struct ipc_protocol_device_info - Structure for the device information 75 + * @execution_stage: CP execution stage 76 + * @ipc_status: IPC states 77 + * @device_sleep_notification: Requested device pm states 78 + */ 79 + struct ipc_protocol_device_info { 80 + __le32 execution_stage; 81 + __le32 ipc_status; 82 + __le32 device_sleep_notification; 83 + }; 84 + 85 + /** 86 + * struct ipc_protocol_ap_shm - Protocol Shared Memory Structure 87 + * @ci: Context information struct 88 + * @device_info: Device information struct 89 + * @msg_head: Point to msg head 90 + * @head_array: Array of head pointer 91 + * @msg_tail: Point to msg tail 92 + * @tail_array: Array of tail pointer 93 + * @msg_ring: Circular buffers for the read/tail and write/head 94 + * indeces. 95 + */ 96 + struct ipc_protocol_ap_shm { 97 + struct ipc_protocol_context_info ci; 98 + struct ipc_protocol_device_info device_info; 99 + __le32 msg_head; 100 + __le32 head_array[IPC_MEM_MAX_PIPES]; 101 + __le32 msg_tail; 102 + __le32 tail_array[IPC_MEM_MAX_PIPES]; 103 + union ipc_mem_msg_entry msg_ring[IPC_MEM_MSG_ENTRIES]; 104 + }; 105 + 106 + /** 107 + * struct iosm_protocol - Structure for IPC protocol. 108 + * @p_ap_shm: Pointer to Protocol Shared Memory Structure 109 + * @pm: Instance to struct iosm_pm 110 + * @pcie: Pointer to struct iosm_pcie 111 + * @imem: Pointer to struct iosm_imem 112 + * @rsp_ring: Array of OS completion objects to be triggered once CP 113 + * acknowledges a request in the message ring 114 + * @dev: Pointer to device structure 115 + * @phy_ap_shm: Physical/Mapped representation of the shared memory info 116 + * @old_msg_tail: Old msg tail ptr, until AP has handled ACK's from CP 117 + */ 118 + struct iosm_protocol { 119 + struct ipc_protocol_ap_shm *p_ap_shm; 120 + struct iosm_pm pm; 121 + struct iosm_pcie *pcie; 122 + struct iosm_imem *imem; 123 + struct ipc_rsp *rsp_ring[IPC_MEM_MSG_ENTRIES]; 124 + struct device *dev; 125 + phys_addr_t phy_ap_shm; 126 + u32 old_msg_tail; 127 + }; 128 + 129 + /** 130 + * struct ipc_call_msg_send_args - Structure for message argument for 131 + * tasklet function. 132 + * @prep_args: Arguments for message preparation function 133 + * @response: Can be NULL if result can be ignored 134 + * @msg_type: Message Type 135 + */ 136 + struct ipc_call_msg_send_args { 137 + union ipc_msg_prep_args *prep_args; 138 + struct ipc_rsp *response; 139 + enum ipc_msg_prep_type msg_type; 140 + }; 141 + 142 + /** 143 + * ipc_protocol_tq_msg_send - prepare the msg and send to CP 144 + * @ipc_protocol: Pointer to ipc_protocol instance 145 + * @msg_type: Message type 146 + * @prep_args: Message arguments 147 + * @response: Pointer to a response object which has a 148 + * completion object and return code. 149 + * 150 + * Returns: 0 on success and failure value on error 151 + */ 152 + int ipc_protocol_tq_msg_send(struct iosm_protocol *ipc_protocol, 153 + enum ipc_msg_prep_type msg_type, 154 + union ipc_msg_prep_args *prep_args, 155 + struct ipc_rsp *response); 156 + 157 + /** 158 + * ipc_protocol_msg_send - Send ipc control message to CP and wait for response 159 + * @ipc_protocol: Pointer to ipc_protocol instance 160 + * @prep: Message type 161 + * @prep_args: Message arguments 162 + * 163 + * Returns: 0 on success and failure value on error 164 + */ 165 + int ipc_protocol_msg_send(struct iosm_protocol *ipc_protocol, 166 + enum ipc_msg_prep_type prep, 167 + union ipc_msg_prep_args *prep_args); 168 + 169 + /** 170 + * ipc_protocol_suspend - Signal to CP that host wants to go to sleep (suspend). 171 + * @ipc_protocol: Pointer to ipc_protocol instance 172 + * 173 + * Returns: true if host can suspend, false if suspend must be aborted. 174 + */ 175 + bool ipc_protocol_suspend(struct iosm_protocol *ipc_protocol); 176 + 177 + /** 178 + * ipc_protocol_s2idle_sleep - Call PM function to set PM variables in s2idle 179 + * sleep/active case 180 + * @ipc_protocol: Pointer to ipc_protocol instance 181 + * @sleep: True for sleep/False for active 182 + */ 183 + void ipc_protocol_s2idle_sleep(struct iosm_protocol *ipc_protocol, bool sleep); 184 + 185 + /** 186 + * ipc_protocol_resume - Signal to CP that host wants to resume operation. 187 + * @ipc_protocol: Pointer to ipc_protocol instance 188 + * 189 + * Returns: true if host can resume, false if there is a problem. 190 + */ 191 + bool ipc_protocol_resume(struct iosm_protocol *ipc_protocol); 192 + 193 + /** 194 + * ipc_protocol_pm_dev_sleep_handle - Handles the Device Sleep state change 195 + * notification. 196 + * @ipc_protocol: Pointer to ipc_protocol instance. 197 + * 198 + * Returns: true if sleep notification handled, false otherwise. 199 + */ 200 + bool ipc_protocol_pm_dev_sleep_handle(struct iosm_protocol *ipc_protocol); 201 + 202 + /** 203 + * ipc_protocol_doorbell_trigger - Wrapper for PM function which wake up the 204 + * device if it is in low power mode 205 + * and trigger a head pointer update interrupt. 206 + * @ipc_protocol: Pointer to ipc_protocol instance. 207 + * @identifier: Specifies what component triggered hpda 208 + * update irq 209 + */ 210 + void ipc_protocol_doorbell_trigger(struct iosm_protocol *ipc_protocol, 211 + u32 identifier); 212 + 213 + /** 214 + * ipc_protocol_sleep_notification_string - Returns last Sleep Notification as 215 + * string. 216 + * @ipc_protocol: Instance pointer of Protocol module. 217 + * 218 + * Returns: Pointer to string. 219 + */ 220 + const char * 221 + ipc_protocol_sleep_notification_string(struct iosm_protocol *ipc_protocol); 222 + 223 + /** 224 + * ipc_protocol_init - Allocates IPC protocol instance 225 + * @ipc_imem: Pointer to iosm_imem structure 226 + * 227 + * Returns: Address of IPC protocol instance on success & NULL on failure. 228 + */ 229 + struct iosm_protocol *ipc_protocol_init(struct iosm_imem *ipc_imem); 230 + 231 + /** 232 + * ipc_protocol_deinit - Deallocates IPC protocol instance 233 + * @ipc_protocol: pointer to the IPC protocol instance 234 + */ 235 + void ipc_protocol_deinit(struct iosm_protocol *ipc_protocol); 236 + 237 + #endif