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