/* * * (C) COPYRIGHT 2014-2017 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. * * A copy of the licence is included with the program, and can also be obtained * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #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" #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_vinstr_client *vinstr_cli; void *vinstr_buffer; 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; 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) { struct kbase_gator_hwcnt_handles *hand; struct kbase_uk_hwcnt_reader_setup setup; 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; dump_size = kbase_vinstr_dump_size(hand->kbdev); hand->vinstr_buffer = kzalloc(dump_size, GFP_KERNEL); if (!hand->vinstr_buffer) goto release_device; in_out_info->kernel_dump_buffer = hand->vinstr_buffer; 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_vinstr_buffer; 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_vinstr_buffer; 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; } } in_out_info->nr_hwc_blocks = i; in_out_info->size = dump_size; setup.jm_bm = in_out_info->bitmask[0]; setup.tiler_bm = in_out_info->bitmask[1]; setup.shader_bm = in_out_info->bitmask[2]; setup.mmu_l2_bm = in_out_info->bitmask[3]; hand->vinstr_cli = kbase_vinstr_hwcnt_kernel_setup(hand->kbdev->vinstr_ctx, &setup, hand->vinstr_buffer); if (!hand->vinstr_cli) { dev_err(hand->kbdev->dev, "Failed to register gator with vinstr core"); goto free_layout; } return hand; free_layout: kfree(in_out_info->hwc_layout); free_vinstr_buffer: kfree(hand->vinstr_buffer); 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_vinstr_detach_client(opaque_handles->vinstr_cli); kfree(opaque_handles->vinstr_buffer); kbase_release_device(opaque_handles->kbdev); kfree(opaque_handles); } } KBASE_EXPORT_SYMBOL(kbase_gator_hwcnt_term); static void dump_worker(struct work_struct *work) { struct kbase_gator_hwcnt_handles *hand; hand = container_of(work, struct kbase_gator_hwcnt_handles, dump_work); if (!kbase_vinstr_hwc_dump(hand->vinstr_cli, BASE_HWCNT_READER_EVENT_MANUAL)) { 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);