GNU Linux-libre 6.8.7-gnu
[releases.git] / 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                 dma_alloc_coherent(&ipc_protocol->pcie->pci->dev,
243                                    sizeof(*ipc_protocol->p_ap_shm),
244                                    &ipc_protocol->phy_ap_shm, GFP_KERNEL);
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         dma_free_coherent(&proto->pcie->pci->dev, sizeof(*proto->p_ap_shm),
279                           proto->p_ap_shm, proto->phy_ap_shm);
280
281         ipc_pm_deinit(proto);
282         kfree(proto);
283 }