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