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

Configure Feed

Select the types of activity you want to include in your feed.

drm/panthor: Implement L2 power on/off via PWR_CONTROL

This patch adds common helpers to issue power commands, poll
transitions, and validate domain state, then wires them into the L2
on/off paths.

The L2 power-on sequence now delegates control of the SHADER and TILER
domains to the MCU when allowed, while the L2 itself is never delegated.
On power-off, dependent domains beneath the L2 are checked, and if
necessary, retracted and powered down to maintain proper domain
ordering.

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

authored by

Karunika Choo and committed by
Boris Brezillon
ee4f9af0 c27787f2

+383
+378
drivers/gpu/drm/panthor/panthor_pwr.c
··· 24 24 PWR_IRQ_COMMAND_NOT_ALLOWED | \ 25 25 PWR_IRQ_COMMAND_INVALID) 26 26 27 + #define PWR_ALL_CORES_MASK GENMASK_U64(63, 0) 28 + 29 + #define PWR_DOMAIN_MAX_BITS 16 30 + 31 + #define PWR_TRANSITION_TIMEOUT_US (2ULL * USEC_PER_SEC) 32 + 33 + #define PWR_RETRACT_TIMEOUT_US (2ULL * USEC_PER_MSEC) 34 + 27 35 /** 28 36 * struct panthor_pwr - PWR_CONTROL block management data. 29 37 */ ··· 67 59 spin_unlock(&ptdev->pwr->reqs_lock); 68 60 } 69 61 PANTHOR_IRQ_HANDLER(pwr, PWR, panthor_pwr_irq_handler); 62 + 63 + static void panthor_pwr_write_command(struct panthor_device *ptdev, u32 command, u64 args) 64 + { 65 + if (args) 66 + gpu_write64(ptdev, PWR_CMDARG, args); 67 + 68 + gpu_write(ptdev, PWR_COMMAND, command); 69 + } 70 + 71 + static const char *get_domain_name(u8 domain) 72 + { 73 + switch (domain) { 74 + case PWR_COMMAND_DOMAIN_L2: 75 + return "L2"; 76 + case PWR_COMMAND_DOMAIN_TILER: 77 + return "Tiler"; 78 + case PWR_COMMAND_DOMAIN_SHADER: 79 + return "Shader"; 80 + case PWR_COMMAND_DOMAIN_BASE: 81 + return "Base"; 82 + case PWR_COMMAND_DOMAIN_STACK: 83 + return "Stack"; 84 + } 85 + return "Unknown"; 86 + } 87 + 88 + static u32 get_domain_base(u8 domain) 89 + { 90 + switch (domain) { 91 + case PWR_COMMAND_DOMAIN_L2: 92 + return PWR_L2_PRESENT; 93 + case PWR_COMMAND_DOMAIN_TILER: 94 + return PWR_TILER_PRESENT; 95 + case PWR_COMMAND_DOMAIN_SHADER: 96 + return PWR_SHADER_PRESENT; 97 + case PWR_COMMAND_DOMAIN_BASE: 98 + return PWR_BASE_PRESENT; 99 + case PWR_COMMAND_DOMAIN_STACK: 100 + return PWR_STACK_PRESENT; 101 + } 102 + return 0; 103 + } 104 + 105 + static u32 get_domain_ready_reg(u32 domain) 106 + { 107 + return get_domain_base(domain) + (PWR_L2_READY - PWR_L2_PRESENT); 108 + } 109 + 110 + static u32 get_domain_pwrtrans_reg(u32 domain) 111 + { 112 + return get_domain_base(domain) + (PWR_L2_PWRTRANS - PWR_L2_PRESENT); 113 + } 114 + 115 + static bool is_valid_domain(u32 domain) 116 + { 117 + return get_domain_base(domain) != 0; 118 + } 119 + 120 + static bool has_rtu(struct panthor_device *ptdev) 121 + { 122 + return ptdev->gpu_info.gpu_features & GPU_FEATURES_RAY_TRAVERSAL; 123 + } 124 + 125 + static u8 get_domain_subdomain(struct panthor_device *ptdev, u32 domain) 126 + { 127 + if (domain == PWR_COMMAND_DOMAIN_SHADER && has_rtu(ptdev)) 128 + return PWR_COMMAND_SUBDOMAIN_RTU; 129 + 130 + return 0; 131 + } 132 + 133 + static int panthor_pwr_domain_wait_transition(struct panthor_device *ptdev, u32 domain, 134 + u32 timeout_us) 135 + { 136 + u32 pwrtrans_reg = get_domain_pwrtrans_reg(domain); 137 + u64 val; 138 + int ret = 0; 139 + 140 + ret = gpu_read64_poll_timeout(ptdev, pwrtrans_reg, val, !(PWR_ALL_CORES_MASK & val), 100, 141 + timeout_us); 142 + if (ret) { 143 + drm_err(&ptdev->base, "%s domain power in transition, pwrtrans(0x%llx)", 144 + get_domain_name(domain), val); 145 + return ret; 146 + } 147 + 148 + return 0; 149 + } 150 + 151 + static void panthor_pwr_debug_info_show(struct panthor_device *ptdev) 152 + { 153 + drm_info(&ptdev->base, "GPU_FEATURES: 0x%016llx", gpu_read64(ptdev, GPU_FEATURES)); 154 + drm_info(&ptdev->base, "PWR_STATUS: 0x%016llx", gpu_read64(ptdev, PWR_STATUS)); 155 + drm_info(&ptdev->base, "L2_PRESENT: 0x%016llx", gpu_read64(ptdev, PWR_L2_PRESENT)); 156 + drm_info(&ptdev->base, "L2_PWRTRANS: 0x%016llx", gpu_read64(ptdev, PWR_L2_PWRTRANS)); 157 + drm_info(&ptdev->base, "L2_READY: 0x%016llx", gpu_read64(ptdev, PWR_L2_READY)); 158 + drm_info(&ptdev->base, "TILER_PRESENT: 0x%016llx", gpu_read64(ptdev, PWR_TILER_PRESENT)); 159 + drm_info(&ptdev->base, "TILER_PWRTRANS: 0x%016llx", gpu_read64(ptdev, PWR_TILER_PWRTRANS)); 160 + drm_info(&ptdev->base, "TILER_READY: 0x%016llx", gpu_read64(ptdev, PWR_TILER_READY)); 161 + drm_info(&ptdev->base, "SHADER_PRESENT: 0x%016llx", gpu_read64(ptdev, PWR_SHADER_PRESENT)); 162 + drm_info(&ptdev->base, "SHADER_PWRTRANS: 0x%016llx", gpu_read64(ptdev, PWR_SHADER_PWRTRANS)); 163 + drm_info(&ptdev->base, "SHADER_READY: 0x%016llx", gpu_read64(ptdev, PWR_SHADER_READY)); 164 + } 165 + 166 + static int panthor_pwr_domain_transition(struct panthor_device *ptdev, u32 cmd, u32 domain, 167 + u64 mask, u32 timeout_us) 168 + { 169 + u32 ready_reg = get_domain_ready_reg(domain); 170 + u32 pwr_cmd = PWR_COMMAND_DEF(cmd, domain, get_domain_subdomain(ptdev, domain)); 171 + u64 expected_val = 0; 172 + u64 val; 173 + int ret = 0; 174 + 175 + if (drm_WARN_ON(&ptdev->base, !is_valid_domain(domain))) 176 + return -EINVAL; 177 + 178 + switch (cmd) { 179 + case PWR_COMMAND_POWER_DOWN: 180 + expected_val = 0; 181 + break; 182 + case PWR_COMMAND_POWER_UP: 183 + expected_val = mask; 184 + break; 185 + default: 186 + drm_err(&ptdev->base, "Invalid power domain transition command (0x%x)", cmd); 187 + return -EINVAL; 188 + } 189 + 190 + ret = panthor_pwr_domain_wait_transition(ptdev, domain, timeout_us); 191 + if (ret) 192 + return ret; 193 + 194 + /* domain already in target state, return early */ 195 + if ((gpu_read64(ptdev, ready_reg) & mask) == expected_val) 196 + return 0; 197 + 198 + panthor_pwr_write_command(ptdev, pwr_cmd, mask); 199 + 200 + ret = gpu_read64_poll_timeout(ptdev, ready_reg, val, (mask & val) == expected_val, 100, 201 + timeout_us); 202 + if (ret) { 203 + drm_err(&ptdev->base, 204 + "timeout waiting on %s power domain transition, cmd(0x%x), arg(0x%llx)", 205 + get_domain_name(domain), pwr_cmd, mask); 206 + panthor_pwr_debug_info_show(ptdev); 207 + return ret; 208 + } 209 + 210 + return 0; 211 + } 212 + 213 + #define panthor_pwr_domain_power_off(__ptdev, __domain, __mask, __timeout_us) \ 214 + panthor_pwr_domain_transition(__ptdev, PWR_COMMAND_POWER_DOWN, __domain, __mask, \ 215 + __timeout_us) 216 + 217 + #define panthor_pwr_domain_power_on(__ptdev, __domain, __mask, __timeout_us) \ 218 + panthor_pwr_domain_transition(__ptdev, PWR_COMMAND_POWER_UP, __domain, __mask, __timeout_us) 219 + 220 + /** 221 + * retract_domain() - Retract control of a domain from MCU 222 + * @ptdev: Device. 223 + * @domain: Domain to retract the control 224 + * 225 + * Retracting L2 domain is not expected since it won't be delegated. 226 + * 227 + * Return: 0 on success or retracted already. 228 + * -EPERM if domain is L2. 229 + * A negative error code otherwise. 230 + */ 231 + static int retract_domain(struct panthor_device *ptdev, u32 domain) 232 + { 233 + const u32 pwr_cmd = PWR_COMMAND_DEF(PWR_COMMAND_RETRACT, domain, 0); 234 + const u64 pwr_status = gpu_read64(ptdev, PWR_STATUS); 235 + const u64 delegated_mask = PWR_STATUS_DOMAIN_DELEGATED(domain); 236 + const u64 allow_mask = PWR_STATUS_DOMAIN_ALLOWED(domain); 237 + u64 val; 238 + int ret; 239 + 240 + if (drm_WARN_ON(&ptdev->base, domain == PWR_COMMAND_DOMAIN_L2)) 241 + return -EPERM; 242 + 243 + ret = gpu_read64_poll_timeout(ptdev, PWR_STATUS, val, !(PWR_STATUS_RETRACT_PENDING & val), 244 + 0, PWR_RETRACT_TIMEOUT_US); 245 + if (ret) { 246 + drm_err(&ptdev->base, "%s domain retract pending", get_domain_name(domain)); 247 + return ret; 248 + } 249 + 250 + if (!(pwr_status & delegated_mask)) { 251 + drm_dbg(&ptdev->base, "%s domain already retracted", get_domain_name(domain)); 252 + return 0; 253 + } 254 + 255 + panthor_pwr_write_command(ptdev, pwr_cmd, 0); 256 + 257 + /* 258 + * On successful retraction 259 + * allow-flag will be set with delegated-flag being cleared. 260 + */ 261 + ret = gpu_read64_poll_timeout(ptdev, PWR_STATUS, val, 262 + ((delegated_mask | allow_mask) & val) == allow_mask, 10, 263 + PWR_TRANSITION_TIMEOUT_US); 264 + if (ret) { 265 + drm_err(&ptdev->base, "Retracting %s domain timeout, cmd(0x%x)", 266 + get_domain_name(domain), pwr_cmd); 267 + return ret; 268 + } 269 + 270 + return 0; 271 + } 272 + 273 + /** 274 + * delegate_domain() - Delegate control of a domain to MCU 275 + * @ptdev: Device. 276 + * @domain: Domain to delegate the control 277 + * 278 + * Delegating L2 domain is prohibited. 279 + * 280 + * Return: 281 + * * 0 on success or delegated already. 282 + * * -EPERM if domain is L2. 283 + * * A negative error code otherwise. 284 + */ 285 + static int delegate_domain(struct panthor_device *ptdev, u32 domain) 286 + { 287 + const u32 pwr_cmd = PWR_COMMAND_DEF(PWR_COMMAND_DELEGATE, domain, 0); 288 + const u64 pwr_status = gpu_read64(ptdev, PWR_STATUS); 289 + const u64 allow_mask = PWR_STATUS_DOMAIN_ALLOWED(domain); 290 + const u64 delegated_mask = PWR_STATUS_DOMAIN_DELEGATED(domain); 291 + u64 val; 292 + int ret; 293 + 294 + if (drm_WARN_ON(&ptdev->base, domain == PWR_COMMAND_DOMAIN_L2)) 295 + return -EPERM; 296 + 297 + /* Already delegated, exit early */ 298 + if (pwr_status & delegated_mask) 299 + return 0; 300 + 301 + /* Check if the command is allowed before delegating. */ 302 + if (!(pwr_status & allow_mask)) { 303 + drm_warn(&ptdev->base, "Delegating %s domain not allowed", get_domain_name(domain)); 304 + return -EPERM; 305 + } 306 + 307 + ret = panthor_pwr_domain_wait_transition(ptdev, domain, PWR_TRANSITION_TIMEOUT_US); 308 + if (ret) 309 + return ret; 310 + 311 + panthor_pwr_write_command(ptdev, pwr_cmd, 0); 312 + 313 + /* 314 + * On successful delegation 315 + * allow-flag will be cleared with delegated-flag being set. 316 + */ 317 + ret = gpu_read64_poll_timeout(ptdev, PWR_STATUS, val, 318 + ((delegated_mask | allow_mask) & val) == delegated_mask, 319 + 10, PWR_TRANSITION_TIMEOUT_US); 320 + if (ret) { 321 + drm_err(&ptdev->base, "Delegating %s domain timeout, cmd(0x%x)", 322 + get_domain_name(domain), pwr_cmd); 323 + return ret; 324 + } 325 + 326 + return 0; 327 + } 328 + 329 + static int panthor_pwr_delegate_domains(struct panthor_device *ptdev) 330 + { 331 + int ret; 332 + 333 + if (!ptdev->pwr) 334 + return 0; 335 + 336 + ret = delegate_domain(ptdev, PWR_COMMAND_DOMAIN_SHADER); 337 + if (ret) 338 + return ret; 339 + 340 + ret = delegate_domain(ptdev, PWR_COMMAND_DOMAIN_TILER); 341 + if (ret) 342 + goto err_retract_shader; 343 + 344 + return 0; 345 + 346 + err_retract_shader: 347 + retract_domain(ptdev, PWR_COMMAND_DOMAIN_SHADER); 348 + 349 + return ret; 350 + } 351 + 352 + /** 353 + * panthor_pwr_domain_force_off - Forcefully power down a domain. 354 + * @ptdev: Device. 355 + * @domain: Domain to forcefully power down. 356 + * 357 + * This function will attempt to retract and power off the requested power 358 + * domain. However, if retraction fails, the operation is aborted. If power off 359 + * fails, the domain will remain retracted and under the host control. 360 + * 361 + * Return: 0 on success or a negative error code on failure. 362 + */ 363 + static int panthor_pwr_domain_force_off(struct panthor_device *ptdev, u32 domain) 364 + { 365 + const u64 domain_ready = gpu_read64(ptdev, get_domain_ready_reg(domain)); 366 + int ret; 367 + 368 + /* Domain already powered down, early exit. */ 369 + if (!domain_ready) 370 + return 0; 371 + 372 + /* Domain has to be in host control to issue power off command. */ 373 + ret = retract_domain(ptdev, domain); 374 + if (ret) 375 + return ret; 376 + 377 + return panthor_pwr_domain_power_off(ptdev, domain, domain_ready, PWR_TRANSITION_TIMEOUT_US); 378 + } 70 379 71 380 void panthor_pwr_unplug(struct panthor_device *ptdev) 72 381 { ··· 427 102 return err; 428 103 429 104 return 0; 105 + } 106 + 107 + void panthor_pwr_l2_power_off(struct panthor_device *ptdev) 108 + { 109 + const u64 l2_allow_mask = PWR_STATUS_DOMAIN_ALLOWED(PWR_COMMAND_DOMAIN_L2); 110 + const u64 pwr_status = gpu_read64(ptdev, PWR_STATUS); 111 + 112 + /* Abort if L2 power off constraints are not satisfied */ 113 + if (!(pwr_status & l2_allow_mask)) { 114 + drm_warn(&ptdev->base, "Power off L2 domain not allowed"); 115 + return; 116 + } 117 + 118 + /* It is expected that when halting the MCU, it would power down its 119 + * delegated domains. However, an unresponsive or hung MCU may not do 120 + * so, which is why we need to check and retract the domains back into 121 + * host control to be powered down in the right order before powering 122 + * down the L2. 123 + */ 124 + if (panthor_pwr_domain_force_off(ptdev, PWR_COMMAND_DOMAIN_TILER)) 125 + return; 126 + 127 + if (panthor_pwr_domain_force_off(ptdev, PWR_COMMAND_DOMAIN_SHADER)) 128 + return; 129 + 130 + panthor_pwr_domain_power_off(ptdev, PWR_COMMAND_DOMAIN_L2, ptdev->gpu_info.l2_present, 131 + PWR_TRANSITION_TIMEOUT_US); 132 + } 133 + 134 + int panthor_pwr_l2_power_on(struct panthor_device *ptdev) 135 + { 136 + const u32 pwr_status = gpu_read64(ptdev, PWR_STATUS); 137 + const u32 l2_allow_mask = PWR_STATUS_DOMAIN_ALLOWED(PWR_COMMAND_DOMAIN_L2); 138 + int ret; 139 + 140 + if ((pwr_status & l2_allow_mask) == 0) { 141 + drm_warn(&ptdev->base, "Power on L2 domain not allowed"); 142 + return -EPERM; 143 + } 144 + 145 + ret = panthor_pwr_domain_power_on(ptdev, PWR_COMMAND_DOMAIN_L2, ptdev->gpu_info.l2_present, 146 + PWR_TRANSITION_TIMEOUT_US); 147 + if (ret) 148 + return ret; 149 + 150 + /* Delegate control of the shader and tiler power domains to the MCU as 151 + * it can better manage which shader/tiler cores need to be powered up 152 + * or can be powered down based on currently running jobs. 153 + * 154 + * If the shader and tiler domains are already delegated to the MCU, 155 + * this call would just return early. 156 + */ 157 + return panthor_pwr_delegate_domains(ptdev); 430 158 } 431 159 432 160 void panthor_pwr_suspend(struct panthor_device *ptdev)
+4
drivers/gpu/drm/panthor/panthor_pwr.h
··· 10 10 11 11 int panthor_pwr_init(struct panthor_device *ptdev); 12 12 13 + void panthor_pwr_l2_power_off(struct panthor_device *ptdev); 14 + 15 + int panthor_pwr_l2_power_on(struct panthor_device *ptdev); 16 + 13 17 void panthor_pwr_suspend(struct panthor_device *ptdev); 14 18 15 19 void panthor_pwr_resume(struct panthor_device *ptdev);
+1
drivers/gpu/drm/panthor/panthor_regs.h
··· 74 74 75 75 #define GPU_FEATURES 0x60 76 76 #define GPU_FEATURES_RAY_INTERSECTION BIT(2) 77 + #define GPU_FEATURES_RAY_TRAVERSAL BIT(5) 77 78 78 79 #define GPU_TIMESTAMP_OFFSET 0x88 79 80 #define GPU_CYCLE_COUNT 0x90