1 /*
2 * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include "qdf_list.h"
21 #include "qdf_status.h"
22 #include "qdf_talloc.h"
23 #include "qdf_types.h"
24 #include "__wlan_dsc.h"
25 #include "wlan_dsc.h"
26 #include "qdf_platform.h"
27
28 #define __dsc_driver_lock(vdev) __dsc_lock((vdev)->psoc->driver)
29 #define __dsc_driver_unlock(vdev) __dsc_unlock((vdev)->psoc->driver)
30
31 static QDF_STATUS
__dsc_vdev_create(struct dsc_psoc * psoc,struct dsc_vdev ** out_vdev)32 __dsc_vdev_create(struct dsc_psoc *psoc, struct dsc_vdev **out_vdev)
33 {
34 struct dsc_vdev *vdev;
35
36 if (!dsc_assert(psoc))
37 return QDF_STATUS_E_INVAL;
38
39 if (!dsc_assert(out_vdev))
40 return QDF_STATUS_E_INVAL;
41
42 *out_vdev = NULL;
43
44 vdev = qdf_talloc_type(psoc, vdev);
45 if (!vdev)
46 return QDF_STATUS_E_NOMEM;
47
48 /* init */
49 vdev->psoc = psoc;
50 __dsc_trans_init(&vdev->trans);
51 __dsc_ops_init(&vdev->ops);
52
53 /* attach */
54 __dsc_driver_lock(vdev);
55 qdf_list_insert_back(&psoc->vdevs, &vdev->node);
56 __dsc_driver_unlock(vdev);
57
58 *out_vdev = vdev;
59
60 return QDF_STATUS_SUCCESS;
61 }
62
dsc_vdev_create(struct dsc_psoc * psoc,struct dsc_vdev ** out_vdev)63 QDF_STATUS dsc_vdev_create(struct dsc_psoc *psoc, struct dsc_vdev **out_vdev)
64 {
65 QDF_STATUS status;
66
67 status = __dsc_vdev_create(psoc, out_vdev);
68
69 return status;
70 }
71
__dsc_vdev_destroy(struct dsc_vdev ** out_vdev)72 static void __dsc_vdev_destroy(struct dsc_vdev **out_vdev)
73 {
74 struct dsc_vdev *vdev;
75
76 if (!dsc_assert(out_vdev))
77 return;
78
79 vdev = *out_vdev;
80 if (!dsc_assert(vdev))
81 return;
82
83 *out_vdev = NULL;
84
85 /* flush pending transitions */
86 while (__dsc_trans_abort(&vdev->trans))
87 ;
88
89 /* detach */
90 __dsc_driver_lock(vdev);
91 qdf_list_remove_node(&vdev->psoc->vdevs, &vdev->node);
92 __dsc_driver_unlock(vdev);
93
94 /* de-init */
95 __dsc_ops_deinit(&vdev->ops);
96 __dsc_trans_deinit(&vdev->trans);
97 vdev->psoc = NULL;
98
99 qdf_tfree(vdev);
100 }
101
dsc_vdev_destroy(struct dsc_vdev ** out_vdev)102 void dsc_vdev_destroy(struct dsc_vdev **out_vdev)
103 {
104 __dsc_vdev_destroy(out_vdev);
105 }
106
__dsc_vdev_wait_for_uptree_ops(struct dsc_vdev * vdev)107 static void __dsc_vdev_wait_for_uptree_ops(struct dsc_vdev *vdev)
108 {
109 bool wait;
110
111 if (!dsc_assert(vdev))
112 return;
113
114 __dsc_driver_lock(vdev);
115 wait = vdev->psoc->ops.count > 0;
116 if (wait)
117 qdf_event_reset(&vdev->psoc->ops.event);
118 __dsc_driver_unlock(vdev);
119
120 if (wait)
121 qdf_wait_single_event(&vdev->psoc->ops.event, 0);
122
123 __dsc_driver_lock(vdev);
124 wait = vdev->psoc->driver->ops.count > 0;
125 if (wait)
126 qdf_event_reset(&vdev->psoc->driver->ops.event);
127 __dsc_driver_unlock(vdev);
128
129 if (wait)
130 qdf_wait_single_event(&vdev->psoc->driver->ops.event, 0);
131 }
132
dsc_vdev_wait_for_uptree_ops(struct dsc_vdev * vdev)133 void dsc_vdev_wait_for_uptree_ops(struct dsc_vdev *vdev)
134 {
135 __dsc_vdev_wait_for_uptree_ops(vdev);
136 }
137
138 #define __dsc_vdev_can_op(vdev) __dsc_vdev_can_trans(vdev)
139
140 /*
141 * __dsc_vdev_can_trans() - Returns if the vdev transition can occur or not
142 * @vdev: The DSC vdev
143 *
144 * This function checks if the vdev transition can occur or not by checking if
145 * any other down the tree/up the tree transition/operation is taking place.
146 *
147 * If there are any driver transition taking place, then the vdev trans/ops
148 * should be rejected and not queued in the DSC queue. Return QDF_STATUS_E_INVAL
149 * in this case.
150 *
151 * If there are any psoc transition taking place because of SSR, then vdev
152 * trans/op should be rejected and queued in the DSC queue so that it may be
153 * resumed after the current trans/op is completed. return QDF_STATUS_E_BUSY
154 * in this case.
155 *
156 * If there is a psoc transition taking place because of psoc idle shutdown,
157 * then the vdev trans/ops should be rejected and queued in the DSC queue so
158 * that it may be resumed after the current trans/ops is completed. Return
159 * QDF_STATUS_E_BUSY in this case.
160 *
161 * If there are any vdev trans/ops taking place, then the vdev trans/ops
162 * should be rejected and queued in the DSC queue so that it may be resumed
163 * after the current trans/ops is completed. Return QDF_STATUS_E_BUSY in this
164 * case.
165 *
166 * Return: QDF_STATUS_SUCCESS if transition is allowed, error code if not.
167 */
__dsc_vdev_can_trans(struct dsc_vdev * vdev)168 static QDF_STATUS __dsc_vdev_can_trans(struct dsc_vdev *vdev)
169 {
170 if (__dsc_trans_active_or_queued(&vdev->psoc->driver->trans))
171 return QDF_STATUS_E_INVAL;
172
173 if (qdf_is_recovering())
174 return QDF_STATUS_E_INVAL;
175
176 if (__dsc_trans_active_or_queued(&vdev->psoc->trans)) {
177 /* psoc idle shutdown(wifi off) needs to be added in DSC queue
178 * to avoid wifi on failure while previous psoc idle shutdown
179 * is in progress and wifi is turned on. And Wifi On also needs
180 * to be added to the queue so that it waits for SSR to
181 * complete.
182 */
183 if (qdf_is_driver_unloading())
184 return QDF_STATUS_E_INVAL;
185 else
186 return QDF_STATUS_E_BUSY;
187 }
188
189 if (__dsc_trans_active_or_queued(&vdev->trans))
190 return QDF_STATUS_E_BUSY;
191
192 return QDF_STATUS_SUCCESS;
193 }
194
195 static QDF_STATUS
__dsc_vdev_trans_start_nolock(struct dsc_vdev * vdev,const char * desc)196 __dsc_vdev_trans_start_nolock(struct dsc_vdev *vdev, const char *desc)
197 {
198 QDF_STATUS status = QDF_STATUS_SUCCESS;
199
200 status = __dsc_vdev_can_trans(vdev);
201 if (QDF_IS_STATUS_ERROR(status))
202 return status;
203
204 return __dsc_trans_start(&vdev->trans, desc);
205 }
206
207 static QDF_STATUS
__dsc_vdev_trans_start(struct dsc_vdev * vdev,const char * desc)208 __dsc_vdev_trans_start(struct dsc_vdev *vdev, const char *desc)
209 {
210 QDF_STATUS status;
211
212 if (!dsc_assert(vdev))
213 return QDF_STATUS_E_INVAL;
214
215 if (!dsc_assert(desc))
216 return QDF_STATUS_E_INVAL;
217
218 __dsc_driver_lock(vdev);
219 status = __dsc_vdev_trans_start_nolock(vdev, desc);
220 __dsc_driver_unlock(vdev);
221
222 return status;
223 }
224
dsc_vdev_trans_start(struct dsc_vdev * vdev,const char * desc)225 QDF_STATUS dsc_vdev_trans_start(struct dsc_vdev *vdev, const char *desc)
226 {
227 QDF_STATUS status;
228
229 dsc_enter_str(desc);
230 status = __dsc_vdev_trans_start(vdev, desc);
231 if (QDF_IS_STATUS_ERROR(status))
232 dsc_exit_status(status);
233
234 return status;
235 }
236
237 static QDF_STATUS
__dsc_vdev_trans_start_wait(struct dsc_vdev * vdev,const char * desc)238 __dsc_vdev_trans_start_wait(struct dsc_vdev *vdev, const char *desc)
239 {
240 QDF_STATUS status;
241 struct dsc_tran tran = { 0 };
242
243 if (!dsc_assert(vdev))
244 return QDF_STATUS_E_INVAL;
245
246 if (!dsc_assert(desc))
247 return QDF_STATUS_E_INVAL;
248
249 __dsc_driver_lock(vdev);
250
251 /* try to start without waiting */
252 status = __dsc_vdev_trans_start_nolock(vdev, desc);
253 if (QDF_IS_STATUS_SUCCESS(status) || status == QDF_STATUS_E_INVAL)
254 goto unlock;
255
256 status = __dsc_trans_queue(&vdev->trans, &tran, desc);
257 if (QDF_IS_STATUS_ERROR(status))
258 goto unlock;
259
260 __dsc_driver_unlock(vdev);
261
262 return __dsc_tran_wait(&tran);
263
264 unlock:
265 __dsc_driver_unlock(vdev);
266
267 return status;
268 }
269
dsc_vdev_trans_start_wait(struct dsc_vdev * vdev,const char * desc)270 QDF_STATUS dsc_vdev_trans_start_wait(struct dsc_vdev *vdev, const char *desc)
271 {
272 QDF_STATUS status;
273
274 dsc_enter_str(desc);
275 status = __dsc_vdev_trans_start_wait(vdev, desc);
276 if (QDF_IS_STATUS_ERROR(status))
277 dsc_exit_status(status);
278
279 return status;
280 }
281
__dsc_vdev_trigger_trans(struct dsc_vdev * vdev)282 static void __dsc_vdev_trigger_trans(struct dsc_vdev *vdev)
283 {
284 if (__dsc_driver_trans_trigger_checked(vdev->psoc->driver))
285 return;
286
287 if (__dsc_psoc_trans_trigger_checked(vdev->psoc))
288 return;
289
290 __dsc_trans_trigger(&vdev->trans);
291 }
292
__dsc_vdev_trans_stop(struct dsc_vdev * vdev)293 static void __dsc_vdev_trans_stop(struct dsc_vdev *vdev)
294 {
295 if (!dsc_assert(vdev))
296 return;
297
298 __dsc_driver_lock(vdev);
299
300 __dsc_trans_stop(&vdev->trans);
301 __dsc_vdev_trigger_trans(vdev);
302
303 __dsc_driver_unlock(vdev);
304 }
305
dsc_vdev_trans_stop(struct dsc_vdev * vdev)306 void dsc_vdev_trans_stop(struct dsc_vdev *vdev)
307 {
308 __dsc_vdev_trans_stop(vdev);
309 }
310
__dsc_vdev_assert_trans_protected(struct dsc_vdev * vdev)311 static void __dsc_vdev_assert_trans_protected(struct dsc_vdev *vdev)
312 {
313 if (!dsc_assert(vdev))
314 return;
315
316 __dsc_driver_lock(vdev);
317 dsc_assert(__dsc_trans_active(&vdev->trans) ||
318 __dsc_trans_active(&vdev->psoc->trans) ||
319 __dsc_trans_active(&vdev->psoc->driver->trans));
320 __dsc_driver_unlock(vdev);
321 }
322
dsc_vdev_assert_trans_protected(struct dsc_vdev * vdev)323 void dsc_vdev_assert_trans_protected(struct dsc_vdev *vdev)
324 {
325 __dsc_vdev_assert_trans_protected(vdev);
326 }
327
__dsc_vdev_op_start(struct dsc_vdev * vdev,const char * func)328 static QDF_STATUS __dsc_vdev_op_start(struct dsc_vdev *vdev, const char *func)
329 {
330 QDF_STATUS status;
331
332 if (!dsc_assert(vdev))
333 return QDF_STATUS_E_INVAL;
334
335 if (!dsc_assert(func))
336 return QDF_STATUS_E_INVAL;
337
338 __dsc_driver_lock(vdev);
339
340 status = __dsc_vdev_can_op(vdev);
341 if (QDF_IS_STATUS_ERROR(status))
342 goto unlock;
343
344 status = __dsc_ops_insert(&vdev->ops, func);
345
346 unlock:
347 __dsc_driver_unlock(vdev);
348
349 return status;
350 }
351
_dsc_vdev_op_start(struct dsc_vdev * vdev,const char * func)352 QDF_STATUS _dsc_vdev_op_start(struct dsc_vdev *vdev, const char *func)
353 {
354 QDF_STATUS status;
355
356 /* do not log from here because it can flood log message because vdev
357 * op protect is per vdev operation
358 */
359
360 status = __dsc_vdev_op_start(vdev, func);
361
362 return status;
363 }
364
__dsc_vdev_op_stop(struct dsc_vdev * vdev,const char * func)365 static void __dsc_vdev_op_stop(struct dsc_vdev *vdev, const char *func)
366 {
367 if (!dsc_assert(vdev))
368 return;
369
370 if (!dsc_assert(func))
371 return;
372
373 __dsc_driver_lock(vdev);
374 if (__dsc_ops_remove(&vdev->ops, func))
375 qdf_event_set(&vdev->ops.event);
376 __dsc_driver_unlock(vdev);
377 }
378
_dsc_vdev_op_stop(struct dsc_vdev * vdev,const char * func)379 void _dsc_vdev_op_stop(struct dsc_vdev *vdev, const char *func)
380 {
381 /* do not log from here because it can flood log message because vdev
382 * op protect is per vdev operation
383 */
384 __dsc_vdev_op_stop(vdev, func);
385 }
386
__dsc_vdev_wait_for_ops(struct dsc_vdev * vdev)387 static void __dsc_vdev_wait_for_ops(struct dsc_vdev *vdev)
388 {
389 bool wait;
390
391 if (!dsc_assert(vdev))
392 return;
393
394 __dsc_driver_lock(vdev);
395
396 wait = vdev->ops.count > 0;
397 if (wait)
398 qdf_event_reset(&vdev->ops.event);
399
400 __dsc_driver_unlock(vdev);
401
402 if (wait)
403 qdf_wait_single_event(&vdev->ops.event, 0);
404 }
405
dsc_vdev_wait_for_ops(struct dsc_vdev * vdev)406 void dsc_vdev_wait_for_ops(struct dsc_vdev *vdev)
407 {
408 __dsc_vdev_wait_for_ops(vdev);
409 }
410
dsc_vdev_get_cached_cmd(struct dsc_vdev * vdev)411 uint8_t dsc_vdev_get_cached_cmd(struct dsc_vdev *vdev)
412 {
413 return vdev->nb_cmd_during_ssr;
414 }
415
dsc_vdev_cache_command(struct dsc_vdev * vdev,uint8_t cmd_id)416 void dsc_vdev_cache_command(struct dsc_vdev *vdev, uint8_t cmd_id)
417 {
418 vdev->nb_cmd_during_ssr = cmd_id;
419 }
420
421