| /* | 
|  * | 
|  * (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); |