| // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note |
| /* |
| * |
| * (C) COPYRIGHT 2019-2021 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 license. |
| * |
| * 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. |
| * |
| */ |
| |
| #include "mali_kbase_csf_csg_debugfs.h" |
| #include <mali_kbase.h> |
| #include <linux/seq_file.h> |
| #include <linux/delay.h> |
| #include <csf/mali_kbase_csf_trace_buffer.h> |
| |
| #if IS_ENABLED(CONFIG_DEBUG_FS) |
| #include "mali_kbase_csf_tl_reader.h" |
| |
| /** |
| * blocked_reason_to_string() - Convert blocking reason id to a string |
| * |
| * @reason_id: blocked_reason |
| * |
| * Return: Suitable string |
| */ |
| static const char *blocked_reason_to_string(u32 reason_id) |
| { |
| /* possible blocking reasons of a cs */ |
| static const char *const cs_blocked_reason[] = { |
| [CS_STATUS_BLOCKED_REASON_REASON_UNBLOCKED] = "UNBLOCKED", |
| [CS_STATUS_BLOCKED_REASON_REASON_WAIT] = "WAIT", |
| [CS_STATUS_BLOCKED_REASON_REASON_PROGRESS_WAIT] = |
| "PROGRESS_WAIT", |
| [CS_STATUS_BLOCKED_REASON_REASON_SYNC_WAIT] = "SYNC_WAIT", |
| [CS_STATUS_BLOCKED_REASON_REASON_DEFERRED] = "DEFERRED", |
| [CS_STATUS_BLOCKED_REASON_REASON_RESOURCE] = "RESOURCE", |
| [CS_STATUS_BLOCKED_REASON_REASON_FLUSH] = "FLUSH" |
| }; |
| |
| if (WARN_ON(reason_id >= ARRAY_SIZE(cs_blocked_reason))) |
| return "UNKNOWN_BLOCKED_REASON_ID"; |
| |
| return cs_blocked_reason[reason_id]; |
| } |
| |
| static void kbasep_csf_scheduler_dump_active_queue_cs_status_wait( |
| struct seq_file *file, u32 wait_status, u32 wait_sync_value, |
| u64 wait_sync_live_value, u64 wait_sync_pointer, u32 sb_status, |
| u32 blocked_reason) |
| { |
| #define WAITING "Waiting" |
| #define NOT_WAITING "Not waiting" |
| |
| seq_printf(file, "SB_MASK: %d\n", |
| CS_STATUS_WAIT_SB_MASK_GET(wait_status)); |
| seq_printf(file, "PROGRESS_WAIT: %s\n", |
| CS_STATUS_WAIT_PROGRESS_WAIT_GET(wait_status) ? |
| WAITING : NOT_WAITING); |
| seq_printf(file, "PROTM_PEND: %s\n", |
| CS_STATUS_WAIT_PROTM_PEND_GET(wait_status) ? |
| WAITING : NOT_WAITING); |
| seq_printf(file, "SYNC_WAIT: %s\n", |
| CS_STATUS_WAIT_SYNC_WAIT_GET(wait_status) ? |
| WAITING : NOT_WAITING); |
| seq_printf(file, "WAIT_CONDITION: %s\n", |
| CS_STATUS_WAIT_SYNC_WAIT_CONDITION_GET(wait_status) ? |
| "greater than" : "less or equal"); |
| seq_printf(file, "SYNC_POINTER: 0x%llx\n", wait_sync_pointer); |
| seq_printf(file, "SYNC_VALUE: %d\n", wait_sync_value); |
| seq_printf(file, "SYNC_LIVE_VALUE: 0x%016llx\n", wait_sync_live_value); |
| seq_printf(file, "SB_STATUS: %u\n", |
| CS_STATUS_SCOREBOARDS_NONZERO_GET(sb_status)); |
| seq_printf(file, "BLOCKED_REASON: %s\n", |
| blocked_reason_to_string(CS_STATUS_BLOCKED_REASON_REASON_GET( |
| blocked_reason))); |
| } |
| |
| static void kbasep_csf_scheduler_dump_active_cs_trace(struct seq_file *file, |
| struct kbase_csf_cmd_stream_info const *const stream) |
| { |
| u32 val = kbase_csf_firmware_cs_input_read(stream, |
| CS_INSTR_BUFFER_BASE_LO); |
| u64 addr = ((u64)kbase_csf_firmware_cs_input_read(stream, |
| CS_INSTR_BUFFER_BASE_HI) << 32) | val; |
| val = kbase_csf_firmware_cs_input_read(stream, |
| CS_INSTR_BUFFER_SIZE); |
| |
| seq_printf(file, "CS_TRACE_BUF_ADDR: 0x%16llx, SIZE: %u\n", addr, val); |
| |
| /* Write offset variable address (pointer) */ |
| val = kbase_csf_firmware_cs_input_read(stream, |
| CS_INSTR_BUFFER_OFFSET_POINTER_LO); |
| addr = ((u64)kbase_csf_firmware_cs_input_read(stream, |
| CS_INSTR_BUFFER_OFFSET_POINTER_HI) << 32) | val; |
| seq_printf(file, "CS_TRACE_BUF_OFFSET_PTR: 0x%16llx\n", addr); |
| |
| /* EVENT_SIZE and EVENT_STATEs */ |
| val = kbase_csf_firmware_cs_input_read(stream, CS_INSTR_CONFIG); |
| seq_printf(file, "TRACE_EVENT_SIZE: 0x%x, TRACE_EVENT_STAES 0x%x\n", |
| CS_INSTR_CONFIG_EVENT_SIZE_GET(val), |
| CS_INSTR_CONFIG_EVENT_STATE_GET(val)); |
| } |
| |
| /** |
| * kbasep_csf_scheduler_dump_active_queue() - Print GPU command queue |
| * debug information |
| * |
| * @file: seq_file for printing to |
| * @queue: Address of a GPU command queue to examine |
| */ |
| static void kbasep_csf_scheduler_dump_active_queue(struct seq_file *file, |
| struct kbase_queue *queue) |
| { |
| u32 *addr; |
| u64 cs_extract; |
| u64 cs_insert; |
| u32 cs_active; |
| u64 wait_sync_pointer; |
| u32 wait_status, wait_sync_value; |
| u32 sb_status; |
| u32 blocked_reason; |
| struct kbase_vmap_struct *mapping; |
| u64 *evt; |
| u64 wait_sync_live_value; |
| |
| if (!queue) |
| return; |
| |
| if (WARN_ON(queue->csi_index == KBASEP_IF_NR_INVALID || |
| !queue->group)) |
| return; |
| |
| /* Ring the doorbell to have firmware update CS_EXTRACT */ |
| kbase_csf_ring_cs_user_doorbell(queue->kctx->kbdev, queue); |
| msleep(100); |
| |
| addr = (u32 *)queue->user_io_addr; |
| cs_insert = addr[CS_INSERT_LO/4] | ((u64)addr[CS_INSERT_HI/4] << 32); |
| |
| addr = (u32 *)(queue->user_io_addr + PAGE_SIZE); |
| cs_extract = addr[CS_EXTRACT_LO/4] | ((u64)addr[CS_EXTRACT_HI/4] << 32); |
| cs_active = addr[CS_ACTIVE/4]; |
| |
| #define KBASEP_CSF_DEBUGFS_CS_HEADER_USER_IO \ |
| "Bind Idx, Ringbuf addr, Prio, Insert offset, Extract offset, Active, Doorbell\n" |
| |
| seq_printf(file, KBASEP_CSF_DEBUGFS_CS_HEADER_USER_IO "%8d, %16llx, %4u, %16llx, %16llx, %6u, %8d\n", |
| queue->csi_index, queue->base_addr, queue->priority, |
| cs_insert, cs_extract, cs_active, queue->doorbell_nr); |
| |
| /* Print status information for blocked group waiting for sync object. For on-slot queues, |
| * if cs_trace is enabled, dump the interface's cs_trace configuration. |
| */ |
| if (kbase_csf_scheduler_group_get_slot(queue->group) < 0) { |
| if (CS_STATUS_WAIT_SYNC_WAIT_GET(queue->status_wait)) { |
| wait_status = queue->status_wait; |
| wait_sync_value = queue->sync_value; |
| wait_sync_pointer = queue->sync_ptr; |
| sb_status = queue->sb_status; |
| blocked_reason = queue->blocked_reason; |
| |
| evt = (u64 *)kbase_phy_alloc_mapping_get(queue->kctx, wait_sync_pointer, &mapping); |
| if (evt) { |
| wait_sync_live_value = evt[0]; |
| kbase_phy_alloc_mapping_put(queue->kctx, mapping); |
| } else { |
| wait_sync_live_value = U64_MAX; |
| } |
| |
| kbasep_csf_scheduler_dump_active_queue_cs_status_wait( |
| file, wait_status, wait_sync_value, |
| wait_sync_live_value, wait_sync_pointer, |
| sb_status, blocked_reason); |
| } |
| } else { |
| struct kbase_device const *const kbdev = |
| queue->group->kctx->kbdev; |
| struct kbase_csf_cmd_stream_group_info const *const ginfo = |
| &kbdev->csf.global_iface.groups[queue->group->csg_nr]; |
| struct kbase_csf_cmd_stream_info const *const stream = |
| &ginfo->streams[queue->csi_index]; |
| u64 cmd_ptr; |
| u32 req_res; |
| |
| if (WARN_ON(!stream)) |
| return; |
| |
| cmd_ptr = kbase_csf_firmware_cs_output(stream, |
| CS_STATUS_CMD_PTR_LO); |
| cmd_ptr |= (u64)kbase_csf_firmware_cs_output(stream, |
| CS_STATUS_CMD_PTR_HI) << 32; |
| req_res = kbase_csf_firmware_cs_output(stream, |
| CS_STATUS_REQ_RESOURCE); |
| |
| seq_printf(file, "CMD_PTR: 0x%llx\n", cmd_ptr); |
| seq_printf(file, "REQ_RESOURCE [COMPUTE]: %d\n", |
| CS_STATUS_REQ_RESOURCE_COMPUTE_RESOURCES_GET(req_res)); |
| seq_printf(file, "REQ_RESOURCE [FRAGMENT]: %d\n", |
| CS_STATUS_REQ_RESOURCE_FRAGMENT_RESOURCES_GET(req_res)); |
| seq_printf(file, "REQ_RESOURCE [TILER]: %d\n", |
| CS_STATUS_REQ_RESOURCE_TILER_RESOURCES_GET(req_res)); |
| seq_printf(file, "REQ_RESOURCE [IDVS]: %d\n", |
| CS_STATUS_REQ_RESOURCE_IDVS_RESOURCES_GET(req_res)); |
| |
| wait_status = kbase_csf_firmware_cs_output(stream, |
| CS_STATUS_WAIT); |
| wait_sync_value = kbase_csf_firmware_cs_output(stream, |
| CS_STATUS_WAIT_SYNC_VALUE); |
| wait_sync_pointer = kbase_csf_firmware_cs_output(stream, |
| CS_STATUS_WAIT_SYNC_POINTER_LO); |
| wait_sync_pointer |= (u64)kbase_csf_firmware_cs_output(stream, |
| CS_STATUS_WAIT_SYNC_POINTER_HI) << 32; |
| |
| sb_status = kbase_csf_firmware_cs_output(stream, |
| CS_STATUS_SCOREBOARDS); |
| blocked_reason = kbase_csf_firmware_cs_output( |
| stream, CS_STATUS_BLOCKED_REASON); |
| |
| evt = (u64 *)kbase_phy_alloc_mapping_get(queue->kctx, wait_sync_pointer, &mapping); |
| if (evt) { |
| wait_sync_live_value = evt[0]; |
| kbase_phy_alloc_mapping_put(queue->kctx, mapping); |
| } else { |
| wait_sync_live_value = U64_MAX; |
| } |
| |
| kbasep_csf_scheduler_dump_active_queue_cs_status_wait( |
| file, wait_status, wait_sync_value, |
| wait_sync_live_value, wait_sync_pointer, sb_status, |
| blocked_reason); |
| /* Dealing with cs_trace */ |
| if (kbase_csf_scheduler_queue_has_trace(queue)) |
| kbasep_csf_scheduler_dump_active_cs_trace(file, stream); |
| else |
| seq_puts(file, "NO CS_TRACE\n"); |
| } |
| |
| seq_puts(file, "\n"); |
| } |
| |
| /* Waiting timeout for STATUS_UPDATE acknowledgment, in milliseconds */ |
| #define CSF_STATUS_UPDATE_TO_MS (100) |
| |
| static void kbasep_csf_scheduler_dump_active_group(struct seq_file *file, |
| struct kbase_queue_group *const group) |
| { |
| if (kbase_csf_scheduler_group_get_slot(group) >= 0) { |
| struct kbase_device *const kbdev = group->kctx->kbdev; |
| unsigned long flags; |
| u32 ep_c, ep_r; |
| char exclusive; |
| struct kbase_csf_cmd_stream_group_info const *const ginfo = |
| &kbdev->csf.global_iface.groups[group->csg_nr]; |
| long remaining = |
| kbase_csf_timeout_in_jiffies(CSF_STATUS_UPDATE_TO_MS); |
| u8 slot_priority = |
| kbdev->csf.scheduler.csg_slots[group->csg_nr].priority; |
| |
| kbase_csf_scheduler_spin_lock(kbdev, &flags); |
| kbase_csf_firmware_csg_input_mask(ginfo, CSG_REQ, |
| ~kbase_csf_firmware_csg_output(ginfo, CSG_ACK), |
| CSG_REQ_STATUS_UPDATE_MASK); |
| kbase_csf_scheduler_spin_unlock(kbdev, flags); |
| kbase_csf_ring_csg_doorbell(kbdev, group->csg_nr); |
| |
| remaining = wait_event_timeout(kbdev->csf.event_wait, |
| !((kbase_csf_firmware_csg_input_read(ginfo, CSG_REQ) ^ |
| kbase_csf_firmware_csg_output(ginfo, CSG_ACK)) & |
| CSG_REQ_STATUS_UPDATE_MASK), remaining); |
| |
| ep_c = kbase_csf_firmware_csg_output(ginfo, |
| CSG_STATUS_EP_CURRENT); |
| ep_r = kbase_csf_firmware_csg_output(ginfo, CSG_STATUS_EP_REQ); |
| |
| if (CSG_STATUS_EP_REQ_EXCLUSIVE_COMPUTE_GET(ep_r)) |
| exclusive = 'C'; |
| else if (CSG_STATUS_EP_REQ_EXCLUSIVE_FRAGMENT_GET(ep_r)) |
| exclusive = 'F'; |
| else |
| exclusive = '0'; |
| |
| if (!remaining) { |
| dev_err(kbdev->dev, |
| "Timed out for STATUS_UPDATE on group %d on slot %d", |
| group->handle, group->csg_nr); |
| |
| seq_printf(file, "*** Warn: Timed out for STATUS_UPDATE on slot %d\n", |
| group->csg_nr); |
| seq_printf(file, "*** The following group-record is likely stale\n"); |
| } |
| |
| seq_puts(file, "GroupID, CSG NR, CSG Prio, Run State, Priority, C_EP(Alloc/Req), F_EP(Alloc/Req), T_EP(Alloc/Req), Exclusive\n"); |
| seq_printf(file, "%7d, %6d, %8d, %9d, %8d, %11d/%3d, %11d/%3d, %11d/%3d, %9c\n", |
| group->handle, |
| group->csg_nr, |
| slot_priority, |
| group->run_state, |
| group->priority, |
| CSG_STATUS_EP_CURRENT_COMPUTE_EP_GET(ep_c), |
| CSG_STATUS_EP_REQ_COMPUTE_EP_GET(ep_r), |
| CSG_STATUS_EP_CURRENT_FRAGMENT_EP_GET(ep_c), |
| CSG_STATUS_EP_REQ_FRAGMENT_EP_GET(ep_r), |
| CSG_STATUS_EP_CURRENT_TILER_EP_GET(ep_c), |
| CSG_STATUS_EP_REQ_TILER_EP_GET(ep_r), |
| exclusive); |
| } else { |
| seq_puts(file, "GroupID, CSG NR, Run State, Priority\n"); |
| seq_printf(file, "%7d, %6d, %9d, %8d\n", |
| group->handle, |
| group->csg_nr, |
| group->run_state, |
| group->priority); |
| } |
| |
| if (group->run_state != KBASE_CSF_GROUP_TERMINATED) { |
| unsigned int i; |
| |
| seq_puts(file, "Bound queues:\n"); |
| |
| for (i = 0; i < MAX_SUPPORTED_STREAMS_PER_GROUP; i++) { |
| kbasep_csf_scheduler_dump_active_queue(file, |
| group->bound_queues[i]); |
| } |
| } |
| |
| seq_puts(file, "\n"); |
| } |
| |
| /** |
| * kbasep_csf_queue_group_debugfs_show() - Print per-context GPU command queue |
| * group debug information |
| * |
| * @file: The seq_file for printing to |
| * @data: The debugfs dentry private data, a pointer to kbase context |
| * |
| * Return: Negative error code or 0 on success. |
| */ |
| static int kbasep_csf_queue_group_debugfs_show(struct seq_file *file, |
| void *data) |
| { |
| u32 gr; |
| struct kbase_context *const kctx = file->private; |
| struct kbase_device *const kbdev = kctx->kbdev; |
| |
| if (WARN_ON(!kctx)) |
| return -EINVAL; |
| |
| seq_printf(file, "MALI_CSF_CSG_DEBUGFS_VERSION: v%u\n", |
| MALI_CSF_CSG_DEBUGFS_VERSION); |
| |
| mutex_lock(&kctx->csf.lock); |
| kbase_csf_scheduler_lock(kbdev); |
| for (gr = 0; gr < MAX_QUEUE_GROUP_NUM; gr++) { |
| struct kbase_queue_group *const group = |
| kctx->csf.queue_groups[gr]; |
| |
| if (group) |
| kbasep_csf_scheduler_dump_active_group(file, group); |
| } |
| kbase_csf_scheduler_unlock(kbdev); |
| mutex_unlock(&kctx->csf.lock); |
| |
| return 0; |
| } |
| |
| /** |
| * kbasep_csf_scheduler_dump_active_groups() - Print debug info for active |
| * GPU command queue groups |
| * |
| * @file: The seq_file for printing to |
| * @data: The debugfs dentry private data, a pointer to kbase_device |
| * |
| * Return: Negative error code or 0 on success. |
| */ |
| static int kbasep_csf_scheduler_dump_active_groups(struct seq_file *file, |
| void *data) |
| { |
| u32 csg_nr; |
| struct kbase_device *kbdev = file->private; |
| u32 num_groups = kbdev->csf.global_iface.group_num; |
| |
| seq_printf(file, "MALI_CSF_CSG_DEBUGFS_VERSION: v%u\n", |
| MALI_CSF_CSG_DEBUGFS_VERSION); |
| |
| kbase_csf_scheduler_lock(kbdev); |
| for (csg_nr = 0; csg_nr < num_groups; csg_nr++) { |
| struct kbase_queue_group *const group = |
| kbdev->csf.scheduler.csg_slots[csg_nr].resident_group; |
| |
| if (!group) |
| continue; |
| |
| seq_printf(file, "\nCtx %d_%d\n", group->kctx->tgid, |
| group->kctx->id); |
| |
| kbasep_csf_scheduler_dump_active_group(file, group); |
| } |
| kbase_csf_scheduler_unlock(kbdev); |
| |
| return 0; |
| } |
| |
| static int kbasep_csf_queue_group_debugfs_open(struct inode *in, |
| struct file *file) |
| { |
| return single_open(file, kbasep_csf_queue_group_debugfs_show, |
| in->i_private); |
| } |
| |
| static int kbasep_csf_active_queue_groups_debugfs_open(struct inode *in, |
| struct file *file) |
| { |
| return single_open(file, kbasep_csf_scheduler_dump_active_groups, |
| in->i_private); |
| } |
| |
| static const struct file_operations kbasep_csf_queue_group_debugfs_fops = { |
| .open = kbasep_csf_queue_group_debugfs_open, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = single_release, |
| }; |
| |
| void kbase_csf_queue_group_debugfs_init(struct kbase_context *kctx) |
| { |
| struct dentry *file; |
| #if (KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE) |
| const mode_t mode = 0444; |
| #else |
| const mode_t mode = 0400; |
| #endif |
| |
| if (WARN_ON(!kctx || IS_ERR_OR_NULL(kctx->kctx_dentry))) |
| return; |
| |
| file = debugfs_create_file("groups", mode, |
| kctx->kctx_dentry, kctx, &kbasep_csf_queue_group_debugfs_fops); |
| |
| if (IS_ERR_OR_NULL(file)) { |
| dev_warn(kctx->kbdev->dev, |
| "Unable to create per context queue groups debugfs entry"); |
| } |
| } |
| |
| static const struct file_operations |
| kbasep_csf_active_queue_groups_debugfs_fops = { |
| .open = kbasep_csf_active_queue_groups_debugfs_open, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = single_release, |
| }; |
| |
| static int kbasep_csf_debugfs_scheduling_timer_enabled_get( |
| void *data, u64 *val) |
| { |
| struct kbase_device *const kbdev = data; |
| |
| *val = kbase_csf_scheduler_timer_is_enabled(kbdev); |
| |
| return 0; |
| } |
| |
| static int kbasep_csf_debugfs_scheduling_timer_enabled_set( |
| void *data, u64 val) |
| { |
| struct kbase_device *const kbdev = data; |
| |
| kbase_csf_scheduler_timer_set_enabled(kbdev, val != 0); |
| |
| return 0; |
| } |
| |
| static int kbasep_csf_debugfs_scheduling_timer_kick_set( |
| void *data, u64 val) |
| { |
| struct kbase_device *const kbdev = data; |
| |
| kbase_csf_scheduler_kick(kbdev); |
| |
| return 0; |
| } |
| |
| DEFINE_SIMPLE_ATTRIBUTE(kbasep_csf_debugfs_scheduling_timer_enabled_fops, |
| &kbasep_csf_debugfs_scheduling_timer_enabled_get, |
| &kbasep_csf_debugfs_scheduling_timer_enabled_set, |
| "%llu\n"); |
| DEFINE_SIMPLE_ATTRIBUTE(kbasep_csf_debugfs_scheduling_timer_kick_fops, |
| NULL, |
| &kbasep_csf_debugfs_scheduling_timer_kick_set, |
| "%llu\n"); |
| |
| /** |
| * kbase_csf_debugfs_scheduler_suspend_get() - get if the scheduler is suspended. |
| * |
| * @data: The debugfs dentry private data, a pointer to kbase_device |
| * @val: The debugfs output value, boolean: 1 suspended, 0 otherwise |
| * |
| * Return: 0 |
| */ |
| static int kbase_csf_debugfs_scheduler_suspend_get( |
| void *data, u64 *val) |
| { |
| struct kbase_device *kbdev = data; |
| struct kbase_csf_scheduler *scheduler = &kbdev->csf.scheduler; |
| |
| kbase_csf_scheduler_lock(kbdev); |
| *val = (scheduler->state == SCHED_SUSPENDED); |
| kbase_csf_scheduler_unlock(kbdev); |
| |
| return 0; |
| } |
| |
| /** |
| * kbase_csf_debugfs_scheduler_suspend_set() - set the scheduler to suspended. |
| * |
| * @data: The debugfs dentry private data, a pointer to kbase_device |
| * @val: The debugfs input value, boolean: 1 suspend, 0 otherwise |
| * |
| * Return: Negative value if already in requested state, 0 otherwise. |
| */ |
| static int kbase_csf_debugfs_scheduler_suspend_set( |
| void *data, u64 val) |
| { |
| struct kbase_device *kbdev = data; |
| struct kbase_csf_scheduler *scheduler = &kbdev->csf.scheduler; |
| enum kbase_csf_scheduler_state state; |
| |
| kbase_csf_scheduler_lock(kbdev); |
| state = scheduler->state; |
| kbase_csf_scheduler_unlock(kbdev); |
| |
| if (val && (state != SCHED_SUSPENDED)) |
| kbase_csf_scheduler_pm_suspend(kbdev); |
| else if (!val && (state == SCHED_SUSPENDED)) |
| kbase_csf_scheduler_pm_resume(kbdev); |
| else |
| return -1; |
| |
| return 0; |
| } |
| |
| DEFINE_SIMPLE_ATTRIBUTE(kbasep_csf_debugfs_scheduler_suspend_fops, |
| &kbase_csf_debugfs_scheduler_suspend_get, |
| &kbase_csf_debugfs_scheduler_suspend_set, |
| "%llu\n"); |
| |
| void kbase_csf_debugfs_init(struct kbase_device *kbdev) |
| { |
| debugfs_create_file("active_groups", 0444, |
| kbdev->mali_debugfs_directory, kbdev, |
| &kbasep_csf_active_queue_groups_debugfs_fops); |
| |
| debugfs_create_file("scheduling_timer_enabled", 0644, |
| kbdev->mali_debugfs_directory, kbdev, |
| &kbasep_csf_debugfs_scheduling_timer_enabled_fops); |
| debugfs_create_file("scheduling_timer_kick", 0200, |
| kbdev->mali_debugfs_directory, kbdev, |
| &kbasep_csf_debugfs_scheduling_timer_kick_fops); |
| debugfs_create_file("scheduler_suspend", 0644, |
| kbdev->mali_debugfs_directory, kbdev, |
| &kbasep_csf_debugfs_scheduler_suspend_fops); |
| |
| kbase_csf_tl_reader_debugfs_init(kbdev); |
| kbase_csf_firmware_trace_buffer_debugfs_init(kbdev); |
| } |
| |
| #else |
| /* |
| * Stub functions for when debugfs is disabled |
| */ |
| void kbase_csf_queue_group_debugfs_init(struct kbase_context *kctx) |
| { |
| } |
| |
| void kbase_csf_debugfs_init(struct kbase_device *kbdev) |
| { |
| } |
| |
| #endif /* CONFIG_DEBUG_FS */ |