Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/imagination: Add firmware trace to debugfs

Firmware trace is exposed at /sys/debug/dri/<dev_nr>/pvr_fw/trace_0.
Trace is enabled via the group mask at
/sys/debug/dri/<dev_nr>/pvr_params/fw_trace_mask.

Changes since v8:
- Corrected license identifiers

Changes since v3:
- Use drm_dev_{enter,exit}

Co-developed-by: Matt Coster <matt.coster@imgtec.com>
Signed-off-by: Matt Coster <matt.coster@imgtec.com>
Signed-off-by: Sarah Walker <sarah.walker@imgtec.com>
Signed-off-by: Donald Robson <donald.robson@imgtec.com>
Link: https://lore.kernel.org/r/009cf9fee347fa96c8a665dc368fc54a5ffceff0.1700668843.git.donald.robson@imgtec.com
Signed-off-by: Maxime Ripard <mripard@kernel.org>

authored by

Sarah Walker and committed by
Maxime Ripard
cb56cd61 6b17baab

+723
+4
drivers/gpu/drm/imagination/Makefile
··· 20 20 pvr_hwrt.o \ 21 21 pvr_job.o \ 22 22 pvr_mmu.o \ 23 + pvr_params.o \ 23 24 pvr_power.o \ 24 25 pvr_queue.o \ 25 26 pvr_stream.o \ ··· 28 27 pvr_sync.o \ 29 28 pvr_vm.o \ 30 29 pvr_vm_mips.o 30 + 31 + powervr-$(CONFIG_DEBUG_FS) += \ 32 + pvr_debugfs.o 31 33 32 34 obj-$(CONFIG_DRM_POWERVR) += powervr.o
+53
drivers/gpu/drm/imagination/pvr_debugfs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only OR MIT 2 + /* Copyright (c) 2023 Imagination Technologies Ltd. */ 3 + 4 + #include "pvr_debugfs.h" 5 + 6 + #include "pvr_device.h" 7 + #include "pvr_fw_trace.h" 8 + #include "pvr_params.h" 9 + 10 + #include <linux/dcache.h> 11 + #include <linux/debugfs.h> 12 + #include <linux/err.h> 13 + #include <linux/kernel.h> 14 + #include <linux/types.h> 15 + 16 + #include <drm/drm_device.h> 17 + #include <drm/drm_file.h> 18 + #include <drm/drm_print.h> 19 + 20 + static const struct pvr_debugfs_entry pvr_debugfs_entries[] = { 21 + {"pvr_params", pvr_params_debugfs_init}, 22 + {"pvr_fw", pvr_fw_trace_debugfs_init}, 23 + }; 24 + 25 + void 26 + pvr_debugfs_init(struct drm_minor *minor) 27 + { 28 + struct drm_device *drm_dev = minor->dev; 29 + struct pvr_device *pvr_dev = to_pvr_device(drm_dev); 30 + struct dentry *root = minor->debugfs_root; 31 + size_t i; 32 + 33 + for (i = 0; i < ARRAY_SIZE(pvr_debugfs_entries); ++i) { 34 + const struct pvr_debugfs_entry *entry = &pvr_debugfs_entries[i]; 35 + struct dentry *dir; 36 + 37 + dir = debugfs_create_dir(entry->name, root); 38 + if (IS_ERR(dir)) { 39 + drm_warn(drm_dev, 40 + "failed to create debugfs dir '%s' (err=%d)", 41 + entry->name, (int)PTR_ERR(dir)); 42 + continue; 43 + } 44 + 45 + entry->init(pvr_dev, dir); 46 + } 47 + } 48 + 49 + /* 50 + * Since all entries are created under &drm_minor->debugfs_root, there's no 51 + * need for a pvr_debugfs_fini() as DRM will clean up everything under its root 52 + * automatically. 53 + */
+29
drivers/gpu/drm/imagination/pvr_debugfs.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only OR MIT */ 2 + /* Copyright (c) 2023 Imagination Technologies Ltd. */ 3 + 4 + #ifndef PVR_DEBUGFS_H 5 + #define PVR_DEBUGFS_H 6 + 7 + /* Forward declaration from <drm/drm_drv.h>. */ 8 + struct drm_minor; 9 + 10 + #if defined(CONFIG_DEBUG_FS) 11 + /* Forward declaration from "pvr_device.h". */ 12 + struct pvr_device; 13 + 14 + /* Forward declaration from <linux/dcache.h>. */ 15 + struct dentry; 16 + 17 + struct pvr_debugfs_entry { 18 + const char *name; 19 + void (*init)(struct pvr_device *pvr_dev, struct dentry *dir); 20 + }; 21 + 22 + void pvr_debugfs_init(struct drm_minor *minor); 23 + #else /* defined(CONFIG_DEBUG_FS) */ 24 + #include <linux/compiler_attributes.h> 25 + 26 + static __always_inline void pvr_debugfs_init(struct drm_minor *minor) {} 27 + #endif /* defined(CONFIG_DEBUG_FS) */ 28 + 29 + #endif /* PVR_DEBUGFS_H */
+9
drivers/gpu/drm/imagination/pvr_device.c
··· 5 5 #include "pvr_device_info.h" 6 6 7 7 #include "pvr_fw.h" 8 + #include "pvr_params.h" 8 9 #include "pvr_power.h" 9 10 #include "pvr_queue.h" 10 11 #include "pvr_rogue_cr_defs.h" ··· 495 494 struct drm_device *drm_dev = from_pvr_device(pvr_dev); 496 495 struct device *dev = drm_dev->dev; 497 496 int err; 497 + 498 + /* 499 + * Setup device parameters. We do this first in case other steps 500 + * depend on them. 501 + */ 502 + err = pvr_device_params_init(&pvr_dev->params); 503 + if (err) 504 + return err; 498 505 499 506 /* Enable and initialize clocks required for the device to operate. */ 500 507 err = pvr_device_clk_init(pvr_dev);
+10
drivers/gpu/drm/imagination/pvr_device.h
··· 7 7 #include "pvr_ccb.h" 8 8 #include "pvr_device_info.h" 9 9 #include "pvr_fw.h" 10 + #include "pvr_params.h" 10 11 #include "pvr_rogue_fwif_stream.h" 11 12 #include "pvr_stream.h" 12 13 ··· 148 147 149 148 /** @fw_dev: Firmware related data. */ 150 149 struct pvr_fw_device fw_dev; 150 + 151 + /** 152 + * @params: Device-specific parameters. 153 + * 154 + * The values of these parameters are initialized from the 155 + * defaults specified as module parameters. They may be 156 + * modified at runtime via debugfs (if enabled). 157 + */ 158 + struct pvr_device_params params; 151 159 152 160 /** @stream_musthave_quirks: Bit array of "must-have" quirks for stream commands. */ 153 161 u32 stream_musthave_quirks[PVR_STREAM_TYPE_MAX][PVR_STREAM_EXTHDR_TYPE_MAX];
+4
drivers/gpu/drm/imagination/pvr_drv.c
··· 2 2 /* Copyright (c) 2023 Imagination Technologies Ltd. */ 3 3 4 4 #include "pvr_context.h" 5 + #include "pvr_debugfs.h" 5 6 #include "pvr_device.h" 6 7 #include "pvr_drv.h" 7 8 #include "pvr_free_list.h" ··· 1378 1377 .ioctls = pvr_drm_driver_ioctls, 1379 1378 .num_ioctls = ARRAY_SIZE(pvr_drm_driver_ioctls), 1380 1379 .fops = &pvr_drm_driver_fops, 1380 + #if defined(CONFIG_DEBUG_FS) 1381 + .debugfs_init = pvr_debugfs_init, 1382 + #endif 1381 1383 1382 1384 .name = PVR_DRIVER_NAME, 1383 1385 .desc = PVR_DRIVER_DESC,
+395
drivers/gpu/drm/imagination/pvr_fw_trace.c
··· 4 4 #include "pvr_device.h" 5 5 #include "pvr_gem.h" 6 6 #include "pvr_rogue_fwif.h" 7 + #include "pvr_rogue_fwif_sf.h" 7 8 #include "pvr_fw_trace.h" 8 9 10 + #include <drm/drm_drv.h> 9 11 #include <drm/drm_file.h> 10 12 11 13 #include <linux/build_bug.h> ··· 120 118 } 121 119 pvr_fw_object_unmap_and_destroy(fw_trace->tracebuf_ctrl_obj); 122 120 } 121 + 122 + /** 123 + * update_logtype() - Send KCCB command to trigger FW to update logtype 124 + * @pvr_dev: Target PowerVR device 125 + * @group_mask: New log group mask. 126 + * 127 + * Returns: 128 + * * 0 on success, 129 + * * Any error returned by pvr_kccb_send_cmd(), or 130 + * * -%EIO if the device is lost. 131 + */ 132 + static int 133 + update_logtype(struct pvr_device *pvr_dev, u32 group_mask) 134 + { 135 + struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace; 136 + struct rogue_fwif_kccb_cmd cmd; 137 + int idx; 138 + int err; 139 + 140 + if (group_mask) 141 + fw_trace->tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_TRACE | group_mask; 142 + else 143 + fw_trace->tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_NONE; 144 + 145 + fw_trace->group_mask = group_mask; 146 + 147 + down_read(&pvr_dev->reset_sem); 148 + if (!drm_dev_enter(from_pvr_device(pvr_dev), &idx)) { 149 + err = -EIO; 150 + goto err_up_read; 151 + } 152 + 153 + cmd.cmd_type = ROGUE_FWIF_KCCB_CMD_LOGTYPE_UPDATE; 154 + cmd.kccb_flags = 0; 155 + 156 + err = pvr_kccb_send_cmd(pvr_dev, &cmd, NULL); 157 + 158 + drm_dev_exit(idx); 159 + 160 + err_up_read: 161 + up_read(&pvr_dev->reset_sem); 162 + 163 + return err; 164 + } 165 + 166 + #if defined(CONFIG_DEBUG_FS) 167 + 168 + static int fw_trace_group_mask_show(struct seq_file *m, void *data) 169 + { 170 + struct pvr_device *pvr_dev = m->private; 171 + 172 + seq_printf(m, "%08x\n", pvr_dev->fw_dev.fw_trace.group_mask); 173 + 174 + return 0; 175 + } 176 + 177 + static int fw_trace_group_mask_open(struct inode *inode, struct file *file) 178 + { 179 + return single_open(file, fw_trace_group_mask_show, inode->i_private); 180 + } 181 + 182 + static ssize_t fw_trace_group_mask_write(struct file *file, const char __user *ubuf, size_t len, 183 + loff_t *offp) 184 + { 185 + struct seq_file *m = file->private_data; 186 + struct pvr_device *pvr_dev = m->private; 187 + u32 new_group_mask; 188 + int err; 189 + 190 + err = kstrtouint_from_user(ubuf, len, 0, &new_group_mask); 191 + if (err) 192 + return err; 193 + 194 + err = update_logtype(pvr_dev, new_group_mask); 195 + if (err) 196 + return err; 197 + 198 + pvr_dev->fw_dev.fw_trace.group_mask = new_group_mask; 199 + 200 + return (ssize_t)len; 201 + } 202 + 203 + static const struct file_operations pvr_fw_trace_group_mask_fops = { 204 + .owner = THIS_MODULE, 205 + .open = fw_trace_group_mask_open, 206 + .read = seq_read, 207 + .write = fw_trace_group_mask_write, 208 + .llseek = default_llseek, 209 + .release = single_release, 210 + }; 211 + 212 + struct pvr_fw_trace_seq_data { 213 + /** @buffer: Pointer to copy of trace data. */ 214 + u32 *buffer; 215 + 216 + /** @start_offset: Starting offset in trace data, as reported by FW. */ 217 + u32 start_offset; 218 + 219 + /** @idx: Current index into trace data. */ 220 + u32 idx; 221 + 222 + /** @assert_buf: Trace assert buffer, as reported by FW. */ 223 + struct rogue_fwif_file_info_buf assert_buf; 224 + }; 225 + 226 + static u32 find_sfid(u32 id) 227 + { 228 + u32 i; 229 + 230 + for (i = 0; i < ARRAY_SIZE(stid_fmts); i++) { 231 + if (stid_fmts[i].id == id) 232 + return i; 233 + } 234 + 235 + return ROGUE_FW_SF_LAST; 236 + } 237 + 238 + static u32 read_fw_trace(struct pvr_fw_trace_seq_data *trace_seq_data, u32 offset) 239 + { 240 + u32 idx; 241 + 242 + idx = trace_seq_data->idx + offset; 243 + if (idx >= ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) 244 + return 0; 245 + 246 + idx = (idx + trace_seq_data->start_offset) % ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS; 247 + return trace_seq_data->buffer[idx]; 248 + } 249 + 250 + /** 251 + * fw_trace_get_next() - Advance trace index to next entry 252 + * @trace_seq_data: Trace sequence data. 253 + * 254 + * Returns: 255 + * * %true if trace index is now pointing to a valid entry, or 256 + * * %false if trace index is pointing to an invalid entry, or has hit the end 257 + * of the trace. 258 + */ 259 + static bool fw_trace_get_next(struct pvr_fw_trace_seq_data *trace_seq_data) 260 + { 261 + u32 id, sf_id; 262 + 263 + while (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) { 264 + id = read_fw_trace(trace_seq_data, 0); 265 + trace_seq_data->idx++; 266 + if (!ROGUE_FW_LOG_VALIDID(id)) 267 + continue; 268 + if (id == ROGUE_FW_SF_MAIN_ASSERT_FAILED) { 269 + /* Assertion failure marks the end of the trace. */ 270 + return false; 271 + } 272 + 273 + sf_id = find_sfid(id); 274 + if (sf_id == ROGUE_FW_SF_FIRST) 275 + continue; 276 + if (sf_id == ROGUE_FW_SF_LAST) { 277 + /* 278 + * Could not match with an ID in the SF table, trace is 279 + * most likely corrupt from this point. 280 + */ 281 + return false; 282 + } 283 + 284 + /* Skip over the timestamp, and any parameters. */ 285 + trace_seq_data->idx += 2 + ROGUE_FW_SF_PARAMNUM(id); 286 + 287 + /* Ensure index is now pointing to a valid trace entry. */ 288 + id = read_fw_trace(trace_seq_data, 0); 289 + if (!ROGUE_FW_LOG_VALIDID(id)) 290 + continue; 291 + 292 + return true; 293 + }; 294 + 295 + /* Hit end of trace data. */ 296 + return false; 297 + } 298 + 299 + /** 300 + * fw_trace_get_first() - Find first valid entry in trace 301 + * @trace_seq_data: Trace sequence data. 302 + * 303 + * Skips over invalid (usually zero) and ROGUE_FW_SF_FIRST entries. 304 + * 305 + * If the trace has no valid entries, this function will exit with the trace 306 + * index pointing to the end of the trace. trace_seq_show() will return an error 307 + * in this state. 308 + */ 309 + static void fw_trace_get_first(struct pvr_fw_trace_seq_data *trace_seq_data) 310 + { 311 + trace_seq_data->idx = 0; 312 + 313 + while (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) { 314 + u32 id = read_fw_trace(trace_seq_data, 0); 315 + 316 + if (ROGUE_FW_LOG_VALIDID(id)) { 317 + u32 sf_id = find_sfid(id); 318 + 319 + if (sf_id != ROGUE_FW_SF_FIRST) 320 + break; 321 + } 322 + trace_seq_data->idx++; 323 + } 324 + } 325 + 326 + static void *fw_trace_seq_start(struct seq_file *s, loff_t *pos) 327 + { 328 + struct pvr_fw_trace_seq_data *trace_seq_data = s->private; 329 + u32 i; 330 + 331 + /* Reset trace index, then advance to *pos. */ 332 + fw_trace_get_first(trace_seq_data); 333 + 334 + for (i = 0; i < *pos; i++) { 335 + if (!fw_trace_get_next(trace_seq_data)) 336 + return NULL; 337 + } 338 + 339 + return (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) ? pos : NULL; 340 + } 341 + 342 + static void *fw_trace_seq_next(struct seq_file *s, void *v, loff_t *pos) 343 + { 344 + struct pvr_fw_trace_seq_data *trace_seq_data = s->private; 345 + 346 + (*pos)++; 347 + if (!fw_trace_get_next(trace_seq_data)) 348 + return NULL; 349 + 350 + return (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) ? pos : NULL; 351 + } 352 + 353 + static void fw_trace_seq_stop(struct seq_file *s, void *v) 354 + { 355 + } 356 + 357 + static int fw_trace_seq_show(struct seq_file *s, void *v) 358 + { 359 + struct pvr_fw_trace_seq_data *trace_seq_data = s->private; 360 + u64 timestamp; 361 + u32 id; 362 + u32 sf_id; 363 + 364 + if (trace_seq_data->idx >= ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) 365 + return -EINVAL; 366 + 367 + id = read_fw_trace(trace_seq_data, 0); 368 + /* Index is not pointing at a valid entry. */ 369 + if (!ROGUE_FW_LOG_VALIDID(id)) 370 + return -EINVAL; 371 + 372 + sf_id = find_sfid(id); 373 + /* Index is not pointing at a valid entry. */ 374 + if (sf_id == ROGUE_FW_SF_LAST) 375 + return -EINVAL; 376 + 377 + timestamp = read_fw_trace(trace_seq_data, 1) | 378 + ((u64)read_fw_trace(trace_seq_data, 2) << 32); 379 + timestamp = (timestamp & ~ROGUE_FWT_TIMESTAMP_TIME_CLRMSK) >> 380 + ROGUE_FWT_TIMESTAMP_TIME_SHIFT; 381 + 382 + seq_printf(s, "[%llu] : ", timestamp); 383 + if (id == ROGUE_FW_SF_MAIN_ASSERT_FAILED) { 384 + seq_printf(s, "ASSERTION %s failed at %s:%u", 385 + trace_seq_data->assert_buf.info, 386 + trace_seq_data->assert_buf.path, 387 + trace_seq_data->assert_buf.line_num); 388 + } else { 389 + seq_printf(s, stid_fmts[sf_id].name, 390 + read_fw_trace(trace_seq_data, 3), 391 + read_fw_trace(trace_seq_data, 4), 392 + read_fw_trace(trace_seq_data, 5), 393 + read_fw_trace(trace_seq_data, 6), 394 + read_fw_trace(trace_seq_data, 7), 395 + read_fw_trace(trace_seq_data, 8), 396 + read_fw_trace(trace_seq_data, 9), 397 + read_fw_trace(trace_seq_data, 10), 398 + read_fw_trace(trace_seq_data, 11), 399 + read_fw_trace(trace_seq_data, 12), 400 + read_fw_trace(trace_seq_data, 13), 401 + read_fw_trace(trace_seq_data, 14), 402 + read_fw_trace(trace_seq_data, 15), 403 + read_fw_trace(trace_seq_data, 16), 404 + read_fw_trace(trace_seq_data, 17), 405 + read_fw_trace(trace_seq_data, 18), 406 + read_fw_trace(trace_seq_data, 19), 407 + read_fw_trace(trace_seq_data, 20), 408 + read_fw_trace(trace_seq_data, 21), 409 + read_fw_trace(trace_seq_data, 22)); 410 + } 411 + seq_puts(s, "\n"); 412 + return 0; 413 + } 414 + 415 + static const struct seq_operations pvr_fw_trace_seq_ops = { 416 + .start = fw_trace_seq_start, 417 + .next = fw_trace_seq_next, 418 + .stop = fw_trace_seq_stop, 419 + .show = fw_trace_seq_show 420 + }; 421 + 422 + static int fw_trace_open(struct inode *inode, struct file *file) 423 + { 424 + struct pvr_fw_trace_buffer *trace_buffer = inode->i_private; 425 + struct rogue_fwif_tracebuf_space *tracebuf_space = 426 + trace_buffer->tracebuf_space; 427 + struct pvr_fw_trace_seq_data *trace_seq_data; 428 + int err; 429 + 430 + trace_seq_data = kzalloc(sizeof(*trace_seq_data), GFP_KERNEL); 431 + if (!trace_seq_data) 432 + return -ENOMEM; 433 + 434 + trace_seq_data->buffer = kcalloc(ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS, 435 + sizeof(*trace_seq_data->buffer), GFP_KERNEL); 436 + if (!trace_seq_data->buffer) { 437 + err = -ENOMEM; 438 + goto err_free_data; 439 + } 440 + 441 + /* 442 + * Take a local copy of the trace buffer, as firmware may still be 443 + * writing to it. This will exist as long as this file is open. 444 + */ 445 + memcpy(trace_seq_data->buffer, trace_buffer->buf, 446 + ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS * sizeof(u32)); 447 + trace_seq_data->start_offset = READ_ONCE(tracebuf_space->trace_pointer); 448 + trace_seq_data->assert_buf = tracebuf_space->assert_buf; 449 + fw_trace_get_first(trace_seq_data); 450 + 451 + err = seq_open(file, &pvr_fw_trace_seq_ops); 452 + if (err) 453 + goto err_free_buffer; 454 + 455 + ((struct seq_file *)file->private_data)->private = trace_seq_data; 456 + 457 + return 0; 458 + 459 + err_free_buffer: 460 + kfree(trace_seq_data->buffer); 461 + 462 + err_free_data: 463 + kfree(trace_seq_data); 464 + 465 + return err; 466 + } 467 + 468 + static int fw_trace_release(struct inode *inode, struct file *file) 469 + { 470 + struct pvr_fw_trace_seq_data *trace_seq_data = 471 + ((struct seq_file *)file->private_data)->private; 472 + 473 + seq_release(inode, file); 474 + kfree(trace_seq_data->buffer); 475 + kfree(trace_seq_data); 476 + 477 + return 0; 478 + } 479 + 480 + static const struct file_operations pvr_fw_trace_fops = { 481 + .owner = THIS_MODULE, 482 + .open = fw_trace_open, 483 + .read = seq_read, 484 + .llseek = seq_lseek, 485 + .release = fw_trace_release, 486 + }; 487 + 488 + void 489 + pvr_fw_trace_mask_update(struct pvr_device *pvr_dev, u32 old_mask, u32 new_mask) 490 + { 491 + if (old_mask != new_mask) 492 + update_logtype(pvr_dev, new_mask); 493 + } 494 + 495 + void 496 + pvr_fw_trace_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir) 497 + { 498 + struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace; 499 + u32 thread_nr; 500 + 501 + static_assert(ARRAY_SIZE(fw_trace->buffers) <= 10, 502 + "The filename buffer is only large enough for a single-digit thread count"); 503 + 504 + for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); ++thread_nr) { 505 + char filename[8]; 506 + 507 + snprintf(filename, ARRAY_SIZE(filename), "trace_%u", thread_nr); 508 + debugfs_create_file(filename, 0400, dir, 509 + &fw_trace->buffers[thread_nr], 510 + &pvr_fw_trace_fops); 511 + } 512 + } 513 + #endif
+147
drivers/gpu/drm/imagination/pvr_params.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only OR MIT 2 + /* Copyright (c) 2023 Imagination Technologies Ltd. */ 3 + 4 + #include "pvr_params.h" 5 + 6 + #include <linux/cache.h> 7 + #include <linux/moduleparam.h> 8 + 9 + static struct pvr_device_params pvr_device_param_defaults __read_mostly = { 10 + #define X(type_, name_, value_, desc_, ...) .name_ = (value_), 11 + PVR_DEVICE_PARAMS 12 + #undef X 13 + }; 14 + 15 + #define PVR_DEVICE_PARAM_NAMED(name_, type_, desc_) \ 16 + module_param_named(name_, pvr_device_param_defaults.name_, type_, \ 17 + 0400); \ 18 + MODULE_PARM_DESC(name_, desc_); 19 + 20 + /* 21 + * This list of defines must contain every type specified in "pvr_params.h" as 22 + * ``PVR_PARAM_TYPE_*_C``. 23 + */ 24 + #define PVR_PARAM_TYPE_X32_MODPARAM uint 25 + 26 + #define X(type_, name_, value_, desc_, ...) \ 27 + PVR_DEVICE_PARAM_NAMED(name_, PVR_PARAM_TYPE_##type_##_MODPARAM, desc_); 28 + PVR_DEVICE_PARAMS 29 + #undef X 30 + 31 + int 32 + pvr_device_params_init(struct pvr_device_params *params) 33 + { 34 + /* 35 + * If heap-allocated parameters are added in the future (e.g. 36 + * modparam's charp type), they must be handled specially here (via 37 + * kstrdup() in the case of charp). Since that's not necessary yet, 38 + * a straight copy will do for now. This change will also require a 39 + * pvr_device_params_fini() function to free any heap-allocated copies. 40 + */ 41 + 42 + *params = pvr_device_param_defaults; 43 + 44 + return 0; 45 + } 46 + 47 + #if defined(CONFIG_DEBUG_FS) 48 + #include "pvr_device.h" 49 + 50 + #include <linux/dcache.h> 51 + #include <linux/debugfs.h> 52 + #include <linux/export.h> 53 + #include <linux/fs.h> 54 + #include <linux/stddef.h> 55 + 56 + /* 57 + * This list of defines must contain every type specified in "pvr_params.h" as 58 + * ``PVR_PARAM_TYPE_*_C``. 59 + */ 60 + #define PVR_PARAM_TYPE_X32_FMT "0x%08llx" 61 + 62 + #define X_SET(name_, mode_) X_SET_##mode_(name_) 63 + #define X_SET_DEF(name_, update_, mode_) X_SET_DEF_##mode_(name_, update_) 64 + 65 + #define X_SET_RO(name_) NULL 66 + #define X_SET_RW(name_) __pvr_device_param_##name_##set 67 + 68 + #define X_SET_DEF_RO(name_, update_) 69 + #define X_SET_DEF_RW(name_, update_) \ 70 + static int \ 71 + X_SET_RW(name_)(void *data, u64 val) \ 72 + { \ 73 + struct pvr_device *pvr_dev = data; \ 74 + /* This is not just (update_) to suppress -Waddress. */ \ 75 + if ((void *)(update_) != NULL) \ 76 + (update_)(pvr_dev, pvr_dev->params.name_, val); \ 77 + pvr_dev->params.name_ = val; \ 78 + return 0; \ 79 + } 80 + 81 + #define X(type_, name_, value_, desc_, mode_, update_) \ 82 + static int \ 83 + __pvr_device_param_##name_##_get(void *data, u64 *val) \ 84 + { \ 85 + struct pvr_device *pvr_dev = data; \ 86 + *val = pvr_dev->params.name_; \ 87 + return 0; \ 88 + } \ 89 + X_SET_DEF(name_, update_, mode_) \ 90 + static int \ 91 + __pvr_device_param_##name_##_open(struct inode *inode, \ 92 + struct file *file) \ 93 + { \ 94 + __simple_attr_check_format(PVR_PARAM_TYPE_##type_##_FMT, \ 95 + 0ull); \ 96 + return simple_attr_open(inode, file, \ 97 + __pvr_device_param_##name_##_get, \ 98 + X_SET(name_, mode_), \ 99 + PVR_PARAM_TYPE_##type_##_FMT); \ 100 + } 101 + PVR_DEVICE_PARAMS 102 + #undef X 103 + 104 + #undef X_SET 105 + #undef X_SET_RO 106 + #undef X_SET_RW 107 + #undef X_SET_DEF 108 + #undef X_SET_DEF_RO 109 + #undef X_SET_DEF_RW 110 + 111 + static struct { 112 + #define X(type_, name_, value_, desc_, mode_, update_) \ 113 + const struct file_operations name_; 114 + PVR_DEVICE_PARAMS 115 + #undef X 116 + } pvr_device_param_debugfs_fops = { 117 + #define X(type_, name_, value_, desc_, mode_, update_) \ 118 + .name_ = { \ 119 + .owner = THIS_MODULE, \ 120 + .open = __pvr_device_param_##name_##_open, \ 121 + .release = simple_attr_release, \ 122 + .read = simple_attr_read, \ 123 + .write = simple_attr_write, \ 124 + .llseek = generic_file_llseek, \ 125 + }, 126 + PVR_DEVICE_PARAMS 127 + #undef X 128 + }; 129 + 130 + void 131 + pvr_params_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir) 132 + { 133 + #define X_MODE(mode_) X_MODE_##mode_ 134 + #define X_MODE_RO 0400 135 + #define X_MODE_RW 0600 136 + 137 + #define X(type_, name_, value_, desc_, mode_, update_) \ 138 + debugfs_create_file(#name_, X_MODE(mode_), dir, pvr_dev, \ 139 + &pvr_device_param_debugfs_fops.name_); 140 + PVR_DEVICE_PARAMS 141 + #undef X 142 + 143 + #undef X_MODE 144 + #undef X_MODE_RO 145 + #undef X_MODE_RW 146 + } 147 + #endif
+72
drivers/gpu/drm/imagination/pvr_params.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only OR MIT */ 2 + /* Copyright (c) 2023 Imagination Technologies Ltd. */ 3 + 4 + #ifndef PVR_PARAMS_H 5 + #define PVR_PARAMS_H 6 + 7 + #include "pvr_rogue_fwif.h" 8 + 9 + #include <linux/cache.h> 10 + #include <linux/compiler_attributes.h> 11 + 12 + /* 13 + * This is the definitive list of types allowed in the definition of 14 + * %PVR_DEVICE_PARAMS. 15 + */ 16 + #define PVR_PARAM_TYPE_X32_C u32 17 + 18 + /* 19 + * This macro defines all device-specific parameters; that is parameters which 20 + * are set independently per device. 21 + * 22 + * The X-macro accepts the following arguments. Arguments marked with [debugfs] 23 + * are ignored when debugfs is disabled; values used for these arguments may 24 + * safely be gated behind CONFIG_DEBUG_FS. 25 + * 26 + * @type_: The definitive list of allowed values is PVR_PARAM_TYPE_*_C. 27 + * @name_: Name of the parameter. This is used both as the field name in C and 28 + * stringified as the parameter name. 29 + * @value_: Initial/default value. 30 + * @desc_: String literal used as help text to describe the usage of this 31 + * parameter. 32 + * @mode_: [debugfs] One of {RO,RW}. The access mode of the debugfs entry for 33 + * this parameter. 34 + * @update_: [debugfs] When debugfs support is enabled, parameters may be 35 + * updated at runtime. When this happens, this function will be 36 + * called to allow changes to propagate. The signature of this 37 + * function is: 38 + * 39 + * void (*)(struct pvr_device *pvr_dev, T old_val, T new_val) 40 + * 41 + * Where T is the C type associated with @type_. 42 + * 43 + * If @mode_ does not allow write access, this function will never be 44 + * called. In this case, or if no update callback is required, you 45 + * should specify NULL for this argument. 46 + */ 47 + #define PVR_DEVICE_PARAMS \ 48 + X(X32, fw_trace_mask, ROGUE_FWIF_LOG_TYPE_NONE, \ 49 + "Enable FW trace for the specified groups. Specifying 0 disables " \ 50 + "all FW tracing.", \ 51 + RW, pvr_fw_trace_mask_update) 52 + 53 + struct pvr_device_params { 54 + #define X(type_, name_, value_, desc_, ...) \ 55 + PVR_PARAM_TYPE_##type_##_C name_; 56 + PVR_DEVICE_PARAMS 57 + #undef X 58 + }; 59 + 60 + int pvr_device_params_init(struct pvr_device_params *params); 61 + 62 + #if defined(CONFIG_DEBUG_FS) 63 + /* Forward declaration from "pvr_device.h". */ 64 + struct pvr_device; 65 + 66 + /* Forward declaration from <linux/dcache.h>. */ 67 + struct dentry; 68 + 69 + void pvr_params_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir); 70 + #endif /* defined(CONFIG_DEBUG_FS) */ 71 + 72 + #endif /* PVR_PARAMS_H */