blob: ff5f947e2da57296fefe4524a40e9399790c2b6d [file] [log] [blame]
// 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 <mali_kbase.h>
#include <linux/slab.h>
#include <csf/mali_kbase_csf_registers.h>
#include <csf/mali_kbase_csf_firmware.h>
#include <backend/gpu/mali_kbase_pm_internal.h>
#include <linux/mali_kbase_debug_coresight_csf.h>
#include <debug/backend/mali_kbase_debug_coresight_internal_csf.h>
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);
}