blob: 5d39f15cdb59b354e61f79842358208838128761 [file] [log] [blame]
/*
* Copyright (C) 2012-2017 ARM Limited or its affiliates.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
#include "ssi_config.h"
#include "ssi_driver.h"
#include "cc_crypto_ctx.h"
#include "ssi_sysfs.h"
#ifdef ENABLE_CC_SYSFS
static struct ssi_drvdata *sys_get_drvdata(void);
static ssize_t ssi_sys_regdump_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
struct ssi_drvdata *drvdata = sys_get_drvdata();
u32 register_value;
int offset = 0;
register_value = cc_ioread(drvdata, CC_REG(HOST_SIGNATURE));
offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%s \t(0x%lX)\t 0x%08X\n", "HOST_SIGNATURE ", DX_HOST_SIGNATURE_REG_OFFSET, register_value);
register_value = cc_ioread(drvdata, CC_REG(HOST_IRR));
offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%s \t(0x%lX)\t 0x%08X\n", "HOST_IRR ", DX_HOST_IRR_REG_OFFSET, register_value);
register_value = cc_ioread(drvdata, CC_REG(HOST_POWER_DOWN_EN));
offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%s \t(0x%lX)\t 0x%08X\n", "HOST_POWER_DOWN_EN ", DX_HOST_POWER_DOWN_EN_REG_OFFSET, register_value);
register_value = cc_ioread(drvdata, CC_REG(AXIM_MON_ERR));
offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%s \t(0x%lX)\t 0x%08X\n", "AXIM_MON_ERR ", DX_AXIM_MON_ERR_REG_OFFSET, register_value);
register_value = cc_ioread(drvdata, CC_REG(DSCRPTR_QUEUE_CONTENT));
offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%s \t(0x%lX)\t 0x%08X\n", "DSCRPTR_QUEUE_CONTENT", DX_DSCRPTR_QUEUE_CONTENT_REG_OFFSET, register_value);
return offset;
}
static ssize_t ssi_sys_help_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
char *help_str[] = {
"cat reg_dump ", "Print several of CC register values",
};
int i = 0, offset = 0;
offset += scnprintf(buf + offset, PAGE_SIZE - offset, "Usage:\n");
for (i = 0; i < ARRAY_SIZE(help_str); i += 2)
offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%s\t\t%s\n", help_str[i], help_str[i + 1]);
return offset;
}
/********************************************************
* SYSFS objects *
********************************************************/
/*
* Structure used to create a directory
* and its attributes in sysfs.
*/
struct sys_dir {
struct kobject *sys_dir_kobj;
struct attribute_group sys_dir_attr_group;
struct attribute **sys_dir_attr_list;
u32 num_of_attrs;
struct ssi_drvdata *drvdata; /* Associated driver context */
};
/* top level directory structures */
static struct sys_dir sys_top_dir;
/* TOP LEVEL ATTRIBUTES */
static struct kobj_attribute ssi_sys_top_level_attrs[] = {
__ATTR(dump_regs, 0444, ssi_sys_regdump_show, NULL),
__ATTR(help, 0444, ssi_sys_help_show, NULL),
#if defined CC_CYCLE_COUNT
__ATTR(stats_host, 0664, ssi_sys_stat_host_db_show, ssi_sys_stats_host_db_clear),
__ATTR(stats_cc, 0664, ssi_sys_stat_cc_db_show, ssi_sys_stats_cc_db_clear),
#endif
};
static struct ssi_drvdata *sys_get_drvdata(void)
{
/* TODO: supporting multiple SeP devices would require avoiding
* global "top_dir" and finding associated "top_dir" by traversing
* up the tree to the kobject which matches one of the top_dir's
*/
return sys_top_dir.drvdata;
}
static int sys_init_dir(struct sys_dir *sys_dir, struct ssi_drvdata *drvdata,
struct kobject *parent_dir_kobj, const char *dir_name,
struct kobj_attribute *attrs, u32 num_of_attrs)
{
int i;
memset(sys_dir, 0, sizeof(struct sys_dir));
sys_dir->drvdata = drvdata;
/* initialize directory kobject */
sys_dir->sys_dir_kobj =
kobject_create_and_add(dir_name, parent_dir_kobj);
if (!(sys_dir->sys_dir_kobj))
return -ENOMEM;
/* allocate memory for directory's attributes list */
sys_dir->sys_dir_attr_list =
kcalloc(num_of_attrs + 1, sizeof(struct attribute *),
GFP_KERNEL);
if (!(sys_dir->sys_dir_attr_list)) {
kobject_put(sys_dir->sys_dir_kobj);
return -ENOMEM;
}
sys_dir->num_of_attrs = num_of_attrs;
/* initialize attributes list */
for (i = 0; i < num_of_attrs; ++i)
sys_dir->sys_dir_attr_list[i] = &attrs[i].attr;
/* last list entry should be NULL */
sys_dir->sys_dir_attr_list[num_of_attrs] = NULL;
sys_dir->sys_dir_attr_group.attrs = sys_dir->sys_dir_attr_list;
return sysfs_create_group(sys_dir->sys_dir_kobj,
&sys_dir->sys_dir_attr_group);
}
static void sys_free_dir(struct sys_dir *sys_dir)
{
if (!sys_dir)
return;
kfree(sys_dir->sys_dir_attr_list);
if (sys_dir->sys_dir_kobj)
kobject_put(sys_dir->sys_dir_kobj);
}
int ssi_sysfs_init(struct kobject *sys_dev_obj, struct ssi_drvdata *drvdata)
{
int retval;
struct device *dev = drvdata_to_dev(drvdata);
dev_info(dev, "setup sysfs under %s\n", sys_dev_obj->name);
/* Initialize top directory */
retval = sys_init_dir(&sys_top_dir, drvdata, sys_dev_obj, "cc_info",
ssi_sys_top_level_attrs,
ARRAY_SIZE(ssi_sys_top_level_attrs));
return retval;
}
void ssi_sysfs_fini(void)
{
sys_free_dir(&sys_top_dir);
}
#endif /*ENABLE_CC_SYSFS*/