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

drm/panthor: Support GLB_REQ.STATE field for Mali-G1 GPUs

Add support for the GLB_REQ.STATE field introduced in CSF v4.1+, which
replaces the HALT bit to provide finer control over the MCU state. This
change implements basic handling for transitioning the MCU between
ACTIVE and HALT states on Mali-G1 GPUs.

The update introduces new helpers to issue the state change requests,
poll for MCU halt completion, and restore the MCU to an active state
after halting.

Reviewed-by: Steven Price <steven.price@arm.com>
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
Link: https://patch.msgid.link/20251125125548.3282320-7-karunika.choo@arm.com
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>

authored by

Karunika Choo and committed by
Boris Brezillon
51407254 9ee52f5c

+80 -16
+73 -16
drivers/gpu/drm/panthor/panthor_fw.c
··· 34 34 #define PROGRESS_TIMEOUT_SCALE_SHIFT 10 35 35 #define IDLE_HYSTERESIS_US 800 36 36 #define PWROFF_HYSTERESIS_US 10000 37 + #define MCU_HALT_TIMEOUT_US (1ULL * USEC_PER_SEC) 37 38 38 39 /** 39 40 * struct panthor_fw_binary_hdr - Firmware binary header. ··· 317 316 return NULL; 318 317 319 318 return &ptdev->fw->iface.streams[csg_slot][cs_slot]; 319 + } 320 + 321 + static bool panthor_fw_has_glb_state(struct panthor_device *ptdev) 322 + { 323 + struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev); 324 + 325 + return glb_iface->control->version >= CSF_IFACE_VERSION(4, 1, 0); 320 326 } 321 327 322 328 /** ··· 1005 997 GLB_IDLE_EN | 1006 998 GLB_IDLE; 1007 999 1000 + if (panthor_fw_has_glb_state(ptdev)) 1001 + glb_iface->input->ack_irq_mask |= GLB_STATE_MASK; 1002 + 1008 1003 panthor_fw_update_reqs(glb_iface, req, GLB_IDLE_EN, GLB_IDLE_EN); 1009 1004 panthor_fw_toggle_reqs(glb_iface, req, ack, 1010 1005 GLB_CFG_ALLOC_EN | ··· 1081 1070 drm_err(&ptdev->base, "Failed to stop MCU"); 1082 1071 } 1083 1072 1073 + static bool panthor_fw_mcu_halted(struct panthor_device *ptdev) 1074 + { 1075 + struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev); 1076 + bool halted; 1077 + 1078 + halted = gpu_read(ptdev, MCU_STATUS) == MCU_STATUS_HALT; 1079 + 1080 + if (panthor_fw_has_glb_state(ptdev)) 1081 + halted &= (GLB_STATE_GET(glb_iface->output->ack) == GLB_STATE_HALT); 1082 + 1083 + return halted; 1084 + } 1085 + 1086 + static void panthor_fw_halt_mcu(struct panthor_device *ptdev) 1087 + { 1088 + struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev); 1089 + 1090 + if (panthor_fw_has_glb_state(ptdev)) 1091 + panthor_fw_update_reqs(glb_iface, req, GLB_STATE(GLB_STATE_HALT), GLB_STATE_MASK); 1092 + else 1093 + panthor_fw_update_reqs(glb_iface, req, GLB_HALT, GLB_HALT); 1094 + 1095 + gpu_write(ptdev, CSF_DOORBELL(CSF_GLB_DOORBELL_ID), 1); 1096 + } 1097 + 1098 + static bool panthor_fw_wait_mcu_halted(struct panthor_device *ptdev) 1099 + { 1100 + bool halted = false; 1101 + 1102 + if (read_poll_timeout_atomic(panthor_fw_mcu_halted, halted, halted, 10, 1103 + MCU_HALT_TIMEOUT_US, 0, ptdev)) { 1104 + drm_warn(&ptdev->base, "Timed out waiting for MCU to halt"); 1105 + return false; 1106 + } 1107 + 1108 + return true; 1109 + } 1110 + 1111 + static void panthor_fw_mcu_set_active(struct panthor_device *ptdev) 1112 + { 1113 + struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev); 1114 + 1115 + if (panthor_fw_has_glb_state(ptdev)) 1116 + panthor_fw_update_reqs(glb_iface, req, GLB_STATE(GLB_STATE_ACTIVE), GLB_STATE_MASK); 1117 + else 1118 + panthor_fw_update_reqs(glb_iface, req, 0, GLB_HALT); 1119 + } 1120 + 1084 1121 /** 1085 1122 * panthor_fw_pre_reset() - Call before a reset. 1086 1123 * @ptdev: Device. ··· 1145 1086 ptdev->reset.fast = false; 1146 1087 1147 1088 if (!on_hang) { 1148 - struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev); 1149 - u32 status; 1150 - 1151 - panthor_fw_update_reqs(glb_iface, req, GLB_HALT, GLB_HALT); 1152 - gpu_write(ptdev, CSF_DOORBELL(CSF_GLB_DOORBELL_ID), 1); 1153 - if (!gpu_read_poll_timeout(ptdev, MCU_STATUS, status, 1154 - status == MCU_STATUS_HALT, 10, 1155 - 100000)) { 1156 - ptdev->reset.fast = true; 1157 - } else { 1089 + panthor_fw_halt_mcu(ptdev); 1090 + if (!panthor_fw_wait_mcu_halted(ptdev)) 1158 1091 drm_warn(&ptdev->base, "Failed to cleanly suspend MCU"); 1159 - } 1092 + else 1093 + ptdev->reset.fast = true; 1160 1094 } 1095 + panthor_fw_stop(ptdev); 1161 1096 1162 1097 panthor_job_irq_suspend(&ptdev->fw->irq); 1163 1098 panthor_fw_stop(ptdev); ··· 1180 1127 */ 1181 1128 panthor_reload_fw_sections(ptdev, true); 1182 1129 } else { 1183 - /* The FW detects 0 -> 1 transitions. Make sure we reset 1184 - * the HALT bit before the FW is rebooted. 1130 + /* 1131 + * If the FW was previously successfully halted in the pre-reset 1132 + * operation, we need to transition it to active again before 1133 + * the FW is rebooted. 1185 1134 * This is not needed on a slow reset because FW sections are 1186 1135 * re-initialized. 1187 1136 */ 1188 - struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev); 1189 - 1190 - panthor_fw_update_reqs(glb_iface, req, 0, GLB_HALT); 1137 + panthor_fw_mcu_set_active(ptdev); 1191 1138 } 1192 1139 1193 1140 ret = panthor_fw_start(ptdev); ··· 1224 1171 /* Make sure the IRQ handler cannot be called after that point. */ 1225 1172 if (ptdev->fw->irq.irq) 1226 1173 panthor_job_irq_suspend(&ptdev->fw->irq); 1174 + 1175 + panthor_fw_halt_mcu(ptdev); 1176 + if (!panthor_fw_wait_mcu_halted(ptdev)) 1177 + drm_warn(&ptdev->base, "Failed to halt MCU on unplug"); 1227 1178 1228 1179 panthor_fw_stop(ptdev); 1229 1180 }
+7
drivers/gpu/drm/panthor/panthor_fw.h
··· 214 214 #define GLB_FWCFG_UPDATE BIT(9) 215 215 #define GLB_IDLE_EN BIT(10) 216 216 #define GLB_SLEEP BIT(12) 217 + #define GLB_STATE_MASK GENMASK(14, 12) 218 + #define GLB_STATE_ACTIVE 0 219 + #define GLB_STATE_HALT 1 220 + #define GLB_STATE_SLEEP 2 221 + #define GLB_STATE_SUSPEND 3 222 + #define GLB_STATE(x) (((x) << 12) & GLB_STATE_MASK) 223 + #define GLB_STATE_GET(x) (((x) & GLB_STATE_MASK) >> 12) 217 224 #define GLB_INACTIVE_COMPUTE BIT(20) 218 225 #define GLB_INACTIVE_FRAGMENT BIT(21) 219 226 #define GLB_INACTIVE_TILER BIT(22)