* [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support
@ 2026-10-30 21:18 Rob Clark
2026-10-30 21:18 ` [PATCH v4 01/19] drm/msm: Import adreno_snapshot.h Rob Clark
` (17 more replies)
0 siblings, 18 replies; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Abhinav Kumar,
David Airlie, Dmitry Baryshkov, Jessica Zhang, Konrad Dybcio,
open list, Marijn Suijten, Sean Paul, Simona Vetter
This series introduces gen8 devcoredump support. Compared to previous
gens, it:
1. Switches to generated tables. Compared to hand modifying headers from
kgsl (which were originally generated, then adjusted a bit by hand before
adding to kgsl tree. And then copied to drm/msm and lightly modified
some more in the process).
In the process, we also switch to generated cp_protect tables.
2. Aligns to the "snapshot" format that kgsl/etc use for hw state capture.
The non-generation specific bits (SMMU fault info, capturing relevant
BOs, etc) still works as before. Everything gen8 specific gets encoded
as "snapshot" sections, ascii85 encoded in the resulting devcoredump
file (similar to captured BOs).
3. Try to make it easier to at least compare kgsl snapshot code with
upstream, for porting fixes/etc. State capture is tedious and mind-
numbing, and the last thing you want to do when debugging a GPU hang
is going down the rabbit hole of having to first debug gpu state
capture.
However, I don't love kgsl_snapshot_add_section(). It does avoid
duplicating snapshot header stuff everywhere. But the ergonomics
are not great. So instead I went with "align with what I wish kgsl
looked like..
The mesa/crashdec side of this is at:
https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/45304
I'd appreciate tested-by's on Hawi, Maili, Milos, and Pakala, since I
don't have these devices at-hand to test myself. To test devcoredump,
you can use the msm_recovery test in igt.. or just:
cat /sys/kernel/debug/dri/*/gpu > /dev/null
NOTE: I'm still tracking down some descrepancies compared to kgsl. I
think at least we should be matching on the excluded access-
protected regs, so hopefully collecting a devcoredump doesn't
make things go boom. Tested on glymur (windows fw) and on
kaanapali MTP (android fw).
NOTE: Uses $codename for generated file/table names, rather than continue
to perpetuate the equally meaningless gen8_x_y naming from kgsl into
upstream. At least the codenames are familiar upstream and this
removes an extra level of name translation.
v4:
- Drop RB_GC_GMEM_PROTECT from nonctx list, now that we are computing
the value directly
- Add missing 'break' in switch
- Move RBBM_MISC_STATUS read to after we check GX is on
- Rename sysprof_setup
- Add cleanup.h support for disabling IFPC, and use it in debugfs
snapshot path
- Collect tags
- Link to v3: https://lore.kernel.org/all/20261029201730.151754-1-robin.clark@oss.qualcomm.com/
v3:
- Drop generated nonctx list for now
- Exclude access protected registers
- Shuffle around some debugbus blocks to match kgsl
- Use CP_PROTECT_PIPE regs to pickup the extra spans if we exceed the
64 CP_PROTECT_GLOBAL regs
- Inhibit IFPC for gpu debugfs dump
- Fix cx_dbgc capture
- Misc other small fixes
- Link to v2: https://lore.kernel.org/all/20261021204244.67160-1-robin.clark@oss.qualcomm.com/
v2:
- Fix incorrect protect list for Kaanapali
- Use __free() for snapshot_sect finalization
- Fix missing early return error path in gen8_snapshot_mempool()
- Fix aperture setting in gen8_legacy_snapshot_mvc()
- Fix first_slice in gen8_snapshot_mempool()
- Remove GBIF regs in generated nonctx list
- Link to v1: https://lore.kernel.org/all/20261020204728.28844-1-robin.clark@oss.qualcomm.com/
Rob Clark (19):
drm/msm: Import adreno_snapshot.h
drm/msm/a8xx: Export aperture helpers
drm/msm/a8xx: Add a8xx_aperture_acquire_raw()
drm/msm/a8xx: Export slice helper
drm/msm/a8xx: Add missing a850 ifpc reglist entries
drm/msm/a8xx: Allow nonctx reglist to include GMU regs
drm/msm/a8xx: Stop getting GMEM_PROTECT from nonctx list
drm/msm/adreno: Fix PROTECT span length
drm/msm: Rename sysprof_setup()
drm/msm: Add cleanup help for disabling IFPC
drm/msm/debugfs: Use cleanup.h for gpu state dump
drm/msm: Inhibit IFPC for state snapshot
drm/msm/adreno: add temporary __maybe_unused
drm/msm/a8xx: Import generated headers
drm/msm/a8xx: devcoredump state capture support
drm/msm/a8xx: Add all CP_PROTECT_PIPE to pwrup reglist
drm/msm/a8xx: Use CP_PROTECT_PIPE regs
drm/msm/a8xx: Switch to generated protect regs
drm/msm/adreno: Remove temporary __maybe_unused
drivers/gpu/drm/msm/Makefile | 4 +-
drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 17 +-
drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 13 +-
drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 16 +-
drivers/gpu/drm/msm/adreno/a8xx_catalog.c | 404 +--
drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 65 +-
drivers/gpu/drm/msm/adreno/a8xx_gpu_state.c | 1401 ++++++++++
.../gpu/drm/msm/adreno/adreno_gen8_snapshot.h | 576 ++++
drivers/gpu/drm/msm/adreno/adreno_gpu.h | 2 +
drivers/gpu/drm/msm/adreno/adreno_snapshot.h | 530 ++++
.../drm/msm/adreno/catalog/adreno_glymur.h | 2307 ++++++++++++++++
.../gpu/drm/msm/adreno/catalog/adreno_hawi.h | 2387 +++++++++++++++++
.../drm/msm/adreno/catalog/adreno_kaanapali.h | 2317 ++++++++++++++++
.../gpu/drm/msm/adreno/catalog/adreno_maili.h | 1882 +++++++++++++
.../gpu/drm/msm/adreno/catalog/adreno_milos.h | 1977 ++++++++++++++
.../drm/msm/adreno/catalog/adreno_pakala.h | 2161 +++++++++++++++
drivers/gpu/drm/msm/msm_debugfs.c | 31 +-
drivers/gpu/drm/msm/msm_gpu.h | 6 +-
drivers/gpu/drm/msm/msm_perfcntr.c | 7 +-
drivers/gpu/drm/msm/msm_submitqueue.c | 4 +-
drivers/gpu/drm/msm/registers/adreno/a6xx.xml | 4 +
21 files changed, 15754 insertions(+), 357 deletions(-)
create mode 100644 drivers/gpu/drm/msm/adreno/a8xx_gpu_state.c
create mode 100644 drivers/gpu/drm/msm/adreno/adreno_gen8_snapshot.h
create mode 100644 drivers/gpu/drm/msm/adreno/adreno_snapshot.h
create mode 100644 drivers/gpu/drm/msm/adreno/catalog/adreno_glymur.h
create mode 100644 drivers/gpu/drm/msm/adreno/catalog/adreno_hawi.h
create mode 100644 drivers/gpu/drm/msm/adreno/catalog/adreno_kaanapali.h
create mode 100644 drivers/gpu/drm/msm/adreno/catalog/adreno_maili.h
create mode 100644 drivers/gpu/drm/msm/adreno/catalog/adreno_milos.h
create mode 100644 drivers/gpu/drm/msm/adreno/catalog/adreno_pakala.h
--
2.56.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v4 01/19] drm/msm: Import adreno_snapshot.h
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:27 ` sashiko-bot
2026-10-30 21:18 ` [PATCH v4 02/19] drm/msm/a8xx: Export aperture helpers Rob Clark
` (16 subsequent siblings)
17 siblings, 1 reply; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Sean Paul,
Konrad Dybcio, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Marijn Suijten, David Airlie, Simona Vetter, open list
Based on kgsl_snapshot.h from downstream KMD. We'll be re-using some of
this for gen8 devcoredump.
Tested-by: Alexander Koskovich <akoskovich@pm.me> # Milos (Nothing Phone 3a)
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/adreno_snapshot.h | 321 +++++++++++++++++++
1 file changed, 321 insertions(+)
create mode 100644 drivers/gpu/drm/msm/adreno/adreno_snapshot.h
diff --git a/drivers/gpu/drm/msm/adreno/adreno_snapshot.h b/drivers/gpu/drm/msm/adreno/adreno_snapshot.h
new file mode 100644
index 000000000000..f9956e0bc1d5
--- /dev/null
+++ b/drivers/gpu/drm/msm/adreno/adreno_snapshot.h
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#pragma once
+
+#include <linux/types.h>
+
+/* Snapshot header */
+
+/* High word is static, low word is snapshot version ID */
+#define SNAPSHOT_MAGIC 0x504D0002
+
+#define DEBUG_SECTION_SZ(_dwords) (((_dwords) * sizeof(unsigned int)) \
+ + sizeof(struct adreno_snapshot_debug))
+
+/* GPU ID scheme:
+ * [16:31] - core identifer (0x0002 for 2D or 0x0003 for 3D)
+ * [00:16] - GPU specific identifier
+ */
+
+struct adreno_snapshot_header {
+ __u32 magic; /* Magic identifier */
+ __u32 gpuid; /* GPU ID - see above */
+ /* Added in snapshot version 2 */
+ __u32 chipid; /* Chip ID from the GPU */
+} __packed;
+
+/* Section header */
+#define SNAPSHOT_SECTION_MAGIC 0xABCD
+
+struct adreno_snapshot_section_header {
+ __u16 magic; /* Magic identifier */
+ __u16 id; /* Type of section */
+ __u32 size; /* Size of the section including this header */
+} __packed;
+
+/* Section identifiers */
+#define ADRENO_SNAPSHOT_SECTION_OS 0x0101
+#define ADRENO_SNAPSHOT_SECTION_REGS 0x0201
+#define ADRENO_SNAPSHOT_SECTION_REGS_V2 0x0202
+#define ADRENO_SNAPSHOT_SECTION_RB_V2 0x0302
+#define ADRENO_SNAPSHOT_SECTION_IB_V2 0x0402
+#define ADRENO_SNAPSHOT_SECTION_INDEXED_REGS 0x0501
+#define ADRENO_SNAPSHOT_SECTION_INDEXED_REGS_V2 0x0502
+#define ADRENO_SNAPSHOT_SECTION_DEBUG 0x0901
+#define ADRENO_SNAPSHOT_SECTION_DEBUGBUS 0x0A01
+#define ADRENO_SNAPSHOT_SECTION_GPU_OBJECT_V2 0x0B02
+#define ADRENO_SNAPSHOT_SECTION_MEMLIST_V2 0x0E02
+#define ADRENO_SNAPSHOT_SECTION_SHADER 0x1201
+#define ADRENO_SNAPSHOT_SECTION_SHADER_V2 0x1202
+#define ADRENO_SNAPSHOT_SECTION_SHADER_V3 0x1203
+#define ADRENO_SNAPSHOT_SECTION_MVC 0x1501
+#define ADRENO_SNAPSHOT_SECTION_MVC_V2 0x1502
+#define ADRENO_SNAPSHOT_SECTION_MVC_V3 0x1503
+#define ADRENO_SNAPSHOT_SECTION_GMU_MEMORY 0x1701
+#define ADRENO_SNAPSHOT_SECTION_SIDE_DEBUGBUS 0x1801
+#define ADRENO_SNAPSHOT_SECTION_TRACE_BUFFER 0x1901
+#define ADRENO_SNAPSHOT_SECTION_EVENTLOG 0x1A01
+
+#define ADRENO_SNAPSHOT_SECTION_END 0xFFFF
+
+/* OS sub-section header */
+#define ADRENO_SNAPSHOT_OS_LINUX_V4 0x00000203
+
+/* Linux OS specific information */
+struct adreno_snapshot_linux_v4 {
+ int osid; /* subsection OS identifier */
+ __u32 seconds; /* Unix timestamp for the snapshot */
+ __u32 power_flags; /* Current power flags */
+ __u32 power_level; /* Current power level */
+ __u32 power_interval_timeout; /* Power interval timeout */
+ __u32 grpclk; /* Current GP clock value */
+ __u32 busclk; /* Current busclk value */
+ __u64 ptbase; /* Current ptbase */
+ __u64 ptbase_lpac; /* Current LPAC ptbase */
+ __u32 pid; /* PID of the process that owns the PT */
+ __u32 pid_lpac; /* PID of the LPAC process that owns the PT */
+ __u32 current_context; /* ID of the current context */
+ __u32 current_context_lpac; /* ID of the current LPAC context */
+ __u32 ctxtcount; /* Number of contexts appended to section */
+ unsigned char release[32]; /* kernel release */
+ unsigned char version[32]; /* kernel version */
+ unsigned char comm[16]; /* Name of the process that owns the PT */
+ unsigned char comm_lpac[16]; /* Name of the LPAC process that owns the PT */
+} __packed;
+
+/*
+ * This structure contains a record of an active context.
+ * These are appended one after another in the OS section below
+ * the header above
+ */
+struct adreno_snapshot_linux_context_v2 {
+ __u32 id; /* The context ID */
+ __u32 timestamp_queued; /* The last queued timestamp */
+ __u32 timestamp_consumed; /* The last timestamp consumed by HW */
+ __u32 timestamp_retired; /* The last timestamp retired by HW */
+};
+
+/* Ringbuffer sub-section header */
+struct adreno_snapshot_rb_v2 {
+ int start; /* dword at the start of the dump */
+ int end; /* dword at the end of the dump */
+ int rbsize; /* Size (in dwords) of the ringbuffer */
+ int wptr; /* Current index of the CPU write pointer */
+ int rptr; /* Current index of the GPU read pointer */
+ int count; /* Number of dwords in the dump */
+ __u32 timestamp_queued; /* The last queued timestamp */
+ __u32 timestamp_retired; /* The last timestamp retired by HW */
+ __u64 gpuaddr; /* The GPU address of the ringbuffer */
+ __u32 id; /* Ringbuffer identifier */
+} __packed;
+
+/* Replay or Memory list section, both sections have same header */
+struct adreno_snapshot_mem_list_v2 {
+ /*
+ * Number of IBs to replay for replay section or
+ * number of memory list entries for mem list section
+ */
+ int num_entries;
+ /* Pagetable base to which the replay IBs or memory entries belong */
+ __u64 ptbase;
+} __packed;
+
+/* Indirect buffer sub-section header (v2) */
+struct adreno_snapshot_ib_v2 {
+ __u64 gpuaddr; /* GPU address of the IB */
+ __u64 ptbase; /* Base for the pagetable the GPU address is valid in */
+ __u64 size; /* Size of the IB */
+} __packed;
+
+/* GMU memory ID's */
+#define SNAPSHOT_GMU_MEM_UNKNOWN 0x00
+#define SNAPSHOT_GMU_MEM_HFI 0x01
+#define SNAPSHOT_GMU_MEM_LOG 0x02
+#define SNAPSHOT_GMU_MEM_BWTABLE 0x03
+#define SNAPSHOT_GMU_MEM_DEBUG 0x04
+#define SNAPSHOT_GMU_MEM_BIN_BLOCK 0x05
+#define SNAPSHOT_GMU_MEM_CONTEXT_QUEUE 0x06
+#define SNAPSHOT_GMU_MEM_HW_FENCE 0x07
+#define SNAPSHOT_GMU_MEM_WARMBOOT 0x08
+#define SNAPSHOT_GMU_MEM_VRB 0x09
+#define SNAPSHOT_GMU_MEM_TRACE 0x0a
+
+/* GMU memory section data */
+struct adreno_snapshot_gmu_mem {
+ int type;
+ uint64_t hostaddr;
+ uint64_t gmuaddr;
+ uint64_t gpuaddr;
+} __packed;
+
+/* Register sub-section header */
+struct adreno_snapshot_regs {
+ __u32 count; /* Number of register pairs in the section */
+} __packed;
+
+/* Indexed register sub-section header */
+struct adreno_snapshot_indexed_regs {
+ __u32 index_reg; /* Offset of the index register for this section */
+ __u32 data_reg; /* Offset of the data register for this section */
+ int start; /* Starting index */
+ int count; /* Number of dwords in the data */
+} __packed;
+
+struct adreno_snapshot_indexed_regs_v2 {
+ u32 index_reg; /* Offset of the index register for this section */
+ u32 data_reg; /* Offset of the data register for this section */
+ u32 start; /* Starting index */
+ u32 count; /* Number of dwords in the data */
+ u32 pipe_id; /* Id of pipe, BV, Br etc */
+ u32 slice_id; /* Slice ID to be dumped */
+} __packed;
+
+/* MVC register sub-section header */
+struct adreno_snapshot_mvc_regs {
+ int ctxt_id;
+ int cluster_id;
+} __packed;
+
+struct adreno_snapshot_mvc_regs_v2 {
+ int ctxt_id;
+ int cluster_id;
+ int pipe_id;
+ int location_id;
+} __packed;
+
+struct adreno_snapshot_mvc_regs_v3 {
+ u32 ctxt_id;
+ u32 cluster_id;
+ u32 pipe_id;
+ u32 location_id;
+ u32 slice_id;
+ u32 sp_id;
+ u32 usptp_id;
+} __packed;
+
+/* Debug data sub-section header */
+
+/* A5XX debug sections */
+#define SNAPSHOT_DEBUG_CP_MEQ 7
+#define SNAPSHOT_DEBUG_CP_PM4_RAM 8
+#define SNAPSHOT_DEBUG_CP_PFP_RAM 9
+#define SNAPSHOT_DEBUG_CP_ROQ 10
+#define SNAPSHOT_DEBUG_SHADER_MEMORY 11
+#define SNAPSHOT_DEBUG_CP_MERCIU 12
+#define SNAPSHOT_DEBUG_SQE_VERSION 14
+
+/* GMU Version information */
+#define SNAPSHOT_DEBUG_GMU_CORE_VERSION 15
+#define SNAPSHOT_DEBUG_GMU_CORE_DEV_VERSION 16
+#define SNAPSHOT_DEBUG_GMU_PWR_VERSION 17
+#define SNAPSHOT_DEBUG_GMU_PWR_DEV_VERSION 18
+#define SNAPSHOT_DEBUG_GMU_HFI_VERSION 19
+#define SNAPSHOT_DEBUG_AQE_VERSION 20
+
+/* Slice mask info */
+#define SNAPSHOT_DEBUG_SLICE_MASK 32
+
+struct adreno_snapshot_debug {
+ int type; /* Type identifier for the attached tata */
+ int size; /* Size of the section in dwords */
+} __packed;
+
+struct adreno_snapshot_debugbus {
+ int id; /* Debug bus ID */
+ int count; /* Number of dwords in the dump */
+} __packed;
+
+struct adreno_snapshot_side_debugbus {
+ int id; /* Debug bus ID */
+ int size; /* Number of dwords in the dump */
+ int valid_data; /* Mask of valid bits of the side debugbus */
+} __packed;
+
+struct adreno_snapshot_shader {
+ int type; /* SP/TP statetype */
+ int index; /* SP/TP index */
+ int size; /* Number of dwords in the dump */
+} __packed;
+
+struct adreno_snapshot_shader_v2 {
+ int type; /* SP/TP statetype */
+ int index; /* SP/TP index */
+ int usptp; /* USPTP index */
+ int pipe_id; /* Pipe id */
+ int location; /* Location value */
+ u32 size; /* Number of dwords in the dump */
+} __packed;
+
+struct adreno_snapshot_shader_v3 {
+ u32 type; /* SP/TP statetype */
+ u32 slice_id; /* Slice ID */
+ u32 sp_index; /* SP/TP index */
+ u32 usptp; /* USPTP index */
+ u32 pipe_id; /* Pipe id */
+ u32 location; /* Location value */
+ u32 ctxt_id; /* Context ID */
+ u32 size; /* Number of dwords in the dump */
+} __packed;
+
+#define TRACE_BUF_NUM_SIG 4
+
+/**
+ * enum trace_buffer_source - Bits to identify the source block of trace buffer information
+ * GX_DBGC : Signals captured from GX block
+ * CX_DBGC : Signals captured from CX block
+ */
+enum trace_buffer_source {
+ GX_DBGC = 1,
+ CX_DBGC = 2,
+};
+
+/**
+ * adreno_snapshot_trace_buffer: Header Information for the tracebuffer in snapshot.
+ */
+struct adreno_snapshot_trace_buffer {
+ /** @dbgc_ctrl: Identify source for trace */
+ __u16 dbgc_ctrl;
+ /** @dbgc_ctrl: Identify source for trace */
+ __u16 segment;
+ /** @granularity: The total number of segments in each packet */
+ __u16 granularity;
+ /** @ping_blk: Signal block */
+ __u16 ping_blk[TRACE_BUF_NUM_SIG];
+ /** @ping_idx: Signal Index */
+ __u16 ping_idx[TRACE_BUF_NUM_SIG];
+ /** @size: Number of bytes in the dump */
+ __u32 size;
+} __packed;
+
+#define SNAPSHOT_GPU_OBJECT_SHADER 1
+#define SNAPSHOT_GPU_OBJECT_IB 2
+#define SNAPSHOT_GPU_OBJECT_GENERIC 3
+#define SNAPSHOT_GPU_OBJECT_DRAW 4
+#define SNAPSHOT_GPU_OBJECT_GLOBAL 5
+
+struct adreno_snapshot_gpu_object_v2 {
+ int type; /* Type of GPU object */
+ __u64 gpuaddr; /* GPU address of the object */
+ __u64 ptbase; /* Base for the pagetable the GPU address is valid in */
+ __u64 size; /* Size of the object (in dwords) */
+} __packed;
+
+struct adreno_snapshot_eventlog {
+ /** @type: Type of the event log buffer */
+ __u16 type;
+ /** @version: Version of the event log buffer */
+ __u16 version;
+ /** @size: Size of the eventlog buffer in bytes */
+ u32 size;
+} __packed;
+
+struct adreno_snapshot_gmu_version {
+ /** @type: Type of the GMU version buffer */
+ u32 type;
+ /** @value: GMU FW version value */
+ u32 value;
+} __packed;
+
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 02/19] drm/msm/a8xx: Export aperture helpers
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
2026-10-30 21:18 ` [PATCH v4 01/19] drm/msm: Import adreno_snapshot.h Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:18 ` [PATCH v4 03/19] drm/msm/a8xx: Add a8xx_aperture_acquire_raw() Rob Clark
` (15 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Sean Paul,
Konrad Dybcio, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Marijn Suijten, David Airlie, Simona Vetter, open list
We'll need these from gen8 gpu state snapshot code as well.
Tested-by: Alexander Koskovich <akoskovich@pm.me> # Milos (Nothing Phone 3a)
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 3 +++
drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 6 +++---
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
index 81d21acccea1..fb000b79fe2f 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
@@ -348,6 +348,9 @@ void a6xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
void a6xx_flush_yield(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
int a6xx_zap_shader_init(struct msm_gpu *gpu);
+void a8xx_aperture_acquire(struct msm_gpu *gpu, enum adreno_pipe pipe, unsigned long *flags);
+void a8xx_aperture_release(struct msm_gpu *gpu, unsigned long flags);
+void a8xx_aperture_clear(struct msm_gpu *gpu);
void a8xx_bus_clear_pending_transactions(struct adreno_gpu *adreno_gpu, bool gx_off);
int a8xx_fault_handler(void *arg, unsigned long iova, int flags, void *data);
void a8xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
index e7d04c865968..a14379e26fbe 100644
--- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
@@ -32,7 +32,7 @@ static void a8xx_aperture_slice_set(struct msm_gpu *gpu, enum adreno_pipe pipe,
a6xx_gpu->cached_aperture = val;
}
-static void a8xx_aperture_acquire(struct msm_gpu *gpu, enum adreno_pipe pipe, unsigned long *flags)
+void a8xx_aperture_acquire(struct msm_gpu *gpu, enum adreno_pipe pipe, unsigned long *flags)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
@@ -42,7 +42,7 @@ static void a8xx_aperture_acquire(struct msm_gpu *gpu, enum adreno_pipe pipe, un
a8xx_aperture_slice_set(gpu, pipe, 0);
}
-static void a8xx_aperture_release(struct msm_gpu *gpu, unsigned long flags)
+void a8xx_aperture_release(struct msm_gpu *gpu, unsigned long flags)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
@@ -50,7 +50,7 @@ static void a8xx_aperture_release(struct msm_gpu *gpu, unsigned long flags)
spin_unlock_irqrestore(&a6xx_gpu->aperture_lock, flags);
}
-static void a8xx_aperture_clear(struct msm_gpu *gpu)
+void a8xx_aperture_clear(struct msm_gpu *gpu)
{
unsigned long flags;
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 03/19] drm/msm/a8xx: Add a8xx_aperture_acquire_raw()
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
2026-10-30 21:18 ` [PATCH v4 01/19] drm/msm: Import adreno_snapshot.h Rob Clark
2026-10-30 21:18 ` [PATCH v4 02/19] drm/msm/a8xx: Export aperture helpers Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:18 ` [PATCH v4 04/19] drm/msm/a8xx: Export slice helper Rob Clark
` (14 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Sean Paul,
Konrad Dybcio, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Marijn Suijten, David Airlie, Simona Vetter, open list
The snapshot code will need more control, setting additional bitfields.
Rather than add a list of additional args, add a helper to acquire the
aperture which directly takes a reg value.
Tested-by: Alexander Koskovich <akoskovich@pm.me> # Milos (Nothing Phone 3a)
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 1 +
drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 21 +++++++++++++--------
2 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
index fb000b79fe2f..2a2ddba6761f 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
@@ -348,6 +348,7 @@ void a6xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
void a6xx_flush_yield(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
int a6xx_zap_shader_init(struct msm_gpu *gpu);
+void a8xx_aperture_acquire_raw(struct msm_gpu *gpu, u32 val, unsigned long *flags);
void a8xx_aperture_acquire(struct msm_gpu *gpu, enum adreno_pipe pipe, unsigned long *flags);
void a8xx_aperture_release(struct msm_gpu *gpu, unsigned long flags);
void a8xx_aperture_clear(struct msm_gpu *gpu);
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
index a14379e26fbe..a0983e2ee641 100644
--- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
@@ -16,13 +16,10 @@
#define GPU_PAS_ID 13
-static void a8xx_aperture_slice_set(struct msm_gpu *gpu, enum adreno_pipe pipe, u32 slice)
+static void a8xx_aperture_slice_set(struct msm_gpu *gpu, u32 val)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
- u32 val;
-
- val = A8XX_CP_APERTURE_CNTL_HOST_PIPEID(pipe) | A8XX_CP_APERTURE_CNTL_HOST_SLICEID(slice);
if (a6xx_gpu->cached_aperture == val)
return;
@@ -32,14 +29,20 @@ static void a8xx_aperture_slice_set(struct msm_gpu *gpu, enum adreno_pipe pipe,
a6xx_gpu->cached_aperture = val;
}
-void a8xx_aperture_acquire(struct msm_gpu *gpu, enum adreno_pipe pipe, unsigned long *flags)
+void a8xx_aperture_acquire_raw(struct msm_gpu *gpu, u32 val, unsigned long *flags)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
spin_lock_irqsave(&a6xx_gpu->aperture_lock, *flags);
- a8xx_aperture_slice_set(gpu, pipe, 0);
+ a8xx_aperture_slice_set(gpu, val);
+
+}
+
+void a8xx_aperture_acquire(struct msm_gpu *gpu, enum adreno_pipe pipe, unsigned long *flags)
+{
+ a8xx_aperture_acquire_raw(gpu, A8XX_CP_APERTURE_CNTL_HOST_PIPEID(pipe), flags);
}
void a8xx_aperture_release(struct msm_gpu *gpu, unsigned long flags)
@@ -74,8 +77,10 @@ static u32 a8xx_read_pipe_slice(struct msm_gpu *gpu, enum adreno_pipe pipe, u32
unsigned long flags;
u32 val;
- spin_lock_irqsave(&a6xx_gpu->aperture_lock, flags);
- a8xx_aperture_slice_set(gpu, pipe, slice);
+ a8xx_aperture_acquire_raw(gpu,
+ A8XX_CP_APERTURE_CNTL_HOST_PIPEID(pipe) |
+ A8XX_CP_APERTURE_CNTL_HOST_SLICEID(slice),
+ &flags);
val = gpu_read(gpu, offset);
spin_unlock_irqrestore(&a6xx_gpu->aperture_lock, flags);
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 04/19] drm/msm/a8xx: Export slice helper
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
` (2 preceding siblings ...)
2026-10-30 21:18 ` [PATCH v4 03/19] drm/msm/a8xx: Add a8xx_aperture_acquire_raw() Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:18 ` [PATCH v4 05/19] drm/msm/a8xx: Add missing a850 ifpc reglist entries Rob Clark
` (13 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Sean Paul,
Konrad Dybcio, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Marijn Suijten, David Airlie, Simona Vetter, open list
Something we'll need from the gpu snapshot code.
Tested-by: Alexander Koskovich <akoskovich@pm.me> # Milos (Nothing Phone 3a)
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 1 +
drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
index 2a2ddba6761f..6b23d301c144 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
@@ -359,6 +359,7 @@ u64 a8xx_gmu_get_timestamp(struct msm_gpu *gpu);
u64 a8xx_gpu_busy(struct msm_gpu *gpu, unsigned long *out_sample_rate);
int a8xx_gpu_feature_probe(struct msm_gpu *gpu);
void a8xx_gpu_get_slice_info(struct msm_gpu *gpu);
+u32 a8xx_get_first_slice(struct a6xx_gpu *a6xx_gpu);
int a8xx_hw_init(struct msm_gpu *gpu);
irqreturn_t a8xx_irq(struct msm_gpu *gpu);
void a8xx_llc_activate(struct a6xx_gpu *a6xx_gpu);
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
index a0983e2ee641..8e91edf16ec2 100644
--- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
@@ -127,7 +127,7 @@ void a8xx_gpu_get_slice_info(struct msm_gpu *gpu)
}
}
-static u32 a8xx_get_first_slice(struct a6xx_gpu *a6xx_gpu)
+u32 a8xx_get_first_slice(struct a6xx_gpu *a6xx_gpu)
{
return ffs(a6xx_gpu->slice_mask) - 1;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 05/19] drm/msm/a8xx: Add missing a850 ifpc reglist entries
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
` (3 preceding siblings ...)
2026-10-30 21:18 ` [PATCH v4 04/19] drm/msm/a8xx: Export slice helper Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:18 ` [PATCH v4 06/19] drm/msm/a8xx: Allow nonctx reglist to include GMU regs Rob Clark
` (12 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Konrad Dybcio,
Sean Paul, Konrad Dybcio, Dmitry Baryshkov, Abhinav Kumar,
Jessica Zhang, Marijn Suijten, David Airlie, Simona Vetter,
open list
We want to save/restore all of the CP_PROTECT regs across IFPC.
Tested-by: Alexander Koskovich <akoskovich@pm.me> # Milos (Nothing Phone 3a)
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a8xx_catalog.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_catalog.c b/drivers/gpu/drm/msm/adreno/a8xx_catalog.c
index 55a805ab438c..45cd986ad4b5 100644
--- a/drivers/gpu/drm/msm/adreno/a8xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a8xx_catalog.c
@@ -1477,6 +1477,13 @@ static const u32 a850_ifpc_reglist_regs[] = {
REG_A8XX_CP_PROTECT_GLOBAL(53),
REG_A8XX_CP_PROTECT_GLOBAL(54),
REG_A8XX_CP_PROTECT_GLOBAL(55),
+ REG_A8XX_CP_PROTECT_GLOBAL(56),
+ REG_A8XX_CP_PROTECT_GLOBAL(57),
+ REG_A8XX_CP_PROTECT_GLOBAL(58),
+ REG_A8XX_CP_PROTECT_GLOBAL(59),
+ REG_A8XX_CP_PROTECT_GLOBAL(60),
+ REG_A8XX_CP_PROTECT_GLOBAL(61),
+ REG_A8XX_CP_PROTECT_GLOBAL(62),
REG_A8XX_CP_PROTECT_GLOBAL(63),
};
DECLARE_ADRENO_REGLIST_LIST(a850_ifpc_reglist);
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 06/19] drm/msm/a8xx: Allow nonctx reglist to include GMU regs
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
` (4 preceding siblings ...)
2026-10-30 21:18 ` [PATCH v4 05/19] drm/msm/a8xx: Add missing a850 ifpc reglist entries Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:18 ` [PATCH v4 07/19] drm/msm/a8xx: Stop getting GMEM_PROTECT from nonctx list Rob Clark
` (11 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Konrad Dybcio,
Sean Paul, Konrad Dybcio, Dmitry Baryshkov, Abhinav Kumar,
Jessica Zhang, Marijn Suijten, David Airlie, Simona Vetter,
open list
From hw PoV, it's all part of the GPU. But from dt perspective it is
it's own device with a separate io space. So we need to detect this and
direct writes to the correct place.
Acked-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Tested-by: Alexander Koskovich <akoskovich@pm.me> # Milos (Nothing Phone 3a)
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
index 8e91edf16ec2..795c16d88f45 100644
--- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
@@ -370,6 +370,7 @@ static void a8xx_set_ubwc_config(struct msm_gpu *gpu)
static void a8xx_nonctxt_config(struct msm_gpu *gpu, u32 *gmem_protect)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gmu *gmu = &to_a6xx_gpu(adreno_gpu)->gmu;
const struct a6xx_info *info = adreno_gpu->info->a6xx;
const struct adreno_reglist_pipe *regs = info->nonctxt_reglist;
unsigned int pipe_id, i;
@@ -389,7 +390,11 @@ static void a8xx_nonctxt_config(struct msm_gpu *gpu, u32 *gmem_protect)
if (regs[i].offset == REG_A8XX_RB_GC_GMEM_PROTECT)
*gmem_protect = regs[i].value;
- gpu_write(gpu, regs[i].offset, regs[i].value);
+ if ((regs[i].offset << 2) >= gmu->mmio_offset) {
+ gmu_write(gmu, regs[i].offset, regs[i].value);
+ } else {
+ gpu_write(gpu, regs[i].offset, regs[i].value);
+ }
}
a8xx_aperture_release(gpu, flags);
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 07/19] drm/msm/a8xx: Stop getting GMEM_PROTECT from nonctx list
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
` (5 preceding siblings ...)
2026-10-30 21:18 ` [PATCH v4 06/19] drm/msm/a8xx: Allow nonctx reglist to include GMU regs Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:18 ` [PATCH v4 08/19] drm/msm/adreno: Fix PROTECT span length Rob Clark
` (10 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Sean Paul,
Konrad Dybcio, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Marijn Suijten, David Airlie, Simona Vetter, open list
We can just simplify things and calculate the value, instead of manually
adding it to the nonctx list.
Tested-by: Alexander Koskovich <akoskovich@pm.me> # Milos (Nothing Phone 3a)
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a8xx_catalog.c | 6 ------
drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 12 ++++--------
2 files changed, 4 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_catalog.c b/drivers/gpu/drm/msm/adreno/a8xx_catalog.c
index 45cd986ad4b5..8d85f6cad960 100644
--- a/drivers/gpu/drm/msm/adreno/a8xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a8xx_catalog.c
@@ -27,7 +27,6 @@ static const struct adreno_reglist_pipe a830_nonctxt_regs[] = {
{ REG_A7XX_RB_CCU_CNTL, 0x00000068, BIT(PIPE_BR) },
/* Partially enable perf clear, Disable DINT to c/z be data forwarding */
{ REG_A7XX_RB_CCU_DBG_ECO_CNTL, 0x00002200, BIT(PIPE_BR) },
- { REG_A8XX_RB_GC_GMEM_PROTECT, 0x0c000000, BIT(PIPE_BR) },
/* Configure number of outstanding transactions to 32 */
{ REG_A8XX_RB_RESOLVE_PREFETCH_CNTL, 0x00000007, BIT(PIPE_BR) },
/* Configure UCHE request time out to 16 cycles for CCU/UCHE arbitration */
@@ -309,7 +308,6 @@ static const struct adreno_reglist_pipe x285_nonctxt_regs[] = {
{ REG_A8XX_PC_CHICKEN_BITS_2, 0x00000200, BIT(PIPE_BV) | BIT(PIPE_BR) },
{ REG_A8XX_PC_CHICKEN_BITS_3, 0x00500000, BIT(PIPE_BV) | BIT(PIPE_BR) },
{ REG_A8XX_PC_CHICKEN_BITS_4, 0x00500050, BIT(PIPE_BV) | BIT(PIPE_BR) },
- { REG_A8XX_RB_GC_GMEM_PROTECT, 0x15000000, BIT(PIPE_BR) },
{ REG_A8XX_RB_RESOLVE_PREFETCH_CNTL, 0x00000007, BIT(PIPE_BR) },
{ REG_A8XX_RB_CMP_DBG_ECO_CNTL, 0x00004000, BIT(PIPE_BR) },
{ REG_A8XX_RBBM_NC_MODE_CNTL, 0x00000001, BIT(PIPE_NONE) },
@@ -483,7 +481,6 @@ static const struct adreno_reglist_pipe a810_nonctxt_regs[] = {
{ REG_A8XX_VFD_CB_BUSY_REQ_CNT, 0x00200020, BIT(PIPE_BV) | BIT(PIPE_BR) },
{ REG_A8XX_VFD_CB_LP_REQ_CNT, 0x00100020, BIT(PIPE_BV) | BIT(PIPE_BR) },
{ REG_A8XX_VPC_FLATSHADE_MODE_CNTL, 0x00000001, BIT(PIPE_BV) | BIT(PIPE_BR) },
- { REG_A8XX_RB_GC_GMEM_PROTECT, 0x00900000, BIT(PIPE_BR) },
{ },
};
@@ -793,7 +790,6 @@ static const struct adreno_reglist_pipe a840_nonctxt_regs[] = {
{ REG_A7XX_RB_CCU_CNTL, 0x00000068, BIT(PIPE_BR) },
/* Partially enable perf clear, Disable DINT to c/z be data forwarding */
{ REG_A7XX_RB_CCU_DBG_ECO_CNTL, 0x00002200, BIT(PIPE_BR) },
- { REG_A8XX_RB_GC_GMEM_PROTECT, 0x12000000, BIT(PIPE_BR) },
{ REG_A8XX_RB_RESOLVE_PREFETCH_CNTL, 0x00000007, BIT(PIPE_BR) },
{ REG_A8XX_RB_CMP_DBG_ECO_CNTL, 0x00004000, BIT(PIPE_BR) },
{ REG_A8XX_RBBM_NC_MODE_CNTL, 0x00000001, BIT(PIPE_NONE) },
@@ -1186,7 +1182,6 @@ static const struct adreno_reglist_pipe a845_nonctxt_regs[] = {
{ REG_A7XX_RB_CCU_CNTL, 0x00000068, BIT(PIPE_BR) },
{ REG_A7XX_RB_CCU_DBG_ECO_CNTL, BIT(10) | BIT(9), BIT(PIPE_BR) },
{ REG_A6XX_RB_DBG_ECO_CNTL, BIT(17), BIT(PIPE_BV) | BIT(PIPE_BR) },
- { REG_A8XX_RB_GC_GMEM_PROTECT, 0x0C000000, BIT(PIPE_BR) },
{ REG_A6XX_RB_RBP_CNTL, BIT(5) | BIT(6), BIT(PIPE_BV) | BIT(PIPE_BR) },
{ REG_A8XX_RB_RESOLVE_PREFETCH_CNTL, 0x00000007, BIT(PIPE_BR) },
{ REG_A8XX_RB_CMP_DBG_ECO_CNTL, 0x00004000, BIT(PIPE_BR) },
@@ -1245,7 +1240,6 @@ static const struct adreno_reglist_pipe a850_nonctxt_regs[] = {
{ REG_A7XX_RB_CCU_CNTL, 0x00000068, BIT(PIPE_BR) },
{ REG_A7XX_RB_CCU_DBG_ECO_CNTL, BIT(10) | BIT(9), BIT(PIPE_BR) },
{ REG_A6XX_RB_DBG_ECO_CNTL, BIT(17), BIT(PIPE_BV) | BIT(PIPE_BR) },
- { REG_A8XX_RB_GC_GMEM_PROTECT, 0x12000000, BIT(PIPE_BR) },
{ REG_A6XX_RB_RBP_CNTL, BIT(5) | BIT(6), BIT(PIPE_BV) | BIT(PIPE_BR) },
{ REG_A8XX_RB_RESOLVE_PREFETCH_CNTL, 0x00000007, BIT(PIPE_BR) },
{ REG_A8XX_RB_CMP_DBG_ECO_CNTL, 0x00004000, BIT(PIPE_BR) },
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
index 795c16d88f45..a6146372cf7e 100644
--- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
@@ -367,7 +367,7 @@ static void a8xx_set_ubwc_config(struct msm_gpu *gpu)
hbb_lo << 1 | ubwc_mode);
}
-static void a8xx_nonctxt_config(struct msm_gpu *gpu, u32 *gmem_protect)
+static void a8xx_nonctxt_config(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gmu *gmu = &to_a6xx_gpu(adreno_gpu)->gmu;
@@ -387,9 +387,6 @@ static void a8xx_nonctxt_config(struct msm_gpu *gpu, u32 *gmem_protect)
if (!(BIT(pipe_id) & regs[i].pipe))
continue;
- if (regs[i].offset == REG_A8XX_RB_GC_GMEM_PROTECT)
- *gmem_protect = regs[i].value;
-
if ((regs[i].offset << 2) >= gmu->mmio_offset) {
gmu_write(gmu, regs[i].offset, regs[i].value);
} else {
@@ -635,7 +632,6 @@ static int hw_init(struct msm_gpu *gpu)
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
unsigned int pipe_id, i;
- u32 gmem_protect = 0;
u64 gmem_range_min;
int ret;
@@ -716,7 +712,7 @@ static int hw_init(struct msm_gpu *gpu)
gpu_write(gpu, REG_A8XX_UCHE_CLIENT_PF, BIT(7) | 0x1);
- a8xx_nonctxt_config(gpu, &gmem_protect);
+ a8xx_nonctxt_config(gpu);
/* Enable fault detection */
gpu_write(gpu, REG_A8XX_RBBM_INTERFACE_HANG_INT_CNTL, BIT(30) | 0xcfffff);
@@ -835,8 +831,8 @@ static int hw_init(struct msm_gpu *gpu)
* GMEM_PROTECT register should be programmed after GPU is transitioned to
* non-secure mode
*/
- a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_RB_GC_GMEM_PROTECT, gmem_protect);
- WARN_ON(!gmem_protect);
+ a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_RB_GC_GMEM_PROTECT,
+ (adreno_gpu->info->gmem / SZ_4K) << 16);
a8xx_aperture_clear(gpu);
if (!a6xx_gpu->pwrup_reglist_emitted) {
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 08/19] drm/msm/adreno: Fix PROTECT span length
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
` (6 preceding siblings ...)
2026-10-30 21:18 ` [PATCH v4 07/19] drm/msm/a8xx: Stop getting GMEM_PROTECT from nonctx list Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:18 ` [PATCH v4 09/19] drm/msm: Rename sysprof_setup() Rob Clark
` (9 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Konrad Dybcio,
Sean Paul, Konrad Dybcio, Dmitry Baryshkov, Abhinav Kumar,
Jessica Zhang, Marijn Suijten, David Airlie, Simona Vetter,
open list
Don't let the length bleed into b31.
Tested-by: Alexander Koskovich <akoskovich@pm.me> # Milos (Nothing Phone 3a)
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
index 6b23d301c144..c49301be504b 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
@@ -244,7 +244,7 @@ struct a7xx_cp_smmu_info {
*/
#define A6XX_PROTECT_NORDWR(_reg, _len) \
((1 << 31) | \
- (((_len) & 0x3FFF) << 18) | ((_reg) & 0x3FFFF))
+ (((_len) & 0x1FFF) << 18) | ((_reg) & 0x3FFFF))
/*
* Same as above, but allow reads over the range. For areas of mixed use (such
@@ -252,7 +252,7 @@ struct a7xx_cp_smmu_info {
* single register
*/
#define A6XX_PROTECT_RDONLY(_reg, _len) \
- ((((_len) & 0x3FFF) << 18) | ((_reg) & 0x3FFFF))
+ ((((_len) & 0x1FFF) << 18) | ((_reg) & 0x3FFFF))
extern const struct adreno_gpu_funcs a6xx_gpu_funcs;
extern const struct adreno_gpu_funcs a6xx_gmuwrapper_funcs;
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 09/19] drm/msm: Rename sysprof_setup()
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
` (7 preceding siblings ...)
2026-10-30 21:18 ` [PATCH v4 08/19] drm/msm/adreno: Fix PROTECT span length Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:18 ` [PATCH v4 10/19] drm/msm: Add cleanup help for disabling IFPC Rob Clark
` (8 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Sean Paul,
Konrad Dybcio, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Marijn Suijten, David Airlie, Simona Vetter, open list
We'll need this for other non-sysprof related things, so give it a
better name.
It keeps the behavior of implicit force_on when sysprof is active, to
avoid accidentially re-enabling IFPC when sysprof is active. That isn't
a problem yet (PERFCNTR_CONFIG is not intended to be used with SYSPROF),
but it will be for future users.
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 17 +++++++++++++----
drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 6 +++---
drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 2 +-
drivers/gpu/drm/msm/msm_gpu.h | 2 +-
drivers/gpu/drm/msm/msm_perfcntr.c | 8 ++++----
drivers/gpu/drm/msm/msm_submitqueue.c | 4 ++--
6 files changed, 24 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
index 662f09bec9d5..f3ec78226ed3 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
@@ -2128,13 +2128,22 @@ static int a6xx_gmu_get_irq(struct a6xx_gmu *gmu, struct platform_device *pdev,
return irq;
}
-void a6xx_gmu_sysprof_setup(struct msm_gpu *gpu, bool force_on)
+/**
+ * a6xx_gmu_ifpc_update - Update IFPC inhibit OOB request
+ * @gpu: the gpu device
+ * @force_on: whether to force GPU on (disable IFPC)
+ *
+ * Update IFPC OOB request, if force_on is true, set an OOB request to
+ * force the GPU on (disable IFPC), if false drop the OOB request.
+ */
+void a6xx_gmu_ifpc_update(struct msm_gpu *gpu, bool force_on)
{
- bool sysprof = msm_gpu_sysprof_no_ifpc(gpu) || force_on;
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
+ force_on |= msm_gpu_sysprof_no_ifpc(gpu);
+
/* Nothing to do if GPU is suspended. We will handle this during GMU resume */
if (!pm_runtime_get_if_active(&gpu->pdev->dev))
return;
@@ -2145,9 +2154,9 @@ void a6xx_gmu_sysprof_setup(struct msm_gpu *gpu, bool force_on)
* 'Perfcounter select' register values are lost during IFPC collapse. To avoid that,
* use the currently unused perfcounter oob vote to block IFPC when sysprof is active
*/
- if (sysprof && !test_and_set_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status))
+ if (force_on && !test_and_set_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status))
a6xx_gmu_set_oob(gmu, GMU_OOB_PERFCOUNTER_SET);
- else if (!sysprof && test_and_clear_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status))
+ else if (!force_on && test_and_clear_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status))
a6xx_gmu_clear_oob(gmu, GMU_OOB_PERFCOUNTER_SET);
mutex_unlock(&gmu->lock);
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
index 09d76cdf059f..70c0024bdef4 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
@@ -2851,7 +2851,7 @@ const struct adreno_gpu_funcs a6xx_gpu_funcs = {
.create_private_vm = a6xx_create_private_vm,
.get_rptr = a6xx_get_rptr,
.progress = a6xx_progress,
- .sysprof_setup = a6xx_gmu_sysprof_setup,
+ .ifpc_update = a6xx_gmu_ifpc_update,
.perfcntr_configure = a6xx_perfcntr_configure,
},
.init = a6xx_gpu_init,
@@ -2922,7 +2922,7 @@ const struct adreno_gpu_funcs a7xx_gpu_funcs = {
.create_private_vm = a6xx_create_private_vm,
.get_rptr = a6xx_get_rptr,
.progress = a6xx_progress,
- .sysprof_setup = a6xx_gmu_sysprof_setup,
+ .ifpc_update = a6xx_gmu_ifpc_update,
.perfcntr_configure = a6xx_perfcntr_configure,
},
.init = a6xx_gpu_init,
@@ -2953,7 +2953,7 @@ const struct adreno_gpu_funcs a8xx_gpu_funcs = {
.create_private_vm = a6xx_create_private_vm,
.get_rptr = a6xx_get_rptr,
.progress = a8xx_progress,
- .sysprof_setup = a6xx_gmu_sysprof_setup,
+ .ifpc_update = a6xx_gmu_ifpc_update,
.perfcntr_configure = a6xx_perfcntr_configure,
.perfcntr_flush = a8xx_perfcntr_flush,
},
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
index c49301be504b..639e63c29f4b 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
@@ -298,7 +298,7 @@ void a6xx_gmu_clear_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state);
int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node);
int a6xx_gmu_wrapper_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node);
void a6xx_gmu_remove(struct a6xx_gpu *a6xx_gpu);
-void a6xx_gmu_sysprof_setup(struct msm_gpu *gpu, bool force_on);
+void a6xx_gmu_ifpc_update(struct msm_gpu *gpu, bool force_on);
void a6xx_preempt_init(struct msm_gpu *gpu);
void a6xx_preempt_hw_init(struct msm_gpu *gpu);
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index d27d54bdb7a7..2e1b1bea37ce 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -93,7 +93,7 @@ struct msm_gpu_funcs {
* for cmdstream that is buffered in this FIFO upstream of the CP fw.
*/
bool (*progress)(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
- void (*sysprof_setup)(struct msm_gpu *gpu, bool force_on);
+ void (*ifpc_update)(struct msm_gpu *gpu, bool force_on);
/* Configure perfcntr SELect regs: */
void (*perfcntr_configure)(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
diff --git a/drivers/gpu/drm/msm/msm_perfcntr.c b/drivers/gpu/drm/msm/msm_perfcntr.c
index ce65b1160955..7b4a6e49d3a7 100644
--- a/drivers/gpu/drm/msm/msm_perfcntr.c
+++ b/drivers/gpu/drm/msm/msm_perfcntr.c
@@ -259,8 +259,8 @@ sample_worker(struct kthread_work *work)
}
/* Inhibit IFPC while accessing registers: */
- if (gpu->funcs->sysprof_setup)
- gpu->funcs->sysprof_setup(gpu, true);
+ if (gpu->funcs->ifpc_update)
+ gpu->funcs->ifpc_update(gpu, true);
if (gpu->funcs->perfcntr_flush)
gpu->funcs->perfcntr_flush(gpu);
@@ -297,8 +297,8 @@ sample_worker(struct kthread_work *work)
}
/* Re-enable IFPC: */
- if (gpu->funcs->sysprof_setup)
- gpu->funcs->sysprof_setup(gpu, false);
+ if (gpu->funcs->ifpc_update)
+ gpu->funcs->ifpc_update(gpu, false);
smp_store_release(&stream->fifo.head, head);
wake_up_all(&stream->poll_wq);
diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c
index d307cdd17d90..43103b054ee7 100644
--- a/drivers/gpu/drm/msm/msm_submitqueue.c
+++ b/drivers/gpu/drm/msm/msm_submitqueue.c
@@ -43,8 +43,8 @@ int msm_context_set_sysprof(struct msm_context *ctx, struct msm_gpu *gpu, int sy
}
/* Some gpu families require additional setup for sysprof */
- if (gpu->funcs->sysprof_setup)
- gpu->funcs->sysprof_setup(gpu, false);
+ if (gpu->funcs->ifpc_update)
+ gpu->funcs->ifpc_update(gpu, false);
ctx->sysprof = sysprof;
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 10/19] drm/msm: Add cleanup help for disabling IFPC
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
` (8 preceding siblings ...)
2026-10-30 21:18 ` [PATCH v4 09/19] drm/msm: Rename sysprof_setup() Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:18 ` [PATCH v4 11/19] drm/msm/debugfs: Use cleanup.h for gpu state dump Rob Clark
` (7 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Sean Paul,
Konrad Dybcio, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Marijn Suijten, David Airlie, Simona Vetter, open list
Simplify error path cleanup for IFPC-disable critical sections.
Note that this moves where we re-enable IFPC until after the
wake_up_all(), but that is harmless.
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/msm_gpu.h | 4 ++++
drivers/gpu/drm/msm/msm_perfcntr.c | 7 +------
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index 2e1b1bea37ce..4e18c2427c69 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -381,6 +381,10 @@ msm_gpu_sysprof_no_ifpc(struct msm_gpu *gpu)
return refcount_read(&gpu->sysprof_active) > 1;
}
+DEFINE_GUARD(ifpc_disable, struct msm_gpu *,
+ if (_T->funcs->ifpc_update) _T->funcs->ifpc_update(_T, true),
+ if (_T->funcs->ifpc_update) _T->funcs->ifpc_update(_T, false));
+
/*
* The number of priority levels provided by drm gpu scheduler. The
* DRM_SCHED_PRIORITY_KERNEL priority level is treated specially in some
diff --git a/drivers/gpu/drm/msm/msm_perfcntr.c b/drivers/gpu/drm/msm/msm_perfcntr.c
index 7b4a6e49d3a7..995857273aca 100644
--- a/drivers/gpu/drm/msm/msm_perfcntr.c
+++ b/drivers/gpu/drm/msm/msm_perfcntr.c
@@ -259,8 +259,7 @@ sample_worker(struct kthread_work *work)
}
/* Inhibit IFPC while accessing registers: */
- if (gpu->funcs->ifpc_update)
- gpu->funcs->ifpc_update(gpu, true);
+ guard(ifpc_disable)(gpu);
if (gpu->funcs->perfcntr_flush)
gpu->funcs->perfcntr_flush(gpu);
@@ -296,10 +295,6 @@ sample_worker(struct kthread_work *work)
}
}
- /* Re-enable IFPC: */
- if (gpu->funcs->ifpc_update)
- gpu->funcs->ifpc_update(gpu, false);
-
smp_store_release(&stream->fifo.head, head);
wake_up_all(&stream->poll_wq);
}
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 11/19] drm/msm/debugfs: Use cleanup.h for gpu state dump
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
` (9 preceding siblings ...)
2026-10-30 21:18 ` [PATCH v4 10/19] drm/msm: Add cleanup help for disabling IFPC Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:38 ` sashiko-bot
2026-10-30 21:18 ` [PATCH v4 12/19] drm/msm: Inhibit IFPC for state snapshot Rob Clark
` (6 subsequent siblings)
17 siblings, 1 reply; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark,
Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul,
Marijn Suijten, David Airlie, Simona Vetter, open list
We need to move the gpu lock around a bit for the next patch. Use
scoped_guard/etc so we don't have to make the error path handling more
complicated.
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/msm_debugfs.c | 31 ++++++++++++-------------------
1 file changed, 12 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/msm/msm_debugfs.c b/drivers/gpu/drm/msm/msm_debugfs.c
index f12701e286ec..b0ddc02f67b5 100644
--- a/drivers/gpu/drm/msm/msm_debugfs.c
+++ b/drivers/gpu/drm/msm/msm_debugfs.c
@@ -70,43 +70,36 @@ static int msm_gpu_open(struct inode *inode, struct file *file)
struct drm_device *dev = inode->i_private;
struct msm_drm_private *priv = dev->dev_private;
struct msm_gpu *gpu = priv->gpu;
- struct msm_gpu_show_priv *show_priv;
int ret;
if (!gpu || !gpu->funcs->gpu_state_get)
return -ENODEV;
- show_priv = kmalloc_obj(*show_priv);
+ struct msm_gpu_show_priv *show_priv __free(kfree)
+ = kmalloc_obj(*show_priv);
if (!show_priv)
return -ENOMEM;
- ret = mutex_lock_interruptible(&gpu->lock);
- if (ret)
- goto free_priv;
-
- pm_runtime_get_sync(&gpu->pdev->dev);
- msm_gpu_hw_init(gpu);
- show_priv->state = gpu->funcs->gpu_state_get(gpu);
- pm_runtime_put_sync(&gpu->pdev->dev);
-
- mutex_unlock(&gpu->lock);
+ scoped_guard (pm_runtime_active, &gpu->pdev->dev) {
+ scoped_cond_guard (mutex_intr, return PTR_ERR(scope.lock), &gpu->lock) {
+ msm_gpu_hw_init(gpu);
+ show_priv->state = gpu->funcs->gpu_state_get(gpu);
+ }
+ }
if (IS_ERR(show_priv->state)) {
- ret = PTR_ERR(show_priv->state);
- goto free_priv;
+ return PTR_ERR(show_priv->state);
}
show_priv->dev = dev;
ret = single_open(file, msm_gpu_show, show_priv);
if (ret)
- goto free_priv;
+ return ret;
- return 0;
+ retain_and_null_ptr(show_priv);
-free_priv:
- kfree(show_priv);
- return ret;
+ return 0;
}
static const struct file_operations msm_gpu_fops = {
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 12/19] drm/msm: Inhibit IFPC for state snapshot
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
` (10 preceding siblings ...)
2026-10-30 21:18 ` [PATCH v4 11/19] drm/msm/debugfs: Use cleanup.h for gpu state dump Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:38 ` sashiko-bot
2026-10-30 21:18 ` [PATCH v4 13/19] drm/msm/adreno: add temporary __maybe_unused Rob Clark
` (5 subsequent siblings)
17 siblings, 1 reply; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark,
Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul,
Marijn Suijten, David Airlie, Simona Vetter, open list
We need the GPU to stay awake for this.
Tested-by: Alexander Koskovich <akoskovich@pm.me> # Milos (Nothing Phone 3a)
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/msm_debugfs.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/gpu/drm/msm/msm_debugfs.c b/drivers/gpu/drm/msm/msm_debugfs.c
index b0ddc02f67b5..224401b4ab12 100644
--- a/drivers/gpu/drm/msm/msm_debugfs.c
+++ b/drivers/gpu/drm/msm/msm_debugfs.c
@@ -81,6 +81,8 @@ static int msm_gpu_open(struct inode *inode, struct file *file)
return -ENOMEM;
scoped_guard (pm_runtime_active, &gpu->pdev->dev) {
+ guard(ifpc_disable)(gpu);
+
scoped_cond_guard (mutex_intr, return PTR_ERR(scope.lock), &gpu->lock) {
msm_gpu_hw_init(gpu);
show_priv->state = gpu->funcs->gpu_state_get(gpu);
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 13/19] drm/msm/adreno: add temporary __maybe_unused
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
` (11 preceding siblings ...)
2026-10-30 21:18 ` [PATCH v4 12/19] drm/msm: Inhibit IFPC for state snapshot Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:18 ` [PATCH v4 15/19] drm/msm/a8xx: devcoredump state capture support Rob Clark
` (4 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Sean Paul,
Konrad Dybcio, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Marijn Suijten, David Airlie, Simona Vetter, open list
To keep this series bisectable. This will be removed once there maybe-
unused protect tables are gone.
Tested-by: Alexander Koskovich <akoskovich@pm.me> # Milos (Nothing Phone 3a)
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/adreno_gpu.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
index 6d6e4c7cf919..c43efb85fde7 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
@@ -168,7 +168,7 @@ struct adreno_protect {
};
#define DECLARE_ADRENO_PROTECT(name, __count_max) \
-static const struct adreno_protect name = { \
+static const struct adreno_protect name __maybe_unused = { \
.regs = name ## _regs, \
.count = ARRAY_SIZE(name ## _regs), \
.count_max = __count_max, \
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 15/19] drm/msm/a8xx: devcoredump state capture support
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
` (12 preceding siblings ...)
2026-10-30 21:18 ` [PATCH v4 13/19] drm/msm/adreno: add temporary __maybe_unused Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:46 ` sashiko-bot
2026-10-30 21:18 ` [PATCH v4 16/19] drm/msm/a8xx: Add all CP_PROTECT_PIPE to pwrup reglist Rob Clark
` (3 subsequent siblings)
17 siblings, 1 reply; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark,
Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul,
Marijn Suijten, David Airlie, Simona Vetter, Konrad Dybcio,
open list
This attempts to make it easier to compare to kgsl, the downstream
driver, to make it easier to spot bugs or missing things. Such as
re-using function names, etc.
Co-developed-by: Akhil P Oommen <akhilpo@oss.qualcomm.com>
Tested-by: Alexander Koskovich <akoskovich@pm.me> # Milos (Nothing Phone 3a)
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/Makefile | 4 +-
drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 7 +
drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 5 +
drivers/gpu/drm/msm/adreno/a8xx_gpu_state.c | 1401 +++++++++++++++++
drivers/gpu/drm/msm/adreno/adreno_snapshot.h | 209 +++
drivers/gpu/drm/msm/registers/adreno/a6xx.xml | 4 +
6 files changed, 1629 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/msm/adreno/a8xx_gpu_state.c
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 6196a325b6f1..042fd8fd3f57 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -31,7 +31,9 @@ adreno-y := \
adreno-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o \
-adreno-$(CONFIG_DRM_MSM_GPU_STATE) += adreno/a6xx_gpu_state.o
+adreno-$(CONFIG_DRM_MSM_GPU_STATE) += \
+ adreno/a6xx_gpu_state.o \
+ adreno/a8xx_gpu_state.o \
msm-display-$(CONFIG_DRM_MSM_HDMI) += \
hdmi/hdmi.o \
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
index 70c0024bdef4..cfe9ef7ae652 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
@@ -2946,9 +2946,16 @@ const struct adreno_gpu_funcs a8xx_gpu_funcs = {
.active_ring = a6xx_active_ring,
.irq = a8xx_irq,
.destroy = a6xx_destroy,
+#if defined(CONFIG_DRM_MSM_GPU_STATE)
+ .show = a8xx_show,
+#endif
.gpu_busy = a8xx_gpu_busy,
.gpu_get_freq = a6xx_gmu_get_freq,
.gpu_set_freq = a6xx_gpu_set_freq,
+#if defined(CONFIG_DRM_MSM_GPU_STATE)
+ .gpu_state_get = a8xx_gpu_state_get,
+ .gpu_state_put = a8xx_gpu_state_put,
+#endif
.create_vm = a6xx_create_vm,
.create_private_vm = a6xx_create_private_vm,
.get_rptr = a6xx_get_rptr,
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
index 639e63c29f4b..18656ac339a9 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
@@ -369,4 +369,9 @@ void a8xx_preempt_irq(struct msm_gpu *gpu);
bool a8xx_progress(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
void a8xx_perfcntr_flush(struct msm_gpu *gpu);
void a8xx_recover(struct msm_gpu *gpu);
+void a8xx_show(struct msm_gpu *gpu, struct msm_gpu_state *state,
+ struct drm_printer *p);
+struct msm_gpu_state *a8xx_gpu_state_get(struct msm_gpu *gpu);
+int a8xx_gpu_state_put(struct msm_gpu_state *state);
+
#endif /* __A6XX_GPU_H__ */
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu_state.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu_state.c
new file mode 100644
index 000000000000..3c696244cd66
--- /dev/null
+++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu_state.c
@@ -0,0 +1,1401 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include "msm_gem.h"
+#include "a6xx_gpu.h"
+#include "a6xx_gmu.h"
+#include "adreno_snapshot.h"
+#include "adreno_gen8_snapshot.h"
+
+#define NUM_SLICES(gpu) (hweight32((gpu)->slice_mask))
+
+static inline int next_slice(int slice, u32 slice_mask)
+{
+ for (; slice < fls(slice_mask); slice++)
+ if (BIT(slice) & slice_mask)
+ return slice;
+
+ return slice;
+}
+
+#define NEXT_SLICE(_slice, _region, _slice_mask) \
+ ((_region == UNSLICE) ? fls(_slice_mask) : next_slice(_slice, _slice_mask))
+
+#define FIRST_SLICE(_region, _slice_mask) \
+ ((_region == UNSLICE) ? 0 : NEXT_SLICE(0, _region, _slice_mask))
+
+#define FOR_EACH_SLICE(_slice, _region, _slice_mask) \
+ for (_slice = FIRST_SLICE(_region, _slice_mask); \
+ _slice < fls(_slice_mask); \
+ _slice = NEXT_SLICE(++(_slice), _region, _slice_mask))
+
+/* Use SLICE_ID to access the region (0 for unsliced) */
+#define SLICE_ID(region, j) ((region == SLICE) ? j : 0)
+
+/*
+ * Use HEADER_SLICE_ID to specify the slice in the section header (UINT_MAX for unsliced).
+ * This allows snapshot parsers to differentiate between slice ID 0 and unsliced regions.
+ */
+#define HEADER_SLICE_ID(region, j) ((region == SLICE) ? j : UINT_MAX)
+
+#define GEN8_CP_APERTURE_REG_VAL_NS(_sliceid, _pipe, _cluster, _context) ( \
+ A8XX_CP_APERTURE_CNTL_HOST_SLICEID(_sliceid) | \
+ A8XX_CP_APERTURE_CNTL_HOST_PIPEID(_pipe) | \
+ A8XX_CP_APERTURE_CNTL_HOST_CLUSTERID(_cluster) | \
+ A8XX_CP_APERTURE_CNTL_HOST_CONTEXTID3D(_context) | \
+ 0)
+
+#define GEN8_CP_APERTURE_REG_VAL(_sliceid, _pipe, _cluster, _context) ( \
+ A8XX_CP_APERTURE_CNTL_HOST_USESLICEID | \
+ GEN8_CP_APERTURE_REG_VAL_NS(_sliceid, _pipe, _cluster, _context))
+
+#define GEN8_SP_READ_SEL_VAL(_contextid, _sliceid, _location, _pipe, _statetype, _usptp, _sptp) ( \
+ A7XX_SP_READ_SEL_CONTEXT(_contextid) | \
+ A7XX_SP_READ_SEL_SLICE(_sliceid) | \
+ A7XX_SP_READ_SEL_LOCATION(_location) | \
+ A7XX_SP_READ_SEL_PIPE(_pipe) | \
+ A7XX_SP_READ_SEL_STATETYPE(_statetype) | \
+ A7XX_SP_READ_SEL_USPTP(_usptp) | \
+ A7XX_SP_READ_SEL_SPTP(_sptp) | \
+ 0)
+
+/*
+ * Allocate 4MB for the crashdumper scratch region - 2MB for the script and
+ * the rest for the data.
+ *
+ * Note gen8_snapshot_indexed_registers() is worst case, needing separate
+ * CD_WRITE + CD_READ per reg, 0xa000 times (so script is ~1.2MB)
+ *
+ * TODO we could perhaps be clever and fill in the out addrs in a 2nd pass
+ * to accomodate invocations that need larger script and smaller data, like
+ * indexed regs.
+ */
+#define A8XX_CD_SIZE SZ_4M
+#define A8XX_CD_DATA_OFFSET (2 * SZ_1M)
+#define A8XX_CD_DATA_SIZE (A8XX_CD_SIZE - A8XX_CD_DATA_OFFSET)
+
+static int a8xx_crashdumper_init(struct msm_gpu *gpu,
+ struct adreno_crashdumper *dumper)
+{
+ dumper->ptr = msm_gem_kernel_new(gpu->dev,
+ A8XX_CD_SIZE, MSM_BO_WC, gpu->vm,
+ &dumper->bo, &dumper->iova);
+
+ if (!IS_ERR(dumper->ptr)) {
+ msm_gem_object_set_name(dumper->bo, "crashdump");
+ dumper->data_offset = A8XX_CD_DATA_OFFSET;
+ }
+
+ return PTR_ERR_OR_ZERO(dumper->ptr);
+}
+
+static inline int __cd_run(struct msm_gpu *gpu, struct adreno_crashdumper *dumper)
+{
+ u32 val;
+ int ret;
+
+ if (IS_ERR_OR_NULL(dumper->ptr))
+ return -EINVAL;
+
+ /* Make sure all pending memory writes are posted */
+ wmb();
+
+ gpu_write64(gpu, REG_A8XX_CP_CRASH_DUMP_SCRIPT_BASE, dumper->iova);
+
+ gpu_write(gpu, REG_A8XX_CP_CRASH_DUMP_CNTL, 1);
+
+ /* NOTE: kgsl uses 500ms timeout */
+ ret = gpu_poll_timeout(gpu, REG_A8XX_CP_CRASH_DUMP_STATUS, val,
+ val & 0x02, 100, 100000);
+
+ gpu_write(gpu, REG_A8XX_CP_CRASH_DUMP_CNTL, 0);
+
+ drm_WARN_ONCE(gpu->dev, ret, "crashdumper timed out: status=%08x (%d)\n", val, ret);
+
+ return ret;
+}
+
+static inline bool __cd_check_overflow(struct cd_script *p)
+{
+ if (WARN_ON_ONCE(p->in > (u64 *)(p->dumper->ptr + A8XX_CD_DATA_OFFSET)))
+ return false;
+ if (WARN_ON_ONCE(p->out > p->dumper->iova + A8XX_CD_SIZE))
+ return false;
+ return true;
+}
+
+static const struct gen8_snapshot_block_list *
+block_list(struct msm_gpu *gpu)
+{
+ return to_adreno_gpu(gpu)->info->snapshot_block_list;
+}
+
+/* The GMU is ioremapped separately, matching how it is described in dt.
+ * However the snapshot tables generated from hw spec do not take into
+ * account dt, and consider it as yet another sub-block of the GPU. This
+ * helper helps to bridge the worlds.
+ */
+static uint32_t reg_read(struct msm_gpu *gpu, uint32_t reg)
+{
+ struct a6xx_gmu *gmu = &to_a6xx_gpu(to_adreno_gpu(gpu))->gmu;
+ if ((reg << 2) >= gmu->mmio_offset)
+ return gmu_read(gmu, reg);
+ return gpu_read(gpu, reg);
+}
+
+static void adreno_snapshot_registers_v2(struct msm_gpu *gpu,
+ struct adreno_gpu_state *state,
+ const u32 *ptr)
+{
+ int k, count;
+
+ /* Figure out how many registers we are going to dump */
+ count = adreno_snapshot_regs_count(ptr);
+
+ snapshot_sect (state, REGS_V2, 4 * count, {}) {
+ for (; ptr[0] != UINT_MAX; ptr += 2) {
+ u32 cnt = REG_COUNT(ptr);
+
+ if (cnt == 1) {
+ snapshot_sect_u32(§, BIT(31) | ptr[0]);
+ } else {
+ snapshot_sect_u32(§, ptr[0]);
+ snapshot_sect_u32(§, cnt);
+ }
+
+ for (k = ptr[0]; k <= ptr[1]; k++) {
+ snapshot_sect_u32(§, reg_read(gpu, k));
+ }
+ }
+ }
+}
+
+static void snapshot_mvc_regs_v3_subheader(struct adreno_snapshot_sect *sect, const u32 *ptr)
+{
+ /*
+ * Note this might look like adreno_snapshot_registers_v2(),
+ * but looks are deceiving. The meaning of b31 is inverted
+ * and the 2nd dword is end-offset instead of count.
+ */
+ if (REG_COUNT(ptr) == 1) {
+ snapshot_sect_u32(sect, ptr[0]);
+ } else {
+ snapshot_sect_u32(sect, BIT(31) | ptr[0]);
+ snapshot_sect_u32(sect, ptr[1]);
+ }
+}
+
+static void gen8_legacy_snapshot_registers(struct msm_gpu *gpu,
+ struct adreno_gpu_state *state,
+ const struct gen8_reg_list *regs,
+ u32 slice_id)
+{
+ int count;
+
+ /* Figure out how many registers we are going to dump */
+ count = adreno_snapshot_regs_count(regs->regs);
+
+ snapshot_sect (state, MVC_V3, 4 * count, {
+ .ctxt_id = 0,
+ .cluster_id = CLUSTER_NONE,
+ .pipe_id = PIPE_NONE,
+ .location_id = UINT_MAX,
+ .sp_id = UINT_MAX,
+ .usptp_id = UINT_MAX,
+ .slice_id = HEADER_SLICE_ID(regs->slice_region, slice_id),
+ }) {
+ unsigned long flags;
+
+ if (regs->sel)
+ gpu_write(gpu, regs->sel->host_reg, regs->sel->val);
+
+ if (regs->slice_region) {
+ u32 val = GEN8_CP_APERTURE_REG_VAL(slice_id, 0, 0, 0);
+ a8xx_aperture_acquire_raw(gpu, val, &flags);
+ }
+
+ /* Make sure the previous writes are posted before reading */
+ mb();
+
+ for (const u32 *ptr = regs->regs; ptr[0] != UINT_MAX; ptr += 2) {
+ snapshot_mvc_regs_v3_subheader(§, ptr);
+
+ for (int k = ptr[0]; k <= ptr[1]; k++) {
+ snapshot_sect_u32(§, reg_read(gpu, k));
+ }
+ }
+
+ if (regs->slice_region)
+ a8xx_aperture_release(gpu, flags);
+ }
+}
+
+static bool gen8_reglist_snapshot(struct msm_gpu *gpu, struct adreno_gpu_state *state,
+ struct adreno_crashdumper *dumper)
+{
+ const struct gen8_snapshot_block_list *b = block_list(gpu);
+ u32 slice_mask = to_a6xx_gpu(to_adreno_gpu(gpu))->slice_mask;
+
+ if (!dumper) {
+ for (u32 i = 0; i < b->num_reg_list; i++) {
+ const struct gen8_reg_list *regs = &b->reg_list[i];
+ u32 j;
+
+ FOR_EACH_SLICE (j, regs->slice_region, slice_mask) {
+ u32 slice_id = SLICE_ID(regs->slice_region, j);
+ gen8_legacy_snapshot_registers(gpu, state, regs, slice_id);
+ }
+
+ /*
+ * The downstream reglist contains registers in other memory regions
+ * (cx_misc/cx_mem and cx_dbgc) and we need to plumb through their
+ * offsets and map them to read them on the CPU. For now only read the
+ * first two regions.
+ */
+ if (i == 1)
+ break;
+ }
+ return true;
+ }
+
+ for (u32 i = 0; i < b->num_reg_list; i++) {
+ const struct gen8_reg_list *regs = &b->reg_list[i];
+ u32 j;
+
+ FOR_EACH_SLICE (j, regs->slice_region, slice_mask) {
+ u32 slice_id = SLICE_ID(regs->slice_region, j);
+ struct cd_script p = CD_INIT(dumper);
+
+ CD_WRITE(&p, REG_A8XX_CP_APERTURE_CNTL_CD,
+ GEN8_CP_APERTURE_REG_VAL(j, 0, 0, 0));
+
+ /* Program the SEL_CNTL_CD register appropriately */
+ if (regs->sel)
+ CD_WRITE(&p, regs->sel->cd_reg, regs->sel->val);
+
+ for (const u32 *ptr = regs->regs; ptr[0] != UINT_MAX; ptr += 2) {
+ u32 count = REG_COUNT(ptr);
+
+ CD_READ(&p, ptr[0], count);
+ }
+
+ /* Marker for end of script */
+ CD_FINISH(&p);
+
+ /* Try to run the crash dumper and bail if it times out */
+ if (CD_RUN(&p, gpu))
+ return false;
+
+ u32 count = adreno_snapshot_regs_count(regs->regs);
+ snapshot_sect (state, MVC_V3, 4 * count, {
+ .ctxt_id = 0,
+ .cluster_id = CLUSTER_NONE,
+ .pipe_id = PIPE_NONE,
+ .location_id = UINT_MAX,
+ .sp_id = UINT_MAX,
+ .usptp_id = UINT_MAX,
+ .slice_id = HEADER_SLICE_ID(regs->slice_region, slice_id),
+ }) {
+ const u32 *src = dumper->ptr + A8XX_CD_DATA_OFFSET;
+
+ for (const u32 *ptr = regs->regs; ptr[0] != UINT_MAX; ptr += 2) {
+ u32 count = REG_COUNT(ptr);
+
+ snapshot_mvc_regs_v3_subheader(§, ptr);
+ snapshot_sect_data(§, src, count * sizeof(u32));
+
+ src += count;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool gen8_snapshot_indexed_registers(
+ struct msm_gpu *gpu, struct adreno_gpu_state *state, struct adreno_crashdumper *dumper,
+ u32 index, u32 data, u32 start, u32 count, u32 pipe_id, u32 slice_id)
+{
+ struct adreno_snapshot_indexed_regs_v2 hdr = {
+ .index_reg = index,
+ .data_reg = data,
+ .count = count,
+ .start = start,
+ .pipe_id = pipe_id,
+ .slice_id = slice_id,
+ };
+ u32 s = (slice_id == UINT_MAX) ? 0 : slice_id;
+
+ if (!dumper) {
+ snapshot_sect (state, INDEXED_REGS_V2, count * sizeof(u32), hdr) {
+ unsigned long flags;
+
+ u32 val = GEN8_CP_APERTURE_REG_VAL(s, pipe_id, 0, 0);
+ a8xx_aperture_acquire_raw(gpu, val, &flags);
+
+ for (int i = 0; i < count; i++) {
+ /* Write the address register */
+ gpu_write(gpu, index, start + i);
+
+ /* Make sure the write finishes */
+ wmb();
+
+ snapshot_sect_u32(§, reg_read(gpu, data));
+
+ /* Make sure the read finishes */
+ rmb();
+ }
+
+ a8xx_aperture_release(gpu, flags);
+ }
+
+ return true;
+ }
+
+ struct cd_script p = CD_INIT(dumper);
+
+ CD_WRITE(&p, REG_A8XX_CP_APERTURE_CNTL_CD,
+ GEN8_CP_APERTURE_REG_VAL(s, pipe_id, 0, 0));
+
+ for (int i = 0; i < count; i++) {
+ /* Write the address register */
+ CD_WRITE(&p, index, start + i);
+ CD_READ(&p, data, 1);
+ }
+
+ /* Marker for end of script */
+ CD_FINISH(&p);
+
+ /* Try to run the crash dumper and bail if it times out */
+ if (CD_RUN(&p, gpu))
+ return false;
+
+ snapshot_sect (state, INDEXED_REGS_V2, count * sizeof(u32), hdr) {
+ const u32 *src = dumper->ptr + A8XX_CD_DATA_OFFSET;
+ snapshot_sect_data(§, src, count * sizeof(u32));
+ }
+
+ return true;
+}
+
+static bool gen8_snapshot_cp_indexed_regs(struct msm_gpu *gpu,
+ struct adreno_gpu_state *state,
+ struct adreno_crashdumper *dumper)
+{
+ const struct gen8_snapshot_block_list *b = block_list(gpu);
+
+ for (int i = 0; i < b->index_registers_len; i++) {
+ bool ret = gen8_snapshot_indexed_registers(gpu, state, dumper,
+ b->index_registers[i].addr,
+ b->index_registers[i].data, 0,
+ b->index_registers[i].size,
+ b->index_registers[i].pipe_id, UINT_MAX);
+ if (!ret)
+ return false;
+ }
+
+ return true;
+}
+
+static bool gen8_snapshot_mempool(struct msm_gpu *gpu,
+ struct adreno_gpu_state *state,
+ struct adreno_crashdumper *dumper)
+{
+ const struct gen8_snapshot_block_list *b = block_list(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(to_adreno_gpu(gpu));
+ u32 slice_mask = a6xx_gpu->slice_mask;
+ u32 first_slice = a8xx_get_first_slice(a6xx_gpu);
+ bool ret = true;
+
+ for (int i = 0; i < b->mempool_index_registers_len && ret; i++) {
+ const struct gen8_cp_indexed_reg *cp_indexed_reg =
+ &b->mempool_index_registers[i];
+ unsigned long flags;
+ u32 j;
+
+ FOR_EACH_SLICE (j, cp_indexed_reg->slice_region, slice_mask) {
+ u32 pipe_id = cp_indexed_reg->pipe_id;
+
+ /* Set CP_CHICKEN_DBG[StabilizeMVC] to stabilize it while dumping */
+ a8xx_aperture_acquire_raw(gpu,
+ GEN8_CP_APERTURE_REG_VAL_NS(first_slice, pipe_id, 0, 0),
+ &flags);
+ gpu_rmw(gpu, REG_A8XX_CP_CHICKEN_DBG_PIPE, BIT(2), BIT(2));
+ a8xx_aperture_release(gpu, flags);
+
+ a8xx_aperture_acquire_raw(gpu,
+ GEN8_CP_APERTURE_REG_VAL(j, pipe_id, 0, 0),
+ &flags);
+ gpu_rmw(gpu, REG_A8XX_CP_SLICE_CHICKEN_DBG_PIPE, BIT(2), BIT(2));
+ a8xx_aperture_release(gpu, flags);
+
+ ret = gen8_snapshot_indexed_registers(gpu, state, dumper,
+ cp_indexed_reg->addr,
+ cp_indexed_reg->data, 0,
+ cp_indexed_reg->size,
+ cp_indexed_reg->pipe_id,
+ HEADER_SLICE_ID(cp_indexed_reg->slice_region, j));
+
+ /* Reset CP_CHICKEN_DBG[StabilizeMVC] once we are done */
+ a8xx_aperture_acquire_raw(gpu,
+ GEN8_CP_APERTURE_REG_VAL_NS(first_slice, pipe_id, 0, 0),
+ &flags);
+ gpu_rmw(gpu, REG_A8XX_CP_CHICKEN_DBG_PIPE, BIT(2), 0);
+ a8xx_aperture_release(gpu, flags);
+
+ a8xx_aperture_acquire_raw(gpu,
+ GEN8_CP_APERTURE_REG_VAL(j, pipe_id, 0, 0),
+ &flags);
+ gpu_rmw(gpu, REG_A8XX_CP_SLICE_CHICKEN_DBG_PIPE, BIT(2), 0);
+ a8xx_aperture_release(gpu, flags);
+
+ if (!ret)
+ break;
+ }
+ }
+
+ /* Clear aperture register */
+ a8xx_aperture_clear(gpu);
+
+ return ret;
+}
+
+static void gen8_legacy_snapshot_mvc(struct msm_gpu *gpu,
+ struct adreno_gpu_state *state,
+ const struct gen8_cluster_registers *cluster,
+ u32 slice_id)
+{
+ int count;
+
+ /* Figure out how many registers we are going to dump */
+ count = adreno_snapshot_regs_count(cluster->regs);
+
+ snapshot_sect (state, MVC_V3, 4 * count, {
+ .ctxt_id = (cluster->context_id == STATE_FORCE_CTXT_1) ? 1 : 0,
+ .cluster_id = cluster->cluster_id,
+ .pipe_id = cluster->pipe_id,
+ .location_id = UINT_MAX,
+ .sp_id = UINT_MAX,
+ .usptp_id = UINT_MAX,
+ .slice_id = HEADER_SLICE_ID(cluster->slice_region, slice_id),
+ }) {
+ unsigned long flags;
+
+ /*
+ * Set the AHB control for the Host to read from the
+ * cluster/context for this iteration.
+ */
+ u32 val = GEN8_CP_APERTURE_REG_VAL(
+ slice_id, cluster->pipe_id,
+ cluster->cluster_id, cluster->context_id);
+ a8xx_aperture_acquire_raw(gpu, val, &flags);
+
+ if (cluster->sel)
+ gpu_write(gpu, cluster->sel->host_reg, cluster->sel->val);
+
+ /* Make sure the previous writes are posted before reading */
+ mb();
+
+ for (const u32 *ptr = cluster->regs; ptr[0] != UINT_MAX; ptr += 2) {
+ snapshot_mvc_regs_v3_subheader(§, ptr);
+
+ for (int k = ptr[0]; k <= ptr[1]; k++) {
+ snapshot_sect_u32(§, reg_read(gpu, k));
+ }
+ }
+
+ a8xx_aperture_release(gpu, flags);
+ }
+}
+
+static bool gen8_snapshot_mvc_regs(
+ struct msm_gpu *gpu, struct adreno_gpu_state *state, struct adreno_crashdumper *dumper,
+ const struct gen8_cluster_registers *clusters, size_t num_cluster)
+{
+ u32 slice_mask = to_a6xx_gpu(to_adreno_gpu(gpu))->slice_mask;
+
+ if (!dumper) {
+ for (int i = 0; i < num_cluster; i++) {
+ const struct gen8_cluster_registers *cluster = &clusters[i];
+ u32 j;
+
+ FOR_EACH_SLICE (j, cluster->slice_region, slice_mask) {
+ u32 slice_id = SLICE_ID(cluster->slice_region, j);
+ gen8_legacy_snapshot_mvc(gpu, state, cluster, slice_id);
+ }
+ }
+ return true;
+ }
+
+ for (int i = 0; i < num_cluster; i++) {
+ const struct gen8_cluster_registers *cluster = &clusters[i];
+ u32 j;
+
+ FOR_EACH_SLICE (j, cluster->slice_region, slice_mask) {
+ u32 slice_id = SLICE_ID(cluster->slice_region, j);
+ struct cd_script p = CD_INIT(dumper);
+
+ CD_WRITE(&p, REG_A8XX_CP_APERTURE_CNTL_CD,
+ GEN8_CP_APERTURE_REG_VAL(j, cluster->pipe_id,
+ cluster->cluster_id,
+ cluster->context_id));
+
+ if (cluster->sel)
+ CD_WRITE(&p, cluster->sel->cd_reg, cluster->sel->val);
+
+ for (const u32 *ptr = cluster->regs; ptr[0] != UINT_MAX; ptr += 2) {
+ CD_READ(&p, ptr[0], REG_COUNT(ptr));
+ }
+
+ /* Marker for end of script */
+ CD_FINISH(&p);
+
+ /* Try to run the crash dumper and bail if it times out */
+ if (CD_RUN(&p, gpu))
+ return false;
+
+ u32 count = adreno_snapshot_regs_count(cluster->regs);
+
+ snapshot_sect (state, MVC_V3, 4 * count, {
+ .ctxt_id = (cluster->context_id == STATE_FORCE_CTXT_1) ? 1 : 0,
+ .cluster_id = cluster->cluster_id,
+ .pipe_id = cluster->pipe_id,
+ .location_id = UINT_MAX,
+ .sp_id = UINT_MAX,
+ .usptp_id = UINT_MAX,
+ .slice_id = HEADER_SLICE_ID(cluster->slice_region, slice_id),
+ }) {
+ const u32 *src = dumper->ptr + A8XX_CD_DATA_OFFSET;
+
+ for (const u32 *ptr = cluster->regs; ptr[0] != UINT_MAX; ptr += 2) {
+ u32 count = REG_COUNT(ptr);
+
+ snapshot_mvc_regs_v3_subheader(§, ptr);
+ snapshot_sect_data(§, src, count * sizeof(u32));
+
+ src += count;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+static u32 gen8_read_dbgahb(struct msm_gpu *gpu, u32 regbase, u32 reg)
+{
+ return gpu_read(gpu, REG_A7XX_SP_AHB_READ_APERTURE + reg - regbase);
+}
+
+static void gen8_legacy_snapshot_cluster_dbgahb(
+ struct msm_gpu *gpu, struct adreno_gpu_state *state,
+ const struct gen8_sptp_cluster_registers *cluster,
+ u32 sp, u32 usptp, u32 slice_id)
+{
+ int count;
+
+ /* Figure out how many registers we are going to dump */
+ count = adreno_snapshot_regs_count(cluster->regs);
+
+ snapshot_sect (state, MVC_V3, 4 * count, {
+ .ctxt_id = cluster->context_id,
+ .cluster_id = cluster->cluster_id,
+ .pipe_id = cluster->pipe_id,
+ .location_id = cluster->location_id,
+ .sp_id = sp,
+ .usptp_id = usptp,
+ .slice_id = HEADER_SLICE_ID(cluster->slice_region, slice_id),
+ }) {
+ u32 read_sel = GEN8_SP_READ_SEL_VAL(
+ cluster->context_id, slice_id, cluster->location_id,
+ cluster->pipe_id, cluster->statetype, usptp, sp);
+
+ gpu_write(gpu, REG_A7XX_SP_READ_SEL, read_sel);
+
+ /*
+ * An explicit barrier is needed so that reads do not happen before
+ * the register write.
+ */
+ mb();
+
+ for (const u32 *ptr = cluster->regs; ptr[0] != UINT_MAX; ptr += 2) {
+ snapshot_mvc_regs_v3_subheader(§, ptr);
+
+ for (int k = ptr[0]; k <= ptr[1]; k++) {
+ u32 val = gen8_read_dbgahb(gpu, cluster->regbase, k);
+ snapshot_sect_u32(§, val);
+ }
+ }
+ }
+}
+
+static bool gen8_snapshot_dbgahb_regs(struct msm_gpu *gpu,
+ struct adreno_gpu_state *state,
+ struct adreno_crashdumper *dumper)
+{
+ const struct gen8_snapshot_block_list *b = block_list(gpu);
+ u32 slice_mask = to_a6xx_gpu(to_adreno_gpu(gpu))->slice_mask;
+
+ if (!dumper) {
+ for (int i = 0; i < b->num_sptp_clusters; i++) {
+ const struct gen8_sptp_cluster_registers *cluster =
+ &b->sptp_clusters[i];
+
+ for (u32 sp = 0; sp < cluster->num_sps; sp++) {
+ for (u32 usptp = 0; usptp < cluster->num_usptps; usptp++) {
+ u32 j;
+
+ FOR_EACH_SLICE (j, cluster->slice_region, slice_mask) {
+ u32 slice_id = SLICE_ID(cluster->slice_region, j);
+
+ gen8_legacy_snapshot_cluster_dbgahb(
+ gpu, state, cluster,
+ sp, usptp, slice_id);
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ for (int i = 0; i < b->num_sptp_clusters; i++) {
+ const struct gen8_sptp_cluster_registers *cluster =
+ &b->sptp_clusters[i];
+
+ for (u32 sp = 0; sp < cluster->num_sps; sp++) {
+ for (u32 usptp = 0; usptp < cluster->num_usptps; usptp++) {
+ u32 j;
+
+ FOR_EACH_SLICE (j, cluster->slice_region, slice_mask) {
+ u32 slice_id = SLICE_ID(cluster->slice_region, j);
+
+ /* Build the crash script */
+ struct cd_script p = CD_INIT(dumper);
+
+ u32 read_sel = GEN8_SP_READ_SEL_VAL(
+ cluster->context_id, slice_id,
+ cluster->location_id, cluster->pipe_id,
+ cluster->statetype, usptp, sp);
+
+ /* Program the aperture */
+ CD_WRITE(&p, REG_A7XX_SP_READ_SEL, read_sel);
+
+ for (const u32 *ptr = cluster->regs; ptr[0] != UINT_MAX; ptr += 2) {
+ u32 count = REG_COUNT(ptr);
+ u32 reg = REG_A7XX_SP_AHB_READ_APERTURE + ptr[0] - cluster->regbase;
+
+ CD_READ(&p, reg, count);
+ }
+
+ /* Marker for end of script */
+ CD_FINISH(&p);
+
+ /* Try to run the crash dumper and bail if it times out */
+ if (CD_RUN(&p, gpu))
+ return false;
+
+ u32 count = adreno_snapshot_regs_count(cluster->regs);
+ snapshot_sect (state, MVC_V3, 4 * count, {
+ .ctxt_id = cluster->context_id,
+ .cluster_id = cluster->cluster_id,
+ .pipe_id = cluster->pipe_id,
+ .location_id = cluster->location_id,
+ .sp_id = sp,
+ .usptp_id = usptp,
+ .slice_id = HEADER_SLICE_ID(cluster->slice_region, slice_id),
+ }) {
+ const u32 *src = dumper->ptr + A8XX_CD_DATA_OFFSET;
+
+ for (const u32 *ptr = cluster->regs; ptr[0] != UINT_MAX; ptr += 2) {
+ u32 count = REG_COUNT(ptr);
+
+ snapshot_mvc_regs_v3_subheader(§, ptr);
+ snapshot_sect_data(§, src, count * sizeof(u32));
+
+ src += count;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static void gen8_legacy_snapshot_shader(struct msm_gpu *gpu, struct adreno_gpu_state *state,
+ const struct gen8_shader_block *block,
+ u32 sp, u32 ctxt, u32 slice_id)
+{
+ snapshot_sect (state, SHADER_V3, block->size * sizeof(u32), {
+ .type = block->statetype,
+ .slice_id = HEADER_SLICE_ID(block->slice_region, slice_id),
+ .sp_index = sp,
+ .usptp = block->usptp_id,
+ .pipe_id = block->pipeid,
+ .location = block->location,
+ .ctxt_id = ctxt,
+ .size = block->size,
+ }) {
+ u32 read_sel = GEN8_SP_READ_SEL_VAL(
+ ctxt, slice_id, block->location, block->pipeid,
+ block->statetype, block->usptp_id, sp);
+
+ gpu_write(gpu, REG_A7XX_SP_READ_SEL, read_sel);
+
+ /*
+ * An explicit barrier is needed so that reads do not happen before
+ * the register write.
+ */
+ mb();
+
+ for (int i = 0; i < block->size; i++) {
+ u32 val = gpu_read(gpu, REG_A7XX_SP_AHB_READ_APERTURE + i);
+ snapshot_sect_u32(§, val);
+ }
+ }
+}
+
+static bool gen8_snapshot_shader(struct msm_gpu *gpu,
+ struct adreno_gpu_state *state,
+ struct adreno_crashdumper *dumper)
+{
+ const struct gen8_snapshot_block_list *b = block_list(gpu);
+ u32 slice_mask = to_a6xx_gpu(to_adreno_gpu(gpu))->slice_mask;
+
+ if (!dumper) {
+ for (int i = 0; i < b->num_shader_blocks; i++) {
+ const struct gen8_shader_block *block = &b->shader_blocks[i];
+ u32 j;
+
+ FOR_EACH_SLICE (j, block->slice_region, slice_mask) {
+ u32 slice_id = SLICE_ID(block->slice_region, j);
+
+ for (u32 sp = 0; sp < block->num_sps; sp++) {
+ for (u32 ctxt = 0; ctxt < block->num_ctx; ctxt++) {
+ gen8_legacy_snapshot_shader(
+ gpu, state, block, sp, ctxt, slice_id);
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ for (int i = 0; i < b->num_shader_blocks; i++) {
+ const struct gen8_shader_block *block = &b->shader_blocks[i];
+ u32 j;
+
+ FOR_EACH_SLICE (j, block->slice_region, slice_mask) {
+ u32 slice_id = SLICE_ID(block->slice_region, j);
+
+ for (u32 sp = 0; sp < block->num_sps; sp++) {
+ for (u32 ctxt = 0; ctxt < block->num_ctx; ctxt++) {
+ /* Build the crash script */
+ struct cd_script p = CD_INIT(dumper);
+
+ u32 read_sel = GEN8_SP_READ_SEL_VAL(
+ ctxt, slice_id, block->location, block->pipeid,
+ block->statetype, block->usptp_id, sp);
+
+ /* Program the aperture */
+ CD_WRITE(&p, REG_A7XX_SP_READ_SEL, read_sel);
+
+ /* Read all the data in one chunk */
+ CD_READ(&p, REG_A7XX_SP_AHB_READ_APERTURE, block->size);
+
+ /* Marker for end of script */
+ CD_FINISH(&p);
+
+ /* Try to run the crash dumper and bail if it times out */
+ if (CD_RUN(&p, gpu))
+ return false;
+
+ snapshot_sect (state, SHADER_V3, block->size * sizeof(u32), {
+ .type = block->statetype,
+ .slice_id = HEADER_SLICE_ID(block->slice_region, slice_id),
+ .sp_index = sp,
+ .usptp = block->usptp_id,
+ .pipe_id = block->pipeid,
+ .location = block->location,
+ .ctxt_id = ctxt,
+ .size = block->size,
+ }) {
+ const u32 *src = dumper->ptr + A8XX_CD_DATA_OFFSET;
+ snapshot_sect_data(§, src, block->size * sizeof(u32));
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static void a8xx_snapshot_gmu_bo(struct adreno_gpu_state *state,
+ struct a6xx_gmu_bo *bo,
+ unsigned type)
+{
+ snapshot_sect (state, GMU_MEMORY, bo->size, {
+ .type = type,
+ .gmuaddr = bo->iova,
+ }) {
+ snapshot_sect_data(§, bo->virt, bo->size);
+ }
+}
+
+static void a8xx_snapshot_gpu_bo(struct adreno_gpu_state *state,
+ struct drm_gem_object *bo,
+ uint64_t iova)
+{
+ if (!IS_ERR_OR_NULL(bo))
+ return;
+
+ snapshot_sect (state, GPU_OBJECT_V2, bo->size, {
+ .type = SNAPSHOT_GPU_OBJECT_GLOBAL,
+ .gpuaddr = iova,
+ .size = bo->size / 4, /* dwords */
+ }) {
+ void *ptr = msm_gem_get_vaddr(bo);
+ snapshot_sect_data(§, ptr, bo->size);
+ msm_gem_put_vaddr(bo);
+ }
+}
+
+static void a8xx_snapshot_gmu_hfi_history(struct msm_gpu *gpu,
+ struct adreno_gpu_state *state)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+ struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
+ unsigned i, j;
+
+ BUILD_BUG_ON(ARRAY_SIZE(gmu->queues) != ARRAY_SIZE(state->hfi_queue_history));
+
+ for (i = 0; i < ARRAY_SIZE(gmu->queues); i++) {
+ struct a6xx_hfi_queue *queue = &gmu->queues[i];
+ for (j = 0; j < HFI_HISTORY_SZ; j++) {
+ unsigned idx = (j + queue->history_idx) % HFI_HISTORY_SZ;
+ state->hfi_queue_history[i][j] = queue->history[idx];
+ }
+ }
+}
+
+#define GEN8_DEBUGBUS_BLOCK_SIZE 0x100
+
+#define GEN8_DEBUGBUS_BLOCK_IDX_1 128
+#define GEN8_DEBUGBUS_BLOCK_IDX_2 192
+
+static void gen8_dbgc_debugbus_select(struct msm_gpu *gpu, u32 block_id, u32 index)
+{
+ u32 reg;
+
+ reg = A7XX_DBGC_CFG_DBGBUS_SEL_D_PING_INDEX(index) |
+ A7XX_DBGC_CFG_DBGBUS_SEL_D_PING_BLK_SEL(block_id);
+
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_SEL_A, reg);
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_SEL_B, reg);
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_SEL_C, reg);
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_SEL_D, reg);
+
+ /*
+ * There needs to be a delay of 1 us to ensure enough time for correct
+ * data is funneled into the trace buffer
+ */
+ udelay(1);
+}
+
+static void gen8_dbgc_side_debug_bus_read(struct msm_gpu *gpu,
+ u32 block_id, u32 index, struct adreno_snapshot_sect *sect)
+{
+ u32 reg, side_data;
+
+ gen8_dbgc_debugbus_select(gpu, block_id, index);
+
+ reg = gpu_read(gpu, REG_A6XX_DBGC_CFG_DBGBUS_OVER);
+ side_data = FIELD_GET(GENMASK(27, 24), reg);
+
+ snapshot_sect_u32(sect, side_data);
+}
+
+static void gen8_snapshot_dbgc_side_debugbus_block(
+ struct msm_gpu *gpu, uint32_t block_id, size_t count,
+ struct adreno_snapshot_sect *sect)
+{
+ for (int i = 0; i < count; i++)
+ gen8_dbgc_side_debug_bus_read(gpu, block_id, i, sect);
+}
+
+static void gen8_dbgc_debug_bus_read(struct msm_gpu *gpu,
+ u32 block_id, u32 index, struct adreno_snapshot_sect *sect)
+{
+ gen8_dbgc_debugbus_select(gpu, block_id, index);
+
+ snapshot_sect_u32(sect, gpu_read(gpu, REG_A6XX_DBGC_CFG_DBGBUS_TRACE_BUF2));
+ snapshot_sect_u32(sect, gpu_read(gpu, REG_A6XX_DBGC_CFG_DBGBUS_TRACE_BUF1));
+}
+
+static void gen8_snapshot_dbgc_debugbus_block(
+ struct msm_gpu *gpu, uint32_t block_id, size_t count,
+ struct adreno_snapshot_sect *sect)
+{
+ for (int i = 0; i < count; i++)
+ gen8_dbgc_debug_bus_read(gpu, block_id, i, sect);
+}
+
+static void gen8_snapshot_debugbus(struct msm_gpu *gpu, struct adreno_gpu_state *state)
+{
+ const struct gen8_snapshot_block_list *b = block_list(gpu);
+
+ /* Set up the GX debug bus */
+
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_CNTLT,
+ A6XX_DBGC_CFG_DBGBUS_CNTLT_SEGT(0xf));
+
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_CNTLM,
+ A6XX_DBGC_CFG_DBGBUS_CNTLM_ENABLE(0xf));
+
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_IVTL_0, 0);
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_IVTL_1, 0);
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_IVTL_2, 0);
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_IVTL_3, 0);
+
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_BYTEL_0, 0x76543210);
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_BYTEL_1, 0xFEDCBA98);
+
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_MASKL_0, 0);
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_MASKL_1, 0);
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_MASKL_2, 0);
+ gpu_write(gpu, REG_A6XX_DBGC_CFG_DBGBUS_MASKL_3, 0);
+
+ /* Read the blocks: */
+
+ for (int i = 0; i < b->debugbus_blocks_len; i++) {
+ size_t count = GEN8_DEBUGBUS_BLOCK_SIZE;
+ uint32_t block_id = b->debugbus_blocks[i];
+
+ snapshot_sect (state, DEBUGBUS, 4 * 2 * count, {
+ /* debugbus section hdr: */
+ .id = block_id,
+ .count = count * 2,
+ }) {
+ gen8_snapshot_dbgc_debugbus_block(gpu, block_id, count, §);
+ }
+
+ snapshot_sect (state, SIDE_DEBUGBUS, 4 * count, {
+ /* side_debugbus section hdr: */
+ .id = block_id,
+ .size = count,
+ .valid_data = 0x4,
+ }) {
+ gen8_snapshot_dbgc_side_debugbus_block(gpu, block_id, count, §);
+ }
+ }
+
+ for (int i = 0; i < b->gbif_debugbus_blocks_len; i++) {
+ size_t count = GEN8_DEBUGBUS_BLOCK_SIZE;
+ uint32_t block_id = b->gbif_debugbus_blocks[i];
+
+ snapshot_sect (state, DEBUGBUS, 4 * 2 * count, {
+ /* debugbus section hdr: */
+ .id = block_id,
+ .count = count * 2,
+ }) {
+ gen8_snapshot_dbgc_debugbus_block(gpu, block_id, count, §);
+ }
+
+ snapshot_sect (state, SIDE_DEBUGBUS, 4 * count, {
+ /* side_debugbus section hdr: */
+ .id = block_id,
+ .size = count,
+ .valid_data = 0x4,
+ }) {
+ gen8_snapshot_dbgc_side_debugbus_block(gpu, block_id, count, §);
+ }
+ }
+}
+
+#define cxdbg_write(gpu, offset, val) \
+ gpu_write((gpu), (offset) + 0x18400, (val))
+
+#define cxdbg_read(gpu, offset) \
+ gpu_read((gpu), (offset) + 0x18400)
+
+static void gen8_cx_dbgc_debugbus_select(struct msm_gpu *gpu, u32 block_id, u32 index)
+{
+ u32 reg;
+
+ reg = A7XX_CX_DBGC_CFG_DBGBUS_SEL_A_PING_INDEX(index) |
+ A7XX_CX_DBGC_CFG_DBGBUS_SEL_A_PING_BLK_SEL(block_id);
+
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_SEL_A, reg);
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_SEL_B, reg);
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_SEL_C, reg);
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_SEL_D, reg);
+
+ /*
+ * There needs to be a delay of 1 us to ensure enough time for correct
+ * data is funneled into the trace buffer
+ */
+ udelay(1);
+}
+
+static void gen8_debugbus_internal_read_setup(struct msm_gpu *gpu)
+{
+ /* Clear the DBGC logic/events */
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_CNTLM, 0x70000000);
+
+ /* Enable the internal buffer mode and program to tail mode */
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_OPL, 0x28000000);
+
+ /* Program the logic unit 0 to high when the side data is 1 */
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_BYTEL_0, 0x0000000c);
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_MASKL_0, 0x000000f0);
+
+ /* Enable the DBGC logic/events */
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_CNTLM, 0x07000000);
+}
+
+static void gen8_cx_gc_us_i_0_debugbus_read(
+ struct msm_gpu *gpu, u32 block_id, u32 index, u32 data[2])
+{
+ gen8_cx_dbgc_debugbus_select(gpu, block_id, index);
+
+ /*
+ * Select lower 32 bits of internal buffer data for internal mux[1:0]
+ * DBGC_CFG_DBGBUS_IDX.internalIndex0 = 24 (0x18)
+ * DBGC_CFG_DBGBUS_IDX.internalIndex1 = 25 (0x19)
+ */
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_IDX, 0x00001918);
+ udelay(1);
+
+ data[0] = cxdbg_read(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_TRACE_BUF3);
+
+ /*
+ * Select upper 32 bits of internal buffer data for internal mux[1:0]
+ * DBGC_CFG_DBGBUS_IDX.internalIndex0 = 26 (0x1a)
+ * DBGC_CFG_DBGBUS_IDX.internalIndex1 = 27 (0x1b)
+ */
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_IDX, 0x00001a1b);
+ udelay(1);
+
+ data[1] = cxdbg_read(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_TRACE_BUF3);
+}
+
+/* gen8_cx_dbgc_debug_bus_read() - Read data from trace bus */
+static void gen8_cx_debug_bus_read(
+ struct msm_gpu *gpu, u32 block_id, u32 index, u32 data[2])
+{
+ gen8_cx_dbgc_debugbus_select(gpu, block_id, index);
+
+ data[0] = cxdbg_read(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_TRACE_BUF2);
+ data[1] = cxdbg_read(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_TRACE_BUF1);
+}
+
+static void gen8_snapshot_cx_dbgc_debugbus_block(
+ struct msm_gpu *gpu, uint32_t block_id, size_t count,
+ struct adreno_snapshot_sect *sect)
+{
+ /* This block requires an out of order access due to a HW limitation.
+ * (QCTDD12240539)
+ */
+ if (block_id == DEBUGBUS_CX_GC_US_I_0) {
+ static u32 data[GEN8_DEBUGBUS_BLOCK_SIZE * 2];
+ gen8_debugbus_internal_read_setup(gpu);
+ for (int i = 0; i < GEN8_DEBUGBUS_BLOCK_IDX_1; i++)
+ gen8_cx_gc_us_i_0_debugbus_read(gpu, block_id, i, &data[i * 2]);
+ for (int i = GEN8_DEBUGBUS_BLOCK_IDX_2; i < GEN8_DEBUGBUS_BLOCK_SIZE; i++)
+ gen8_cx_gc_us_i_0_debugbus_read(gpu, block_id, i, &data[i * 2]);
+ for (int i = GEN8_DEBUGBUS_BLOCK_IDX_1; i < GEN8_DEBUGBUS_BLOCK_IDX_2; i++)
+ gen8_cx_gc_us_i_0_debugbus_read(gpu, block_id, i, &data[i * 2]);
+ snapshot_sect_arr(sect, data);
+ } else {
+ for (int i = 0; i < count; i++) {
+ u32 data[2];
+ gen8_cx_debug_bus_read(gpu, block_id, i, data);
+ snapshot_sect_arr(sect, data);
+ }
+ }
+}
+
+
+/* gen8_cx_side_dbgc_debug_bus_read() - Read data from trace bus */
+static void gen8_cx_side_debug_bus_read(
+ struct msm_gpu *gpu, u32 block_id, u32 index, struct adreno_snapshot_sect *sect)
+{
+ u32 reg, side_data;
+
+ gen8_cx_dbgc_debugbus_select(gpu, block_id, index);
+
+ reg = cxdbg_read(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_OVER);
+ side_data = FIELD_GET(GENMASK(27, 24), reg);
+
+ snapshot_sect_u32(sect, side_data);
+
+}
+
+static void gen8_snapshot_cx_side_dbgc_debugbus_block(
+ struct msm_gpu *gpu, uint32_t block_id, size_t count,
+ struct adreno_snapshot_sect *sect)
+{
+ for (int i = 0; i < count; i++)
+ gen8_cx_side_debug_bus_read(gpu, block_id, i, sect);
+}
+
+static void gen8_snapshot_cx_debugbus(struct msm_gpu *gpu, struct adreno_gpu_state *state)
+{
+ const struct gen8_snapshot_block_list *b = block_list(gpu);
+
+ /* Set up the CX debug bus */
+
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_CNTLT,
+ A6XX_DBGC_CFG_DBGBUS_CNTLT_SEGT(0xf));
+
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_CNTLM,
+ A6XX_DBGC_CFG_DBGBUS_CNTLM_ENABLE(0xf));
+
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_IVTL_0, 0);
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_IVTL_1, 0);
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_IVTL_2, 0);
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_IVTL_3, 0);
+
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_BYTEL_0, 0x76543210);
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_BYTEL_1, 0xFEDCBA98);
+
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_MASKL_0, 0);
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_MASKL_1, 0);
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_MASKL_2, 0);
+ cxdbg_write(gpu, REG_A6XX_CX_DBGC_CFG_DBGBUS_MASKL_3, 0);
+
+ /* Read the blocks: */
+
+ for (int i = 0; i < b->cx_debugbus_blocks_len; i++) {
+ size_t count = GEN8_DEBUGBUS_BLOCK_SIZE;
+ uint32_t block_id = b->cx_debugbus_blocks[i];
+
+ snapshot_sect (state, DEBUGBUS, 4 * 2 * count, {
+ /* debugbus section hdr: */
+ .id = block_id,
+ .count = count * 2,
+ }) {
+ gen8_snapshot_cx_dbgc_debugbus_block(gpu, block_id, count, §);
+ }
+
+ snapshot_sect (state, SIDE_DEBUGBUS, 4 * count, {
+ /* side_debugbus section hdr: */
+ .id = block_id,
+ .size = count,
+ .valid_data = 0x4,
+ }) {
+ gen8_snapshot_cx_side_dbgc_debugbus_block(gpu, block_id, count, §);
+ }
+ }
+}
+
+struct msm_gpu_state *a8xx_gpu_state_get(struct msm_gpu *gpu)
+{
+ const struct gen8_snapshot_block_list *b = block_list(gpu);
+ struct adreno_crashdumper _dumper = { 0 }, *dumper = NULL;
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+ struct adreno_gpu_state *state;
+
+ /*
+ * GPU state snapshot is inherently single threaded. All paths
+ * are serialized on gpu->lock. Make that clear, and prevent
+ * future bugs:
+ */
+ WARN_ON_ONCE(!mutex_is_locked(&gpu->lock));
+
+ if (!b)
+ return NULL;
+
+ state = kzalloc_obj(*state);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&state->objs);
+
+ /* Get the generic state from the adreno core */
+ adreno_gpu_state_get(gpu, &state->base);
+
+ adreno_snapshot_registers_v2(gpu, state, b->gmu_cx_unsliced_regs);
+
+ a8xx_snapshot_gmu_bo(state, &a6xx_gpu->gmu.log, SNAPSHOT_GMU_MEM_LOG);
+ a8xx_snapshot_gmu_bo(state, &a6xx_gpu->gmu.hfi, SNAPSHOT_GMU_MEM_HFI);
+ a8xx_snapshot_gmu_bo(state, &a6xx_gpu->gmu.debug, SNAPSHOT_GMU_MEM_DEBUG);
+
+ a8xx_snapshot_gmu_hfi_history(gpu, state);
+
+ snapshot_sect (state, DEBUG, 4, {
+ .type = SNAPSHOT_DEBUG_SLICE_MASK,
+ .size = 1,
+ }) {
+ snapshot_sect_u32(§, a6xx_gpu->slice_mask);
+ }
+ gen8_snapshot_cx_debugbus(gpu, state);
+
+ /* If GX isn't on the rest of the data isn't going to be accessible */
+ if (!a6xx_gmu_gx_is_on(adreno_gpu))
+ return &state->base;
+
+ gen8_snapshot_debugbus(gpu, state);
+
+ /*
+ * Try to initialize the crashdumper, if we are not dumping state
+ * with the SMMU stalled. The crashdumper needs memory access to
+ * write out GPU state, so we need to skip this when the SMMU is
+ * stalled in response to an iova fault
+ */
+ bool stalled = !!(gpu_read(gpu, REG_A8XX_RBBM_MISC_STATUS) &
+ A8XX_RBBM_MISC_STATUS_SMMU_STALLED_ON_FAULT);
+ if (!stalled && !gpu->needs_hw_init &&
+ !a8xx_crashdumper_init(gpu, &_dumper)) {
+ dumper = &_dumper;
+ }
+
+ /* Clear aperture register */
+ a8xx_aperture_clear(gpu);
+
+ /*
+ * Assert the isStatic bit before triggering snapshot.
+ * BIT(0): GPU activity during snapshot dump
+ * BIT(1): GPU UNSLICE activity during snapshot dump
+ * Similarly, BIT(4) for slice-0, BIT(5) for slice-1 and so on.
+ */
+ gpu_write(gpu, REG_A8XX_RBBM_SNAPSHOT_STATUS, BIT(0) | BIT(1) |
+ FIELD_PREP(GENMASK(7, 4), a6xx_gpu->slice_mask));
+
+ /* Dump the registers which get affected by crash dumper trigger */
+ for (int i = 0; i < b->num_pre_crashdumper_regs; i++) {
+ const struct gen8_reg_list *regs = &b->pre_crashdumper_regs[i];
+ u32 j;
+
+ FOR_EACH_SLICE (j, regs->slice_region, a6xx_gpu->slice_mask) {
+ u32 slice_id = SLICE_ID(regs->slice_region, j);
+ gen8_legacy_snapshot_registers(gpu, state, regs, slice_id);
+ }
+ }
+
+ /*
+ * If crashdumper timed out while dumping this section skip everything
+ * since even AHB acceses to the GPU might cause NoC errors.
+ */
+ if (!gen8_reglist_snapshot(gpu, state, dumper))
+ goto out;
+
+ /* CP indexed regs data */
+ if (!gen8_snapshot_cp_indexed_regs(gpu, state, dumper))
+ goto out;
+
+ /* Mempool debug data */
+ if (!gen8_snapshot_mempool(gpu, state, dumper))
+ goto out;
+
+ /*
+ * CP MVC register section
+ * If crashdumper timed out while dumping any section below skip everything
+ * since even AHB acceses to the GPU might cause NoC errors.
+ */
+ if (!gen8_snapshot_mvc_regs(gpu, state, dumper, b->cp_clusters, b->num_cp_clusters))
+ goto out;
+
+ /* MVC register section */
+ if (!gen8_snapshot_mvc_regs(gpu, state, dumper, b->clusters, b->num_clusters))
+ goto out;
+
+ /* registers dumped through DBG AHB */
+ if (!gen8_snapshot_dbgahb_regs(gpu, state, dumper))
+ goto out;
+
+ /* Shader memory */
+ if (!gen8_snapshot_shader(gpu, state, dumper))
+ goto out;
+
+ /*
+ * Capture preempt records after cp_clusters, to make this easier
+ * to decode (ie. so we already know CP_CONTEXT_SWITCH_SMMU_INFO and
+ * CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR) when crashdec sees
+ * the logged GPU objs)
+ */
+ for (unsigned i = 0; i < gpu->nr_rings; i++) {
+ a8xx_snapshot_gpu_bo(state, a6xx_gpu->preempt_bo[i],
+ a6xx_gpu->preempt_iova[i]);
+ a8xx_snapshot_gpu_bo(state, a6xx_gpu->preempt_smmu_bo[i],
+ a6xx_gpu->preempt_smmu_iova[i]);
+ }
+
+out:
+ gpu_write(gpu, REG_A8XX_RBBM_SNAPSHOT_STATUS, 0x0);
+
+ if (dumper)
+ msm_gem_kernel_put(dumper->bo, gpu->vm);
+
+ state->gpu_initialized = !gpu->needs_hw_init;
+
+ return &state->base;
+}
+
+static void a8xx_gpu_state_destroy(struct kref *kref)
+{
+ struct adreno_state_memobj *obj, *tmp;
+ struct msm_gpu_state *state = container_of(kref,
+ struct msm_gpu_state, ref);
+ struct adreno_gpu_state *adreno_state = container_of(state,
+ struct adreno_gpu_state, base);
+
+ list_for_each_entry_safe (obj, tmp, &adreno_state->objs, node) {
+ list_del(&obj->node);
+ kvfree(obj->ptr);
+ kfree(obj);
+ }
+
+ adreno_gpu_state_destroy(state);
+ kfree(adreno_state);
+}
+
+int a8xx_gpu_state_put(struct msm_gpu_state *state)
+{
+ if (IS_ERR_OR_NULL(state))
+ return 1;
+
+ return kref_put(&state->ref, a8xx_gpu_state_destroy);
+}
+
+void a8xx_show(struct msm_gpu *gpu, struct msm_gpu_state *state,
+ struct drm_printer *p)
+{
+ struct adreno_gpu_state *adreno_state = container_of(state,
+ struct adreno_gpu_state, base);
+ struct adreno_state_memobj *obj;
+
+ if (IS_ERR_OR_NULL(state))
+ return;
+
+ drm_printf(p, "gpu-initialized: %d\n", adreno_state->gpu_initialized);
+
+ adreno_show(gpu, state, p);
+
+ drm_puts(p, "gmu-hfi:\n");
+ for (int i = 0; i < ARRAY_SIZE(adreno_state->hfi_queue_history); i++) {
+ drm_printf(p, " queue-history[%u]:", i);
+ for (int j = 0; j < HFI_HISTORY_SZ; j++) {
+ drm_printf(p, " %d", adreno_state->hfi_queue_history[i][j]);
+ }
+ drm_printf(p, "\n");
+ }
+
+ /*
+ * Parsing note: do not assume each snapshot obj is only a single
+ * section, assume sub-allocation.
+ */
+ drm_puts(p, "snapshot:\n");
+ list_for_each_entry (obj, &adreno_state->objs, node) {
+ drm_printf(p, " size: %zu\n", obj->size);
+ adreno_show_object(p, &obj->ptr, obj->size, &obj->encoded);
+ }
+}
diff --git a/drivers/gpu/drm/msm/adreno/adreno_snapshot.h b/drivers/gpu/drm/msm/adreno/adreno_snapshot.h
index f9956e0bc1d5..1c56be7249f7 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_snapshot.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_snapshot.h
@@ -7,6 +7,8 @@
#include <linux/types.h>
+#include "msm_gpu.h"
+
/* Snapshot header */
/* High word is static, low word is snapshot version ID */
@@ -150,6 +152,7 @@ struct adreno_snapshot_gmu_mem {
uint64_t gmuaddr;
uint64_t gpuaddr;
} __packed;
+#define STRUCT_GMU_MEMORY adreno_snapshot_gmu_mem
/* Register sub-section header */
struct adreno_snapshot_regs {
@@ -172,6 +175,7 @@ struct adreno_snapshot_indexed_regs_v2 {
u32 pipe_id; /* Id of pipe, BV, Br etc */
u32 slice_id; /* Slice ID to be dumped */
} __packed;
+#define STRUCT_INDEXED_REGS_V2 adreno_snapshot_indexed_regs_v2
/* MVC register sub-section header */
struct adreno_snapshot_mvc_regs {
@@ -195,6 +199,7 @@ struct adreno_snapshot_mvc_regs_v3 {
u32 sp_id;
u32 usptp_id;
} __packed;
+#define STRUCT_MVC_V3 adreno_snapshot_mvc_regs_v3
/* Debug data sub-section header */
@@ -222,17 +227,20 @@ struct adreno_snapshot_debug {
int type; /* Type identifier for the attached tata */
int size; /* Size of the section in dwords */
} __packed;
+#define STRUCT_DEBUG adreno_snapshot_debug
struct adreno_snapshot_debugbus {
int id; /* Debug bus ID */
int count; /* Number of dwords in the dump */
} __packed;
+#define STRUCT_DEBUGBUS adreno_snapshot_debugbus
struct adreno_snapshot_side_debugbus {
int id; /* Debug bus ID */
int size; /* Number of dwords in the dump */
int valid_data; /* Mask of valid bits of the side debugbus */
} __packed;
+#define STRUCT_SIDE_DEBUGBUS adreno_snapshot_side_debugbus
struct adreno_snapshot_shader {
int type; /* SP/TP statetype */
@@ -259,6 +267,7 @@ struct adreno_snapshot_shader_v3 {
u32 ctxt_id; /* Context ID */
u32 size; /* Number of dwords in the dump */
} __packed;
+#define STRUCT_SHADER_V3 adreno_snapshot_shader_v3
#define TRACE_BUF_NUM_SIG 4
@@ -302,6 +311,7 @@ struct adreno_snapshot_gpu_object_v2 {
__u64 ptbase; /* Base for the pagetable the GPU address is valid in */
__u64 size; /* Size of the object (in dwords) */
} __packed;
+#define STRUCT_GPU_OBJECT_V2 adreno_snapshot_gpu_object_v2
struct adreno_snapshot_eventlog {
/** @type: Type of the event log buffer */
@@ -319,3 +329,202 @@ struct adreno_snapshot_gmu_version {
u32 value;
} __packed;
+struct adreno_snapshot_empty_header { u8 dummy; };
+#define STRUCT_REGS_V2 adreno_snapshot_debugbus
+
+#define REG_COUNT(_ptr) ((_ptr[1] - _ptr[0]) + 1)
+
+static int adreno_snapshot_regs_count(const u32 *ptr)
+{
+ unsigned int count = 0;
+ unsigned int group_count;
+
+ for ( ; ptr[0] != UINT_MAX; ptr += 2) {
+ group_count = REG_COUNT(ptr);
+ if (group_count == 1)
+ count += group_count + 1;
+ else
+ count += group_count + 2;
+ }
+ return count;
+}
+
+struct adreno_gpu_state {
+ struct msm_gpu_state base;
+
+ s32 hfi_queue_history[HFI_MAX_QUEUES][HFI_HISTORY_SZ];
+
+ /* list of struct adreno_state_memobj */
+ struct list_head objs;
+
+ bool gpu_initialized;
+};
+
+struct adreno_state_memobj {
+ struct list_head node;
+ void *ptr; /* replaced by ascii85 encoded string in adreno_show_object() */
+ bool encoded;
+ size_t size;
+};
+
+/**
+ * A on-stack helper to build snapshot sections setting the size field when the
+ * helper goes out of scope, to simplify constructing snapshot sections, but
+ * without resorting to the callback approach that kgsl uses.
+ */
+struct adreno_snapshot_sect {
+ struct adreno_state_memobj *obj;
+ void *cur, *end;
+};
+
+static void __snapshot_sect_fini(struct adreno_snapshot_sect *s)
+{
+ if (!s->obj)
+ return;
+ struct adreno_snapshot_section_header *hdr = (void *)s->obj->ptr;
+ hdr->size = s->obj->size = ((u8 *)s->cur - (u8 *)s->obj->ptr);
+}
+
+DEFINE_FREE(sect_fini, struct adreno_snapshot_sect, __snapshot_sect_fini(&_T));
+
+static struct adreno_snapshot_sect
+__snapshot_sect_init(struct adreno_gpu_state *state, uint16_t id, size_t extra_sz,
+ void *sect_hdr, size_t sect_hdr_sz, const char *func, int line)
+{
+ /* A few section types have no section header: */
+ switch (id) {
+ case ADRENO_SNAPSHOT_SECTION_REGS_V2:
+ sect_hdr_sz = 0;
+ break;
+ default:
+ break;
+ }
+
+ pr_debug("%s:%d: snapshot_sect(id=%04x, extra_sz=%zu, sect_hdr_sz=%zu)\n",
+ func, line, id, extra_sz, sect_hdr_sz);
+
+ size_t sz = sizeof(struct adreno_snapshot_section_header) + sect_hdr_sz + extra_sz;
+ struct adreno_state_memobj *obj = kzalloc_obj(*obj);
+ void *ptr = kvzalloc(sz, GFP_KERNEL);
+
+ if (!obj || !ptr) {
+ kfree(obj);
+ kvfree(ptr);
+ return (struct adreno_snapshot_sect){};
+ }
+
+ obj->ptr = ptr;
+
+ list_add_tail(&obj->node, &state->objs);
+
+ /* Common snapshot section header: */
+ struct adreno_snapshot_section_header *hdr = ptr;
+ hdr->magic = SNAPSHOT_SECTION_MAGIC;
+ hdr->id = id;
+ /* size filled out in __snapshot_sect_fini() */
+ ptr += sizeof(*hdr);
+
+ /* Section type header: */
+ memcpy(ptr, sect_hdr, sect_hdr_sz);
+ ptr += sect_hdr_sz;
+
+ return (struct adreno_snapshot_sect) {
+ .obj = obj,
+ .cur = ptr,
+ .end = ptr + extra_sz,
+ };
+}
+
+static void
+snapshot_sect_data(struct adreno_snapshot_sect *s, const void *data, size_t sz)
+{
+ /* overflow check: */
+ if (WARN_ON_ONCE(s->cur + sz > s->end))
+ return;
+
+ memcpy(s->cur, data, sz);
+ s->cur += sz;
+}
+
+#define snapshot_sect_arr(_s, _d) snapshot_sect_data(_s, _d, sizeof(_d))
+
+static void
+snapshot_sect_u32(struct adreno_snapshot_sect *s, u32 val)
+{
+ snapshot_sect_data(s, &val, sizeof(val));
+}
+
+#define __snapshot_sect(_label, _state, _id, _extra_sz, ...) \
+ for (struct adreno_snapshot_sect sect __free(sect_fini) = ({ \
+ struct STRUCT_ ## _id sect_hdr = __VA_ARGS__; \
+ __snapshot_sect_init(_state, ADRENO_SNAPSHOT_SECTION_ ## _id, \
+ _extra_sz, §_hdr, \
+ sizeof(sect_hdr), \
+ __func__, __LINE__); }); \
+ sect.obj; ({ goto _label; })) \
+ if (false) { \
+_label: \
+ break; \
+ } else
+
+#define snapshot_sect(_id, _extra_sz, ...) \
+ __snapshot_sect(__UNIQUE_ID(label), _id, _extra_sz, __VA_ARGS__)
+
+struct adreno_crashdumper {
+ void *ptr;
+ size_t data_offset;
+ struct drm_gem_object *bo;
+ u64 iova;
+};
+
+/**
+ * On-stack object to represent a single crashdumper invocation, tracking
+ * in/out ptrs and checking for overflow.
+ */
+struct cd_script {
+ struct adreno_crashdumper *dumper;
+ u64 *in;
+ u64 out;
+};
+
+/* These should be implemented by the gen specific backend: */
+static inline int __cd_run(struct msm_gpu *gpu, struct adreno_crashdumper *dumper);
+static inline bool __cd_check_overflow(struct cd_script *p);
+
+static inline struct cd_script CD_INIT(struct adreno_crashdumper *dumper)
+{
+ return (struct cd_script) {
+ .dumper = dumper,
+ .in = dumper->ptr,
+ .out = dumper->iova + dumper->data_offset,
+ };
+}
+
+static inline void CD_WRITE(struct cd_script *p, u32 reg, u32 val)
+{
+ *(p->in++) = val;
+ *(p->in++) = FIELD_PREP(GENMASK_U64(63, 44), reg) | BIT(21) | BIT(0);
+ __cd_check_overflow(p);
+}
+
+static inline void CD_READ(struct cd_script *p, u32 reg, u32 dwords)
+{
+ *(p->in++) = p->out;
+ *(p->in++) = FIELD_PREP(GENMASK_U64(63, 44), reg) | dwords;
+ p->out += sizeof(u32) * dwords;
+ __cd_check_overflow(p);
+}
+
+static void CD_FINISH(struct cd_script *p)
+{
+ *(p->in++) = 0;
+ *(p->in++) = 0;
+ __cd_check_overflow(p);
+}
+
+static int CD_RUN(struct cd_script *p, struct msm_gpu *gpu)
+{
+ if (!__cd_check_overflow(p))
+ return -ENOSPC;
+ return __cd_run(gpu, p->dumper);
+}
diff --git a/drivers/gpu/drm/msm/registers/adreno/a6xx.xml b/drivers/gpu/drm/msm/registers/adreno/a6xx.xml
index cf2fb20203b7..aa639de60ace 100644
--- a/drivers/gpu/drm/msm/registers/adreno/a6xx.xml
+++ b/drivers/gpu/drm/msm/registers/adreno/a6xx.xml
@@ -5086,6 +5086,7 @@ by a particular renderpass/blit.
<reg32 offset="0x0005" name="CFG_DBGBUS_CNTLM">
<bitfield high="27" low="24" name="ENABLE"/>
</reg32>
+ <reg32 offset="0x0006" name="CFG_DBGBUS_OPL"/>
<reg32 offset="0x0008" name="CFG_DBGBUS_IVTL_0"/>
<reg32 offset="0x0009" name="CFG_DBGBUS_IVTL_1"/>
<reg32 offset="0x000a" name="CFG_DBGBUS_IVTL_2"/>
@@ -5115,8 +5116,11 @@ by a particular renderpass/blit.
<bitfield high="31" low="28" name="BYTEL15"/>
</reg32>
+ <reg32 offset="0x001e" name="CFG_DBGBUS_IDX"/>
+ <reg32 offset="0x0026" name="CFG_DBGBUS_OVER"/>
<reg32 offset="0x002f" name="CFG_DBGBUS_TRACE_BUF1"/>
<reg32 offset="0x0030" name="CFG_DBGBUS_TRACE_BUF2"/>
+ <reg32 offset="0x0031" name="CFG_DBGBUS_TRACE_BUF3"/>
</domain>
<domain name="A7XX_CX_DBGC" width="32" varset="chip">
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 16/19] drm/msm/a8xx: Add all CP_PROTECT_PIPE to pwrup reglist
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
` (13 preceding siblings ...)
2026-10-30 21:18 ` [PATCH v4 15/19] drm/msm/a8xx: devcoredump state capture support Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:18 ` [PATCH v4 17/19] drm/msm/a8xx: Use CP_PROTECT_PIPE regs Rob Clark
` (2 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Konrad Dybcio,
Sean Paul, Konrad Dybcio, Dmitry Baryshkov, Abhinav Kumar,
Jessica Zhang, Marijn Suijten, David Airlie, Simona Vetter,
open list
We're going to start using more of the CP_PROTECT_PIPE regs. Rather
than missing to restore one somewhere, just add them all to the pwrup
reglist.
Tested-by: Alexander Koskovich <akoskovich@pm.me> # Milos (Nothing Phone 3a)
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a8xx_catalog.c | 75 +++++++++++++++++++++++
1 file changed, 75 insertions(+)
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_catalog.c b/drivers/gpu/drm/msm/adreno/a8xx_catalog.c
index ad9141f76d88..6fb35db73db3 100644
--- a/drivers/gpu/drm/msm/adreno/a8xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a8xx_catalog.c
@@ -276,6 +276,21 @@ static const struct adreno_reglist_pipe a830_dyn_pwrup_reglist_regs[] = {
BIT(PIPE_BV) | BIT(PIPE_LPAC) | BIT(PIPE_AQE0) |
BIT(PIPE_AQE1) | BIT(PIPE_DDE_BR) | BIT(PIPE_DDE_BV) },
{ REG_A8XX_CP_PROTECT_CNTL_PIPE, 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(0), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(1), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(2), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(3), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(4), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(5), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(6), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(7), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(8), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(9), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(10), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(11), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(12), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(13), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(14), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
{ REG_A8XX_CP_PROTECT_PIPE(15), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
{ REG_A8XX_GRAS_TSEFE_DBG_ECO_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
{ REG_A8XX_GRAS_NC_MODE_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
@@ -437,6 +452,21 @@ static const struct adreno_reglist_pipe x285_dyn_pwrup_reglist_regs[] = {
BIT(PIPE_BV) | BIT(PIPE_LPAC) | BIT(PIPE_AQE0) |
BIT(PIPE_AQE1) | BIT(PIPE_DDE_BR) | BIT(PIPE_DDE_BV) },
{ REG_A8XX_CP_PROTECT_CNTL_PIPE, 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC)},
+ { REG_A8XX_CP_PROTECT_PIPE(0), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(1), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(2), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(3), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(4), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(5), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(6), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(7), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(8), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(9), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(10), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(11), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(12), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(13), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(14), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
{ REG_A8XX_CP_PROTECT_PIPE(15), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
{ REG_A8XX_RB_GC_GMEM_PROTECT, 0, BIT(PIPE_BR) },
{ REG_A8XX_RB_LPAC_GMEM_PROTECT, 0, BIT(PIPE_BR) },
@@ -670,6 +700,21 @@ DECLARE_ADRENO_REGLIST_LIST(a810_ifpc_reglist);
static const struct adreno_reglist_pipe a810_dyn_pwrup_reglist_regs[] = {
{ REG_A8XX_CP_PROTECT_CNTL_PIPE, 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(0), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(1), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(2), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(3), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(4), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(5), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(6), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(7), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(8), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(9), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(10), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(11), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(12), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(13), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
+ { REG_A8XX_CP_PROTECT_PIPE(14), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
{ REG_A8XX_CP_PROTECT_PIPE(15), 0, BIT(PIPE_BR) | BIT(PIPE_BV) },
{ REG_A8XX_GRAS_TSEFE_DBG_ECO_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
{ REG_A8XX_GRAS_NC_MODE_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
@@ -1083,6 +1128,21 @@ static const struct adreno_reglist_pipe a840_dyn_pwrup_reglist_regs[] = {
BIT(PIPE_BV) | BIT(PIPE_LPAC) | BIT(PIPE_AQE0) |
BIT(PIPE_AQE1) | BIT(PIPE_DDE_BR) | BIT(PIPE_DDE_BV) },
{ REG_A8XX_CP_PROTECT_CNTL_PIPE, 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC)},
+ { REG_A8XX_CP_PROTECT_PIPE(0), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(1), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(2), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(3), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(4), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(5), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(6), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(7), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(8), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(9), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(10), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(11), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(12), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(13), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(14), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
{ REG_A8XX_CP_PROTECT_PIPE(15), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
{ REG_A8XX_RB_GC_GMEM_PROTECT, 0, BIT(PIPE_BR) },
{ REG_A8XX_RB_LPAC_GMEM_PROTECT, 0, BIT(PIPE_BR) },
@@ -1532,6 +1592,21 @@ static const struct adreno_reglist_pipe a850_dyn_pwrup_reglist_regs[] = {
BIT(PIPE_BV) | BIT(PIPE_LPAC) | BIT(PIPE_AQE0) |
BIT(PIPE_AQE1) | BIT(PIPE_DDE_BR) | BIT(PIPE_DDE_BV) },
{ REG_A8XX_CP_PROTECT_CNTL_PIPE, 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(0), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(1), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(2), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(3), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(4), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(5), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(6), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(7), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(8), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(9), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(10), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(11), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(12), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(13), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
+ { REG_A8XX_CP_PROTECT_PIPE(14), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
{ REG_A8XX_CP_PROTECT_PIPE(15), 0, BIT(PIPE_BR) | BIT(PIPE_BV) | BIT(PIPE_LPAC) },
{ REG_A8XX_RB_GC_GMEM_PROTECT, 0, BIT(PIPE_BR) },
{ REG_A8XX_RB_LPAC_GMEM_PROTECT, 0, BIT(PIPE_BR) },
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 17/19] drm/msm/a8xx: Use CP_PROTECT_PIPE regs
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
` (14 preceding siblings ...)
2026-10-30 21:18 ` [PATCH v4 16/19] drm/msm/a8xx: Add all CP_PROTECT_PIPE to pwrup reglist Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:18 ` [PATCH v4 18/19] drm/msm/a8xx: Switch to generated protect regs Rob Clark
2026-10-30 21:18 ` [PATCH v4 19/19] drm/msm/adreno: Remove temporary __maybe_unused Rob Clark
17 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Konrad Dybcio,
Sean Paul, Konrad Dybcio, Dmitry Baryshkov, Abhinav Kumar,
Jessica Zhang, Marijn Suijten, David Airlie, Simona Vetter,
open list
If there are not enough global CP_PROTECT_GLOBAL regs, we can use
CP_PROTECT_PIPE regs for the extra spans. We just need to configure
them the same on all pipes.
Tested-by: Alexander Koskovich <akoskovich@pm.me> # Milos (Nothing Phone 3a)
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
index a6146372cf7e..1f8dd688b967 100644
--- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
@@ -258,12 +258,21 @@ static void a8xx_set_cp_protect(struct msm_gpu *gpu)
a8xx_aperture_clear(gpu);
- for (i = 0; i < protect->count; i++) {
+ for (i = 0; i < min(64, protect->count); i++) {
/* Intentionally skip writing to some registers */
- if (protect->regs[i]) {
- gpu_write(gpu, REG_A8XX_CP_PROTECT_GLOBAL(i), protect->regs[i]);
- final_cfg = protect->regs[i];
- }
+ if (!protect->regs[i])
+ continue;
+ gpu_write(gpu, REG_A8XX_CP_PROTECT_GLOBAL(i), protect->regs[i]);
+ final_cfg = protect->regs[i];
+ }
+
+ for (; i < protect->count; i++) {
+ /* Intentionally skip writing to some registers */
+ if (!protect->regs[i])
+ continue;
+ a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_CP_PROTECT_PIPE(i - 64), protect->regs[i]);
+ a8xx_write_pipe(gpu, PIPE_BV, REG_A8XX_CP_PROTECT_PIPE(i - 64), protect->regs[i]);
+ final_cfg = protect->regs[i];
}
/*
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 18/19] drm/msm/a8xx: Switch to generated protect regs
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
` (15 preceding siblings ...)
2026-10-30 21:18 ` [PATCH v4 17/19] drm/msm/a8xx: Use CP_PROTECT_PIPE regs Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
2026-10-30 21:18 ` [PATCH v4 19/19] drm/msm/adreno: Remove temporary __maybe_unused Rob Clark
17 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Sean Paul,
Konrad Dybcio, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Marijn Suijten, David Airlie, Simona Vetter, open list
These also fix some perfcntr SEL regs which were mistakenly blocked
since we previously just copied the protect tables from downstream kgsl
(and only sometimes remembered to fix these).
Tested-by: Alexander Koskovich <akoskovich@pm.me> # Milos (Nothing Phone 3a)
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a8xx_catalog.c | 301 +---------------------
1 file changed, 13 insertions(+), 288 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_catalog.c b/drivers/gpu/drm/msm/adreno/a8xx_catalog.c
index 6fb35db73db3..dcb61bb09489 100644
--- a/drivers/gpu/drm/msm/adreno/a8xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a8xx_catalog.c
@@ -82,58 +82,6 @@ static const struct adreno_reglist_pipe a830_nonctxt_regs[] = {
{ },
};
-static const u32 a830_protect_regs[] = {
- A6XX_PROTECT_RDONLY(0x00000, 0x03a3),
- A6XX_PROTECT_RDONLY(0x003b4, 0x008b),
- A6XX_PROTECT_NORDWR(0x00440, 0x001f),
- A6XX_PROTECT_RDONLY(0x00580, 0x005f),
- A6XX_PROTECT_NORDWR(0x005e0, 0x011f),
- A6XX_PROTECT_RDONLY(0x0074a, 0x0005),
- A6XX_PROTECT_RDONLY(0x00759, 0x0026),
- A6XX_PROTECT_RDONLY(0x00789, 0x0000),
- A6XX_PROTECT_RDONLY(0x0078c, 0x0013),
- A6XX_PROTECT_NORDWR(0x00800, 0x0029),
- A6XX_PROTECT_NORDWR(0x00837, 0x00af),
- A6XX_PROTECT_RDONLY(0x008e7, 0x00c9),
- A6XX_PROTECT_NORDWR(0x008ec, 0x00c3),
- A6XX_PROTECT_NORDWR(0x009b1, 0x0250),
- A6XX_PROTECT_RDONLY(0x00ce0, 0x0001),
- A6XX_PROTECT_RDONLY(0x00df0, 0x0000),
- A6XX_PROTECT_NORDWR(0x00df1, 0x0000),
- A6XX_PROTECT_NORDWR(0x00e01, 0x0000),
- A6XX_PROTECT_NORDWR(0x00e03, 0x1fff),
- A6XX_PROTECT_NORDWR(0x03c00, 0x00c5),
- A6XX_PROTECT_RDONLY(0x03cc6, 0x1fff),
- A6XX_PROTECT_NORDWR(0x08600, 0x01ff),
- A6XX_PROTECT_NORDWR(0x08e00, 0x00ff),
- A6XX_PROTECT_RDONLY(0x08f00, 0x0000),
- A6XX_PROTECT_NORDWR(0x08f01, 0x01be),
- A6XX_PROTECT_NORDWR(0x09600, 0x01ff),
- A6XX_PROTECT_RDONLY(0x0981a, 0x02e5),
- A6XX_PROTECT_NORDWR(0x09e00, 0x01ff),
- A6XX_PROTECT_NORDWR(0x0a600, 0x01ff),
- A6XX_PROTECT_NORDWR(0x0ae00, 0x0000),
- A6XX_PROTECT_NORDWR(0x0ae02, 0x0004),
- A6XX_PROTECT_NORDWR(0x0ae08, 0x0006),
- A6XX_PROTECT_NORDWR(0x0ae10, 0x036f),
- A6XX_PROTECT_NORDWR(0x0b600, 0x1fff),
- A6XX_PROTECT_NORDWR(0x0dc00, 0x1fff),
- A6XX_PROTECT_RDONLY(0x0fc00, 0x1fff),
- A6XX_PROTECT_NORDWR(0x18400, 0x003f),
- A6XX_PROTECT_RDONLY(0x18440, 0x013f),
- A6XX_PROTECT_NORDWR(0x18580, 0x1fff),
- A6XX_PROTECT_NORDWR(0x1b400, 0x1fff),
- A6XX_PROTECT_NORDWR(0x1f400, 0x0477),
- A6XX_PROTECT_RDONLY(0x1f878, 0x0787),
- A6XX_PROTECT_NORDWR(0x1f930, 0x0329),
- A6XX_PROTECT_NORDWR(0x20000, 0x1fff),
- A6XX_PROTECT_NORDWR(0x27800, 0x007f),
- A6XX_PROTECT_RDONLY(0x27880, 0x0381),
- A6XX_PROTECT_NORDWR(0x27882, 0x0001),
- A6XX_PROTECT_NORDWR(0x27c02, 0x0000),
-};
-DECLARE_ADRENO_PROTECT(a830_protect, 64);
-
static const u32 a830_pwrup_reglist_regs[] = {
REG_A6XX_UCHE_MODE_CNTL,
REG_A8XX_UCHE_VARB_IDLE_TIMEOUT,
@@ -364,63 +312,6 @@ static const struct adreno_reglist_pipe x285_nonctxt_regs[] = {
{ },
};
-static const u32 x285_protect_regs[] = {
- A6XX_PROTECT_RDONLY(0x00008, 0x039b),
- A6XX_PROTECT_RDONLY(0x003b4, 0x008b),
- A6XX_PROTECT_NORDWR(0x00440, 0x001f),
- A6XX_PROTECT_RDONLY(0x00580, 0x005f),
- A6XX_PROTECT_NORDWR(0x005e0, 0x011f),
- A6XX_PROTECT_RDONLY(0x0074a, 0x0005),
- A6XX_PROTECT_RDONLY(0x00759, 0x0026),
- A6XX_PROTECT_RDONLY(0x00789, 0x0000),
- A6XX_PROTECT_RDONLY(0x0078c, 0x0013),
- A6XX_PROTECT_NORDWR(0x00800, 0x0029),
- A6XX_PROTECT_NORDWR(0x0082c, 0x0000),
- A6XX_PROTECT_NORDWR(0x00837, 0x00af),
- A6XX_PROTECT_RDONLY(0x008e7, 0x00c9),
- A6XX_PROTECT_NORDWR(0x008ec, 0x00c3),
- A6XX_PROTECT_NORDWR(0x009b1, 0x0250),
- A6XX_PROTECT_RDONLY(0x00ce0, 0x0001),
- A6XX_PROTECT_RDONLY(0x00df0, 0x0000),
- A6XX_PROTECT_NORDWR(0x00df1, 0x0000),
- A6XX_PROTECT_NORDWR(0x00e01, 0x0000),
- A6XX_PROTECT_NORDWR(0x00e03, 0x1fff),
- A6XX_PROTECT_NORDWR(0x03c00, 0x00c5),
- A6XX_PROTECT_RDONLY(0x03cc6, 0x0039),
- A6XX_PROTECT_NORDWR(0x03d00, 0x1fff),
- A6XX_PROTECT_NORDWR(0x08600, 0x01ff),
- A6XX_PROTECT_NORDWR(0x08e00, 0x00ff),
- A6XX_PROTECT_RDONLY(0x08f00, 0x0000),
- A6XX_PROTECT_NORDWR(0x08f01, 0x01be),
- A6XX_PROTECT_NORDWR(0x09600, 0x01ff),
- A6XX_PROTECT_RDONLY(0x0981a, 0x02e5),
- A6XX_PROTECT_NORDWR(0x09e00, 0x01ff),
- A6XX_PROTECT_NORDWR(0x0a600, 0x01ff),
- A6XX_PROTECT_NORDWR(0x0a82e, 0x0000),
- A6XX_PROTECT_NORDWR(0x0ae00, 0x0006),
- A6XX_PROTECT_NORDWR(0x0ae08, 0x0006),
- A6XX_PROTECT_NORDWR(0x0ae10, 0x00bf),
- A6XX_PROTECT_RDONLY(0x0aed0, 0x002f),
- A6XX_PROTECT_NORDWR(0x0af00, 0x027f),
- A6XX_PROTECT_NORDWR(0x0b600, 0x1fff),
- A6XX_PROTECT_NORDWR(0x0dc00, 0x1fff),
- A6XX_PROTECT_RDONLY(0x0fc00, 0x1fff),
- A6XX_PROTECT_NORDWR(0x18400, 0x003f),
- A6XX_PROTECT_RDONLY(0x18440, 0x013f),
- A6XX_PROTECT_NORDWR(0x18580, 0x1fff),
- A6XX_PROTECT_NORDWR(0x1b400, 0x1fff),
- A6XX_PROTECT_NORDWR(0x1f400, 0x0477),
- A6XX_PROTECT_RDONLY(0x1f878, 0x0507),
- A6XX_PROTECT_NORDWR(0x1f930, 0x0329),
- A6XX_PROTECT_NORDWR(0x1fd80, 0x1fff),
- A6XX_PROTECT_NORDWR(0x27800, 0x007f),
- A6XX_PROTECT_RDONLY(0x27880, 0x0385),
- A6XX_PROTECT_NORDWR(0x27882, 0x000a),
- A6XX_PROTECT_NORDWR(0x27c06, 0x0000),
-};
-
-DECLARE_ADRENO_PROTECT(x285_protect, 15);
-
static const struct adreno_reglist_pipe x285_dyn_pwrup_reglist_regs[] = {
{ REG_A8XX_GRAS_TSEFE_DBG_ECO_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
{ REG_A8XX_GRAS_NC_MODE_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) },
@@ -522,57 +413,6 @@ static const struct adreno_reglist_pipe a810_nonctxt_regs[] = {
{ },
};
-static const u32 a810_protect_regs[] = {
- A6XX_PROTECT_RDONLY(0x00000, 0x03a3),
- A6XX_PROTECT_RDONLY(0x003b4, 0x008b),
- A6XX_PROTECT_NORDWR(0x00440, 0x001f),
- A6XX_PROTECT_RDONLY(0x00580, 0x005f),
- A6XX_PROTECT_NORDWR(0x005e0, 0x011f),
- A6XX_PROTECT_RDONLY(0x0074a, 0x0005),
- A6XX_PROTECT_RDONLY(0x00759, 0x0026),
- A6XX_PROTECT_RDONLY(0x00789, 0x0000),
- A6XX_PROTECT_RDONLY(0x0078c, 0x0013),
- A6XX_PROTECT_NORDWR(0x00800, 0x0029),
- A6XX_PROTECT_NORDWR(0x00837, 0x00af),
- A6XX_PROTECT_RDONLY(0x008e7, 0x00c9),
- A6XX_PROTECT_NORDWR(0x008ec, 0x00c3),
- A6XX_PROTECT_NORDWR(0x009b1, 0x0250),
- A6XX_PROTECT_RDONLY(0x00ce0, 0x0001),
- A6XX_PROTECT_RDONLY(0x00df0, 0x0000),
- A6XX_PROTECT_NORDWR(0x00df1, 0x0000),
- A6XX_PROTECT_NORDWR(0x00e01, 0x0000),
- A6XX_PROTECT_NORDWR(0x00e03, 0x1fff),
- A6XX_PROTECT_NORDWR(0x03c00, 0x00c5),
- A6XX_PROTECT_RDONLY(0x03cc6, 0x1fff),
- A6XX_PROTECT_NORDWR(0x08600, 0x01ff),
- A6XX_PROTECT_NORDWR(0x08e00, 0x00ff),
- A6XX_PROTECT_RDONLY(0x08f00, 0x0000),
- A6XX_PROTECT_NORDWR(0x08f01, 0x01be),
- A6XX_PROTECT_NORDWR(0x09600, 0x01ff),
- A6XX_PROTECT_RDONLY(0x0981a, 0x02e5),
- A6XX_PROTECT_NORDWR(0x09e00, 0x01ff),
- A6XX_PROTECT_NORDWR(0x0a600, 0x01ff),
- A6XX_PROTECT_NORDWR(0x0ae00, 0x0006),
- A6XX_PROTECT_NORDWR(0x0ae08, 0x0006),
- A6XX_PROTECT_NORDWR(0x0ae10, 0x036f),
- A6XX_PROTECT_NORDWR(0x0b600, 0x1fff),
- A6XX_PROTECT_NORDWR(0x0dc00, 0x1fff),
- A6XX_PROTECT_RDONLY(0x0fc00, 0x1fff),
- A6XX_PROTECT_NORDWR(0x18400, 0x003f),
- A6XX_PROTECT_RDONLY(0x18440, 0x013f),
- A6XX_PROTECT_NORDWR(0x18580, 0x1fff),
- A6XX_PROTECT_NORDWR(0x1b400, 0x1fff),
- A6XX_PROTECT_NORDWR(0x1f400, 0x0477),
- A6XX_PROTECT_RDONLY(0x1f878, 0x0787),
- A6XX_PROTECT_NORDWR(0x1f930, 0x0329),
- A6XX_PROTECT_NORDWR(0x20000, 0x1fff),
- A6XX_PROTECT_NORDWR(0x27800, 0x007f),
- A6XX_PROTECT_RDONLY(0x27880, 0x0381),
- A6XX_PROTECT_NORDWR(0x27882, 0x0001),
- A6XX_PROTECT_NORDWR(0x27c02, 0x0000),
-};
-DECLARE_ADRENO_PROTECT(a810_protect, 64);
-
static const uint32_t a810_pwrup_reglist_regs[] = {
REG_A6XX_UCHE_MODE_CNTL,
REG_A8XX_UCHE_VARB_IDLE_TIMEOUT,
@@ -889,64 +729,6 @@ static const struct adreno_reglist_pipe a840_nonctxt_regs[] = {
{ },
};
-static const u32 a840_protect_regs[] = {
- A6XX_PROTECT_RDONLY(0x00008, 0x039b),
- A6XX_PROTECT_RDONLY(0x003b4, 0x008b),
- A6XX_PROTECT_NORDWR(0x00440, 0x001f),
- A6XX_PROTECT_RDONLY(0x00580, 0x005f),
- A6XX_PROTECT_NORDWR(0x005e0, 0x011f),
- A6XX_PROTECT_RDONLY(0x0074a, 0x0005),
- A6XX_PROTECT_RDONLY(0x00759, 0x001b),
- A6XX_PROTECT_NORDWR(0x00775, 0x000a),
- A6XX_PROTECT_RDONLY(0x00789, 0x0000),
- A6XX_PROTECT_RDONLY(0x0078c, 0x0013),
- A6XX_PROTECT_NORDWR(0x00800, 0x0029),
- A6XX_PROTECT_NORDWR(0x00837, 0x00af),
- A6XX_PROTECT_RDONLY(0x008e7, 0x00c9),
- A6XX_PROTECT_NORDWR(0x008ec, 0x00c3),
- A6XX_PROTECT_NORDWR(0x009b1, 0x0250),
- A6XX_PROTECT_NORDWR(0x00c07, 0x0008),
- A6XX_PROTECT_RDONLY(0x00ce0, 0x0001),
- A6XX_PROTECT_RDONLY(0x00df0, 0x0000),
- A6XX_PROTECT_NORDWR(0x00df1, 0x0000),
- A6XX_PROTECT_NORDWR(0x00e01, 0x0000),
- A6XX_PROTECT_NORDWR(0x00e03, 0x1fff),
- A6XX_PROTECT_NORDWR(0x03c00, 0x00c5),
- A6XX_PROTECT_RDONLY(0x03cc6, 0x0039),
- A6XX_PROTECT_NORDWR(0x03d00, 0x1fff),
- A6XX_PROTECT_NORDWR(0x08600, 0x01ff),
- A6XX_PROTECT_NORDWR(0x08e00, 0x00ff),
- A6XX_PROTECT_RDONLY(0x08f00, 0x0000),
- A6XX_PROTECT_NORDWR(0x08f01, 0x01be),
- A6XX_PROTECT_NORDWR(0x09600, 0x01ff),
- A6XX_PROTECT_RDONLY(0x0981a, 0x02e5),
- A6XX_PROTECT_NORDWR(0x09e00, 0x01ff),
- A6XX_PROTECT_NORDWR(0x0a600, 0x01ff),
- A6XX_PROTECT_NORDWR(0x0a82e, 0x0000),
- A6XX_PROTECT_NORDWR(0x0ae00, 0x0000),
- A6XX_PROTECT_NORDWR(0x0ae02, 0x0004),
- A6XX_PROTECT_NORDWR(0x0ae08, 0x0006),
- A6XX_PROTECT_NORDWR(0x0ae10, 0x00bf),
- A6XX_PROTECT_RDONLY(0x0aed0, 0x002f),
- A6XX_PROTECT_NORDWR(0x0af00, 0x027f),
- A6XX_PROTECT_NORDWR(0x0b600, 0x1fff),
- A6XX_PROTECT_NORDWR(0x0dc00, 0x1fff),
- A6XX_PROTECT_RDONLY(0x0fc00, 0x1fff),
- A6XX_PROTECT_NORDWR(0x18400, 0x003f),
- A6XX_PROTECT_RDONLY(0x18440, 0x013f),
- A6XX_PROTECT_NORDWR(0x18580, 0x1fff),
- A6XX_PROTECT_NORDWR(0x1b400, 0x1fff),
- A6XX_PROTECT_NORDWR(0x1f400, 0x0477),
- A6XX_PROTECT_RDONLY(0x1f878, 0x0507),
- A6XX_PROTECT_NORDWR(0x1f930, 0x0329),
- A6XX_PROTECT_NORDWR(0x1fd80, 0x1fff),
- A6XX_PROTECT_NORDWR(0x27800, 0x007f),
- A6XX_PROTECT_RDONLY(0x27880, 0x0385),
- A6XX_PROTECT_NORDWR(0x27882, 0x0009),
- A6XX_PROTECT_NORDWR(0x27c06, 0x0000),
-};
-DECLARE_ADRENO_PROTECT(a840_protect, 15);
-
static const struct adreno_reglist a840_gbif[] = {
{ REG_A6XX_GBIF_QSB_SIDE0, 0x00071e20 },
{ REG_A6XX_GBIF_QSB_SIDE1, 0x00071e20 },
@@ -1350,67 +1132,6 @@ static const struct adreno_reglist_pipe a850_nonctxt_regs[] = {
{ },
};
-static const u32 a850_protect_regs[] = {
- A6XX_PROTECT_RDONLY(0x00008, 0x039b),
- A6XX_PROTECT_RDONLY(0x003b4, 0x008b),
- A6XX_PROTECT_NORDWR(0x00440, 0x001f),
- A6XX_PROTECT_RDONLY(0x00580, 0x005f),
- A6XX_PROTECT_NORDWR(0x005e0, 0x011f),
- A6XX_PROTECT_RDONLY(0x0074a, 0x0005),
- A6XX_PROTECT_RDONLY(0x00759, 0x001b),
- A6XX_PROTECT_NORDWR(0x00775, 0x000a),
- A6XX_PROTECT_RDONLY(0x00789, 0x0000),
- A6XX_PROTECT_RDONLY(0x0078c, 0x0013),
- A6XX_PROTECT_NORDWR(0x00800, 0x0029),
- A6XX_PROTECT_NORDWR(0x00837, 0x00af),
- A6XX_PROTECT_RDONLY(0x008e7, 0x00c9),
- A6XX_PROTECT_NORDWR(0x008ec, 0x00c3),
- A6XX_PROTECT_NORDWR(0x009b1, 0x0250),
- A6XX_PROTECT_NORDWR(0x00c07, 0x0008),
- A6XX_PROTECT_RDONLY(0x00ce0, 0x0001),
- A6XX_PROTECT_RDONLY(0x00df0, 0x0000),
- A6XX_PROTECT_NORDWR(0x00df1, 0x0000),
- A6XX_PROTECT_NORDWR(0x00df5, 0x0003),
- A6XX_PROTECT_RDONLY(0x00df9, 0x0007),
- A6XX_PROTECT_NORDWR(0x00e01, 0x0000),
- A6XX_PROTECT_NORDWR(0x00e03, 0x1fff),
- A6XX_PROTECT_NORDWR(0x03c00, 0x00c5),
- A6XX_PROTECT_RDONLY(0x03cc6, 0x0039),
- A6XX_PROTECT_NORDWR(0x03d00, 0x1fff),
- A6XX_PROTECT_NORDWR(0x08600, 0x01ff),
- A6XX_PROTECT_NORDWR(0x08e00, 0x00ff),
- A6XX_PROTECT_RDONLY(0x08f00, 0x0000),
- A6XX_PROTECT_NORDWR(0x08f01, 0x01be),
- A6XX_PROTECT_NORDWR(0x09600, 0x01ff),
- A6XX_PROTECT_RDONLY(0x0981a, 0x02e5),
- A6XX_PROTECT_NORDWR(0x09e00, 0x01ff),
- A6XX_PROTECT_NORDWR(0x0a600, 0x01ff),
- A6XX_PROTECT_NORDWR(0x0a82e, 0x0000),
- A6XX_PROTECT_NORDWR(0x0ae00, 0x0000),
- A6XX_PROTECT_NORDWR(0x0ae02, 0x0004),
- A6XX_PROTECT_NORDWR(0x0ae08, 0x0006),
- A6XX_PROTECT_NORDWR(0x0ae10, 0x00bf),
- A6XX_PROTECT_RDONLY(0x0aed0, 0x002f),
- A6XX_PROTECT_NORDWR(0x0af00, 0x027f),
- A6XX_PROTECT_NORDWR(0x0b600, 0x1fff),
- A6XX_PROTECT_NORDWR(0x0dc00, 0x1fff),
- A6XX_PROTECT_RDONLY(0x0fc00, 0x1fff),
- A6XX_PROTECT_NORDWR(0x18400, 0x003f),
- A6XX_PROTECT_RDONLY(0x18440, 0x013f),
- A6XX_PROTECT_NORDWR(0x18580, 0x1fff),
- A6XX_PROTECT_NORDWR(0x1ac00, 0x1fff),
- A6XX_PROTECT_NORDWR(0x1cc00, 0x1fff),
- A6XX_PROTECT_NORDWR(0x1f400, 0x0477),
- A6XX_PROTECT_RDONLY(0x1f878, 0x0507),
- A6XX_PROTECT_NORDWR(0x1f930, 0x0329),
- A6XX_PROTECT_NORDWR(0x1fd80, 0x1fff),
- A6XX_PROTECT_NORDWR(0x27800, 0x007f),
- A6XX_PROTECT_RDONLY(0x27880, 0x0385),
- A6XX_PROTECT_NORDWR(0x27882, 0x0035),
- A6XX_PROTECT_NORDWR(0x27c06, 0x0000),
-};
-DECLARE_ADRENO_PROTECT(a850_protect, 15);
-
static const u32 a850_pwrup_reglist_regs[] = {
REG_A8XX_RB_CMP_NC_MODE_CNTL,
REG_A8XX_SP_HLSQ_GC_GMEM_RANGE_MIN,
@@ -1631,7 +1352,7 @@ static const struct adreno_info a8xx_gpus[] = {
ADRENO_QUIRK_IFPC,
.funcs = &a8xx_gpu_funcs,
.a6xx = &(const struct a6xx_info) {
- .protect = &a830_protect,
+ .protect = &pakala_protect,
.nonctxt_reglist = a830_nonctxt_regs,
.pwrup_reglist = &a830_pwrup_reglist,
.dyn_pwrup_reglist = &a830_dyn_pwrup_reglist,
@@ -1676,7 +1397,7 @@ static const struct adreno_info a8xx_gpus[] = {
ADRENO_QUIRK_SOFTFUSE,
.funcs = &a8xx_gpu_funcs,
.a6xx = &(const struct a6xx_info) {
- .protect = &x285_protect,
+ .protect = &glymur_protect,
.nonctxt_reglist = x285_nonctxt_regs,
.pwrup_reglist = &a840_pwrup_reglist,
.dyn_pwrup_reglist = &x285_dyn_pwrup_reglist,
@@ -1721,7 +1442,7 @@ static const struct adreno_info a8xx_gpus[] = {
ADRENO_QUIRK_IFPC,
.funcs = &a8xx_gpu_funcs,
.a6xx = &(const struct a6xx_info) {
- .protect = &a840_protect,
+ .protect = &kaanapali_protect,
.nonctxt_reglist = a840_nonctxt_regs,
.pwrup_reglist = &a840_pwrup_reglist,
.dyn_pwrup_reglist = &a840_dyn_pwrup_reglist,
@@ -1767,7 +1488,7 @@ static const struct adreno_info a8xx_gpus[] = {
.funcs = &a8xx_gpu_funcs,
.zapfw = "gen80300_zap.mbn",
.a6xx = &(const struct a6xx_info) {
- .protect = &a810_protect,
+ .protect = &milos_protect,
.nonctxt_reglist = a810_nonctxt_regs,
.pwrup_reglist = &a810_pwrup_reglist,
.dyn_pwrup_reglist = &a810_dyn_pwrup_reglist,
@@ -1810,7 +1531,7 @@ static const struct adreno_info a8xx_gpus[] = {
ADRENO_QUIRK_SOFTFUSE,
.funcs = &a8xx_gpu_funcs,
.a6xx = &(const struct a6xx_info) {
- .protect = &x285_protect,
+ .protect = &glymur_protect,
.nonctxt_reglist = x285_nonctxt_regs,
.pwrup_reglist = &a840_pwrup_reglist,
.dyn_pwrup_reglist = &x285_dyn_pwrup_reglist,
@@ -1853,7 +1574,7 @@ static const struct adreno_info a8xx_gpus[] = {
ADRENO_QUIRK_IFPC,
.funcs = &a8xx_gpu_funcs,
.a6xx = &(const struct a6xx_info) {
- .protect = &a850_protect,
+ .protect = &maili_protect,
.nonctxt_reglist = a845_nonctxt_regs,
.pwrup_reglist = &a850_pwrup_reglist,
.dyn_pwrup_reglist = &a850_dyn_pwrup_reglist,
@@ -1897,7 +1618,7 @@ static const struct adreno_info a8xx_gpus[] = {
ADRENO_QUIRK_IFPC,
.funcs = &a8xx_gpu_funcs,
.a6xx = &(const struct a6xx_info) {
- .protect = &a850_protect,
+ .protect = &hawi_protect,
.nonctxt_reglist = a850_nonctxt_regs,
.pwrup_reglist = &a850_pwrup_reglist,
.dyn_pwrup_reglist = &a850_dyn_pwrup_reglist,
@@ -1932,6 +1653,10 @@ DECLARE_ADRENO_GPULIST(a8xx);
static inline __always_unused void __build_asserts(void)
{
- BUILD_BUG_ON(a810_protect.count > a810_protect.count_max);
- BUILD_BUG_ON(a830_protect.count > a830_protect.count_max);
+ BUILD_BUG_ON(glymur_protect.count > glymur_protect.count_max);
+ BUILD_BUG_ON(hawi_protect.count > hawi_protect.count_max);
+ BUILD_BUG_ON(kaanapali_protect.count > kaanapali_protect.count_max);
+ BUILD_BUG_ON(maili_protect.count > maili_protect.count_max);
+ BUILD_BUG_ON(milos_protect.count > milos_protect.count_max);
+ BUILD_BUG_ON(pakala_protect.count > pakala_protect.count_max);
}
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 19/19] drm/msm/adreno: Remove temporary __maybe_unused
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
` (16 preceding siblings ...)
2026-10-30 21:18 ` [PATCH v4 18/19] drm/msm/a8xx: Switch to generated protect regs Rob Clark
@ 2026-10-30 21:18 ` Rob Clark
17 siblings, 0 replies; 23+ messages in thread
From: Rob Clark @ 2026-10-30 21:18 UTC (permalink / raw)
To: dri-devel
Cc: linux-arm-msm, freedreno, Alexander Koskovich, Jie Zhang,
Akhil P Oommen, Bhaskar Reddy Valluru, Rob Clark, Sean Paul,
Konrad Dybcio, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Marijn Suijten, David Airlie, Simona Vetter, open list
Now that is unneeded.
Tested-by: Alexander Koskovich <akoskovich@pm.me> # Milos (Nothing Phone 3a)
Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/adreno_gpu.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
index ed83dd927b54..3ef3ac2d86f9 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
@@ -170,7 +170,7 @@ struct adreno_protect {
};
#define DECLARE_ADRENO_PROTECT(name, __count_max) \
-static const struct adreno_protect name __maybe_unused = { \
+static const struct adreno_protect name = { \
.regs = name ## _regs, \
.count = ARRAY_SIZE(name ## _regs), \
.count_max = __count_max, \
--
2.56.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v4 01/19] drm/msm: Import adreno_snapshot.h
2026-10-30 21:18 ` [PATCH v4 01/19] drm/msm: Import adreno_snapshot.h Rob Clark
@ 2026-10-30 21:27 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-10-30 21:27 UTC (permalink / raw)
To: Rob Clark, dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] drm/msm/adreno: malformed kernel-doc comments in adreno_snapshot.h
--
commit 4b4f4543f019fd5896ff1aace169bcf52f15975d
Author: Rob Clark <robin.clark@oss.qualcomm.com>
drm/msm: Import adreno_snapshot.h
This commit imports adreno_snapshot.h based on downstream kgsl_snapshot.h for
use with gen8 devcoredump support.
> diff --git a/drivers/gpu/drm/msm/adreno/adreno_snapshot.h b/drivers/gpu/drm/msm/adreno/adreno_snapshot.h
> new file mode 100644
> index 0000000000000..f9956e0bc1d5f
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/adreno/adreno_snapshot.h
> @@ -0,0 +1,321 @@
[ ... ]
> +/**
> + * enum trace_buffer_source - Bits to identify the source block of trace buffer information
> + * GX_DBGC : Signals captured from GX block
> + * CX_DBGC : Signals captured from CX block
> + */
> +enum trace_buffer_source {
> + GX_DBGC = 1,
> + CX_DBGC = 2,
> +};
> +
> +/**
> + * adreno_snapshot_trace_buffer: Header Information for the tracebuffer in snapshot.
> + */
> +struct adreno_snapshot_trace_buffer {
> + /** @dbgc_ctrl: Identify source for trace */
> + __u16 dbgc_ctrl;
> + /** @dbgc_ctrl: Identify source for trace */
> + __u16 segment;
[Severity: Low]
This isn't a bug, but there are a few kernel-doc formatting issues here when
running scripts/kernel-doc on drivers/gpu/drm/msm/adreno/adreno_snapshot.h:
1. In enum trace_buffer_source, should GX_DBGC and CX_DBGC have the leading @
prefix (@GX_DBGC: and @CX_DBGC:) so scripts/kernel-doc recognizes the
enumerator descriptions?
2. In the comment header for struct adreno_snapshot_trace_buffer, should it
begin with "struct adreno_snapshot_trace_buffer -" so scripts/kernel-doc does
not parse it as a function prototype?
3. Inside struct adreno_snapshot_trace_buffer, the inline kernel-doc comment
above the segment field repeats @dbgc_ctrl instead of documenting @segment.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261030211928.45761-1-robin.clark@oss.qualcomm.com?part=1
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v4 12/19] drm/msm: Inhibit IFPC for state snapshot
2026-10-30 21:18 ` [PATCH v4 12/19] drm/msm: Inhibit IFPC for state snapshot Rob Clark
@ 2026-10-30 21:38 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-10-30 21:38 UTC (permalink / raw)
To: Rob Clark, dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] drm/msm: race re-enabling IFPC during gpu_state_get() in msm_gpu_open()
- [High] drm/msm: premature GMU_OOB_PERFCOUNTER_SET before msm_gpu_hw_init()
--
commit 158363591a2450fd461587eb02116600b0d3c278
Author: Rob Clark <robin.clark@oss.qualcomm.com>
drm/msm: Inhibit IFPC for state snapshot
This commit adds an ifpc_disable guard in msm_gpu_open() to keep the GPU
awake while capturing a GPU state snapshot via debugfs.
> diff --git a/drivers/gpu/drm/msm/msm_debugfs.c b/drivers/gpu/drm/msm/msm_debugfs.c
> index e1b872576c7b1..14b588aeeb562 100644
> --- a/drivers/gpu/drm/msm/msm_debugfs.c
> +++ b/drivers/gpu/drm/msm/msm_debugfs.c
> @@ -81,6 +81,8 @@ static int msm_gpu_open(struct inode *inode, struct file *file)
> return -ENOMEM;
>
> scoped_guard (pm_runtime_active, &gpu->pdev->dev) {
> + guard(ifpc_disable)(gpu);
> +
> scoped_cond_guard (mutex_intr, return PTR_ERR(scope.lock), &gpu->lock) {
> msm_gpu_hw_init(gpu);
> show_priv->state = gpu->funcs->gpu_state_get(gpu);
[Severity: High]
As reported by Sashiko on v3 [1], can concurrent callers clear the
GMU_OOB_PERFCOUNTER_SET vote and re-enable IFPC while gpu_state_get() or
sample_worker() is still reading GPU GX registers?
In drivers/gpu/drm/msm/msm_gpu.h, the ifpc_disable guard toggles
ifpc_update() with a boolean rather than a reference count:
DEFINE_GUARD(ifpc_disable, struct msm_gpu *,
if (_T->funcs->ifpc_update) _T->funcs->ifpc_update(_T, true),
if (_T->funcs->ifpc_update) _T->funcs->ifpc_update(_T, false));
And in drivers/gpu/drm/msm/adreno/a6xx_gmu.c, a6xx_gmu_ifpc_update() tracks
the OOB request with a single bit (GMU_STATUS_OOB_PERF_SET) in gmu->status:
if (force_on && !test_and_set_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status))
a6xx_gmu_set_oob(gmu, GMU_OOB_PERFCOUNTER_SET);
else if (!force_on && test_and_clear_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status))
a6xx_gmu_clear_oob(gmu, GMU_OOB_PERFCOUNTER_SET);
When sysprof is not active (refcount_read(&gpu->sysprof_active) == 1),
several paths can clear GMU_STATUS_OOB_PERF_SET and drop
GMU_OOB_PERFCOUNTER_SET while another thread is inside gpu_state_get():
1. Because guard(ifpc_disable)(gpu) in msm_gpu_open() runs outside
gpu->lock, a second process opening the debugfs file enters
guard(ifpc_disable)(gpu) (which is a no-op since GMU_STATUS_OOB_PERF_SET
is already set) and blocks on gpu->lock. If that waiter is interrupted by
a signal, or when the first caller unlocks gpu->lock and exits its
ifpc_disable guard after the second caller has entered gpu_state_get(),
a6xx_gmu_ifpc_update(gpu, false) clears GMU_OOB_PERFCOUNTER_SET while
gpu_state_get() is actively accessing GX registers.
2. sample_worker() in drivers/gpu/drm/msm/msm_perfcntr.c runs on gpu->worker
without gpu->lock and also uses guard(ifpc_disable)(gpu). When either
sample_worker() or msm_gpu_open() finishes first, its guard cleanup clears
GMU_OOB_PERFCOUNTER_SET for the other.
3. msm_context_set_sysprof() in drivers/gpu/drm/msm/msm_submitqueue.c runs
without gpu->lock (including on every DRM fd close via msm_postclose())
and unconditionally calls gpu->funcs->ifpc_update(gpu, false), which also
clears GMU_OOB_PERFCOUNTER_SET mid-snapshot.
4. On A8xx, hw_init() in drivers/gpu/drm/msm/adreno/a8xx_gpu.c sets
GMU_OOB_PERFCOUNTER_SET and GMU_STATUS_OOB_PERF_SET when
msm_gpu_sysprof_no_perfcntr_zap(gpu) is true (such as when
gpu->perfcntrs->stream is active with sysprof_active == 1), whereas
a6xx_gmu_ifpc_update() only checks msm_gpu_sysprof_no_ifpc(gpu). Exiting
guard(ifpc_disable)(gpu) in msm_gpu_open() will then clear the active
perfcntr stream's OOB vote.
[Severity: High]
Also in msm_gpu_open(), does placing guard(ifpc_disable)(gpu) before
msm_gpu_hw_init(gpu) assert GMU_OOB_PERFCOUNTER_SET before the initial
GMU_OOB_GPU_SET handshake when waking from runtime suspend?
When the GPU is runtime-suspended, entering scoped_guard(pm_runtime_active,
&gpu->pdev->dev) runs a6xx_gmu_pm_resume(), which sets
gpu->needs_hw_init = true and boots the GMU while deferring the
GMU_OOB_GPU_SET (and legacy GMU_OOB_BOOT_SLUMBER) handshake until
msm_gpu_hw_init(gpu).
Calling guard(ifpc_disable)(gpu) before msm_gpu_hw_init(gpu) causes
a6xx_gmu_ifpc_update(gpu, true) to set GMU_STATUS_OOB_PERF_SET and call
a6xx_gmu_set_oob(gmu, GMU_OOB_PERFCOUNTER_SET) while gpu->needs_hw_init is
still true and before hw_init() has performed the GMU_OOB_GPU_SET handshake.
Furthermore, when msm_gpu_hw_init(gpu) subsequently calls hw_init() in
drivers/gpu/drm/msm/adreno/a6xx_gpu.c or
drivers/gpu/drm/msm/adreno/a8xx_gpu.c while msm_gpu_sysprof_no_ifpc(gpu) or
msm_gpu_sysprof_no_perfcntr_zap(gpu) is true:
if (!ret && msm_gpu_sysprof_no_ifpc(gpu)) {
ret = a6xx_gmu_set_oob(gmu, GMU_OOB_PERFCOUNTER_SET);
if (!ret)
set_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status);
}
hw_init() does not check whether GMU_STATUS_OOB_PERF_SET is already set and
calls a6xx_gmu_set_oob(gmu, GMU_OOB_PERFCOUNTER_SET) a second time while the
OOB bit is already asserted. Should guard(ifpc_disable)(gpu) be placed inside
the gpu->lock scope after msm_gpu_hw_init(gpu)?
[1] https://lore.kernel.org/r/20261029201730.151754-12-robin.clark@oss.qualcomm.com
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261030211928.45761-1-robin.clark@oss.qualcomm.com?part=12
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v4 11/19] drm/msm/debugfs: Use cleanup.h for gpu state dump
2026-10-30 21:18 ` [PATCH v4 11/19] drm/msm/debugfs: Use cleanup.h for gpu state dump Rob Clark
@ 2026-10-30 21:38 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-10-30 21:38 UTC (permalink / raw)
To: Rob Clark, dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] drm/msm/debugfs: internal cleanup.h scope.lock access in msm_gpu_open()
--
commit dbb53fa1a9b9e541e82f4c7d41aa75f373848b93
Author: Rob Clark <robin.clark@oss.qualcomm.com>
drm/msm/debugfs: Use cleanup.h for gpu state dump
This commit converts msm_gpu_open() in drivers/gpu/drm/msm/msm_debugfs.c to
use cleanup.h helpers (__free, scoped_guard, and scoped_cond_guard) to
simplify error handling when capturing the GPU state.
> diff --git a/drivers/gpu/drm/msm/msm_debugfs.c b/drivers/gpu/drm/msm/msm_debugfs.c
> index d05307bfa1235..e1b872576c7b1 100644
> --- a/drivers/gpu/drm/msm/msm_debugfs.c
> +++ b/drivers/gpu/drm/msm/msm_debugfs.c
> @@ -70,43 +70,36 @@ static int msm_gpu_open(struct inode *inode, struct file *file)
> struct drm_device *dev = inode->i_private;
> struct msm_drm_private *priv = dev->dev_private;
> struct msm_gpu *gpu = priv->gpu;
> - struct msm_gpu_show_priv *show_priv;
> int ret;
>
> if (!gpu || !gpu->funcs->gpu_state_get)
> return -ENODEV;
>
> - show_priv = kmalloc_obj(*show_priv);
> + struct msm_gpu_show_priv *show_priv __free(kfree)
> + = kmalloc_obj(*show_priv);
> if (!show_priv)
> return -ENOMEM;
>
> - ret = mutex_lock_interruptible(&gpu->lock);
> - if (ret)
> - goto free_priv;
> -
> - pm_runtime_get_sync(&gpu->pdev->dev);
> - msm_gpu_hw_init(gpu);
> - show_priv->state = gpu->funcs->gpu_state_get(gpu);
> - pm_runtime_put_sync(&gpu->pdev->dev);
> -
> - mutex_unlock(&gpu->lock);
> + scoped_guard (pm_runtime_active, &gpu->pdev->dev) {
> + scoped_cond_guard (mutex_intr, return PTR_ERR(scope.lock), &gpu->lock) {
[Severity: Low]
This isn't a runtime bug, but in msm_gpu_open() in
drivers/gpu/drm/msm/msm_debugfs.c, does referencing scope.lock inside
scoped_cond_guard() reach into internal implementation details of
include/linux/cleanup.h while also shadowing the outer scope variable?
Both __scoped_guard() and __scoped_cond_guard() in include/linux/cleanup.h
synthesize a for-loop variable named scope:
#define __scoped_cond_guard(_name, _fail, _label, args...) \
for (CLASS(_name, scope)(args); true; ({ goto _label; })) \
if (!__guard_ptr(_name)(&scope)) { \
BUILD_BUG_ON(!__is_cond_ptr(_name)); \
_fail; \
Because scoped_cond_guard() is nested inside scoped_guard(), the inner
scope variable shadows the outer one, and .lock is an internal struct
member where DEFINE_LOCK_GUARD_1_COND_4() stores ERR_PTR(_RET) for
consumption via ACQUIRE() and ACQUIRE_ERR().
Would it be cleaner to pass return -EINTR as the failure statement to
scoped_cond_guard(mutex_intr, ...), or use ACQUIRE(mutex_intr, ...) with
ACQUIRE_ERR() if the exact return value is needed?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261030211928.45761-1-robin.clark@oss.qualcomm.com?part=11
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v4 15/19] drm/msm/a8xx: devcoredump state capture support
2026-10-30 21:18 ` [PATCH v4 15/19] drm/msm/a8xx: devcoredump state capture support Rob Clark
@ 2026-10-30 21:46 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-10-30 21:46 UTC (permalink / raw)
To: Rob Clark, dri-devel
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] drm/msm/a8xx: inverted BO check and UAF in a8xx_snapshot_gpu_bo()
- [High] drm/msm/a8xx: wrong GX power check helper in a8xx_gpu_state_get()
- [High] drm/msm/a8xx: invalid slice 0 access in gen8_snapshot_mempool()
- [Medium] drm/msm/a8xx: dirty CP_APERTURE_CNTL_HOST state in a8xx_gpu_state_get()
--
commit 7da07b63ff0536a6ed00689438a2759bfa00dddf
Author: Rob Clark <robin.clark@oss.qualcomm.com>
drm/msm/a8xx: devcoredump state capture support
This commit adds devcoredump GPU state capture and snapshot formatting
support for Adreno Gen8 (A8xx) GPUs, including crashdumper script
execution, register and shader block dumping, debugbus capture, and GMU
state collection.
> diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu_state.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu_state.c
> new file mode 100644
> index 0000000000000..3c696244cd663
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu_state.c
> @@ -0,0 +1,1401 @@
[ ... ]
> +#define NEXT_SLICE(_slice, _region, _slice_mask) \
> + ((_region == UNSLICE) ? fls(_slice_mask) : next_slice(_slice, _slice_mask))
> +
> +#define FIRST_SLICE(_region, _slice_mask) \
> + ((_region == UNSLICE) ? 0 : NEXT_SLICE(0, _region, _slice_mask))
> +
> +#define FOR_EACH_SLICE(_slice, _region, _slice_mask) \
> + for (_slice = FIRST_SLICE(_region, _slice_mask); \
> + _slice < fls(_slice_mask); \
> + _slice = NEXT_SLICE(++(_slice), _region, _slice_mask))
[ ... ]
> +static void gen8_legacy_snapshot_registers(struct msm_gpu *gpu,
> + struct adreno_gpu_state *state,
> + const struct gen8_reg_list *regs,
> + u32 slice_id)
> +{
> + int count;
> +
> + /* Figure out how many registers we are going to dump */
> + count = adreno_snapshot_regs_count(regs->regs);
> +
> + snapshot_sect (state, MVC_V3, 4 * count, {
> + .ctxt_id = 0,
> + .cluster_id = CLUSTER_NONE,
> + .pipe_id = PIPE_NONE,
> + .location_id = UINT_MAX,
> + .sp_id = UINT_MAX,
> + .usptp_id = UINT_MAX,
> + .slice_id = HEADER_SLICE_ID(regs->slice_region, slice_id),
> + }) {
> + unsigned long flags;
> +
> + if (regs->sel)
> + gpu_write(gpu, regs->sel->host_reg, regs->sel->val);
> +
> + if (regs->slice_region) {
> + u32 val = GEN8_CP_APERTURE_REG_VAL(slice_id, 0, 0, 0);
> + a8xx_aperture_acquire_raw(gpu, val, &flags);
> + }
> +
> + /* Make sure the previous writes are posted before reading */
> + mb();
> +
> + for (const u32 *ptr = regs->regs; ptr[0] != UINT_MAX; ptr += 2) {
> + snapshot_mvc_regs_v3_subheader(§, ptr);
> +
> + for (int k = ptr[0]; k <= ptr[1]; k++) {
> + snapshot_sect_u32(§, reg_read(gpu, k));
> + }
> + }
> +
> + if (regs->slice_region)
> + a8xx_aperture_release(gpu, flags);
> + }
> +}
[Severity: Medium]
As reported by Sashiko on v3 [1], does gen8_legacy_snapshot_registers()
leave REG_A8XX_CP_APERTURE_CNTL_HOST and a6xx_gpu->cached_aperture with a
stale slice configuration when transitioning from a SLICE entry to an
UNSLICE entry?
In drivers/gpu/drm/msm/adreno/a8xx_gpu.c, a8xx_aperture_acquire_raw() writes
the aperture register and updates cached_aperture, while
a8xx_aperture_release() only unlocks a6xx_gpu->aperture_lock without
resetting the register:
drivers/gpu/drm/msm/adreno/a8xx_gpu.c:a8xx_aperture_release() {
spin_unlock_irqrestore(&a6xx_gpu->aperture_lock, flags);
}
Because gen8_legacy_snapshot_registers() only calls
a8xx_aperture_acquire_raw() when regs->slice_region is non-zero (SLICE) and
does not clear the aperture on release or when regs->slice_region is
UNSLICE, any UNSLICE entry that follows a SLICE entry in
b->pre_crashdumper_regs or b->reg_list will be read while
A8XX_CP_APERTURE_CNTL_HOST_USESLICEID and the previous slice_id are still
active in REG_A8XX_CP_APERTURE_CNTL_HOST.
In addition, gen8_legacy_snapshot_registers() writes regs->sel->host_reg
before calling a8xx_aperture_acquire_raw(), so the selector register is
programmed before the target slice aperture is selected and outside
aperture_lock (unlike gen8_legacy_snapshot_mvc()).
Similarly, at the out label in a8xx_gpu_state_get(), a8xx_aperture_clear()
is not called before returning:
drivers/gpu/drm/msm/adreno/a8xx_gpu_state.c:a8xx_gpu_state_get() {
...
out:
gpu_write(gpu, REG_A8XX_RBBM_SNAPSHOT_STATUS, 0x0);
if (dumper)
msm_gem_kernel_put(dumper->bo, gpu->vm);
}
When dumper is NULL (such as during an SMMU stall-on-fault devcoredump where
the GPU resumes without a reset) or on an early goto out path, can leaving
REG_A8XX_CP_APERTURE_CNTL_HOST and cached_aperture programmed to the last
cluster, pipe, slice, and context corrupt subsequent host AHB accesses?
[ ... ]
> +static bool gen8_snapshot_mempool(struct msm_gpu *gpu,
> + struct adreno_gpu_state *state,
> + struct adreno_crashdumper *dumper)
> +{
> + const struct gen8_snapshot_block_list *b = block_list(gpu);
> + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(to_adreno_gpu(gpu));
> + u32 slice_mask = a6xx_gpu->slice_mask;
> + u32 first_slice = a8xx_get_first_slice(a6xx_gpu);
> + bool ret = true;
> +
> + for (int i = 0; i < b->mempool_index_registers_len && ret; i++) {
> + const struct gen8_cp_indexed_reg *cp_indexed_reg =
> + &b->mempool_index_registers[i];
> + unsigned long flags;
> + u32 j;
> +
> + FOR_EACH_SLICE (j, cp_indexed_reg->slice_region, slice_mask) {
> + u32 pipe_id = cp_indexed_reg->pipe_id;
> +
> + /* Set CP_CHICKEN_DBG[StabilizeMVC] to stabilize it while dumping */
> + a8xx_aperture_acquire_raw(gpu,
> + GEN8_CP_APERTURE_REG_VAL_NS(first_slice, pipe_id, 0, 0),
> + &flags);
> + gpu_rmw(gpu, REG_A8XX_CP_CHICKEN_DBG_PIPE, BIT(2), BIT(2));
> + a8xx_aperture_release(gpu, flags);
> +
> + a8xx_aperture_acquire_raw(gpu,
> + GEN8_CP_APERTURE_REG_VAL(j, pipe_id, 0, 0),
> + &flags);
> + gpu_rmw(gpu, REG_A8XX_CP_SLICE_CHICKEN_DBG_PIPE, BIT(2), BIT(2));
> + a8xx_aperture_release(gpu, flags);
> +
> + ret = gen8_snapshot_indexed_registers(gpu, state, dumper,
> + cp_indexed_reg->addr,
> + cp_indexed_reg->data, 0,
> + cp_indexed_reg->size,
> + cp_indexed_reg->pipe_id,
> + HEADER_SLICE_ID(cp_indexed_reg->slice_region, j));
> +
> + /* Reset CP_CHICKEN_DBG[StabilizeMVC] once we are done */
> + a8xx_aperture_acquire_raw(gpu,
> + GEN8_CP_APERTURE_REG_VAL_NS(first_slice, pipe_id, 0, 0),
> + &flags);
> + gpu_rmw(gpu, REG_A8XX_CP_CHICKEN_DBG_PIPE, BIT(2), 0);
> + a8xx_aperture_release(gpu, flags);
> +
> + a8xx_aperture_acquire_raw(gpu,
> + GEN8_CP_APERTURE_REG_VAL(j, pipe_id, 0, 0),
> + &flags);
> + gpu_rmw(gpu, REG_A8XX_CP_SLICE_CHICKEN_DBG_PIPE, BIT(2), 0);
> + a8xx_aperture_release(gpu, flags);
> +
> + if (!ret)
> + break;
> + }
> + }
[Severity: High]
As reported by Sashiko on v3 [2], when cp_indexed_reg->slice_region is
UNSLICE in gen8_snapshot_mempool(), FIRST_SLICE(UNSLICE, slice_mask) sets j
to 0 regardless of slice_mask.
In that case, gen8_snapshot_mempool() still programs
GEN8_CP_APERTURE_REG_VAL(0, pipe_id, 0, 0) (which sets
A8XX_CP_APERTURE_CNTL_HOST_USESLICEID for slice 0) and performs a
read-modify-write on the per-slice register
REG_A8XX_CP_SLICE_CHICKEN_DBG_PIPE before and after
gen8_snapshot_indexed_registers().
On A8xx GPUs with partial slice configurations where slice 0 is fused off in
slice_mask (which is why first_slice is queried via a8xx_get_first_slice()
for REG_A8XX_CP_CHICKEN_DBG_PIPE), won't accessing
REG_A8XX_CP_SLICE_CHICKEN_DBG_PIPE on slice 0 during UNSLICE iterations
access a disabled slice and trigger an AHB/NoC bus error? Should
REG_A8XX_CP_SLICE_CHICKEN_DBG_PIPE only be touched when
cp_indexed_reg->slice_region == SLICE (and REG_A8XX_CP_CHICKEN_DBG_PIPE when
cp_indexed_reg->slice_region == UNSLICE)?
[ ... ]
> +static void a8xx_snapshot_gpu_bo(struct adreno_gpu_state *state,
> + struct drm_gem_object *bo,
> + uint64_t iova)
> +{
> + if (!IS_ERR_OR_NULL(bo))
> + return;
> +
> + snapshot_sect (state, GPU_OBJECT_V2, bo->size, {
> + .type = SNAPSHOT_GPU_OBJECT_GLOBAL,
> + .gpuaddr = iova,
> + .size = bo->size / 4, /* dwords */
> + }) {
> + void *ptr = msm_gem_get_vaddr(bo);
> + snapshot_sect_data(§, ptr, bo->size);
> + msm_gem_put_vaddr(bo);
> + }
> +}
[Severity: High]
As reported by Sashiko on v3 [3], is the IS_ERR_OR_NULL(bo) check inverted in
a8xx_snapshot_gpu_bo()?
With if (!IS_ERR_OR_NULL(bo)) return;, valid GEM buffer objects return early
without being captured, while a NULL or ERR_PTR bo falls through and
dereferences bo->size (for example, when preemption is disabled and
a6xx_gpu->preempt_bo[0] and a6xx_gpu->preempt_smmu_bo[0] are NULL).
Even with the condition fixed to if (IS_ERR_OR_NULL(bo)) return;,
a8xx_gpu_state_get() calls a8xx_snapshot_gpu_bo() in a loop over
gpu->nr_rings:
drivers/gpu/drm/msm/adreno/a8xx_gpu_state.c:a8xx_gpu_state_get() {
...
for (unsigned i = 0; i < gpu->nr_rings; i++) {
a8xx_snapshot_gpu_bo(state, a6xx_gpu->preempt_bo[i],
a6xx_gpu->preempt_iova[i]);
a8xx_snapshot_gpu_bo(state, a6xx_gpu->preempt_smmu_bo[i],
a6xx_gpu->preempt_smmu_iova[i]);
}
}
If a6xx_preempt_init() fails after allocating a6xx_gpu->preempt_bo[0], it
calls a6xx_preempt_fini() and falls back to gpu->nr_rings = 1, but
a6xx_preempt_fini() frees a6xx_gpu->preempt_bo[i] via msm_gem_kernel_put()
without clearing the pointer to NULL. Should a8xx_gpu_state_get() guard this
loop with if (gpu->nr_rings > 1), and should a8xx_snapshot_gpu_bo() also
check IS_ERR(ptr) after msm_gem_get_vaddr(bo) before passing ptr to
snapshot_sect_data() and calling msm_gem_put_vaddr(bo)?
[ ... ]
> +struct msm_gpu_state *a8xx_gpu_state_get(struct msm_gpu *gpu)
> +{
[ ... ]
> + snapshot_sect (state, DEBUG, 4, {
> + .type = SNAPSHOT_DEBUG_SLICE_MASK,
> + .size = 1,
> + }) {
> + snapshot_sect_u32(§, a6xx_gpu->slice_mask);
> + }
> + gen8_snapshot_cx_debugbus(gpu, state);
> +
> + /* If GX isn't on the rest of the data isn't going to be accessible */
> + if (!a6xx_gmu_gx_is_on(adreno_gpu))
> + return &state->base;
[Severity: High]
As reported by Sashiko on v3 [4], should a8xx_gpu_state_get() call
a8xx_gmu_gx_is_on(adreno_gpu) (or adreno_gpu->funcs->gx_is_on(adreno_gpu))
here instead of a6xx_gmu_gx_is_on(adreno_gpu)?
In drivers/gpu/drm/msm/adreno/a6xx_gmu.c, a6xx_gmu_gx_is_on() reads
REG_A6XX_GMU_SPTPRAC_PWR_CLK_STATUS and checks the A6xx GX power and clock
bits because adreno_is_a7xx(adreno_gpu) is false on A8xx:
drivers/gpu/drm/msm/adreno/a6xx_gmu.c:a6xx_gmu_gx_is_on() {
...
val = gmu_read(gmu, REG_A6XX_GMU_SPTPRAC_PWR_CLK_STATUS);
if (adreno_is_a7xx(adreno_gpu))
return !(val &
(A7XX_GMU_SPTPRAC_PWR_CLK_STATUS_GX_HM_GDSC_POWER_OFF |
A7XX_GMU_SPTPRAC_PWR_CLK_STATUS_GX_HM_CLK_OFF));
return !(val &
(A6XX_GMU_SPTPRAC_PWR_CLK_STATUS_GX_HM_GDSC_POWER_OFF |
A6XX_GMU_SPTPRAC_PWR_CLK_STATUS_GX_HM_CLK_OFF));
}
On A8xx, GX power status is reported via REG_A8XX_CX_MISC_GFX_PWR_CLK_STATUS
on the A850 family (where GMU MX may be off) or REG_A8XX_GMU_PWR_CLK_STATUS
in a8xx_gmu_gx_is_on(). Can calling a6xx_gmu_gx_is_on() on A8xx misreport GX
power status and allow unclocked GX register accesses when GX is powered
off?
[1] https://lore.kernel.org/r/20261029201730.151754-13-robin.clark@oss.qualcomm.com
[2] https://lore.kernel.org/r/20261029201730.151754-13-robin.clark@oss.qualcomm.com
[3] https://lore.kernel.org/r/20261029201730.151754-13-robin.clark@oss.qualcomm.com
[4] https://lore.kernel.org/r/20261029201730.151754-13-robin.clark@oss.qualcomm.com
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261030211928.45761-1-robin.clark@oss.qualcomm.com?part=15
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-10-30 21:46 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-30 21:18 [PATCH v4 00/19] drm/msm/a8xx: GPU state capture support Rob Clark
2026-10-30 21:18 ` [PATCH v4 01/19] drm/msm: Import adreno_snapshot.h Rob Clark
2026-10-30 21:27 ` sashiko-bot
2026-10-30 21:18 ` [PATCH v4 02/19] drm/msm/a8xx: Export aperture helpers Rob Clark
2026-10-30 21:18 ` [PATCH v4 03/19] drm/msm/a8xx: Add a8xx_aperture_acquire_raw() Rob Clark
2026-10-30 21:18 ` [PATCH v4 04/19] drm/msm/a8xx: Export slice helper Rob Clark
2026-10-30 21:18 ` [PATCH v4 05/19] drm/msm/a8xx: Add missing a850 ifpc reglist entries Rob Clark
2026-10-30 21:18 ` [PATCH v4 06/19] drm/msm/a8xx: Allow nonctx reglist to include GMU regs Rob Clark
2026-10-30 21:18 ` [PATCH v4 07/19] drm/msm/a8xx: Stop getting GMEM_PROTECT from nonctx list Rob Clark
2026-10-30 21:18 ` [PATCH v4 08/19] drm/msm/adreno: Fix PROTECT span length Rob Clark
2026-10-30 21:18 ` [PATCH v4 09/19] drm/msm: Rename sysprof_setup() Rob Clark
2026-10-30 21:18 ` [PATCH v4 10/19] drm/msm: Add cleanup help for disabling IFPC Rob Clark
2026-10-30 21:18 ` [PATCH v4 11/19] drm/msm/debugfs: Use cleanup.h for gpu state dump Rob Clark
2026-10-30 21:38 ` sashiko-bot
2026-10-30 21:18 ` [PATCH v4 12/19] drm/msm: Inhibit IFPC for state snapshot Rob Clark
2026-10-30 21:38 ` sashiko-bot
2026-10-30 21:18 ` [PATCH v4 13/19] drm/msm/adreno: add temporary __maybe_unused Rob Clark
2026-10-30 21:18 ` [PATCH v4 15/19] drm/msm/a8xx: devcoredump state capture support Rob Clark
2026-10-30 21:46 ` sashiko-bot
2026-10-30 21:18 ` [PATCH v4 16/19] drm/msm/a8xx: Add all CP_PROTECT_PIPE to pwrup reglist Rob Clark
2026-10-30 21:18 ` [PATCH v4 17/19] drm/msm/a8xx: Use CP_PROTECT_PIPE regs Rob Clark
2026-10-30 21:18 ` [PATCH v4 18/19] drm/msm/a8xx: Switch to generated protect regs Rob Clark
2026-10-30 21:18 ` [PATCH v4 19/19] drm/msm/adreno: Remove temporary __maybe_unused Rob Clark
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.