| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* Copyright 2018 Marty E. Plummer <hanetzer@startmail.com> */ |
| 3 | /* Copyright 2019 Linaro, Ltd., Rob Herring <robh@kernel.org> */ |
| 4 | /* Copyright 2019 Collabora ltd. */ |
| 5 | #include <linux/bitfield.h> |
| 6 | #include <linux/bitmap.h> |
| 7 | #include <linux/delay.h> |
| 8 | #include <linux/dma-mapping.h> |
| 9 | #include <linux/interrupt.h> |
| 10 | #include <linux/io.h> |
| 11 | #include <linux/iopoll.h> |
| 12 | #include <linux/platform_device.h> |
| 13 | #include <linux/pm_runtime.h> |
| 14 | |
| 15 | #include <drm/drm_print.h> |
| 16 | |
| 17 | #include "panfrost_device.h" |
| 18 | #include "panfrost_features.h" |
| 19 | #include "panfrost_issues.h" |
| 20 | #include "panfrost_gpu.h" |
| 21 | #include "panfrost_perfcnt.h" |
| 22 | #include "panfrost_regs.h" |
| 23 | |
| 24 | static irqreturn_t panfrost_gpu_irq_handler(int irq, void *data) |
| 25 | { |
| 26 | struct panfrost_device *pfdev = data; |
| 27 | u32 fault_status, state; |
| 28 | |
| 29 | if (test_bit(PANFROST_COMP_BIT_GPU, pfdev->is_suspended)) |
| 30 | return IRQ_NONE; |
| 31 | |
| 32 | fault_status = gpu_read(pfdev, GPU_FAULT_STATUS); |
| 33 | state = gpu_read(pfdev, GPU_INT_STAT); |
| 34 | if (!state) |
| 35 | return IRQ_NONE; |
| 36 | |
| 37 | if (state & GPU_IRQ_MASK_ERROR) { |
| 38 | u64 address = (u64) gpu_read(pfdev, GPU_FAULT_ADDRESS_HI) << 32; |
| 39 | address |= gpu_read(pfdev, GPU_FAULT_ADDRESS_LO); |
| 40 | |
| 41 | dev_warn(pfdev->base.dev, "GPU Fault 0x%08x (%s) at 0x%016llx\n" , |
| 42 | fault_status, panfrost_exception_name(fault_status & 0xFF), |
| 43 | address); |
| 44 | |
| 45 | if (state & GPU_IRQ_MULTIPLE_FAULT) |
| 46 | dev_warn(pfdev->base.dev, "There were multiple GPU faults - some have not been reported\n" ); |
| 47 | |
| 48 | gpu_write(pfdev, GPU_INT_MASK, 0); |
| 49 | } |
| 50 | |
| 51 | if (state & GPU_IRQ_PERFCNT_SAMPLE_COMPLETED) |
| 52 | panfrost_perfcnt_sample_done(pfdev); |
| 53 | |
| 54 | if (state & GPU_IRQ_CLEAN_CACHES_COMPLETED) |
| 55 | panfrost_perfcnt_clean_cache_done(pfdev); |
| 56 | |
| 57 | gpu_write(pfdev, GPU_INT_CLEAR, state); |
| 58 | |
| 59 | return IRQ_HANDLED; |
| 60 | } |
| 61 | |
| 62 | int panfrost_gpu_soft_reset(struct panfrost_device *pfdev) |
| 63 | { |
| 64 | int ret; |
| 65 | u32 val; |
| 66 | |
| 67 | gpu_write(pfdev, GPU_INT_MASK, 0); |
| 68 | gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_RESET_COMPLETED); |
| 69 | |
| 70 | clear_bit(nr: PANFROST_COMP_BIT_GPU, addr: pfdev->is_suspended); |
| 71 | |
| 72 | gpu_write(pfdev, GPU_CMD, GPU_CMD_SOFT_RESET); |
| 73 | ret = readl_relaxed_poll_timeout(pfdev->iomem + GPU_INT_RAWSTAT, |
| 74 | val, val & GPU_IRQ_RESET_COMPLETED, 10, 10000); |
| 75 | |
| 76 | if (ret) { |
| 77 | dev_err(pfdev->base.dev, "gpu soft reset timed out, attempting hard reset\n" ); |
| 78 | |
| 79 | gpu_write(pfdev, GPU_CMD, GPU_CMD_HARD_RESET); |
| 80 | ret = readl_relaxed_poll_timeout(pfdev->iomem + GPU_INT_RAWSTAT, val, |
| 81 | val & GPU_IRQ_RESET_COMPLETED, 100, 10000); |
| 82 | if (ret) { |
| 83 | dev_err(pfdev->base.dev, "gpu hard reset timed out\n" ); |
| 84 | return ret; |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_MASK_ALL); |
| 89 | |
| 90 | /* Only enable the interrupts we care about */ |
| 91 | gpu_write(pfdev, GPU_INT_MASK, |
| 92 | GPU_IRQ_MASK_ERROR | |
| 93 | GPU_IRQ_PERFCNT_SAMPLE_COMPLETED | |
| 94 | GPU_IRQ_CLEAN_CACHES_COMPLETED); |
| 95 | |
| 96 | /* |
| 97 | * All in-flight jobs should have released their cycle |
| 98 | * counter references upon reset, but let us make sure |
| 99 | */ |
| 100 | if (drm_WARN_ON(&pfdev->base, atomic_read(&pfdev->cycle_counter.use_count) != 0)) |
| 101 | atomic_set(v: &pfdev->cycle_counter.use_count, i: 0); |
| 102 | |
| 103 | return 0; |
| 104 | } |
| 105 | |
| 106 | void panfrost_gpu_amlogic_quirk(struct panfrost_device *pfdev) |
| 107 | { |
| 108 | /* |
| 109 | * The Amlogic integrated Mali-T820, Mali-G31 & Mali-G52 needs |
| 110 | * these undocumented bits in GPU_PWR_OVERRIDE1 to be set in order |
| 111 | * to operate correctly. |
| 112 | */ |
| 113 | gpu_write(pfdev, GPU_PWR_KEY, GPU_PWR_KEY_UNLOCK); |
| 114 | gpu_write(pfdev, GPU_PWR_OVERRIDE1, 0xfff | (0x20 << 16)); |
| 115 | } |
| 116 | |
| 117 | static void panfrost_gpu_init_quirks(struct panfrost_device *pfdev) |
| 118 | { |
| 119 | u32 quirks = 0; |
| 120 | |
| 121 | if (panfrost_has_hw_issue(pfdev, issue: HW_ISSUE_8443) || |
| 122 | panfrost_has_hw_issue(pfdev, issue: HW_ISSUE_11035)) |
| 123 | quirks |= SC_LS_PAUSEBUFFER_DISABLE; |
| 124 | |
| 125 | if (panfrost_has_hw_issue(pfdev, issue: HW_ISSUE_10327)) |
| 126 | quirks |= SC_SDC_DISABLE_OQ_DISCARD; |
| 127 | |
| 128 | if (panfrost_has_hw_issue(pfdev, issue: HW_ISSUE_10797)) |
| 129 | quirks |= SC_ENABLE_TEXGRD_FLAGS; |
| 130 | |
| 131 | if (!panfrost_has_hw_issue(pfdev, issue: GPUCORE_1619)) { |
| 132 | if (panfrost_model_cmp(pfdev, id: 0x750) < 0) /* T60x, T62x, T72x */ |
| 133 | quirks |= SC_LS_ATTR_CHECK_DISABLE; |
| 134 | else if (panfrost_model_cmp(pfdev, id: 0x880) <= 0) /* T76x, T8xx */ |
| 135 | quirks |= SC_LS_ALLOW_ATTR_TYPES; |
| 136 | } |
| 137 | |
| 138 | if (panfrost_has_hw_issue(pfdev, issue: HW_ISSUE_TTRX_2968_TTRX_3162)) |
| 139 | quirks |= SC_VAR_ALGORITHM; |
| 140 | |
| 141 | if (panfrost_has_hw_feature(pfdev, feat: HW_FEATURE_TLS_HASHING)) |
| 142 | quirks |= SC_TLS_HASH_ENABLE; |
| 143 | |
| 144 | if (quirks) |
| 145 | gpu_write(pfdev, GPU_SHADER_CONFIG, quirks); |
| 146 | |
| 147 | |
| 148 | quirks = gpu_read(pfdev, GPU_TILER_CONFIG); |
| 149 | |
| 150 | /* Set tiler clock gate override if required */ |
| 151 | if (panfrost_has_hw_issue(pfdev, issue: HW_ISSUE_T76X_3953)) |
| 152 | quirks |= TC_CLOCK_GATE_OVERRIDE; |
| 153 | |
| 154 | gpu_write(pfdev, GPU_TILER_CONFIG, quirks); |
| 155 | |
| 156 | |
| 157 | quirks = 0; |
| 158 | if ((panfrost_model_eq(pfdev, id: 0x860) || panfrost_model_eq(pfdev, id: 0x880)) && |
| 159 | pfdev->features.revision >= 0x2000) |
| 160 | quirks |= JM_MAX_JOB_THROTTLE_LIMIT << JM_JOB_THROTTLE_LIMIT_SHIFT; |
| 161 | else if (panfrost_model_eq(pfdev, id: 0x6000) && |
| 162 | pfdev->features.coherency_features == COHERENCY_ACE) |
| 163 | quirks |= (COHERENCY_ACE_LITE | COHERENCY_ACE) << |
| 164 | JM_FORCE_COHERENCY_FEATURES_SHIFT; |
| 165 | |
| 166 | if (panfrost_has_hw_feature(pfdev, feat: HW_FEATURE_IDVS_GROUP_SIZE)) |
| 167 | quirks |= JM_DEFAULT_IDVS_GROUP_SIZE << JM_IDVS_GROUP_SIZE_SHIFT; |
| 168 | |
| 169 | if (quirks) |
| 170 | gpu_write(pfdev, GPU_JM_CONFIG, quirks); |
| 171 | |
| 172 | /* Here goes platform specific quirks */ |
| 173 | if (pfdev->comp->vendor_quirk) |
| 174 | pfdev->comp->vendor_quirk(pfdev); |
| 175 | } |
| 176 | |
| 177 | #define MAX_HW_REVS 6 |
| 178 | |
| 179 | struct panfrost_model { |
| 180 | const char *name; |
| 181 | u32 id; |
| 182 | u64 features; |
| 183 | u64 issues; |
| 184 | struct { |
| 185 | u32 revision; |
| 186 | u64 issues; |
| 187 | } revs[MAX_HW_REVS]; |
| 188 | }; |
| 189 | |
| 190 | #define GPU_MODEL(_name, _id, ...) \ |
| 191 | {\ |
| 192 | .name = __stringify(_name), \ |
| 193 | .id = _id, \ |
| 194 | .features = hw_features_##_name, \ |
| 195 | .issues = hw_issues_##_name, \ |
| 196 | .revs = { __VA_ARGS__ }, \ |
| 197 | } |
| 198 | |
| 199 | #define GPU_REV_EXT(name, _rev, _p, _s, stat) \ |
| 200 | {\ |
| 201 | .revision = (_rev) << 12 | (_p) << 4 | (_s), \ |
| 202 | .issues = hw_issues_##name##_r##_rev##p##_p##stat, \ |
| 203 | } |
| 204 | #define GPU_REV(name, r, p) GPU_REV_EXT(name, r, p, 0, ) |
| 205 | |
| 206 | static const struct panfrost_model gpu_models[] = { |
| 207 | /* T60x has an oddball version */ |
| 208 | GPU_MODEL(t600, 0x600, |
| 209 | GPU_REV_EXT(t600, 0, 0, 1, _15dev0)), |
| 210 | GPU_MODEL(t620, 0x620, |
| 211 | GPU_REV(t620, 0, 1), GPU_REV(t620, 1, 0)), |
| 212 | GPU_MODEL(t720, 0x720), |
| 213 | GPU_MODEL(t760, 0x750, |
| 214 | GPU_REV(t760, 0, 0), GPU_REV(t760, 0, 1), |
| 215 | GPU_REV_EXT(t760, 0, 1, 0, _50rel0), |
| 216 | GPU_REV(t760, 0, 2), GPU_REV(t760, 0, 3)), |
| 217 | GPU_MODEL(t820, 0x820), |
| 218 | GPU_MODEL(t830, 0x830), |
| 219 | GPU_MODEL(t860, 0x860), |
| 220 | GPU_MODEL(t880, 0x880), |
| 221 | |
| 222 | GPU_MODEL(g71, 0x6000, |
| 223 | GPU_REV_EXT(g71, 0, 0, 1, _05dev0)), |
| 224 | GPU_MODEL(g72, 0x6001), |
| 225 | GPU_MODEL(g51, 0x7000), |
| 226 | GPU_MODEL(g76, 0x7001), |
| 227 | GPU_MODEL(g52, 0x7002), |
| 228 | GPU_MODEL(g31, 0x7003, |
| 229 | GPU_REV(g31, 1, 0)), |
| 230 | |
| 231 | GPU_MODEL(g57, 0x9001, |
| 232 | GPU_REV(g57, 0, 0)), |
| 233 | |
| 234 | /* MediaTek MT8192 has a Mali-G57 with a different GPU ID from the |
| 235 | * standard. Arm's driver does not appear to handle this model. |
| 236 | * ChromeOS has a hack downstream for it. Treat it as equivalent to |
| 237 | * standard Mali-G57 for now. |
| 238 | */ |
| 239 | GPU_MODEL(g57, 0x9003, |
| 240 | GPU_REV(g57, 0, 0)), |
| 241 | |
| 242 | /* MediaTek MT8188 Mali-G57 MC3 */ |
| 243 | GPU_MODEL(g57, 0x9093, |
| 244 | GPU_REV(g57, 0, 0)), |
| 245 | {0}, |
| 246 | }; |
| 247 | |
| 248 | static int panfrost_gpu_init_features(struct panfrost_device *pfdev) |
| 249 | { |
| 250 | u32 gpu_id, num_js, major, minor, status, rev; |
| 251 | const char *name = "unknown" ; |
| 252 | u64 hw_feat = 0; |
| 253 | u64 hw_issues = hw_issues_all; |
| 254 | const struct panfrost_model *model; |
| 255 | int i; |
| 256 | |
| 257 | pfdev->features.l2_features = gpu_read(pfdev, GPU_L2_FEATURES); |
| 258 | pfdev->features.core_features = gpu_read(pfdev, GPU_CORE_FEATURES); |
| 259 | pfdev->features.tiler_features = gpu_read(pfdev, GPU_TILER_FEATURES); |
| 260 | pfdev->features.mem_features = gpu_read(pfdev, GPU_MEM_FEATURES); |
| 261 | pfdev->features.mmu_features = gpu_read(pfdev, GPU_MMU_FEATURES); |
| 262 | pfdev->features.thread_features = gpu_read(pfdev, GPU_THREAD_FEATURES); |
| 263 | pfdev->features.max_threads = gpu_read(pfdev, GPU_THREAD_MAX_THREADS); |
| 264 | pfdev->features.thread_max_workgroup_sz = gpu_read(pfdev, GPU_THREAD_MAX_WORKGROUP_SIZE); |
| 265 | pfdev->features.thread_max_barrier_sz = gpu_read(pfdev, GPU_THREAD_MAX_BARRIER_SIZE); |
| 266 | pfdev->features.coherency_features = gpu_read(pfdev, GPU_COHERENCY_FEATURES); |
| 267 | pfdev->features.afbc_features = gpu_read(pfdev, GPU_AFBC_FEATURES); |
| 268 | for (i = 0; i < 4; i++) |
| 269 | pfdev->features.texture_features[i] = gpu_read(pfdev, GPU_TEXTURE_FEATURES(i)); |
| 270 | |
| 271 | pfdev->features.as_present = gpu_read(pfdev, GPU_AS_PRESENT); |
| 272 | |
| 273 | pfdev->features.js_present = gpu_read(pfdev, GPU_JS_PRESENT); |
| 274 | num_js = hweight32(pfdev->features.js_present); |
| 275 | for (i = 0; i < num_js; i++) |
| 276 | pfdev->features.js_features[i] = gpu_read(pfdev, GPU_JS_FEATURES(i)); |
| 277 | |
| 278 | pfdev->features.shader_present = gpu_read(pfdev, GPU_SHADER_PRESENT_LO); |
| 279 | pfdev->features.shader_present |= (u64)gpu_read(pfdev, GPU_SHADER_PRESENT_HI) << 32; |
| 280 | |
| 281 | pfdev->features.tiler_present = gpu_read(pfdev, GPU_TILER_PRESENT_LO); |
| 282 | pfdev->features.tiler_present |= (u64)gpu_read(pfdev, GPU_TILER_PRESENT_HI) << 32; |
| 283 | |
| 284 | pfdev->features.l2_present = gpu_read(pfdev, GPU_L2_PRESENT_LO); |
| 285 | pfdev->features.l2_present |= (u64)gpu_read(pfdev, GPU_L2_PRESENT_HI) << 32; |
| 286 | pfdev->features.nr_core_groups = hweight64(pfdev->features.l2_present); |
| 287 | |
| 288 | pfdev->features.stack_present = gpu_read(pfdev, GPU_STACK_PRESENT_LO); |
| 289 | pfdev->features.stack_present |= (u64)gpu_read(pfdev, GPU_STACK_PRESENT_HI) << 32; |
| 290 | |
| 291 | pfdev->features.thread_tls_alloc = gpu_read(pfdev, GPU_THREAD_TLS_ALLOC); |
| 292 | |
| 293 | gpu_id = gpu_read(pfdev, GPU_ID); |
| 294 | pfdev->features.revision = gpu_id & 0xffff; |
| 295 | pfdev->features.id = gpu_id >> 16; |
| 296 | |
| 297 | /* The T60x has an oddball ID value. Fix it up to the standard Midgard |
| 298 | * format so we (and userspace) don't have to special case it. |
| 299 | */ |
| 300 | if (pfdev->features.id == 0x6956) |
| 301 | pfdev->features.id = 0x0600; |
| 302 | |
| 303 | major = (pfdev->features.revision >> 12) & 0xf; |
| 304 | minor = (pfdev->features.revision >> 4) & 0xff; |
| 305 | status = pfdev->features.revision & 0xf; |
| 306 | rev = pfdev->features.revision; |
| 307 | |
| 308 | gpu_id = pfdev->features.id; |
| 309 | |
| 310 | for (model = gpu_models; model->name; model++) { |
| 311 | int best = -1; |
| 312 | |
| 313 | if (!panfrost_model_eq(pfdev, id: model->id)) |
| 314 | continue; |
| 315 | |
| 316 | name = model->name; |
| 317 | hw_feat = model->features; |
| 318 | hw_issues |= model->issues; |
| 319 | for (i = 0; i < MAX_HW_REVS; i++) { |
| 320 | if (model->revs[i].revision == rev) { |
| 321 | best = i; |
| 322 | break; |
| 323 | } else if (model->revs[i].revision == (rev & ~0xf)) |
| 324 | best = i; |
| 325 | } |
| 326 | |
| 327 | if (best >= 0) |
| 328 | hw_issues |= model->revs[best].issues; |
| 329 | |
| 330 | break; |
| 331 | } |
| 332 | |
| 333 | if (!model->name) { |
| 334 | dev_err(pfdev->base.dev, "GPU model not found: mali-%s id rev %#x %#x\n" , |
| 335 | name, gpu_id, rev); |
| 336 | return -ENODEV; |
| 337 | } |
| 338 | |
| 339 | bitmap_from_u64(dst: pfdev->features.hw_features, mask: hw_feat); |
| 340 | bitmap_from_u64(dst: pfdev->features.hw_issues, mask: hw_issues); |
| 341 | |
| 342 | dev_info(pfdev->base.dev, "mali-%s id 0x%x major 0x%x minor 0x%x status 0x%x" , |
| 343 | name, gpu_id, major, minor, status); |
| 344 | dev_info(pfdev->base.dev, "features: %64pb, issues: %64pb" , |
| 345 | pfdev->features.hw_features, |
| 346 | pfdev->features.hw_issues); |
| 347 | |
| 348 | dev_info(pfdev->base.dev, "Features: L2:0x%08x Shader:0x%08x Tiler:0x%08x Mem:0x%0x MMU:0x%08x AS:0x%x JS:0x%x" , |
| 349 | pfdev->features.l2_features, |
| 350 | pfdev->features.core_features, |
| 351 | pfdev->features.tiler_features, |
| 352 | pfdev->features.mem_features, |
| 353 | pfdev->features.mmu_features, |
| 354 | pfdev->features.as_present, |
| 355 | pfdev->features.js_present); |
| 356 | |
| 357 | dev_info(pfdev->base.dev, "shader_present=0x%0llx l2_present=0x%0llx" , |
| 358 | pfdev->features.shader_present, pfdev->features.l2_present); |
| 359 | |
| 360 | return 0; |
| 361 | } |
| 362 | |
| 363 | void panfrost_cycle_counter_get(struct panfrost_device *pfdev) |
| 364 | { |
| 365 | if (atomic_inc_not_zero(v: &pfdev->cycle_counter.use_count)) |
| 366 | return; |
| 367 | |
| 368 | spin_lock(lock: &pfdev->cycle_counter.lock); |
| 369 | if (atomic_inc_return(v: &pfdev->cycle_counter.use_count) == 1) |
| 370 | gpu_write(pfdev, GPU_CMD, GPU_CMD_CYCLE_COUNT_START); |
| 371 | spin_unlock(lock: &pfdev->cycle_counter.lock); |
| 372 | } |
| 373 | |
| 374 | void panfrost_cycle_counter_put(struct panfrost_device *pfdev) |
| 375 | { |
| 376 | if (atomic_add_unless(v: &pfdev->cycle_counter.use_count, a: -1, u: 1)) |
| 377 | return; |
| 378 | |
| 379 | spin_lock(lock: &pfdev->cycle_counter.lock); |
| 380 | if (atomic_dec_return(v: &pfdev->cycle_counter.use_count) == 0) |
| 381 | gpu_write(pfdev, GPU_CMD, GPU_CMD_CYCLE_COUNT_STOP); |
| 382 | spin_unlock(lock: &pfdev->cycle_counter.lock); |
| 383 | } |
| 384 | |
| 385 | unsigned long long panfrost_cycle_counter_read(struct panfrost_device *pfdev) |
| 386 | { |
| 387 | u32 hi, lo; |
| 388 | |
| 389 | do { |
| 390 | hi = gpu_read(pfdev, GPU_CYCLE_COUNT_HI); |
| 391 | lo = gpu_read(pfdev, GPU_CYCLE_COUNT_LO); |
| 392 | } while (hi != gpu_read(pfdev, GPU_CYCLE_COUNT_HI)); |
| 393 | |
| 394 | return ((u64)hi << 32) | lo; |
| 395 | } |
| 396 | |
| 397 | unsigned long long panfrost_timestamp_read(struct panfrost_device *pfdev) |
| 398 | { |
| 399 | u32 hi, lo; |
| 400 | |
| 401 | do { |
| 402 | hi = gpu_read(pfdev, GPU_TIMESTAMP_HI); |
| 403 | lo = gpu_read(pfdev, GPU_TIMESTAMP_LO); |
| 404 | } while (hi != gpu_read(pfdev, GPU_TIMESTAMP_HI)); |
| 405 | |
| 406 | return ((u64)hi << 32) | lo; |
| 407 | } |
| 408 | |
| 409 | static u64 panfrost_get_core_mask(struct panfrost_device *pfdev) |
| 410 | { |
| 411 | u64 core_mask; |
| 412 | |
| 413 | if (pfdev->features.l2_present == 1) |
| 414 | return U64_MAX; |
| 415 | |
| 416 | /* |
| 417 | * Only support one core group now. |
| 418 | * ~(l2_present - 1) unsets all bits in l2_present except |
| 419 | * the bottom bit. (l2_present - 2) has all the bits in |
| 420 | * the first core group set. AND them together to generate |
| 421 | * a mask of cores in the first core group. |
| 422 | */ |
| 423 | core_mask = ~(pfdev->features.l2_present - 1) & |
| 424 | (pfdev->features.l2_present - 2); |
| 425 | dev_info_once(pfdev->base.dev, "using only 1st core group (%lu cores from %lu)\n" , |
| 426 | hweight64(core_mask), |
| 427 | hweight64(pfdev->features.shader_present)); |
| 428 | |
| 429 | return core_mask; |
| 430 | } |
| 431 | |
| 432 | void panfrost_gpu_power_on(struct panfrost_device *pfdev) |
| 433 | { |
| 434 | int ret; |
| 435 | u32 val; |
| 436 | u64 core_mask; |
| 437 | |
| 438 | panfrost_gpu_init_quirks(pfdev); |
| 439 | core_mask = panfrost_get_core_mask(pfdev); |
| 440 | |
| 441 | gpu_write(pfdev, L2_PWRON_LO, pfdev->features.l2_present & core_mask); |
| 442 | ret = readl_relaxed_poll_timeout(pfdev->iomem + L2_READY_LO, |
| 443 | val, val == (pfdev->features.l2_present & core_mask), |
| 444 | 10, 20000); |
| 445 | if (ret) |
| 446 | dev_err(pfdev->base.dev, "error powering up gpu L2" ); |
| 447 | |
| 448 | gpu_write(pfdev, SHADER_PWRON_LO, |
| 449 | pfdev->features.shader_present & core_mask); |
| 450 | ret = readl_relaxed_poll_timeout(pfdev->iomem + SHADER_READY_LO, |
| 451 | val, val == (pfdev->features.shader_present & core_mask), |
| 452 | 10, 20000); |
| 453 | if (ret) |
| 454 | dev_err(pfdev->base.dev, "error powering up gpu shader" ); |
| 455 | |
| 456 | gpu_write(pfdev, TILER_PWRON_LO, pfdev->features.tiler_present); |
| 457 | ret = readl_relaxed_poll_timeout(pfdev->iomem + TILER_READY_LO, |
| 458 | val, val == pfdev->features.tiler_present, 10, 1000); |
| 459 | if (ret) |
| 460 | dev_err(pfdev->base.dev, "error powering up gpu tiler" ); |
| 461 | } |
| 462 | |
| 463 | void panfrost_gpu_power_off(struct panfrost_device *pfdev) |
| 464 | { |
| 465 | int ret; |
| 466 | u32 val; |
| 467 | |
| 468 | gpu_write(pfdev, SHADER_PWROFF_LO, pfdev->features.shader_present); |
| 469 | ret = readl_relaxed_poll_timeout(pfdev->iomem + SHADER_PWRTRANS_LO, |
| 470 | val, !val, 1, 2000); |
| 471 | if (ret) |
| 472 | dev_err(pfdev->base.dev, "shader power transition timeout" ); |
| 473 | |
| 474 | gpu_write(pfdev, TILER_PWROFF_LO, pfdev->features.tiler_present); |
| 475 | ret = readl_relaxed_poll_timeout(pfdev->iomem + TILER_PWRTRANS_LO, |
| 476 | val, !val, 1, 2000); |
| 477 | if (ret) |
| 478 | dev_err(pfdev->base.dev, "tiler power transition timeout" ); |
| 479 | |
| 480 | gpu_write(pfdev, L2_PWROFF_LO, pfdev->features.l2_present); |
| 481 | ret = readl_poll_timeout(pfdev->iomem + L2_PWRTRANS_LO, |
| 482 | val, !val, 0, 2000); |
| 483 | if (ret) |
| 484 | dev_err(pfdev->base.dev, "l2 power transition timeout" ); |
| 485 | } |
| 486 | |
| 487 | void panfrost_gpu_suspend_irq(struct panfrost_device *pfdev) |
| 488 | { |
| 489 | set_bit(nr: PANFROST_COMP_BIT_GPU, addr: pfdev->is_suspended); |
| 490 | |
| 491 | gpu_write(pfdev, GPU_INT_MASK, 0); |
| 492 | synchronize_irq(irq: pfdev->gpu_irq); |
| 493 | } |
| 494 | |
| 495 | int panfrost_gpu_init(struct panfrost_device *pfdev) |
| 496 | { |
| 497 | int err; |
| 498 | |
| 499 | err = panfrost_gpu_soft_reset(pfdev); |
| 500 | if (err) |
| 501 | return err; |
| 502 | |
| 503 | err = panfrost_gpu_init_features(pfdev); |
| 504 | if (err) |
| 505 | return err; |
| 506 | |
| 507 | err = dma_set_mask_and_coherent(dev: pfdev->base.dev, |
| 508 | DMA_BIT_MASK(FIELD_GET(0xff00, |
| 509 | pfdev->features.mmu_features))); |
| 510 | if (err) |
| 511 | return err; |
| 512 | |
| 513 | dma_set_max_seg_size(dev: pfdev->base.dev, UINT_MAX); |
| 514 | |
| 515 | pfdev->gpu_irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "gpu" ); |
| 516 | if (pfdev->gpu_irq < 0) |
| 517 | return pfdev->gpu_irq; |
| 518 | |
| 519 | err = devm_request_irq(dev: pfdev->base.dev, irq: pfdev->gpu_irq, handler: panfrost_gpu_irq_handler, |
| 520 | IRQF_SHARED, KBUILD_MODNAME "-gpu" , dev_id: pfdev); |
| 521 | if (err) { |
| 522 | dev_err(pfdev->base.dev, "failed to request gpu irq" ); |
| 523 | return err; |
| 524 | } |
| 525 | |
| 526 | panfrost_gpu_power_on(pfdev); |
| 527 | |
| 528 | return 0; |
| 529 | } |
| 530 | |
| 531 | void panfrost_gpu_fini(struct panfrost_device *pfdev) |
| 532 | { |
| 533 | panfrost_gpu_power_off(pfdev); |
| 534 | } |
| 535 | |
| 536 | u32 panfrost_gpu_get_latest_flush_id(struct panfrost_device *pfdev) |
| 537 | { |
| 538 | u32 flush_id; |
| 539 | |
| 540 | if (panfrost_has_hw_feature(pfdev, feat: HW_FEATURE_FLUSH_REDUCTION)) { |
| 541 | /* Flush reduction only makes sense when the GPU is kept powered on between jobs */ |
| 542 | if (pm_runtime_get_if_in_use(dev: pfdev->base.dev)) { |
| 543 | flush_id = gpu_read(pfdev, GPU_LATEST_FLUSH_ID); |
| 544 | pm_runtime_put(dev: pfdev->base.dev); |
| 545 | return flush_id; |
| 546 | } |
| 547 | } |
| 548 | |
| 549 | return 0; |
| 550 | } |
| 551 | |