blob: 52c39400ace9233ea6252c8b0eba1a4184e10345 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* GCIP firmware interface.
*
* Copyright (C) 2022 Google LLC
*/
#include <linux/debugfs.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <gcip/gcip-firmware.h>
#include <gcip/gcip-pm.h>
char *gcip_fw_flavor_str(enum gcip_fw_flavor fw_flavor)
{
switch (fw_flavor) {
case GCIP_FW_FLAVOR_BL1:
return "stage 2 bootloader";
case GCIP_FW_FLAVOR_SYSTEST:
return "test";
case GCIP_FW_FLAVOR_PROD_DEFAULT:
return "prod";
case GCIP_FW_FLAVOR_CUSTOM:
return "custom";
case GCIP_FW_FLAVOR_UNKNOWN:
default:
return "unknown";
}
}
static int gcip_firmware_tracing_active_get(void *data, u64 *val)
{
struct gcip_fw_tracing *fw_tracing = data;
mutex_lock(&fw_tracing->lock);
*val = fw_tracing->active_level;
mutex_unlock(&fw_tracing->lock);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_gcip_firmware_tracing_active, gcip_firmware_tracing_active_get, NULL,
"%llu\n");
static int gcip_firmware_tracing_request_get(void *data, u64 *val)
{
struct gcip_fw_tracing *fw_tracing = data;
mutex_lock(&fw_tracing->lock);
*val = fw_tracing->request_level;
mutex_unlock(&fw_tracing->lock);
return 0;
}
static int gcip_firmware_tracing_set_level_lock(struct gcip_fw_tracing *fw_tracing)
{
unsigned long active_level;
int ret = fw_tracing->set_level(fw_tracing->data, fw_tracing->request_level, &active_level);
if (ret)
dev_warn(fw_tracing->dev, "Failed to set firmware tracing level to %lu: %d",
fw_tracing->request_level, ret);
else
fw_tracing->active_level =
(fw_tracing->request_level & GCIP_FW_TRACING_DEFAULT_VOTE) ?
GCIP_FW_TRACING_DEFAULT_VOTE :
active_level;
return ret;
}
static int gcip_firmware_tracing_request_set(void *data, u64 val)
{
struct gcip_fw_tracing *fw_tracing = data;
int ret = 0;
mutex_lock(&fw_tracing->lock);
fw_tracing->request_level = val;
if (!gcip_pm_get_if_powered(fw_tracing->pm, false)) {
ret = gcip_firmware_tracing_set_level_lock(fw_tracing);
gcip_pm_put(fw_tracing->pm);
}
mutex_unlock(&fw_tracing->lock);
return ret;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_gcip_firmware_tracing_request, gcip_firmware_tracing_request_get,
gcip_firmware_tracing_request_set, "%llu\n");
struct gcip_fw_tracing *gcip_firmware_tracing_create(const struct gcip_fw_tracing_args *args)
{
struct gcip_fw_tracing *fw_tracing;
if (!args->dev || !args->set_level)
return ERR_PTR(-EINVAL);
fw_tracing = kzalloc(sizeof(*fw_tracing), GFP_KERNEL);
if (!fw_tracing)
return ERR_PTR(-ENOMEM);
fw_tracing->dev = args->dev;
fw_tracing->pm = args->pm;
fw_tracing->set_level = args->set_level;
fw_tracing->data = args->data;
fw_tracing->active_level = GCIP_FW_TRACING_DEFAULT_VOTE;
fw_tracing->request_level = GCIP_FW_TRACING_DEFAULT_VOTE;
mutex_init(&fw_tracing->lock);
fw_tracing->dentry = debugfs_create_dir("fw_tracing", args->dentry);
if (IS_ERR(fw_tracing->dentry)) {
dev_warn(args->dev, "Failed to create debug FS tracing");
kfree(fw_tracing);
return (struct gcip_fw_tracing *)fw_tracing->dentry;
}
debugfs_create_file("active", 0440, fw_tracing->dentry, fw_tracing,
&fops_gcip_firmware_tracing_active);
debugfs_create_file("request", 0660, fw_tracing->dentry, fw_tracing,
&fops_gcip_firmware_tracing_request);
return fw_tracing;
}
void gcip_firmware_tracing_destroy(struct gcip_fw_tracing *fw_tracing)
{
if (!fw_tracing)
return;
debugfs_remove_recursive(fw_tracing->dentry);
kfree(fw_tracing);
}
int gcip_firmware_tracing_restore_on_powering(struct gcip_fw_tracing *fw_tracing)
{
int ret = 0;
if (!fw_tracing)
return 0;
gcip_pm_lockdep_assert_held(fw_tracing->pm);
mutex_lock(&fw_tracing->lock);
fw_tracing->active_level = GCIP_FW_TRACING_DEFAULT_VOTE;
if (!(fw_tracing->request_level & GCIP_FW_TRACING_DEFAULT_VOTE))
ret = gcip_firmware_tracing_set_level_lock(fw_tracing);
mutex_unlock(&fw_tracing->lock);
return ret;
}