// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note /* * * (C) COPYRIGHT 2022 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 #include #include #include #include #include #include static const char *coresight_state_to_string(enum kbase_debug_coresight_csf_state state) { switch (state) { case KBASE_DEBUG_CORESIGHT_CSF_DISABLED: return "DISABLED"; case KBASE_DEBUG_CORESIGHT_CSF_ENABLED: return "ENABLED"; default: break; } return "UNKNOWN"; } static bool validate_reg_addr(struct kbase_debug_coresight_csf_client *client, struct kbase_device *kbdev, u32 reg_addr, u8 op_type) { int i; if (reg_addr & 0x3) { dev_err(kbdev->dev, "Invalid operation %d: reg_addr (0x%x) not 32bit aligned", op_type, reg_addr); return false; } for (i = 0; i < client->nr_ranges; i++) { struct kbase_debug_coresight_csf_address_range *range = &client->addr_ranges[i]; if ((range->start <= reg_addr) && (reg_addr <= range->end)) return true; } dev_err(kbdev->dev, "Invalid operation %d: reg_addr (0x%x) not in client range", op_type, reg_addr); return false; } static bool validate_op(struct kbase_debug_coresight_csf_client *client, struct kbase_debug_coresight_csf_op *op) { struct kbase_device *kbdev; u32 reg; if (!op) return false; if (!client) return false; kbdev = (struct kbase_device *)client->drv_data; switch (op->type) { case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_NOP: return true; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_WRITE_IMM: if (validate_reg_addr(client, kbdev, op->op.write_imm.reg_addr, op->type)) return true; break; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_WRITE_IMM_RANGE: for (reg = op->op.write_imm_range.reg_start; reg <= op->op.write_imm_range.reg_end; reg += sizeof(u32)) { if (!validate_reg_addr(client, kbdev, reg, op->type)) return false; } return true; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_WRITE: if (!op->op.write.ptr) { dev_err(kbdev->dev, "Invalid operation %d: ptr not set", op->type); break; } if (validate_reg_addr(client, kbdev, op->op.write.reg_addr, op->type)) return true; break; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_READ: if (!op->op.read.ptr) { dev_err(kbdev->dev, "Invalid operation %d: ptr not set", op->type); break; } if (validate_reg_addr(client, kbdev, op->op.read.reg_addr, op->type)) return true; break; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_POLL: if (validate_reg_addr(client, kbdev, op->op.poll.reg_addr, op->type)) return true; break; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_BIT_AND: fallthrough; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_BIT_OR: fallthrough; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_BIT_XOR: fallthrough; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_BIT_NOT: if (op->op.bitw.ptr != NULL) return true; dev_err(kbdev->dev, "Invalid bitwise operation pointer"); break; default: dev_err(kbdev->dev, "Invalid operation %d", op->type); break; } return false; } static bool validate_seq(struct kbase_debug_coresight_csf_client *client, struct kbase_debug_coresight_csf_sequence *seq) { struct kbase_debug_coresight_csf_op *ops = seq->ops; int nr_ops = seq->nr_ops; int i; for (i = 0; i < nr_ops; i++) { if (!validate_op(client, &ops[i])) return false; } return true; } static int execute_op(struct kbase_device *kbdev, struct kbase_debug_coresight_csf_op *op) { int result = -EINVAL; u32 reg; dev_dbg(kbdev->dev, "Execute operation %d", op->type); switch (op->type) { case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_NOP: result = 0; break; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_WRITE_IMM: result = kbase_csf_firmware_mcu_register_write(kbdev, op->op.write.reg_addr, op->op.write_imm.val); break; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_WRITE_IMM_RANGE: for (reg = op->op.write_imm_range.reg_start; reg <= op->op.write_imm_range.reg_end; reg += sizeof(u32)) { result = kbase_csf_firmware_mcu_register_write(kbdev, reg, op->op.write_imm_range.val); if (!result) break; } break; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_WRITE: result = kbase_csf_firmware_mcu_register_write(kbdev, op->op.write.reg_addr, *op->op.write.ptr); break; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_READ: result = kbase_csf_firmware_mcu_register_read(kbdev, op->op.read.reg_addr, op->op.read.ptr); break; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_POLL: result = kbase_csf_firmware_mcu_register_poll(kbdev, op->op.poll.reg_addr, op->op.poll.mask, op->op.poll.val); break; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_BIT_AND: *op->op.bitw.ptr &= op->op.bitw.val; result = 0; break; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_BIT_OR: *op->op.bitw.ptr |= op->op.bitw.val; result = 0; break; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_BIT_XOR: *op->op.bitw.ptr ^= op->op.bitw.val; result = 0; break; case KBASE_DEBUG_CORESIGHT_CSF_OP_TYPE_BIT_NOT: *op->op.bitw.ptr = ~(*op->op.bitw.ptr); result = 0; break; default: dev_err(kbdev->dev, "Invalid operation %d", op->type); break; } return result; } static int coresight_config_enable(struct kbase_device *kbdev, struct kbase_debug_coresight_csf_config *config) { int ret = 0; int i; if (!config) return -EINVAL; if (config->state == KBASE_DEBUG_CORESIGHT_CSF_ENABLED) return ret; for (i = 0; config->enable_seq && !ret && i < config->enable_seq->nr_ops; i++) ret = execute_op(kbdev, &config->enable_seq->ops[i]); if (!ret) { dev_dbg(kbdev->dev, "Coresight config (0x%pK) state transition: %s to %s", config, coresight_state_to_string(config->state), coresight_state_to_string(KBASE_DEBUG_CORESIGHT_CSF_ENABLED)); config->state = KBASE_DEBUG_CORESIGHT_CSF_ENABLED; } /* Always assign the return code during config enable. * It gets propagated when calling config disable. */ config->error = ret; return ret; } static int coresight_config_disable(struct kbase_device *kbdev, struct kbase_debug_coresight_csf_config *config) { int ret = 0; int i; if (!config) return -EINVAL; if (config->state == KBASE_DEBUG_CORESIGHT_CSF_DISABLED) return ret; for (i = 0; config->disable_seq && !ret && i < config->disable_seq->nr_ops; i++) ret = execute_op(kbdev, &config->disable_seq->ops[i]); if (!ret) { dev_dbg(kbdev->dev, "Coresight config (0x%pK) state transition: %s to %s", config, coresight_state_to_string(config->state), coresight_state_to_string(KBASE_DEBUG_CORESIGHT_CSF_DISABLED)); config->state = KBASE_DEBUG_CORESIGHT_CSF_DISABLED; } else { /* Only assign the error if ret is not 0. * As we don't want to overwrite an error from config enable */ if (!config->error) config->error = ret; } return ret; } void *kbase_debug_coresight_csf_register(void *drv_data, struct kbase_debug_coresight_csf_address_range *ranges, int nr_ranges) { struct kbase_debug_coresight_csf_client *client, *client_entry; struct kbase_device *kbdev; unsigned long flags; int k; if (unlikely(!drv_data)) { pr_err("NULL drv_data"); return NULL; } kbdev = (struct kbase_device *)drv_data; if (unlikely(!ranges)) { dev_err(kbdev->dev, "NULL ranges"); return NULL; } if (unlikely(!nr_ranges)) { dev_err(kbdev->dev, "nr_ranges is 0"); return NULL; } for (k = 0; k < nr_ranges; k++) { if (ranges[k].end < ranges[k].start) { dev_err(kbdev->dev, "Invalid address ranges 0x%08x - 0x%08x", ranges[k].start, ranges[k].end); return NULL; } } client = kzalloc(sizeof(struct kbase_debug_coresight_csf_client), GFP_KERNEL); if (!client) return NULL; spin_lock_irqsave(&kbdev->csf.coresight.lock, flags); list_for_each_entry(client_entry, &kbdev->csf.coresight.clients, link) { struct kbase_debug_coresight_csf_address_range *client_ranges = client_entry->addr_ranges; int i; for (i = 0; i < client_entry->nr_ranges; i++) { int j; for (j = 0; j < nr_ranges; j++) { if ((ranges[j].start < client_ranges[i].end) && (client_ranges[i].start < ranges[j].end)) { spin_unlock_irqrestore(&kbdev->csf.coresight.lock, flags); kfree(client); dev_err(kbdev->dev, "Client with range 0x%08x - 0x%08x already present at address range 0x%08x - 0x%08x", client_ranges[i].start, client_ranges[i].end, ranges[j].start, ranges[j].end); return NULL; } } } } client->drv_data = drv_data; client->addr_ranges = ranges; client->nr_ranges = nr_ranges; list_add(&client->link, &kbdev->csf.coresight.clients); spin_unlock_irqrestore(&kbdev->csf.coresight.lock, flags); return client; } EXPORT_SYMBOL(kbase_debug_coresight_csf_register); void kbase_debug_coresight_csf_unregister(void *client_data) { struct kbase_debug_coresight_csf_client *client; struct kbase_debug_coresight_csf_config *config_entry; struct kbase_device *kbdev; unsigned long flags; bool retry = true; if (unlikely(!client_data)) { pr_err("NULL client"); return; } client = (struct kbase_debug_coresight_csf_client *)client_data; kbdev = (struct kbase_device *)client->drv_data; if (unlikely(!kbdev)) { pr_err("NULL drv_data in client"); return; } /* check for active config from client */ spin_lock_irqsave(&kbdev->csf.coresight.lock, flags); list_del_init(&client->link); while (retry && !list_empty(&kbdev->csf.coresight.configs)) { retry = false; list_for_each_entry(config_entry, &kbdev->csf.coresight.configs, link) { if (config_entry->client == client) { spin_unlock_irqrestore(&kbdev->csf.coresight.lock, flags); kbase_debug_coresight_csf_config_free(config_entry); spin_lock_irqsave(&kbdev->csf.coresight.lock, flags); retry = true; break; } } } spin_unlock_irqrestore(&kbdev->csf.coresight.lock, flags); kfree(client); } EXPORT_SYMBOL(kbase_debug_coresight_csf_unregister); void * kbase_debug_coresight_csf_config_create(void *client_data, struct kbase_debug_coresight_csf_sequence *enable_seq, struct kbase_debug_coresight_csf_sequence *disable_seq) { struct kbase_debug_coresight_csf_client *client; struct kbase_debug_coresight_csf_config *config; struct kbase_device *kbdev; if (unlikely(!client_data)) { pr_err("NULL client"); return NULL; } client = (struct kbase_debug_coresight_csf_client *)client_data; kbdev = (struct kbase_device *)client->drv_data; if (unlikely(!kbdev)) { pr_err("NULL drv_data in client"); return NULL; } if (enable_seq) { if (!validate_seq(client, enable_seq)) { dev_err(kbdev->dev, "Invalid enable_seq"); return NULL; } } if (disable_seq) { if (!validate_seq(client, disable_seq)) { dev_err(kbdev->dev, "Invalid disable_seq"); return NULL; } } config = kzalloc(sizeof(struct kbase_debug_coresight_csf_config), GFP_KERNEL); if (WARN_ON(!client)) return NULL; config->client = client; config->enable_seq = enable_seq; config->disable_seq = disable_seq; config->error = 0; config->state = KBASE_DEBUG_CORESIGHT_CSF_DISABLED; INIT_LIST_HEAD(&config->link); return config; } EXPORT_SYMBOL(kbase_debug_coresight_csf_config_create); void kbase_debug_coresight_csf_config_free(void *config_data) { struct kbase_debug_coresight_csf_config *config; if (unlikely(!config_data)) { pr_err("NULL config"); return; } config = (struct kbase_debug_coresight_csf_config *)config_data; kbase_debug_coresight_csf_config_disable(config); kfree(config); } EXPORT_SYMBOL(kbase_debug_coresight_csf_config_free); int kbase_debug_coresight_csf_config_enable(void *config_data) { struct kbase_debug_coresight_csf_config *config; struct kbase_debug_coresight_csf_client *client; struct kbase_device *kbdev; struct kbase_debug_coresight_csf_config *config_entry; unsigned long flags; int ret = 0; if (unlikely(!config_data)) { pr_err("NULL config"); return -EINVAL; } config = (struct kbase_debug_coresight_csf_config *)config_data; client = (struct kbase_debug_coresight_csf_client *)config->client; if (unlikely(!client)) { pr_err("NULL client in config"); return -EINVAL; } kbdev = (struct kbase_device *)client->drv_data; if (unlikely(!kbdev)) { pr_err("NULL drv_data in client"); return -EINVAL; } /* Check to prevent double entry of config */ spin_lock_irqsave(&kbdev->csf.coresight.lock, flags); list_for_each_entry(config_entry, &kbdev->csf.coresight.configs, link) { if (config_entry == config) { spin_unlock_irqrestore(&kbdev->csf.coresight.lock, flags); dev_err(kbdev->dev, "Config already enabled"); return -EINVAL; } } spin_unlock_irqrestore(&kbdev->csf.coresight.lock, flags); kbase_csf_scheduler_lock(kbdev); kbase_csf_scheduler_spin_lock(kbdev, &flags); /* Check the state of Scheduler to confirm the desired state of MCU */ if (((kbdev->csf.scheduler.state != SCHED_SUSPENDED) && (kbdev->csf.scheduler.state != SCHED_SLEEPING) && !kbase_csf_scheduler_protected_mode_in_use(kbdev)) || kbase_pm_get_policy(kbdev) == &kbase_pm_always_on_policy_ops) { kbase_csf_scheduler_spin_unlock(kbdev, flags); /* Wait for MCU to reach the stable ON state */ ret = kbase_pm_wait_for_desired_state(kbdev); if (ret) dev_err(kbdev->dev, "Wait for PM state failed when enabling coresight config"); else ret = coresight_config_enable(kbdev, config); kbase_csf_scheduler_spin_lock(kbdev, &flags); } /* Add config to next enable sequence */ if (!ret) { spin_lock(&kbdev->csf.coresight.lock); list_add(&config->link, &kbdev->csf.coresight.configs); spin_unlock(&kbdev->csf.coresight.lock); } kbase_csf_scheduler_spin_unlock(kbdev, flags); kbase_csf_scheduler_unlock(kbdev); return ret; } EXPORT_SYMBOL(kbase_debug_coresight_csf_config_enable); int kbase_debug_coresight_csf_config_disable(void *config_data) { struct kbase_debug_coresight_csf_config *config; struct kbase_debug_coresight_csf_client *client; struct kbase_device *kbdev; struct kbase_debug_coresight_csf_config *config_entry; bool found_in_list = false; unsigned long flags; int ret = 0; if (unlikely(!config_data)) { pr_err("NULL config"); return -EINVAL; } config = (struct kbase_debug_coresight_csf_config *)config_data; /* Exit early if not enabled prior */ if (list_empty(&config->link)) return ret; client = (struct kbase_debug_coresight_csf_client *)config->client; if (unlikely(!client)) { pr_err("NULL client in config"); return -EINVAL; } kbdev = (struct kbase_device *)client->drv_data; if (unlikely(!kbdev)) { pr_err("NULL drv_data in client"); return -EINVAL; } /* Check if the config is in the correct list */ spin_lock_irqsave(&kbdev->csf.coresight.lock, flags); list_for_each_entry(config_entry, &kbdev->csf.coresight.configs, link) { if (config_entry == config) { found_in_list = true; break; } } spin_unlock_irqrestore(&kbdev->csf.coresight.lock, flags); if (!found_in_list) { dev_err(kbdev->dev, "Config looks corrupted"); return -EINVAL; } kbase_csf_scheduler_lock(kbdev); kbase_csf_scheduler_spin_lock(kbdev, &flags); /* Check the state of Scheduler to confirm the desired state of MCU */ if (((kbdev->csf.scheduler.state != SCHED_SUSPENDED) && (kbdev->csf.scheduler.state != SCHED_SLEEPING) && !kbase_csf_scheduler_protected_mode_in_use(kbdev)) || kbase_pm_get_policy(kbdev) == &kbase_pm_always_on_policy_ops) { kbase_csf_scheduler_spin_unlock(kbdev, flags); /* Wait for MCU to reach the stable ON state */ ret = kbase_pm_wait_for_desired_state(kbdev); if (ret) dev_err(kbdev->dev, "Wait for PM state failed when disabling coresight config"); else ret = coresight_config_disable(kbdev, config); kbase_csf_scheduler_spin_lock(kbdev, &flags); } else if (kbdev->pm.backend.mcu_state == KBASE_MCU_OFF) { /* MCU is OFF, so the disable sequence was already executed. * * Propagate any error that would have occurred during the enable * or disable sequence. * * This is done as part of the disable sequence, since the call from * client is synchronous. */ ret = config->error; } /* Remove config from next disable sequence */ spin_lock(&kbdev->csf.coresight.lock); list_del_init(&config->link); spin_unlock(&kbdev->csf.coresight.lock); kbase_csf_scheduler_spin_unlock(kbdev, flags); kbase_csf_scheduler_unlock(kbdev); return ret; } EXPORT_SYMBOL(kbase_debug_coresight_csf_config_disable); static void coresight_config_enable_all(struct work_struct *data) { struct kbase_device *kbdev = container_of(data, struct kbase_device, csf.coresight.enable_work); struct kbase_debug_coresight_csf_config *config_entry; unsigned long flags; spin_lock_irqsave(&kbdev->csf.coresight.lock, flags); list_for_each_entry(config_entry, &kbdev->csf.coresight.configs, link) { spin_unlock_irqrestore(&kbdev->csf.coresight.lock, flags); if (coresight_config_enable(kbdev, config_entry)) dev_err(kbdev->dev, "enable config (0x%pK) failed", config_entry); spin_lock_irqsave(&kbdev->csf.coresight.lock, flags); } spin_unlock_irqrestore(&kbdev->csf.coresight.lock, flags); spin_lock_irqsave(&kbdev->hwaccess_lock, flags); kbase_pm_update_state(kbdev); spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags); wake_up_all(&kbdev->csf.coresight.event_wait); } static void coresight_config_disable_all(struct work_struct *data) { struct kbase_device *kbdev = container_of(data, struct kbase_device, csf.coresight.disable_work); struct kbase_debug_coresight_csf_config *config_entry; unsigned long flags; spin_lock_irqsave(&kbdev->csf.coresight.lock, flags); list_for_each_entry(config_entry, &kbdev->csf.coresight.configs, link) { spin_unlock_irqrestore(&kbdev->csf.coresight.lock, flags); if (coresight_config_disable(kbdev, config_entry)) dev_err(kbdev->dev, "disable config (0x%pK) failed", config_entry); spin_lock_irqsave(&kbdev->csf.coresight.lock, flags); } spin_unlock_irqrestore(&kbdev->csf.coresight.lock, flags); spin_lock_irqsave(&kbdev->hwaccess_lock, flags); kbase_pm_update_state(kbdev); spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags); wake_up_all(&kbdev->csf.coresight.event_wait); } void kbase_debug_coresight_csf_disable_pmode_enter(struct kbase_device *kbdev) { unsigned long flags; dev_dbg(kbdev->dev, "Coresight state %s before protected mode enter", coresight_state_to_string(KBASE_DEBUG_CORESIGHT_CSF_ENABLED)); lockdep_assert_held(&kbdev->csf.scheduler.lock); kbase_pm_lock(kbdev); spin_lock_irqsave(&kbdev->hwaccess_lock, flags); kbdev->csf.coresight.disable_on_pmode_enter = true; kbdev->csf.coresight.enable_on_pmode_exit = false; kbase_pm_update_state(kbdev); spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags); kbase_pm_wait_for_desired_state(kbdev); kbase_pm_unlock(kbdev); } void kbase_debug_coresight_csf_enable_pmode_exit(struct kbase_device *kbdev) { dev_dbg(kbdev->dev, "Coresight state %s after protected mode exit", coresight_state_to_string(KBASE_DEBUG_CORESIGHT_CSF_DISABLED)); lockdep_assert_held(&kbdev->hwaccess_lock); WARN_ON(kbdev->csf.coresight.disable_on_pmode_enter); kbdev->csf.coresight.enable_on_pmode_exit = true; kbase_pm_update_state(kbdev); } void kbase_debug_coresight_csf_state_request(struct kbase_device *kbdev, enum kbase_debug_coresight_csf_state state) { if (unlikely(!kbdev)) return; if (unlikely(!kbdev->csf.coresight.workq)) return; dev_dbg(kbdev->dev, "Coresight state %s requested", coresight_state_to_string(state)); switch (state) { case KBASE_DEBUG_CORESIGHT_CSF_DISABLED: queue_work(kbdev->csf.coresight.workq, &kbdev->csf.coresight.disable_work); break; case KBASE_DEBUG_CORESIGHT_CSF_ENABLED: queue_work(kbdev->csf.coresight.workq, &kbdev->csf.coresight.enable_work); break; default: dev_err(kbdev->dev, "Invalid Coresight state %d", state); break; } } bool kbase_debug_coresight_csf_state_check(struct kbase_device *kbdev, enum kbase_debug_coresight_csf_state state) { struct kbase_debug_coresight_csf_config *config_entry; unsigned long flags; bool success = true; dev_dbg(kbdev->dev, "Coresight check for state: %s", coresight_state_to_string(state)); spin_lock_irqsave(&kbdev->csf.coresight.lock, flags); list_for_each_entry(config_entry, &kbdev->csf.coresight.configs, link) { if (state != config_entry->state) { success = false; break; } } spin_unlock_irqrestore(&kbdev->csf.coresight.lock, flags); return success; } KBASE_EXPORT_TEST_API(kbase_debug_coresight_csf_state_check); bool kbase_debug_coresight_csf_state_wait(struct kbase_device *kbdev, enum kbase_debug_coresight_csf_state state) { const long wait_timeout = kbase_csf_timeout_in_jiffies(kbdev->csf.fw_timeout_ms); struct kbase_debug_coresight_csf_config *config_entry, *next_config_entry; unsigned long flags; bool success = true; dev_dbg(kbdev->dev, "Coresight wait for state: %s", coresight_state_to_string(state)); spin_lock_irqsave(&kbdev->csf.coresight.lock, flags); list_for_each_entry_safe(config_entry, next_config_entry, &kbdev->csf.coresight.configs, link) { const enum kbase_debug_coresight_csf_state prev_state = config_entry->state; long remaining; spin_unlock_irqrestore(&kbdev->csf.coresight.lock, flags); remaining = wait_event_timeout(kbdev->csf.coresight.event_wait, state == config_entry->state, wait_timeout); spin_lock_irqsave(&kbdev->csf.coresight.lock, flags); if (!remaining) { success = false; dev_err(kbdev->dev, "Timeout waiting for Coresight state transition %s to %s", coresight_state_to_string(prev_state), coresight_state_to_string(state)); } } spin_unlock_irqrestore(&kbdev->csf.coresight.lock, flags); return success; } KBASE_EXPORT_TEST_API(kbase_debug_coresight_csf_state_wait); int kbase_debug_coresight_csf_init(struct kbase_device *kbdev) { kbdev->csf.coresight.workq = alloc_ordered_workqueue("Mali CoreSight workqueue", 0); if (kbdev->csf.coresight.workq == NULL) return -ENOMEM; INIT_LIST_HEAD(&kbdev->csf.coresight.clients); INIT_LIST_HEAD(&kbdev->csf.coresight.configs); INIT_WORK(&kbdev->csf.coresight.enable_work, coresight_config_enable_all); INIT_WORK(&kbdev->csf.coresight.disable_work, coresight_config_disable_all); init_waitqueue_head(&kbdev->csf.coresight.event_wait); spin_lock_init(&kbdev->csf.coresight.lock); kbdev->csf.coresight.disable_on_pmode_enter = false; kbdev->csf.coresight.enable_on_pmode_exit = false; return 0; } void kbase_debug_coresight_csf_term(struct kbase_device *kbdev) { struct kbase_debug_coresight_csf_client *client_entry, *next_client_entry; struct kbase_debug_coresight_csf_config *config_entry, *next_config_entry; unsigned long flags; kbdev->csf.coresight.disable_on_pmode_enter = false; kbdev->csf.coresight.enable_on_pmode_exit = false; cancel_work_sync(&kbdev->csf.coresight.enable_work); cancel_work_sync(&kbdev->csf.coresight.disable_work); destroy_workqueue(kbdev->csf.coresight.workq); kbdev->csf.coresight.workq = NULL; spin_lock_irqsave(&kbdev->csf.coresight.lock, flags); list_for_each_entry_safe(config_entry, next_config_entry, &kbdev->csf.coresight.configs, link) { list_del_init(&config_entry->link); kfree(config_entry); } list_for_each_entry_safe(client_entry, next_client_entry, &kbdev->csf.coresight.clients, link) { list_del_init(&client_entry->link); kfree(client_entry); } spin_unlock_irqrestore(&kbdev->csf.coresight.lock, flags); }