1 // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
2 /*
3 *
4 * (C) COPYRIGHT 2020-2022 ARM Limited. All rights reserved.
5 *
6 * This program is free software and is provided to you under the terms of the
7 * GNU General Public License version 2 as published by the Free Software
8 * Foundation, and any use by you of this program is subject to the terms
9 * of such GNU license.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you can access it online at
18 * http://www.gnu.org/licenses/gpl-2.0.html.
19 *
20 */
21
22 #include <linux/fdtable.h>
23 #include <linux/module.h>
24
25 #include <linux/delay.h>
26 #include <linux/mutex.h>
27 #include <linux/ktime.h>
28 #include <linux/version.h>
29 #if (KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE)
30 #include <linux/sched/task.h>
31 #else
32 #include <linux/sched.h>
33 #endif
34 #include "mali_kbase.h"
35 #include "backend/gpu/mali_kbase_irq_internal.h"
36 #include "backend/gpu/mali_kbase_pm_internal.h"
37 #include "backend/gpu/mali_kbase_clk_rate_trace_mgr.h"
38
39 #include <kutf/kutf_suite.h>
40 #include <kutf/kutf_utils.h>
41 #include <kutf/kutf_helpers.h>
42 #include <kutf/kutf_helpers_user.h>
43
44 #include "../mali_kutf_clk_rate_trace_test.h"
45
46 #define MINOR_FOR_FIRST_KBASE_DEV (-1)
47
48 /* KUTF test application pointer for this test */
49 static struct kutf_application *kutf_app;
50
51 enum portal_server_state {
52 PORTAL_STATE_NO_CLK,
53 PORTAL_STATE_LIVE,
54 PORTAL_STATE_CLOSING,
55 };
56
57 /**
58 * struct clk_trace_snapshot - Trace info data on a clock.
59 * @previous_rate: Snapshot start point clock rate.
60 * @current_rate: End point clock rate. It becomes the start rate of the
61 * next trace snapshot.
62 * @rate_up_cnt: Count in the snapshot duration when the clock trace
63 * write is a rate of higher value than the last.
64 * @rate_down_cnt: Count in the snapshot duration when the clock trace write
65 * is a rate of lower value than the last.
66 */
67 struct clk_trace_snapshot {
68 unsigned long previous_rate;
69 unsigned long current_rate;
70 u32 rate_up_cnt;
71 u32 rate_down_cnt;
72 };
73
74 /**
75 * struct kutf_clk_rate_trace_fixture_data - Fixture data for the test.
76 * @kbdev: kbase device for the GPU.
77 * @listener: Clock rate change listener structure.
78 * @invoke_notify: When true, invoke notify command is being executed.
79 * @snapshot: Clock trace update snapshot data array. A snapshot
80 * for each clock contains info accumulated beteen two
81 * GET_TRACE_SNAPSHOT requests.
82 * @nclks: Number of clocks visible to the trace portal.
83 * @pm_ctx_cnt: Net count of PM (Power Management) context INC/DEC
84 * PM_CTX_CNT requests made to the portal. On change from
85 * 0 to 1 (INC), or, 1 to 0 (DEC), a PM context action is
86 * triggered.
87 * @total_update_cnt: Total number of received trace write callbacks.
88 * @server_state: Portal server operational state.
89 * @result_msg: Message for the test result.
90 * @test_status: Portal test reslt status.
91 */
92 struct kutf_clk_rate_trace_fixture_data {
93 struct kbase_device *kbdev;
94 struct kbase_clk_rate_listener listener;
95 bool invoke_notify;
96 struct clk_trace_snapshot snapshot[BASE_MAX_NR_CLOCKS_REGULATORS];
97 unsigned int nclks;
98 unsigned int pm_ctx_cnt;
99 unsigned int total_update_cnt;
100 enum portal_server_state server_state;
101 char const *result_msg;
102 enum kutf_result_status test_status;
103 };
104
105 struct clk_trace_portal_input {
106 struct kutf_helper_named_val cmd_input;
107 enum kbasep_clk_rate_trace_req portal_cmd;
108 int named_val_err;
109 };
110
111 struct kbasep_cmd_name_pair {
112 enum kbasep_clk_rate_trace_req cmd;
113 const char *name;
114 };
115
116 static const struct kbasep_cmd_name_pair kbasep_portal_cmd_name_map[] = {
117 { PORTAL_CMD_GET_PLATFORM, GET_PLATFORM },
118 { PORTAL_CMD_GET_CLK_RATE_MGR, GET_CLK_RATE_MGR },
119 { PORTAL_CMD_GET_CLK_RATE_TRACE, GET_CLK_RATE_TRACE },
120 { PORTAL_CMD_GET_TRACE_SNAPSHOT, GET_TRACE_SNAPSHOT },
121 { PORTAL_CMD_INC_PM_CTX_CNT, INC_PM_CTX_CNT },
122 { PORTAL_CMD_DEC_PM_CTX_CNT, DEC_PM_CTX_CNT },
123 { PORTAL_CMD_CLOSE_PORTAL, CLOSE_PORTAL },
124 { PORTAL_CMD_INVOKE_NOTIFY_42KHZ, INVOKE_NOTIFY_42KHZ },
125 };
126
127 /* Global pointer for the kutf_portal_trace_write() to use. When
128 * this pointer is engaged, new requests for create fixture will fail
129 * hence limiting the use of the portal at any time to a singleton.
130 */
131 static struct kutf_clk_rate_trace_fixture_data *g_ptr_portal_data;
132
133 #define PORTAL_MSG_LEN (KUTF_MAX_LINE_LENGTH - MAX_REPLY_NAME_LEN)
134 static char portal_msg_buf[PORTAL_MSG_LEN];
135
kutf_portal_trace_write(struct kbase_clk_rate_listener * listener,u32 index,u32 new_rate)136 static void kutf_portal_trace_write(
137 struct kbase_clk_rate_listener *listener,
138 u32 index, u32 new_rate)
139 {
140 struct clk_trace_snapshot *snapshot;
141 struct kutf_clk_rate_trace_fixture_data *data;
142
143 if (listener == NULL) {
144 pr_err("%s - index: %u, new_rate: %u, listener is NULL\n",
145 __func__, index, new_rate);
146 return;
147 }
148
149 data = container_of(listener, struct kutf_clk_rate_trace_fixture_data,
150 listener);
151
152 lockdep_assert_held(&data->kbdev->pm.clk_rtm.lock);
153
154 if (WARN_ON(g_ptr_portal_data == NULL))
155 return;
156 if (WARN_ON(index >= g_ptr_portal_data->nclks))
157 return;
158
159 /* This callback is triggered by invoke notify command, skipping */
160 if (data->invoke_notify)
161 return;
162
163 snapshot = &g_ptr_portal_data->snapshot[index];
164 if (new_rate > snapshot->current_rate)
165 snapshot->rate_up_cnt++;
166 else
167 snapshot->rate_down_cnt++;
168 snapshot->current_rate = new_rate;
169 g_ptr_portal_data->total_update_cnt++;
170 }
171
kutf_set_pm_ctx_active(struct kutf_context * context)172 static void kutf_set_pm_ctx_active(struct kutf_context *context)
173 {
174 struct kutf_clk_rate_trace_fixture_data *data = context->fixture;
175
176 if (WARN_ON(data->pm_ctx_cnt != 1))
177 return;
178
179 kbase_pm_context_active(data->kbdev);
180 kbase_pm_wait_for_desired_state(data->kbdev);
181 #if !MALI_USE_CSF
182 kbase_pm_request_gpu_cycle_counter(data->kbdev);
183 #endif
184 }
185
kutf_set_pm_ctx_idle(struct kutf_context * context)186 static void kutf_set_pm_ctx_idle(struct kutf_context *context)
187 {
188 struct kutf_clk_rate_trace_fixture_data *data = context->fixture;
189
190 if (WARN_ON(data->pm_ctx_cnt > 0))
191 return;
192 #if !MALI_USE_CSF
193 kbase_pm_release_gpu_cycle_counter(data->kbdev);
194 #endif
195 kbase_pm_context_idle(data->kbdev);
196 }
197
kutf_clk_trace_do_change_pm_ctx(struct kutf_context * context,struct clk_trace_portal_input * cmd)198 static const char *kutf_clk_trace_do_change_pm_ctx(struct kutf_context *context,
199 struct clk_trace_portal_input *cmd)
200 {
201 struct kutf_clk_rate_trace_fixture_data *data = context->fixture;
202 int seq = cmd->cmd_input.u.val_u64 & 0xFF;
203 const unsigned int cnt = data->pm_ctx_cnt;
204 const enum kbasep_clk_rate_trace_req req = cmd->portal_cmd;
205 char const *errmsg = NULL;
206
207 WARN_ON(req != PORTAL_CMD_INC_PM_CTX_CNT &&
208 req != PORTAL_CMD_DEC_PM_CTX_CNT);
209
210 if (req == PORTAL_CMD_INC_PM_CTX_CNT && cnt < UINT_MAX) {
211 data->pm_ctx_cnt++;
212 if (data->pm_ctx_cnt == 1)
213 kutf_set_pm_ctx_active(context);
214 }
215
216 if (req == PORTAL_CMD_DEC_PM_CTX_CNT && cnt > 0) {
217 data->pm_ctx_cnt--;
218 if (data->pm_ctx_cnt == 0)
219 kutf_set_pm_ctx_idle(context);
220 }
221
222 /* Skip the length check, no chance of overflow for two ints */
223 snprintf(portal_msg_buf, PORTAL_MSG_LEN,
224 "{SEQ:%d, PM_CTX_CNT:%u}", seq, data->pm_ctx_cnt);
225
226 if (kutf_helper_send_named_str(context, "ACK", portal_msg_buf)) {
227 pr_warn("Error in sending ack for adjusting pm_ctx_cnt\n");
228 errmsg = kutf_dsprintf(&context->fixture_pool,
229 "Error in sending ack for adjusting pm_ctx_cnt");
230 }
231
232 return errmsg;
233 }
234
kutf_clk_trace_do_get_rate(struct kutf_context * context,struct clk_trace_portal_input * cmd)235 static const char *kutf_clk_trace_do_get_rate(struct kutf_context *context,
236 struct clk_trace_portal_input *cmd)
237 {
238 struct kutf_clk_rate_trace_fixture_data *data = context->fixture;
239 struct kbase_device *kbdev = data->kbdev;
240 int seq = cmd->cmd_input.u.val_u64 & 0xFF;
241 unsigned long rate;
242 bool idle;
243 int ret;
244 int i;
245 char const *errmsg = NULL;
246
247 WARN_ON((cmd->portal_cmd != PORTAL_CMD_GET_CLK_RATE_MGR) &&
248 (cmd->portal_cmd != PORTAL_CMD_GET_CLK_RATE_TRACE));
249
250 ret = snprintf(portal_msg_buf, PORTAL_MSG_LEN,
251 "{SEQ:%d, RATE:[", seq);
252
253 for (i = 0; i < data->nclks; i++) {
254 spin_lock(&kbdev->pm.clk_rtm.lock);
255 if (cmd->portal_cmd == PORTAL_CMD_GET_CLK_RATE_MGR)
256 rate = kbdev->pm.clk_rtm.clks[i]->clock_val;
257 else
258 rate = data->snapshot[i].current_rate;
259 idle = kbdev->pm.clk_rtm.gpu_idle;
260 spin_unlock(&kbdev->pm.clk_rtm.lock);
261
262 if ((i + 1) == data->nclks)
263 ret += snprintf(portal_msg_buf + ret,
264 PORTAL_MSG_LEN - ret, "0x%lx], GPU_IDLE:%d}",
265 rate, idle);
266 else
267 ret += snprintf(portal_msg_buf + ret,
268 PORTAL_MSG_LEN - ret, "0x%lx, ", rate);
269
270 if (ret >= PORTAL_MSG_LEN) {
271 pr_warn("Message buf overflow with rate array data\n");
272 return kutf_dsprintf(&context->fixture_pool,
273 "Message buf overflow with rate array data");
274 }
275 }
276
277 if (kutf_helper_send_named_str(context, "ACK", portal_msg_buf)) {
278 pr_warn("Error in sending back rate array\n");
279 errmsg = kutf_dsprintf(&context->fixture_pool,
280 "Error in sending rate array");
281 }
282
283 return errmsg;
284 }
285
286 /**
287 * kutf_clk_trace_do_get_snapshot() - Send back the current snapshot
288 * @context: KUTF context
289 * @cmd: The decoded portal input request
290 *
291 * The accumulated clock rate trace information is kept inside as an snapshot
292 * record. A user request of getting the snapshot marks the closure of the
293 * current snapshot record, and the start of the next one. The response
294 * message contains the current snapshot record, with each clock's
295 * data sequentially placed inside (array marker) [ ].
296 *
297 * Return: generated string
298 */
kutf_clk_trace_do_get_snapshot(struct kutf_context * context,struct clk_trace_portal_input * cmd)299 static const char *kutf_clk_trace_do_get_snapshot(struct kutf_context *context,
300 struct clk_trace_portal_input *cmd)
301 {
302 struct kutf_clk_rate_trace_fixture_data *data = context->fixture;
303 struct clk_trace_snapshot snapshot;
304 int seq = cmd->cmd_input.u.val_u64 & 0xFF;
305 int ret;
306 int i;
307 char const *fmt;
308 char const *errmsg = NULL;
309
310 WARN_ON(cmd->portal_cmd != PORTAL_CMD_GET_TRACE_SNAPSHOT);
311
312 ret = snprintf(portal_msg_buf, PORTAL_MSG_LEN,
313 "{SEQ:%d, SNAPSHOT_ARRAY:[", seq);
314
315 for (i = 0; i < data->nclks; i++) {
316 spin_lock(&data->kbdev->pm.clk_rtm.lock);
317 /* copy out the snapshot of the clock */
318 snapshot = data->snapshot[i];
319 /* Set the next snapshot start condition */
320 data->snapshot[i].previous_rate = snapshot.current_rate;
321 data->snapshot[i].rate_up_cnt = 0;
322 data->snapshot[i].rate_down_cnt = 0;
323 spin_unlock(&data->kbdev->pm.clk_rtm.lock);
324
325 /* Check i corresponding to the last clock */
326 if ((i + 1) == data->nclks)
327 fmt = "(0x%lx, 0x%lx, %u, %u)]}";
328 else
329 fmt = "(0x%lx, 0x%lx, %u, %u), ";
330 ret += snprintf(portal_msg_buf + ret, PORTAL_MSG_LEN - ret,
331 fmt, snapshot.previous_rate, snapshot.current_rate,
332 snapshot.rate_up_cnt, snapshot.rate_down_cnt);
333 if (ret >= PORTAL_MSG_LEN) {
334 pr_warn("Message buf overflow with snapshot data\n");
335 return kutf_dsprintf(&context->fixture_pool,
336 "Message buf overflow with snapshot data");
337 }
338 }
339
340 if (kutf_helper_send_named_str(context, "ACK", portal_msg_buf)) {
341 pr_warn("Error in sending back snapshot array\n");
342 errmsg = kutf_dsprintf(&context->fixture_pool,
343 "Error in sending snapshot array");
344 }
345
346 return errmsg;
347 }
348
349 /**
350 * kutf_clk_trace_do_invoke_notify_42k() - Invokes the stored notification callback
351 * @context: KUTF context
352 * @cmd: The decoded portal input request
353 *
354 * Invokes frequency change notification callbacks with a fake
355 * GPU frequency 42 kHz for the top clock domain.
356 *
357 * Return: generated string
358 */
kutf_clk_trace_do_invoke_notify_42k(struct kutf_context * context,struct clk_trace_portal_input * cmd)359 static const char *kutf_clk_trace_do_invoke_notify_42k(
360 struct kutf_context *context,
361 struct clk_trace_portal_input *cmd)
362 {
363 struct kutf_clk_rate_trace_fixture_data *data = context->fixture;
364 int seq = cmd->cmd_input.u.val_u64 & 0xFF;
365 const unsigned long new_rate_hz = 42000;
366 int ret;
367 char const *errmsg = NULL;
368 struct kbase_clk_rate_trace_manager *clk_rtm = &data->kbdev->pm.clk_rtm;
369
370 WARN_ON(cmd->portal_cmd != PORTAL_CMD_INVOKE_NOTIFY_42KHZ);
371
372 spin_lock(&clk_rtm->lock);
373
374 data->invoke_notify = true;
375 kbase_clk_rate_trace_manager_notify_all(
376 clk_rtm, 0, new_rate_hz);
377 data->invoke_notify = false;
378
379 spin_unlock(&clk_rtm->lock);
380
381 ret = snprintf(portal_msg_buf, PORTAL_MSG_LEN,
382 "{SEQ:%d, HZ:%lu}", seq, new_rate_hz);
383
384 if (ret >= PORTAL_MSG_LEN) {
385 pr_warn("Message buf overflow with invoked data\n");
386 return kutf_dsprintf(&context->fixture_pool,
387 "Message buf overflow with invoked data");
388 }
389
390 if (kutf_helper_send_named_str(context, "ACK", portal_msg_buf)) {
391 pr_warn("Error in sending ack for " INVOKE_NOTIFY_42KHZ "request\n");
392 errmsg = kutf_dsprintf(&context->fixture_pool,
393 "Error in sending ack for " INVOKE_NOTIFY_42KHZ "request");
394 }
395
396 return errmsg;
397 }
398
kutf_clk_trace_do_close_portal(struct kutf_context * context,struct clk_trace_portal_input * cmd)399 static const char *kutf_clk_trace_do_close_portal(struct kutf_context *context,
400 struct clk_trace_portal_input *cmd)
401 {
402 struct kutf_clk_rate_trace_fixture_data *data = context->fixture;
403 int seq = cmd->cmd_input.u.val_u64 & 0xFF;
404 char const *errmsg = NULL;
405
406 WARN_ON(cmd->portal_cmd != PORTAL_CMD_CLOSE_PORTAL);
407
408 data->server_state = PORTAL_STATE_CLOSING;
409
410 /* Skip the length check, no chance of overflow for two ints */
411 snprintf(portal_msg_buf, PORTAL_MSG_LEN,
412 "{SEQ:%d, PM_CTX_CNT:%u}", seq, data->pm_ctx_cnt);
413
414 if (kutf_helper_send_named_str(context, "ACK", portal_msg_buf)) {
415 pr_warn("Error in sending ack for " CLOSE_PORTAL "reuquest\n");
416 errmsg = kutf_dsprintf(&context->fixture_pool,
417 "Error in sending ack for " CLOSE_PORTAL "reuquest");
418 }
419
420 return errmsg;
421 }
422
423 /**
424 * kutf_clk_trace_do_get_platform() - Gets platform information
425 * @context: KUTF context
426 * @cmd: The decoded portal input request
427 *
428 * Checks the gpu node in the device tree to see if arbitration is enabled
429 * If so determines device tree whether platform is PV or PTM
430 *
431 * Return: A string to indicate the platform (PV/PTM/GPU/UNKNOWN)
432 */
kutf_clk_trace_do_get_platform(struct kutf_context * context,struct clk_trace_portal_input * cmd)433 static const char *kutf_clk_trace_do_get_platform(
434 struct kutf_context *context,
435 struct clk_trace_portal_input *cmd)
436 {
437 int seq = cmd->cmd_input.u.val_u64 & 0xFF;
438 char const *errmsg = NULL;
439 const void *arbiter_if_node = NULL;
440 const void *power_node = NULL;
441 const char *platform = "GPU";
442 #if defined(CONFIG_MALI_ARBITER_SUPPORT) && defined(CONFIG_OF)
443 struct kutf_clk_rate_trace_fixture_data *data = context->fixture;
444
445 arbiter_if_node =
446 of_get_property(data->kbdev->dev->of_node, "arbiter_if", NULL);
447 #endif
448 if (arbiter_if_node) {
449 power_node = of_find_compatible_node(NULL, NULL,
450 "arm,mali-gpu-power");
451 if (power_node) {
452 platform = "PV";
453 } else {
454 power_node = of_find_compatible_node(NULL, NULL,
455 "arm,mali-ptm");
456 if (power_node)
457 platform = "PTM";
458 else
459 platform = "UNKNOWN";
460 }
461 } else {
462 platform = "GPU";
463 }
464
465 pr_debug("%s - platform is %s\n", __func__, platform);
466 snprintf(portal_msg_buf, PORTAL_MSG_LEN,
467 "{SEQ:%d, PLATFORM:%s}", seq, platform);
468
469 WARN_ON(cmd->portal_cmd != PORTAL_CMD_GET_PLATFORM);
470
471 if (kutf_helper_send_named_str(context, "ACK", portal_msg_buf)) {
472 pr_warn("Error in sending ack for " CLOSE_PORTAL "reuquest\n");
473 errmsg = kutf_dsprintf(&context->fixture_pool,
474 "Error in sending ack for " GET_PLATFORM "request");
475 }
476
477 return errmsg;
478 }
479
kutf_clk_trace_dequeue_portal_cmd(struct kutf_context * context,struct clk_trace_portal_input * cmd)480 static bool kutf_clk_trace_dequeue_portal_cmd(struct kutf_context *context,
481 struct clk_trace_portal_input *cmd)
482 {
483 int i;
484 int err = kutf_helper_receive_named_val(context, &cmd->cmd_input);
485
486 cmd->named_val_err = err;
487 if (err == KUTF_HELPER_ERR_NONE &&
488 cmd->cmd_input.type == KUTF_HELPER_VALTYPE_U64) {
489 /* All portal request commands are of format (named u64):
490 * CMD_NAME=1234
491 * where, 1234 is a (variable) sequence number tag.
492 */
493 for (i = 0; i < PORTAL_TOTAL_CMDS; i++) {
494 if (strcmp(cmd->cmd_input.val_name,
495 kbasep_portal_cmd_name_map[i].name))
496 continue;
497
498 cmd->portal_cmd = kbasep_portal_cmd_name_map[i].cmd;
499 return true;
500 }
501 }
502
503 cmd->portal_cmd = PORTAL_CMD_INVALID;
504 return false;
505 }
506
kutf_clk_trace_flag_result(struct kutf_context * context,enum kutf_result_status result,char const * msg)507 static void kutf_clk_trace_flag_result(struct kutf_context *context,
508 enum kutf_result_status result, char const *msg)
509 {
510 struct kutf_clk_rate_trace_fixture_data *data = context->fixture;
511
512 if (result > data->test_status) {
513 data->test_status = result;
514 if (msg)
515 data->result_msg = msg;
516 if (data->server_state == PORTAL_STATE_LIVE &&
517 result > KUTF_RESULT_WARN) {
518 data->server_state = PORTAL_STATE_CLOSING;
519 }
520 }
521 }
522
kutf_clk_trace_process_portal_cmd(struct kutf_context * context,struct clk_trace_portal_input * cmd)523 static bool kutf_clk_trace_process_portal_cmd(struct kutf_context *context,
524 struct clk_trace_portal_input *cmd)
525 {
526 char const *errmsg = NULL;
527
528 BUILD_BUG_ON(ARRAY_SIZE(kbasep_portal_cmd_name_map) !=
529 PORTAL_TOTAL_CMDS);
530 WARN_ON(cmd->portal_cmd == PORTAL_CMD_INVALID);
531
532 switch (cmd->portal_cmd) {
533 case PORTAL_CMD_GET_PLATFORM:
534 errmsg = kutf_clk_trace_do_get_platform(context, cmd);
535 break;
536 case PORTAL_CMD_GET_CLK_RATE_MGR:
537 fallthrough;
538 case PORTAL_CMD_GET_CLK_RATE_TRACE:
539 errmsg = kutf_clk_trace_do_get_rate(context, cmd);
540 break;
541 case PORTAL_CMD_GET_TRACE_SNAPSHOT:
542 errmsg = kutf_clk_trace_do_get_snapshot(context, cmd);
543 break;
544 case PORTAL_CMD_INC_PM_CTX_CNT:
545 fallthrough;
546 case PORTAL_CMD_DEC_PM_CTX_CNT:
547 errmsg = kutf_clk_trace_do_change_pm_ctx(context, cmd);
548 break;
549 case PORTAL_CMD_CLOSE_PORTAL:
550 errmsg = kutf_clk_trace_do_close_portal(context, cmd);
551 break;
552 case PORTAL_CMD_INVOKE_NOTIFY_42KHZ:
553 errmsg = kutf_clk_trace_do_invoke_notify_42k(context, cmd);
554 break;
555 default:
556 pr_warn("Don't know how to handle portal_cmd: %d, abort session.\n",
557 cmd->portal_cmd);
558 errmsg = kutf_dsprintf(&context->fixture_pool,
559 "Don't know how to handle portal_cmd: %d",
560 cmd->portal_cmd);
561 break;
562 }
563
564 if (errmsg)
565 kutf_clk_trace_flag_result(context, KUTF_RESULT_FAIL, errmsg);
566
567 return (errmsg == NULL);
568 }
569
570 /**
571 * kutf_clk_trace_do_nack_response() - respond a NACK to erroneous input
572 * @context: KUTF context
573 * @cmd: The erroneous input request
574 *
575 * This function deal with an erroneous input request, and respond with
576 * a proper 'NACK' message.
577 *
578 * Return: 0 on success, non-zero on failure
579 */
kutf_clk_trace_do_nack_response(struct kutf_context * context,struct clk_trace_portal_input * cmd)580 static int kutf_clk_trace_do_nack_response(struct kutf_context *context,
581 struct clk_trace_portal_input *cmd)
582 {
583 int seq;
584 int err;
585 char const *errmsg = NULL;
586
587 WARN_ON(cmd->portal_cmd != PORTAL_CMD_INVALID);
588
589 if (cmd->named_val_err == KUTF_HELPER_ERR_NONE &&
590 cmd->cmd_input.type == KUTF_HELPER_VALTYPE_U64) {
591 /* Keep seq number as % 256 */
592 seq = cmd->cmd_input.u.val_u64 & 255;
593 snprintf(portal_msg_buf, PORTAL_MSG_LEN,
594 "{SEQ:%d, MSG: Unknown command '%s'.}", seq,
595 cmd->cmd_input.val_name);
596 err = kutf_helper_send_named_str(context, "NACK",
597 portal_msg_buf);
598 } else
599 err = kutf_helper_send_named_str(context, "NACK",
600 "Wrong portal cmd format (Ref example: CMD_NAME=0X16)");
601
602 if (err) {
603 errmsg = kutf_dsprintf(&context->fixture_pool,
604 "Failed to send portal NACK response");
605 kutf_clk_trace_flag_result(context, KUTF_RESULT_FAIL, errmsg);
606 }
607
608 return err;
609 }
610
611 /**
612 * kutf_clk_trace_barebone_check() - Sanity test on the clock tracing
613 * @context: KUTF context
614 *
615 * This function carries out some basic test on the tracing operation:
616 * 1). GPU idle on test start, trace rate should be 0 (low power state)
617 * 2). Make sure GPU is powered up, the trace rate should match
618 * that from the clcok manager's internal recorded rate
619 * 3). If the GPU active transition occurs following 2), there
620 * must be rate change event from tracing.
621 */
kutf_clk_trace_barebone_check(struct kutf_context * context)622 static void kutf_clk_trace_barebone_check(struct kutf_context *context)
623 {
624 struct kutf_clk_rate_trace_fixture_data *data = context->fixture;
625 struct kbase_device *kbdev = data->kbdev;
626 bool fail = false;
627 bool idle[2] = { false };
628 char const *msg = NULL;
629 int i;
630
631 /* Check consistency if gpu happens to be idle */
632 spin_lock(&kbdev->pm.clk_rtm.lock);
633 idle[0] = kbdev->pm.clk_rtm.gpu_idle;
634 if (kbdev->pm.clk_rtm.gpu_idle) {
635 for (i = 0; i < data->nclks; i++) {
636 if (data->snapshot[i].current_rate) {
637 /* Idle should have a rate 0 */
638 fail = true;
639 break;
640 }
641 }
642 }
643 spin_unlock(&kbdev->pm.clk_rtm.lock);
644 if (fail) {
645 msg = kutf_dsprintf(&context->fixture_pool,
646 "GPU Idle not yielding 0-rate");
647 pr_err("Trace did not see idle rate\n");
648 } else {
649 /* Make local PM active if not done so yet */
650 if (data->pm_ctx_cnt == 0) {
651 /* Ensure the GPU is powered */
652 data->pm_ctx_cnt++;
653 kutf_set_pm_ctx_active(context);
654 }
655 /* Checking the rate is consistent */
656 spin_lock(&kbdev->pm.clk_rtm.lock);
657 idle[1] = kbdev->pm.clk_rtm.gpu_idle;
658 for (i = 0; i < data->nclks; i++) {
659 /* Rate match between the manager and the trace */
660 if (kbdev->pm.clk_rtm.clks[i]->clock_val !=
661 data->snapshot[i].current_rate) {
662 fail = true;
663 break;
664 }
665 }
666 spin_unlock(&kbdev->pm.clk_rtm.lock);
667
668 if (idle[1]) {
669 msg = kutf_dsprintf(&context->fixture_pool,
670 "GPU still idle after set_pm_ctx_active");
671 pr_err("GPU still idle after set_pm_ctx_active\n");
672 }
673
674 if (!msg && fail) {
675 msg = kutf_dsprintf(&context->fixture_pool,
676 "Trace rate not matching Clk manager's read");
677 pr_err("Trace rate not matching Clk manager's read\n");
678 }
679 }
680
681 if (!msg && idle[0] && !idle[1] && !data->total_update_cnt) {
682 msg = kutf_dsprintf(&context->fixture_pool,
683 "Trace update did not occur");
684 pr_err("Trace update did not occur\n");
685 }
686 if (msg)
687 kutf_clk_trace_flag_result(context, KUTF_RESULT_FAIL, msg);
688 else if (!data->total_update_cnt) {
689 msg = kutf_dsprintf(&context->fixture_pool,
690 "No trace update seen during the test!");
691 kutf_clk_trace_flag_result(context, KUTF_RESULT_WARN, msg);
692 }
693 }
694
kutf_clk_trace_end_of_stream(struct clk_trace_portal_input * cmd)695 static bool kutf_clk_trace_end_of_stream(struct clk_trace_portal_input *cmd)
696 {
697 return (cmd->named_val_err == -EBUSY);
698 }
699
kutf_clk_trace_no_clks_dummy(struct kutf_context * context)700 static void kutf_clk_trace_no_clks_dummy(struct kutf_context *context)
701 {
702 struct clk_trace_portal_input cmd;
703 unsigned long timeout = jiffies + HZ * 2;
704 bool has_cmd;
705
706 while (time_before(jiffies, timeout)) {
707 if (kutf_helper_pending_input(context)) {
708 has_cmd = kutf_clk_trace_dequeue_portal_cmd(context,
709 &cmd);
710 if (!has_cmd && kutf_clk_trace_end_of_stream(&cmd))
711 break;
712
713 kutf_helper_send_named_str(context, "NACK",
714 "Fatal! No clocks visible, aborting");
715 }
716 msleep(20);
717 }
718
719 kutf_clk_trace_flag_result(context, KUTF_RESULT_FATAL,
720 "No clocks visble to the portal");
721 }
722
723 /**
724 * mali_kutf_clk_rate_trace_test_portal() - Service portal input
725 * @context: KUTF context
726 *
727 * The test portal operates on input requests. If the input request is one
728 * of the recognized portal commands, it handles it accordingly. Otherwise
729 * a negative response 'NACK' is returned. The portal service terminates
730 * when a 'CLOSE_PORTAL' request is received, or due to an internal error.
731 * Both case would result in the server_state transitioned to CLOSING.
732 *
733 * If the portal is closed on request, a sanity test on the clock rate
734 * trace operation is undertaken via function:
735 * kutf_clk_trace_barebone_check();
736 */
mali_kutf_clk_rate_trace_test_portal(struct kutf_context * context)737 static void mali_kutf_clk_rate_trace_test_portal(struct kutf_context *context)
738 {
739 struct kutf_clk_rate_trace_fixture_data *data = context->fixture;
740 struct clk_trace_portal_input new_cmd;
741
742 pr_debug("Test portal service start\n");
743
744 while (data->server_state == PORTAL_STATE_LIVE) {
745 if (kutf_clk_trace_dequeue_portal_cmd(context, &new_cmd))
746 kutf_clk_trace_process_portal_cmd(context, &new_cmd);
747 else if (kutf_clk_trace_end_of_stream(&new_cmd))
748 /* Dequeue on portal input, end of stream */
749 data->server_state = PORTAL_STATE_CLOSING;
750 else
751 kutf_clk_trace_do_nack_response(context, &new_cmd);
752 }
753
754 /* Closing, exhausting all the pending inputs with NACKs. */
755 if (data->server_state == PORTAL_STATE_CLOSING) {
756 while (kutf_helper_pending_input(context) &&
757 (kutf_clk_trace_dequeue_portal_cmd(context, &new_cmd) ||
758 !kutf_clk_trace_end_of_stream(&new_cmd))) {
759 kutf_helper_send_named_str(context, "NACK",
760 "Portal closing down");
761 }
762 }
763
764 /* If no portal error, do a barebone test here irrespective
765 * whatever the portal live session has been testing, which
766 * is entirely driven by the user-side via portal requests.
767 */
768 if (data->test_status <= KUTF_RESULT_WARN) {
769 if (data->server_state != PORTAL_STATE_NO_CLK)
770 kutf_clk_trace_barebone_check(context);
771 else {
772 /* No clocks case, NACK 2-sec for the fatal situation */
773 kutf_clk_trace_no_clks_dummy(context);
774 }
775 }
776
777 /* If we have changed pm_ctx count, drop it back */
778 if (data->pm_ctx_cnt) {
779 /* Although we count on portal requests, it only has material
780 * impact when from 0 -> 1. So the reverse is a simple one off.
781 */
782 data->pm_ctx_cnt = 0;
783 kutf_set_pm_ctx_idle(context);
784 }
785
786 /* Finally log the test result line */
787 if (data->test_status < KUTF_RESULT_WARN)
788 kutf_test_pass(context, data->result_msg);
789 else if (data->test_status == KUTF_RESULT_WARN)
790 kutf_test_warn(context, data->result_msg);
791 else if (data->test_status == KUTF_RESULT_FATAL)
792 kutf_test_fatal(context, data->result_msg);
793 else
794 kutf_test_fail(context, data->result_msg);
795
796 pr_debug("Test end\n");
797 }
798
799 /**
800 * mali_kutf_clk_rate_trace_create_fixture() - Creates the fixture data
801 * required for mali_kutf_clk_rate_trace_test_portal.
802 * @context: KUTF context.
803 *
804 * Return: Fixture data created on success or NULL on failure
805 */
mali_kutf_clk_rate_trace_create_fixture(struct kutf_context * context)806 static void *mali_kutf_clk_rate_trace_create_fixture(
807 struct kutf_context *context)
808 {
809 struct kutf_clk_rate_trace_fixture_data *data;
810 struct kbase_device *kbdev;
811 unsigned long rate;
812 int i;
813
814 /* Acquire the kbase device */
815 pr_debug("Finding device\n");
816 kbdev = kbase_find_device(MINOR_FOR_FIRST_KBASE_DEV);
817 if (kbdev == NULL) {
818 kutf_test_fail(context, "Failed to find kbase device");
819 return NULL;
820 }
821
822 pr_debug("Creating fixture\n");
823 data = kutf_mempool_alloc(&context->fixture_pool,
824 sizeof(struct kutf_clk_rate_trace_fixture_data));
825 if (!data)
826 return NULL;
827
828 memset(data, 0, sizeof(*data));
829 pr_debug("Hooking up the test portal to kbdev clk rate trace\n");
830 spin_lock(&kbdev->pm.clk_rtm.lock);
831
832 if (g_ptr_portal_data != NULL) {
833 pr_warn("Test portal is already in use, run aborted\n");
834 spin_unlock(&kbdev->pm.clk_rtm.lock);
835 kutf_test_fail(context, "Portal allows single session only");
836 return NULL;
837 }
838
839 for (i = 0; i < BASE_MAX_NR_CLOCKS_REGULATORS; i++) {
840 if (kbdev->pm.clk_rtm.clks[i]) {
841 data->nclks++;
842 if (kbdev->pm.clk_rtm.gpu_idle)
843 rate = 0;
844 else
845 rate = kbdev->pm.clk_rtm.clks[i]->clock_val;
846 data->snapshot[i].previous_rate = rate;
847 data->snapshot[i].current_rate = rate;
848 }
849 }
850
851 spin_unlock(&kbdev->pm.clk_rtm.lock);
852
853 if (data->nclks) {
854 /* Subscribe this test server portal */
855 data->listener.notify = kutf_portal_trace_write;
856 data->invoke_notify = false;
857
858 kbase_clk_rate_trace_manager_subscribe(
859 &kbdev->pm.clk_rtm, &data->listener);
860 /* Update the kutf_server_portal fixture_data pointer */
861 g_ptr_portal_data = data;
862 }
863
864 data->kbdev = kbdev;
865 data->result_msg = NULL;
866 data->test_status = KUTF_RESULT_PASS;
867
868 if (data->nclks == 0) {
869 data->server_state = PORTAL_STATE_NO_CLK;
870 pr_debug("Kbdev has no clocks for rate trace");
871 } else
872 data->server_state = PORTAL_STATE_LIVE;
873
874 pr_debug("Created fixture\n");
875
876 return data;
877 }
878
879 /**
880 * mali_kutf_clk_rate_trace_remove_fixture - Destroy fixture data previously created by
881 * mali_kutf_clk_rate_trace_create_fixture.
882 *
883 * @context: KUTF context.
884 */
mali_kutf_clk_rate_trace_remove_fixture(struct kutf_context * context)885 static void mali_kutf_clk_rate_trace_remove_fixture(
886 struct kutf_context *context)
887 {
888 struct kutf_clk_rate_trace_fixture_data *data = context->fixture;
889 struct kbase_device *kbdev = data->kbdev;
890
891 if (data->nclks) {
892 /* Clean up the portal trace write arrangement */
893 g_ptr_portal_data = NULL;
894
895 kbase_clk_rate_trace_manager_unsubscribe(
896 &kbdev->pm.clk_rtm, &data->listener);
897 }
898 pr_debug("Destroying fixture\n");
899 kbase_release_device(kbdev);
900 pr_debug("Destroyed fixture\n");
901 }
902
903 /**
904 * mali_kutf_clk_rate_trace_test_module_init() - Entry point for test mdoule.
905 *
906 * Return: 0 on success, error code otherwise
907 */
mali_kutf_clk_rate_trace_test_module_init(void)908 static int __init mali_kutf_clk_rate_trace_test_module_init(void)
909 {
910 struct kutf_suite *suite;
911 unsigned int filters;
912 union kutf_callback_data suite_data = { NULL };
913
914 pr_debug("Creating app\n");
915
916 g_ptr_portal_data = NULL;
917 kutf_app = kutf_create_application(CLK_RATE_TRACE_APP_NAME);
918
919 if (!kutf_app) {
920 pr_warn("Creation of app " CLK_RATE_TRACE_APP_NAME
921 " failed!\n");
922 return -ENOMEM;
923 }
924
925 pr_debug("Create suite %s\n", CLK_RATE_TRACE_SUITE_NAME);
926 suite = kutf_create_suite_with_filters_and_data(
927 kutf_app, CLK_RATE_TRACE_SUITE_NAME, 1,
928 mali_kutf_clk_rate_trace_create_fixture,
929 mali_kutf_clk_rate_trace_remove_fixture,
930 KUTF_F_TEST_GENERIC,
931 suite_data);
932
933 if (!suite) {
934 pr_warn("Creation of suite %s failed!\n",
935 CLK_RATE_TRACE_SUITE_NAME);
936 kutf_destroy_application(kutf_app);
937 return -ENOMEM;
938 }
939
940 filters = suite->suite_default_flags;
941 kutf_add_test_with_filters(
942 suite, 0x0, CLK_RATE_TRACE_PORTAL,
943 mali_kutf_clk_rate_trace_test_portal,
944 filters);
945
946 pr_debug("Init complete\n");
947 return 0;
948 }
949
950 /**
951 * mali_kutf_clk_rate_trace_test_module_exit() - Module exit point for this
952 * test.
953 */
mali_kutf_clk_rate_trace_test_module_exit(void)954 static void __exit mali_kutf_clk_rate_trace_test_module_exit(void)
955 {
956 pr_debug("Exit start\n");
957 kutf_destroy_application(kutf_app);
958 pr_debug("Exit complete\n");
959 }
960
961
962 module_init(mali_kutf_clk_rate_trace_test_module_init);
963 module_exit(mali_kutf_clk_rate_trace_test_module_exit);
964
965 MODULE_LICENSE("GPL");
966