2 * Copyright (C) 2012-2017 ARM Limited or its affiliates.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 #include <linux/kernel.h>
18 #include "ssi_config.h"
19 #include "ssi_driver.h"
20 #include "cc_crypto_ctx.h"
21 #include "ssi_sysfs.h"
23 #ifdef ENABLE_CC_SYSFS
25 static struct ssi_drvdata *sys_get_drvdata(void);
29 #include <asm/timex.h>
39 const char *op_type_name;
40 const char *stat_phase_name[MAX_STAT_PHASES];
43 static struct stat_name stat_name_db[MAX_STAT_OP_TYPES] =
46 /* STAT_OP_TYPE_NULL */
47 .op_type_name = "NULL",
48 .stat_phase_name = {NULL},
51 .op_type_name = "Encode",
52 .stat_phase_name[STAT_PHASE_0] = "Init and sanity checks",
53 .stat_phase_name[STAT_PHASE_1] = "Map buffers",
54 .stat_phase_name[STAT_PHASE_2] = "Create sequence",
55 .stat_phase_name[STAT_PHASE_3] = "Send Request",
56 .stat_phase_name[STAT_PHASE_4] = "HW-Q push",
57 .stat_phase_name[STAT_PHASE_5] = "Sequence completion",
58 .stat_phase_name[STAT_PHASE_6] = "HW cycles",
60 { .op_type_name = "Decode",
61 .stat_phase_name[STAT_PHASE_0] = "Init and sanity checks",
62 .stat_phase_name[STAT_PHASE_1] = "Map buffers",
63 .stat_phase_name[STAT_PHASE_2] = "Create sequence",
64 .stat_phase_name[STAT_PHASE_3] = "Send Request",
65 .stat_phase_name[STAT_PHASE_4] = "HW-Q push",
66 .stat_phase_name[STAT_PHASE_5] = "Sequence completion",
67 .stat_phase_name[STAT_PHASE_6] = "HW cycles",
69 { .op_type_name = "Setkey",
70 .stat_phase_name[STAT_PHASE_0] = "Init and sanity checks",
71 .stat_phase_name[STAT_PHASE_1] = "Copy key to ctx",
72 .stat_phase_name[STAT_PHASE_2] = "Create sequence",
73 .stat_phase_name[STAT_PHASE_3] = "Send Request",
74 .stat_phase_name[STAT_PHASE_4] = "HW-Q push",
75 .stat_phase_name[STAT_PHASE_5] = "Sequence completion",
76 .stat_phase_name[STAT_PHASE_6] = "HW cycles",
79 .op_type_name = "Generic",
80 .stat_phase_name[STAT_PHASE_0] = "Interrupt",
81 .stat_phase_name[STAT_PHASE_1] = "ISR-to-Tasklet",
82 .stat_phase_name[STAT_PHASE_2] = "Tasklet start-to-end",
83 .stat_phase_name[STAT_PHASE_3] = "Tasklet:user_cb()",
84 .stat_phase_name[STAT_PHASE_4] = "Tasklet:dx_X_complete() - w/o X_complete()",
85 .stat_phase_name[STAT_PHASE_5] = "",
86 .stat_phase_name[STAT_PHASE_6] = "HW cycles",
91 * Structure used to create a directory
92 * and its attributes in sysfs.
95 struct kobject *sys_dir_kobj;
96 struct attribute_group sys_dir_attr_group;
97 struct attribute **sys_dir_attr_list;
99 struct ssi_drvdata *drvdata; /* Associated driver context */
102 /* top level directory structures */
103 struct sys_dir sys_top_dir;
105 static DEFINE_SPINLOCK(stat_lock);
108 static struct stat_item stat_host_db[MAX_STAT_OP_TYPES][MAX_STAT_PHASES];
109 static struct stat_item stat_cc_db[MAX_STAT_OP_TYPES][MAX_STAT_PHASES];
111 static void init_db(struct stat_item item[MAX_STAT_OP_TYPES][MAX_STAT_PHASES])
116 for (i = 0; i < MAX_STAT_OP_TYPES; i++) {
117 for (j = 0; j < MAX_STAT_PHASES; j++) {
118 item[i][j].min = 0xFFFFFFFF;
121 item[i][j].count = 0;
126 static void update_db(struct stat_item *item, unsigned int result)
130 if (result < item->min)
132 if (result > item->max)
136 static void display_db(struct stat_item item[MAX_STAT_OP_TYPES][MAX_STAT_PHASES])
141 for (i = STAT_OP_TYPE_ENCODE; i < MAX_STAT_OP_TYPES; i++) {
142 for (j = 0; j < MAX_STAT_PHASES; j++) {
143 if (item[i][j].count > 0) {
144 avg = (u64)item[i][j].sum;
145 do_div(avg, item[i][j].count);
146 SSI_LOG_ERR("%s, %s: min=%d avg=%d max=%d sum=%lld count=%d\n",
147 stat_name_db[i].op_type_name, stat_name_db[i].stat_phase_name[j],
148 item[i][j].min, (int)avg, item[i][j].max, (long long)item[i][j].sum, item[i][j].count);
154 /**************************************
155 * Attributes show functions section *
156 **************************************/
158 static ssize_t ssi_sys_stats_host_db_clear(struct kobject *kobj,
159 struct kobj_attribute *attr, const char *buf, size_t count)
161 init_db(stat_host_db);
165 static ssize_t ssi_sys_stats_cc_db_clear(struct kobject *kobj,
166 struct kobj_attribute *attr, const char *buf, size_t count)
172 static ssize_t ssi_sys_stat_host_db_show(struct kobject *kobj,
173 struct kobj_attribute *attr, char *buf)
177 u32 min_cyc, max_cyc;
179 ssize_t buf_len, tmp_len = 0;
181 buf_len = scnprintf(buf, PAGE_SIZE,
182 "phase\t\t\t\t\t\t\tmin[cy]\tavg[cy]\tmax[cy]\t#samples\n");
183 if (buf_len < 0)/* scnprintf shouldn't return negative value according to its implementation*/
185 for (i = STAT_OP_TYPE_ENCODE; i < MAX_STAT_OP_TYPES; i++) {
186 for (j = 0; j < MAX_STAT_PHASES - 1; j++) {
187 if (stat_host_db[i][j].count > 0) {
188 avg = (u64)stat_host_db[i][j].sum;
189 do_div(avg, stat_host_db[i][j].count);
190 min_cyc = stat_host_db[i][j].min;
191 max_cyc = stat_host_db[i][j].max;
193 avg = min_cyc = max_cyc = 0;
195 tmp_len = scnprintf(line, 512,
196 "%s::%s\t\t\t\t\t%6u\t%6u\t%6u\t%7u\n",
197 stat_name_db[i].op_type_name,
198 stat_name_db[i].stat_phase_name[j],
199 min_cyc, (unsigned int)avg, max_cyc,
200 stat_host_db[i][j].count);
201 if (tmp_len < 0)/* scnprintf shouldn't return negative value according to its implementation*/
203 if (buf_len + tmp_len >= PAGE_SIZE)
206 strncat(buf, line, 512);
212 static ssize_t ssi_sys_stat_cc_db_show(struct kobject *kobj,
213 struct kobj_attribute *attr, char *buf)
217 u32 min_cyc, max_cyc;
219 ssize_t buf_len, tmp_len = 0;
221 buf_len = scnprintf(buf, PAGE_SIZE,
222 "phase\tmin[cy]\tavg[cy]\tmax[cy]\t#samples\n");
223 if (buf_len < 0)/* scnprintf shouldn't return negative value according to its implementation*/
225 for (i = STAT_OP_TYPE_ENCODE; i < MAX_STAT_OP_TYPES; i++) {
226 if (stat_cc_db[i][STAT_PHASE_6].count > 0) {
227 avg = (u64)stat_cc_db[i][STAT_PHASE_6].sum;
228 do_div(avg, stat_cc_db[i][STAT_PHASE_6].count);
229 min_cyc = stat_cc_db[i][STAT_PHASE_6].min;
230 max_cyc = stat_cc_db[i][STAT_PHASE_6].max;
232 avg = min_cyc = max_cyc = 0;
234 tmp_len = scnprintf(line, 256,
235 "%s\t%6u\t%6u\t%6u\t%7u\n",
236 stat_name_db[i].op_type_name,
240 stat_cc_db[i][STAT_PHASE_6].count);
242 if (tmp_len < 0)/* scnprintf shouldn't return negative value according to its implementation*/
245 if (buf_len + tmp_len >= PAGE_SIZE)
248 strncat(buf, line, 256);
253 void update_host_stat(unsigned int op_type, unsigned int phase, cycles_t result)
257 spin_lock_irqsave(&stat_lock, flags);
258 update_db(&(stat_host_db[op_type][phase]), (unsigned int)result);
259 spin_unlock_irqrestore(&stat_lock, flags);
263 unsigned int op_type,
265 unsigned int elapsed_cycles)
267 update_db(&(stat_cc_db[op_type][phase]), elapsed_cycles);
270 void display_all_stat_db(void)
272 SSI_LOG_ERR("\n======= CYCLE COUNT STATS =======\n");
273 display_db(stat_host_db);
274 SSI_LOG_ERR("\n======= CC HW CYCLE COUNT STATS =======\n");
275 display_db(stat_cc_db);
277 #endif /*CC_CYCLE_COUNT*/
279 static ssize_t ssi_sys_regdump_show(struct kobject *kobj,
280 struct kobj_attribute *attr, char *buf)
282 struct ssi_drvdata *drvdata = sys_get_drvdata();
284 void __iomem *cc_base = drvdata->cc_base;
287 register_value = CC_HAL_READ_REGISTER(CC_REG_OFFSET(HOST_RGF, HOST_SIGNATURE));
288 offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%s \t(0x%lX)\t 0x%08X \n", "HOST_SIGNATURE ", DX_HOST_SIGNATURE_REG_OFFSET, register_value);
289 register_value = CC_HAL_READ_REGISTER(CC_REG_OFFSET(HOST_RGF, HOST_IRR));
290 offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%s \t(0x%lX)\t 0x%08X \n", "HOST_IRR ", DX_HOST_IRR_REG_OFFSET, register_value);
291 register_value = CC_HAL_READ_REGISTER(CC_REG_OFFSET(HOST_RGF, HOST_POWER_DOWN_EN));
292 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);
293 register_value = CC_HAL_READ_REGISTER(CC_REG_OFFSET(CRY_KERNEL, AXIM_MON_ERR));
294 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);
295 register_value = CC_HAL_READ_REGISTER(CC_REG_OFFSET(CRY_KERNEL, DSCRPTR_QUEUE_CONTENT));
296 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);
300 static ssize_t ssi_sys_help_show(struct kobject *kobj,
301 struct kobj_attribute *attr, char *buf)
304 "cat reg_dump ", "Print several of CC register values",
305 #if defined CC_CYCLE_COUNT
306 "cat stats_host ", "Print host statistics",
307 "echo <number> > stats_host", "Clear host statistics database",
308 "cat stats_cc ", "Print CC statistics",
309 "echo <number> > stats_cc ", "Clear CC statistics database",
312 int i = 0, offset = 0;
314 offset += scnprintf(buf + offset, PAGE_SIZE - offset, "Usage:\n");
315 for (i = 0; i < ARRAY_SIZE(help_str); i += 2)
316 offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%s\t\t%s\n", help_str[i], help_str[i + 1]);
321 /********************************************************
323 ********************************************************/
325 * Structure used to create a directory
326 * and its attributes in sysfs.
329 struct kobject *sys_dir_kobj;
330 struct attribute_group sys_dir_attr_group;
331 struct attribute **sys_dir_attr_list;
333 struct ssi_drvdata *drvdata; /* Associated driver context */
336 /* top level directory structures */
337 static struct sys_dir sys_top_dir;
339 /* TOP LEVEL ATTRIBUTES */
340 static struct kobj_attribute ssi_sys_top_level_attrs[] = {
341 __ATTR(dump_regs, 0444, ssi_sys_regdump_show, NULL),
342 __ATTR(help, 0444, ssi_sys_help_show, NULL),
343 #if defined CC_CYCLE_COUNT
344 __ATTR(stats_host, 0664, ssi_sys_stat_host_db_show, ssi_sys_stats_host_db_clear),
345 __ATTR(stats_cc, 0664, ssi_sys_stat_cc_db_show, ssi_sys_stats_cc_db_clear),
350 static struct ssi_drvdata *sys_get_drvdata(void)
352 /* TODO: supporting multiple SeP devices would require avoiding
353 * global "top_dir" and finding associated "top_dir" by traversing
354 * up the tree to the kobject which matches one of the top_dir's
356 return sys_top_dir.drvdata;
359 static int sys_init_dir(struct sys_dir *sys_dir, struct ssi_drvdata *drvdata,
360 struct kobject *parent_dir_kobj, const char *dir_name,
361 struct kobj_attribute *attrs, u32 num_of_attrs)
365 memset(sys_dir, 0, sizeof(struct sys_dir));
367 sys_dir->drvdata = drvdata;
369 /* initialize directory kobject */
370 sys_dir->sys_dir_kobj =
371 kobject_create_and_add(dir_name, parent_dir_kobj);
373 if (!(sys_dir->sys_dir_kobj))
375 /* allocate memory for directory's attributes list */
376 sys_dir->sys_dir_attr_list =
377 kzalloc(sizeof(struct attribute *) * (num_of_attrs + 1),
380 if (!(sys_dir->sys_dir_attr_list)) {
381 kobject_put(sys_dir->sys_dir_kobj);
385 sys_dir->num_of_attrs = num_of_attrs;
387 /* initialize attributes list */
388 for (i = 0; i < num_of_attrs; ++i)
389 sys_dir->sys_dir_attr_list[i] = &(attrs[i].attr);
391 /* last list entry should be NULL */
392 sys_dir->sys_dir_attr_list[num_of_attrs] = NULL;
394 sys_dir->sys_dir_attr_group.attrs = sys_dir->sys_dir_attr_list;
396 return sysfs_create_group(sys_dir->sys_dir_kobj,
397 &(sys_dir->sys_dir_attr_group));
400 static void sys_free_dir(struct sys_dir *sys_dir)
405 kfree(sys_dir->sys_dir_attr_list);
407 if (sys_dir->sys_dir_kobj)
408 kobject_put(sys_dir->sys_dir_kobj);
411 int ssi_sysfs_init(struct kobject *sys_dev_obj, struct ssi_drvdata *drvdata)
415 #if defined CC_CYCLE_COUNT
416 /* Init. statistics */
417 init_db(stat_host_db);
421 SSI_LOG_ERR("setup sysfs under %s\n", sys_dev_obj->name);
423 /* Initialize top directory */
424 retval = sys_init_dir(&sys_top_dir, drvdata, sys_dev_obj,
425 "cc_info", ssi_sys_top_level_attrs,
426 ARRAY_SIZE(ssi_sys_top_level_attrs));
430 void ssi_sysfs_fini(void)
432 sys_free_dir(&sys_top_dir);
435 #endif /*ENABLE_CC_SYSFS*/