| /* |
| * |
| * (C) COPYRIGHT 2014-2018 ARM Limited. All rights reserved. |
| * |
| * This program is free software and is provided to you under the terms of the |
| * GNU General Public License version 2 as published by the Free Software |
| * Foundation, and any use by you of this program is subject to the terms |
| * of such GNU licence. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, you can access it online at |
| * http://www.gnu.org/licenses/gpl-2.0.html. |
| * |
| * SPDX-License-Identifier: GPL-2.0 |
| * |
| */ |
| |
| #include "mali_kbase.h" |
| #include "mali_kbase_hw.h" |
| #include "mali_kbase_mem_linux.h" |
| #include "mali_kbase_gator_api.h" |
| #include "mali_kbase_gator_hwcnt_names.h" |
| #include "mali_kbase_hwcnt_types.h" |
| #include "mali_kbase_hwcnt_gpu.h" |
| #include "mali_kbase_hwcnt_virtualizer.h" |
| |
| #define MALI_MAX_CORES_PER_GROUP 4 |
| #define MALI_MAX_NUM_BLOCKS_PER_GROUP 8 |
| #define MALI_COUNTERS_PER_BLOCK 64 |
| #define MALI_BYTES_PER_COUNTER 4 |
| |
| struct kbase_gator_hwcnt_handles { |
| struct kbase_device *kbdev; |
| struct kbase_hwcnt_virtualizer_client *hvcli; |
| struct kbase_hwcnt_enable_map enable_map; |
| struct kbase_hwcnt_dump_buffer dump_buf; |
| struct work_struct dump_work; |
| int dump_complete; |
| spinlock_t dump_lock; |
| }; |
| |
| static void dump_worker(struct work_struct *work); |
| |
| const char * const *kbase_gator_hwcnt_init_names(uint32_t *total_counters) |
| { |
| const char * const *hardware_counters; |
| struct kbase_device *kbdev; |
| uint32_t product_id; |
| uint32_t count; |
| |
| if (!total_counters) |
| return NULL; |
| |
| /* Get the first device - it doesn't matter in this case */ |
| kbdev = kbase_find_device(-1); |
| if (!kbdev) |
| return NULL; |
| |
| product_id = kbdev->gpu_props.props.core_props.product_id; |
| |
| if (GPU_ID_IS_NEW_FORMAT(product_id)) { |
| switch (GPU_ID2_MODEL_MATCH_VALUE(product_id)) { |
| case GPU_ID2_PRODUCT_TMIX: |
| hardware_counters = hardware_counters_mali_tMIx; |
| count = ARRAY_SIZE(hardware_counters_mali_tMIx); |
| break; |
| case GPU_ID2_PRODUCT_THEX: |
| hardware_counters = hardware_counters_mali_tHEx; |
| count = ARRAY_SIZE(hardware_counters_mali_tHEx); |
| break; |
| case GPU_ID2_PRODUCT_TSIX: |
| hardware_counters = hardware_counters_mali_tSIx; |
| count = ARRAY_SIZE(hardware_counters_mali_tSIx); |
| break; |
| case GPU_ID2_PRODUCT_TDVX: |
| hardware_counters = hardware_counters_mali_tSIx; |
| count = ARRAY_SIZE(hardware_counters_mali_tSIx); |
| break; |
| case GPU_ID2_PRODUCT_TNOX: |
| hardware_counters = hardware_counters_mali_tNOx; |
| count = ARRAY_SIZE(hardware_counters_mali_tNOx); |
| break; |
| case GPU_ID2_PRODUCT_TGOX: |
| hardware_counters = hardware_counters_mali_tGOx; |
| count = ARRAY_SIZE(hardware_counters_mali_tGOx); |
| break; |
| case GPU_ID2_PRODUCT_TKAX: |
| hardware_counters = hardware_counters_mali_tKAx; |
| count = ARRAY_SIZE(hardware_counters_mali_tKAx); |
| break; |
| case GPU_ID2_PRODUCT_TTRX: |
| hardware_counters = hardware_counters_mali_tTRx; |
| count = ARRAY_SIZE(hardware_counters_mali_tTRx); |
| break; |
| default: |
| hardware_counters = NULL; |
| count = 0; |
| dev_err(kbdev->dev, "Unrecognized product ID: %u\n", |
| product_id); |
| break; |
| } |
| } else { |
| switch (product_id) { |
| /* If we are using a Mali-T60x device */ |
| case GPU_ID_PI_T60X: |
| hardware_counters = hardware_counters_mali_t60x; |
| count = ARRAY_SIZE(hardware_counters_mali_t60x); |
| break; |
| /* If we are using a Mali-T62x device */ |
| case GPU_ID_PI_T62X: |
| hardware_counters = hardware_counters_mali_t62x; |
| count = ARRAY_SIZE(hardware_counters_mali_t62x); |
| break; |
| /* If we are using a Mali-T72x device */ |
| case GPU_ID_PI_T72X: |
| hardware_counters = hardware_counters_mali_t72x; |
| count = ARRAY_SIZE(hardware_counters_mali_t72x); |
| break; |
| /* If we are using a Mali-T76x device */ |
| case GPU_ID_PI_T76X: |
| hardware_counters = hardware_counters_mali_t76x; |
| count = ARRAY_SIZE(hardware_counters_mali_t76x); |
| break; |
| /* If we are using a Mali-T82x device */ |
| case GPU_ID_PI_T82X: |
| hardware_counters = hardware_counters_mali_t82x; |
| count = ARRAY_SIZE(hardware_counters_mali_t82x); |
| break; |
| /* If we are using a Mali-T83x device */ |
| case GPU_ID_PI_T83X: |
| hardware_counters = hardware_counters_mali_t83x; |
| count = ARRAY_SIZE(hardware_counters_mali_t83x); |
| break; |
| /* If we are using a Mali-T86x device */ |
| case GPU_ID_PI_T86X: |
| hardware_counters = hardware_counters_mali_t86x; |
| count = ARRAY_SIZE(hardware_counters_mali_t86x); |
| break; |
| /* If we are using a Mali-T88x device */ |
| case GPU_ID_PI_TFRX: |
| hardware_counters = hardware_counters_mali_t88x; |
| count = ARRAY_SIZE(hardware_counters_mali_t88x); |
| break; |
| default: |
| hardware_counters = NULL; |
| count = 0; |
| dev_err(kbdev->dev, "Unrecognized product ID: %u\n", |
| product_id); |
| break; |
| } |
| } |
| |
| /* Release the kbdev reference. */ |
| kbase_release_device(kbdev); |
| |
| *total_counters = count; |
| |
| /* If we return a string array take a reference on the module (or fail). */ |
| if (hardware_counters && !try_module_get(THIS_MODULE)) |
| return NULL; |
| |
| return hardware_counters; |
| } |
| KBASE_EXPORT_SYMBOL(kbase_gator_hwcnt_init_names); |
| |
| void kbase_gator_hwcnt_term_names(void) |
| { |
| /* Release the module reference. */ |
| module_put(THIS_MODULE); |
| } |
| KBASE_EXPORT_SYMBOL(kbase_gator_hwcnt_term_names); |
| |
| struct kbase_gator_hwcnt_handles *kbase_gator_hwcnt_init(struct kbase_gator_hwcnt_info *in_out_info) |
| { |
| int errcode; |
| struct kbase_gator_hwcnt_handles *hand; |
| const struct kbase_hwcnt_metadata *metadata; |
| struct kbase_hwcnt_physical_enable_map phys_map; |
| uint32_t dump_size = 0, i = 0; |
| |
| if (!in_out_info) |
| return NULL; |
| |
| hand = kzalloc(sizeof(*hand), GFP_KERNEL); |
| if (!hand) |
| return NULL; |
| |
| INIT_WORK(&hand->dump_work, dump_worker); |
| spin_lock_init(&hand->dump_lock); |
| |
| /* Get the first device */ |
| hand->kbdev = kbase_find_device(-1); |
| if (!hand->kbdev) |
| goto free_hand; |
| |
| metadata = kbase_hwcnt_virtualizer_metadata( |
| hand->kbdev->hwcnt_gpu_virt); |
| if (!metadata) |
| goto release_device; |
| |
| errcode = kbase_hwcnt_enable_map_alloc(metadata, &hand->enable_map); |
| if (errcode) |
| goto release_device; |
| |
| errcode = kbase_hwcnt_dump_buffer_alloc(metadata, &hand->dump_buf); |
| if (errcode) |
| goto free_enable_map; |
| |
| in_out_info->kernel_dump_buffer = hand->dump_buf.dump_buf; |
| |
| in_out_info->nr_cores = hand->kbdev->gpu_props.num_cores; |
| in_out_info->nr_core_groups = hand->kbdev->gpu_props.num_core_groups; |
| in_out_info->gpu_id = hand->kbdev->gpu_props.props.core_props.product_id; |
| |
| /* If we are using a v4 device (Mali-T6xx or Mali-T72x) */ |
| if (kbase_hw_has_feature(hand->kbdev, BASE_HW_FEATURE_V4)) { |
| uint32_t cg, j; |
| uint64_t core_mask; |
| |
| /* There are 8 hardware counters blocks per core group */ |
| in_out_info->hwc_layout = kmalloc(sizeof(enum hwc_type) * |
| MALI_MAX_NUM_BLOCKS_PER_GROUP * |
| in_out_info->nr_core_groups, GFP_KERNEL); |
| |
| if (!in_out_info->hwc_layout) |
| goto free_dump_buf; |
| |
| dump_size = in_out_info->nr_core_groups * |
| MALI_MAX_NUM_BLOCKS_PER_GROUP * |
| MALI_COUNTERS_PER_BLOCK * |
| MALI_BYTES_PER_COUNTER; |
| |
| for (cg = 0; cg < in_out_info->nr_core_groups; cg++) { |
| core_mask = hand->kbdev->gpu_props.props.coherency_info.group[cg].core_mask; |
| |
| for (j = 0; j < MALI_MAX_CORES_PER_GROUP; j++) { |
| if (core_mask & (1u << j)) |
| in_out_info->hwc_layout[i++] = SHADER_BLOCK; |
| else |
| in_out_info->hwc_layout[i++] = RESERVED_BLOCK; |
| } |
| |
| in_out_info->hwc_layout[i++] = TILER_BLOCK; |
| in_out_info->hwc_layout[i++] = MMU_L2_BLOCK; |
| |
| in_out_info->hwc_layout[i++] = RESERVED_BLOCK; |
| |
| if (0 == cg) |
| in_out_info->hwc_layout[i++] = JM_BLOCK; |
| else |
| in_out_info->hwc_layout[i++] = RESERVED_BLOCK; |
| } |
| /* If we are using any other device */ |
| } else { |
| uint32_t nr_l2, nr_sc_bits, j; |
| uint64_t core_mask; |
| |
| nr_l2 = hand->kbdev->gpu_props.props.l2_props.num_l2_slices; |
| |
| core_mask = hand->kbdev->gpu_props.props.coherency_info.group[0].core_mask; |
| |
| nr_sc_bits = fls64(core_mask); |
| |
| /* The job manager and tiler sets of counters |
| * are always present */ |
| in_out_info->hwc_layout = kmalloc(sizeof(enum hwc_type) * (2 + nr_sc_bits + nr_l2), GFP_KERNEL); |
| |
| if (!in_out_info->hwc_layout) |
| goto free_dump_buf; |
| |
| dump_size = (2 + nr_sc_bits + nr_l2) * MALI_COUNTERS_PER_BLOCK * MALI_BYTES_PER_COUNTER; |
| |
| in_out_info->hwc_layout[i++] = JM_BLOCK; |
| in_out_info->hwc_layout[i++] = TILER_BLOCK; |
| |
| for (j = 0; j < nr_l2; j++) |
| in_out_info->hwc_layout[i++] = MMU_L2_BLOCK; |
| |
| while (core_mask != 0ull) { |
| if ((core_mask & 1ull) != 0ull) |
| in_out_info->hwc_layout[i++] = SHADER_BLOCK; |
| else |
| in_out_info->hwc_layout[i++] = RESERVED_BLOCK; |
| core_mask >>= 1; |
| } |
| } |
| |
| /* Calculated dump size must be the same as real dump size */ |
| if (WARN_ON(dump_size != metadata->dump_buf_bytes)) |
| goto free_layout; |
| |
| in_out_info->nr_hwc_blocks = i; |
| in_out_info->size = dump_size; |
| |
| phys_map.jm_bm = in_out_info->bitmask[JM_BLOCK]; |
| phys_map.tiler_bm = in_out_info->bitmask[TILER_BLOCK]; |
| phys_map.shader_bm = in_out_info->bitmask[SHADER_BLOCK]; |
| phys_map.mmu_l2_bm = in_out_info->bitmask[MMU_L2_BLOCK]; |
| kbase_hwcnt_gpu_enable_map_from_physical(&hand->enable_map, &phys_map); |
| errcode = kbase_hwcnt_virtualizer_client_create( |
| hand->kbdev->hwcnt_gpu_virt, &hand->enable_map, &hand->hvcli); |
| if (errcode) { |
| dev_err(hand->kbdev->dev, |
| "Failed to register gator with hwcnt virtualizer core"); |
| goto free_layout; |
| } |
| |
| return hand; |
| |
| free_layout: |
| kfree(in_out_info->hwc_layout); |
| free_dump_buf: |
| kbase_hwcnt_dump_buffer_free(&hand->dump_buf); |
| free_enable_map: |
| kbase_hwcnt_enable_map_free(&hand->enable_map); |
| release_device: |
| kbase_release_device(hand->kbdev); |
| free_hand: |
| kfree(hand); |
| return NULL; |
| } |
| KBASE_EXPORT_SYMBOL(kbase_gator_hwcnt_init); |
| |
| void kbase_gator_hwcnt_term(struct kbase_gator_hwcnt_info *in_out_info, struct kbase_gator_hwcnt_handles *opaque_handles) |
| { |
| if (in_out_info) |
| kfree(in_out_info->hwc_layout); |
| |
| if (opaque_handles) { |
| cancel_work_sync(&opaque_handles->dump_work); |
| kbase_hwcnt_virtualizer_client_destroy(opaque_handles->hvcli); |
| kbase_hwcnt_dump_buffer_free(&opaque_handles->dump_buf); |
| kbase_hwcnt_enable_map_free(&opaque_handles->enable_map); |
| kbase_release_device(opaque_handles->kbdev); |
| kfree(opaque_handles); |
| } |
| } |
| KBASE_EXPORT_SYMBOL(kbase_gator_hwcnt_term); |
| |
| static void dump_worker(struct work_struct *work) |
| { |
| int errcode; |
| u64 ts_start_ns; |
| u64 ts_end_ns; |
| struct kbase_gator_hwcnt_handles *hand; |
| |
| hand = container_of(work, struct kbase_gator_hwcnt_handles, dump_work); |
| errcode = kbase_hwcnt_virtualizer_client_dump( |
| hand->hvcli, &ts_start_ns, &ts_end_ns, &hand->dump_buf); |
| if (!errcode) { |
| /* Patch the header to hide other client's counter choices */ |
| kbase_hwcnt_gpu_patch_dump_headers( |
| &hand->dump_buf, &hand->enable_map); |
| /* Zero all non-enabled counters (currently undefined values) */ |
| kbase_hwcnt_dump_buffer_zero_non_enabled( |
| &hand->dump_buf, &hand->enable_map); |
| spin_lock_bh(&hand->dump_lock); |
| hand->dump_complete = 1; |
| spin_unlock_bh(&hand->dump_lock); |
| } else { |
| schedule_work(&hand->dump_work); |
| } |
| } |
| |
| uint32_t kbase_gator_instr_hwcnt_dump_complete( |
| struct kbase_gator_hwcnt_handles *opaque_handles, |
| uint32_t * const success) |
| { |
| |
| if (opaque_handles && success) { |
| *success = opaque_handles->dump_complete; |
| opaque_handles->dump_complete = 0; |
| return *success; |
| } |
| return 0; |
| } |
| KBASE_EXPORT_SYMBOL(kbase_gator_instr_hwcnt_dump_complete); |
| |
| uint32_t kbase_gator_instr_hwcnt_dump_irq(struct kbase_gator_hwcnt_handles *opaque_handles) |
| { |
| if (opaque_handles) |
| schedule_work(&opaque_handles->dump_work); |
| return 0; |
| } |
| KBASE_EXPORT_SYMBOL(kbase_gator_instr_hwcnt_dump_irq); |