dbcd1634aad1281b7bf9929a26ada2f44d78065e
[linux-2.6-microblaze.git] / drivers / staging / ccree / ssi_sysfs.c
1 /*
2  * Copyright (C) 2012-2017 ARM Limited or its affiliates.
3  *
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.
7  *
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.
12  *
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/>.
15  */
16
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"
22
23 #ifdef ENABLE_CC_SYSFS
24
25 static struct ssi_drvdata *sys_get_drvdata(void);
26
27 #ifdef CC_CYCLE_COUNT
28
29 #include <asm/timex.h>
30
31 struct stat_item {
32         unsigned int min;
33         unsigned int max;
34         cycles_t sum;
35         unsigned int count;
36 };
37
38 struct stat_name {
39         const char *op_type_name;
40         const char *stat_phase_name[MAX_STAT_PHASES];
41 };
42
43 static struct stat_name stat_name_db[MAX_STAT_OP_TYPES] =
44 {
45         {
46                 /* STAT_OP_TYPE_NULL */
47                 .op_type_name = "NULL",
48                 .stat_phase_name = {NULL},
49         },
50         {
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",
59         },
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",
68         },
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",
77         },
78         {
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",
87         }
88 };
89
90 /*
91  * Structure used to create a directory
92  * and its attributes in sysfs.
93  */
94 struct sys_dir {
95         struct kobject *sys_dir_kobj;
96         struct attribute_group sys_dir_attr_group;
97         struct attribute **sys_dir_attr_list;
98         u32 num_of_attrs;
99         struct ssi_drvdata *drvdata; /* Associated driver context */
100 };
101
102 /* top level directory structures */
103 struct sys_dir sys_top_dir;
104
105 static DEFINE_SPINLOCK(stat_lock);
106
107 /* List of DBs */
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];
110
111 static void init_db(struct stat_item item[MAX_STAT_OP_TYPES][MAX_STAT_PHASES])
112 {
113         unsigned int i, j;
114
115         /* Clear db */
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;
119                         item[i][j].max = 0;
120                         item[i][j].sum = 0;
121                         item[i][j].count = 0;
122                 }
123         }
124 }
125
126 static void update_db(struct stat_item *item, unsigned int result)
127 {
128         item->count++;
129         item->sum += result;
130         if (result < item->min)
131                 item->min = result;
132         if (result > item->max)
133                 item->max = result;
134 }
135
136 static void display_db(struct stat_item item[MAX_STAT_OP_TYPES][MAX_STAT_PHASES])
137 {
138         unsigned int i, j;
139         u64 avg;
140
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);
149                         }
150                 }
151         }
152 }
153
154 /**************************************
155  * Attributes show functions section  *
156  **************************************/
157
158 static ssize_t ssi_sys_stats_host_db_clear(struct kobject *kobj,
159         struct kobj_attribute *attr, const char *buf, size_t count)
160 {
161         init_db(stat_host_db);
162         return count;
163 }
164
165 static ssize_t ssi_sys_stats_cc_db_clear(struct kobject *kobj,
166         struct kobj_attribute *attr, const char *buf, size_t count)
167 {
168         init_db(stat_cc_db);
169         return count;
170 }
171
172 static ssize_t ssi_sys_stat_host_db_show(struct kobject *kobj,
173                 struct kobj_attribute *attr, char *buf)
174 {
175         int i, j;
176         char line[512];
177         u32 min_cyc, max_cyc;
178         u64 avg;
179         ssize_t buf_len, tmp_len = 0;
180
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*/
184                 return buf_len;
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;
192                         } else {
193                                 avg = min_cyc = max_cyc = 0;
194                         }
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*/
202                                 return buf_len;
203                         if (buf_len + tmp_len >= PAGE_SIZE)
204                                 return buf_len;
205                         buf_len += tmp_len;
206                         strncat(buf, line, 512);
207                 }
208         }
209         return buf_len;
210 }
211
212 static ssize_t ssi_sys_stat_cc_db_show(struct kobject *kobj,
213                 struct kobj_attribute *attr, char *buf)
214 {
215         int i;
216         char line[256];
217         u32 min_cyc, max_cyc;
218         u64 avg;
219         ssize_t buf_len, tmp_len = 0;
220
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*/
224                 return buf_len;
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;
231                 } else {
232                         avg = min_cyc = max_cyc = 0;
233                 }
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,
237                         min_cyc,
238                         (unsigned int)avg,
239                         max_cyc,
240                         stat_cc_db[i][STAT_PHASE_6].count);
241
242                 if (tmp_len < 0)/* scnprintf shouldn't return negative value according to its implementation*/
243                         return buf_len;
244
245                 if (buf_len + tmp_len >= PAGE_SIZE)
246                         return buf_len;
247                 buf_len += tmp_len;
248                 strncat(buf, line, 256);
249         }
250         return buf_len;
251 }
252
253 void update_host_stat(unsigned int op_type, unsigned int phase, cycles_t result)
254 {
255         unsigned long flags;
256
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);
260 }
261
262 void update_cc_stat(
263         unsigned int op_type,
264         unsigned int phase,
265         unsigned int elapsed_cycles)
266 {
267         update_db(&(stat_cc_db[op_type][phase]), elapsed_cycles);
268 }
269
270 void display_all_stat_db(void)
271 {
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);
276 }
277 #endif /*CC_CYCLE_COUNT*/
278
279 static ssize_t ssi_sys_regdump_show(struct kobject *kobj,
280                 struct kobj_attribute *attr, char *buf)
281 {
282         struct ssi_drvdata *drvdata = sys_get_drvdata();
283         u32 register_value;
284         void __iomem *cc_base = drvdata->cc_base;
285         int offset = 0;
286
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);
297         return offset;
298 }
299
300 static ssize_t ssi_sys_help_show(struct kobject *kobj,
301                 struct kobj_attribute *attr, char *buf)
302 {
303         char *help_str[] = {
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",
310                 #endif
311                                 };
312         int i = 0, offset = 0;
313
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]);
317
318         return offset;
319 }
320
321 /********************************************************
322  *              SYSFS objects                           *
323  ********************************************************/
324 /*
325  * Structure used to create a directory
326  * and its attributes in sysfs.
327  */
328 struct sys_dir {
329         struct kobject *sys_dir_kobj;
330         struct attribute_group sys_dir_attr_group;
331         struct attribute **sys_dir_attr_list;
332         u32 num_of_attrs;
333         struct ssi_drvdata *drvdata; /* Associated driver context */
334 };
335
336 /* top level directory structures */
337 static struct sys_dir sys_top_dir;
338
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),
346 #endif
347
348 };
349
350 static struct ssi_drvdata *sys_get_drvdata(void)
351 {
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
355          */
356         return sys_top_dir.drvdata;
357 }
358
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)
362 {
363         int i;
364
365         memset(sys_dir, 0, sizeof(struct sys_dir));
366
367         sys_dir->drvdata = drvdata;
368
369         /* initialize directory kobject */
370         sys_dir->sys_dir_kobj =
371                 kobject_create_and_add(dir_name, parent_dir_kobj);
372
373         if (!(sys_dir->sys_dir_kobj))
374                 return -ENOMEM;
375         /* allocate memory for directory's attributes list */
376         sys_dir->sys_dir_attr_list =
377                 kzalloc(sizeof(struct attribute *) * (num_of_attrs + 1),
378                                 GFP_KERNEL);
379
380         if (!(sys_dir->sys_dir_attr_list)) {
381                 kobject_put(sys_dir->sys_dir_kobj);
382                 return -ENOMEM;
383         }
384
385         sys_dir->num_of_attrs = num_of_attrs;
386
387         /* initialize attributes list */
388         for (i = 0; i < num_of_attrs; ++i)
389                 sys_dir->sys_dir_attr_list[i] = &(attrs[i].attr);
390
391         /* last list entry should be NULL */
392         sys_dir->sys_dir_attr_list[num_of_attrs] = NULL;
393
394         sys_dir->sys_dir_attr_group.attrs = sys_dir->sys_dir_attr_list;
395
396         return sysfs_create_group(sys_dir->sys_dir_kobj,
397                         &(sys_dir->sys_dir_attr_group));
398 }
399
400 static void sys_free_dir(struct sys_dir *sys_dir)
401 {
402         if (!sys_dir)
403                 return;
404
405         kfree(sys_dir->sys_dir_attr_list);
406
407         if (sys_dir->sys_dir_kobj)
408                 kobject_put(sys_dir->sys_dir_kobj);
409 }
410
411 int ssi_sysfs_init(struct kobject *sys_dev_obj, struct ssi_drvdata *drvdata)
412 {
413         int retval;
414
415 #if defined CC_CYCLE_COUNT
416         /* Init. statistics */
417         init_db(stat_host_db);
418         init_db(stat_cc_db);
419 #endif
420
421         SSI_LOG_ERR("setup sysfs under %s\n", sys_dev_obj->name);
422
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));
427         return retval;
428 }
429
430 void ssi_sysfs_fini(void)
431 {
432         sys_free_dir(&sys_top_dir);
433 }
434
435 #endif /*ENABLE_CC_SYSFS*/
436