Updated `README.md` with instructions for building/using the kernel module.
[xeon-phi-kernel-module] / host / pm_ioctl.c
CommitLineData
800f879a
AT
1/*
2 * Copyright 2010-2017 Intel Corporation.
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,
6 * as 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 GNU
11 * General Public License for more details.
12 *
13 * Disclaimer: The codes contained in these modules may be specific to
14 * the Intel Software Development Platform codenamed Knights Ferry,
15 * and the Intel product codenamed Knights Corner, and are not backward
16 * compatible with other Intel products. Additionally, Intel will NOT
17 * support the codes or instruction set in future products.
18 *
19 * Intel offers no warranty of any kind regarding the code. This code is
20 * licensed on an "AS IS" basis and Intel is not obligated to provide
21 * any support, assistance, installation, training, or other services
22 * of any kind. Intel is also not obligated to provide any updates,
23 * enhancements or extensions. Intel specifically disclaims any warranty
24 * of merchantability, non-infringement, fitness for any particular
25 * purpose, and any other warranty.
26 *
27 * Further, Intel disclaims all liability of any kind, including but
28 * not limited to liability for infringement of any proprietary rights,
29 * relating to the use of the code, even if Intel is notified of the
30 * possibility of such liability. Except as expressly stated in an Intel
31 * license agreement provided with this code and agreed upon with Intel,
32 * no license, express or implied, by estoppel or otherwise, to any
33 * intellectual property rights is granted herein.
34 */
35
36/* contains code to handle MIC IO control codes */
37
38
39#include "mic_common.h"
40#include <linux/module.h>
41
42/* helper methods for debugging/unit testing /*/
43static int check_test_msg(mic_ctx_t *mic_ctx, void *buf, uint32_t len);
44
45
46
47#define PM_MMIO_REGVALUE_GET(_name, _offset) \
48int get_##_name(void *data, uint64_t *value) \
49{ \
50 uint64_t bid; \
51 mic_ctx_t *mic_ctx; \
52 \
53 bid = (uint64_t)data; \
54 if (bid >= mic_data.dd_numdevs) { \
55 return -EINVAL; \
56 } \
57 mic_ctx = get_per_dev_ctx(bid); \
58 if (!mic_ctx) { \
59 printk("DD"); \
60 return -EINVAL; \
61 } \
62 \
63 *value = pm_reg_read(mic_ctx, _offset); \
64 return 0; \
65} \
66DEFINE_SIMPLE_ATTRIBUTE(fops_##_name, get_##_name, NULL, "%llu"); \
67
68static PM_MMIO_REGVALUE_GET(svidctrl, SBOX_SVID_CONTROL);
69static PM_MMIO_REGVALUE_GET(pcuctrl, SBOX_PCU_CONTROL);
70static PM_MMIO_REGVALUE_GET(hoststate,SBOX_HOST_PMSTATE);
71static PM_MMIO_REGVALUE_GET(cardstate, SBOX_UOS_PMSTATE);
72static PM_MMIO_REGVALUE_GET(wtimer, SBOX_C3WAKEUP_TIMER);
73static PM_MMIO_REGVALUE_GET(gpmctrl, GBOX_PM_CTRL);
74static PM_MMIO_REGVALUE_GET(core_volt, SBOX_COREVOLT);
75static PM_MMIO_REGVALUE_GET(uos_pcuctrl, SBOX_UOS_PCUCONTROL);
76
77static int depgraph_j2i_show(struct seq_file *s, void *pos)
78{
79 uint64_t bid = (uint64_t)s->private;
80 mic_ctx_t *mic_ctx;
81 int i, j;
82
83 mic_ctx = get_per_dev_ctx(bid);
84 if (!mic_ctx) {
85 return -EINVAL;
86 }
87
88 seq_printf(s,"=================================================================\n");
89 seq_printf(s,"%-10s |%-25s\n", "Scif Node" , "dependent nodes");
90 seq_printf(s,"=================================================================\n");
91
92 for ( i = 0; i <= ms_info.mi_maxid; i++) {
93 seq_printf(s, "%-10d |", i);
94 for (j = 0; j <= ms_info.mi_maxid; j++) {
95 switch(ms_info.mi_depmtrx[j][i]) {
96 case DEP_STATE_DEPENDENT:
97 {
98 /* (A) - active dependency on node i */
99 seq_printf(s, "%d(A),", j);
100 break;
101 }
102 case DEP_STATE_DISCONNECT_READY:
103 {
104 /* (R) - node j has sent PC6 ready message to the host
105 * dependency is not active so node i can go idle
106 */
107 seq_printf(s, "%d(R),", j);
108 break;
109 }
110 case DEP_STATE_DISCONNECTED:
111 {
112 /* (D) - node j is in idle state.
113 * dependency is not active so node i can go idle
114 */
115 seq_printf(s, "%d(D),", j);
116 break;
117 }
118 }
119 }
120 seq_printf(s,"\n=================================================================\n");
121 }
122
123 return 0;
124}
125
126static int depgraph_j2i_open(struct inode *inode, struct file *file)
127{
128 return single_open(file, depgraph_j2i_show, inode->i_private);
129}
130
131static struct file_operations depgraph_j2i_file_ops = {
132 .owner = THIS_MODULE,
133 .open = depgraph_j2i_open,
134 .read = seq_read,
135 .llseek = seq_lseek,
136 .release = single_release
137};
138
139static int depgraph_i2j_show(struct seq_file *s, void *pos)
140{
141 uint64_t bid = (uint64_t)s->private;
142 mic_ctx_t *mic_ctx;
143 int i, j;
144
145 mic_ctx = get_per_dev_ctx(bid);
146 if (!mic_ctx) {
147 return -EINVAL;
148 }
149
150 seq_printf(s,"=================================================================\n");
151 seq_printf(s,"%-10s |%-25s\n", "Scif Node" , "is dependent on Nodes");
152 seq_printf(s,"=================================================================\n");
153
154 for ( i = 0; i <= ms_info.mi_maxid; i++) {
155 seq_printf(s, "%-10d |", i);
156 for (j = 0; j <= ms_info.mi_maxid; j++) {
157 switch(ms_info.mi_depmtrx[i][j]) {
158 case DEP_STATE_DEPENDENT:
159 {
160 /* (A) - active dependency on node j */
161 seq_printf(s, "%d(A),", j);
162 break;
163 }
164 case DEP_STATE_DISCONNECT_READY:
165 {
166 /* (R) - node j has sent PC6 ready message to the host */
167 seq_printf(s, "%d(R),", j);
168 break;
169 }
170 case DEP_STATE_DISCONNECTED:
171 {
172 /* (D) - node j is in idle state.
173 * This should not happen unless node i itself is in idle state
174 */
175 seq_printf(s, "%d(D),", j);
176 break;
177 }
178 }
179 }
180 seq_printf(s,"\n=================================================================\n");
181 }
182
183 return 0;
184}
185
186static int depgraph_i2j_open(struct inode *inode, struct file *file)
187{
188 return single_open(file, depgraph_i2j_show, inode->i_private);
189}
190
191static struct file_operations depgraph_i2j_file_ops = {
192 .owner = THIS_MODULE,
193 .open = depgraph_i2j_open,
194 .read = seq_read,
195 .llseek = seq_lseek,
196 .release = single_release
197};
198
199static int connection_info_show(struct seq_file *s, void *pos) {
200
201 uint64_t bid = (uint64_t)s->private;
202 mic_ctx_t *mic_ctx;
203 int count = 0;
204 struct list_head *position, *tmpq;
205
206 mic_ctx = get_per_dev_ctx(bid);
207 if (!mic_ctx) {
208 return -EINVAL;
209 }
210
211 seq_printf(s,"=========================================================================\n");
212 if(mic_ctx->micpm_ctx.pm_epd != NULL) {
213 seq_printf(s, "%-35s | %35d\n", "Local Node", mic_ctx->micpm_ctx.pm_epd->port.node);
214 seq_printf(s, "%-35s | %35d\n", "Local Port", mic_ctx->micpm_ctx.pm_epd->port.port);
215 seq_printf(s, "%-35s | %35d\n", "Remote Node", mic_ctx->micpm_ctx.pm_epd->peer.node);
216 seq_printf(s, "%-35s | %35d\n", "Remote Port", mic_ctx->micpm_ctx.pm_epd->peer.port);
217 seq_printf(s, "%-35s | %35d\n", "Connection state", mic_ctx->micpm_ctx.pm_epd->state);
218 if(!list_empty(&mic_ctx->micpm_ctx.msg_list)) {
219 list_for_each_safe(position, tmpq, &mic_ctx->micpm_ctx.msg_list) {
220 count++;
221 }
222 } else {
223 count = 0;
224 }
225 seq_printf(s, "%-35s | %35d\n", "Messages in queue", count);
226 } else {
227 seq_printf(s, "%s\n", "No PM connection found");
228 }
229 seq_printf(s,"=========================================================================\n");
230
231 return 0;
232}
233
234static int connection_info_open(struct inode *inode, struct file *file)
235{
236 return single_open(file, connection_info_show, inode->i_private);
237}
238
239static struct file_operations connection_info_file_ops = {
240 .owner = THIS_MODULE,
241 .open = connection_info_open,
242 .read = seq_read,
243 .llseek = seq_lseek,
244 .release = single_release
245};
246
247static int active_set_show(struct seq_file *s, void *pos) {
248
249 uint64_t bid = (uint64_t)s->private;
250 mic_ctx_t *mic_ctx;
251 int i, j = 0;
252 uint8_t *nodemask;
253 uint8_t *temp_buf_ptr;
254
255 mic_ctx = get_per_dev_ctx(bid);
256 if (!mic_ctx) {
257 return -EINVAL;
258 }
259
260 nodemask = (uint8_t*) kzalloc(mic_ctx->micpm_ctx.nodemask.len, GFP_KERNEL);
261 if (!nodemask) {
262 seq_printf(s, "%s\n", "Cannot allocate buffer");
263 return 0;
264 }
265
266 if ((micscif_get_activeset(mic_ctx->bi_id + 1, nodemask))) {
267 seq_printf(s, "%s\n", "Cannot calculate activation set");
268 kfree(nodemask);
269 return 0;
270 }
271
272 seq_printf(s, "%s\n", "Nodes in activation set:");
273 temp_buf_ptr = nodemask;
274 for ( i = 0; i < mic_ctx->micpm_ctx.nodemask.len; i++) {
275 temp_buf_ptr = nodemask + i;
276 for (j = 0; j < 8; j++) {
277 if (*temp_buf_ptr & (1ULL << j))
278 seq_printf(s, "%d ", j + (i * 8));
279 }
280 }
281 seq_printf(s, "\n");
282 kfree(nodemask);
283 return 0;
284}
285
286static int active_set_open(struct inode *inode, struct file *file)
287{
288 return single_open(file, active_set_show, inode->i_private);
289}
290
291static struct file_operations activation_set_file_ops = {
292 .owner = THIS_MODULE,
293 .open = active_set_open,
294 .read = seq_read,
295 .llseek = seq_lseek,
296 .release = single_release
297};
298
299static int deactive_set_show(struct seq_file *s, void *pos) {
300
301 uint64_t bid = (uint64_t)s->private;
302 mic_ctx_t *mic_ctx;
303 int i, j;
304 uint8_t *nodemask;
305 uint8_t *temp_buf_ptr;
306
307 mic_ctx = get_per_dev_ctx(bid);
308 if (!mic_ctx) {
309 return -EINVAL;
310 }
311
312 nodemask = (uint8_t*) kzalloc(mic_ctx->micpm_ctx.nodemask.len, GFP_KERNEL);
313 if (!nodemask) {
314 seq_printf(s, "%s\n", "Cannot allocate buffer");
315 return 0;
316 }
317
318 if ((micscif_get_deactiveset(mic_ctx->bi_id +1, nodemask, 1))) {
319 seq_printf(s, "%s\n", "Cannot calculate activation set");
320 kfree(nodemask);
321 return 0;
322 }
323
324 seq_printf(s, "%s\n", "Nodes in deactivation set:");
325 temp_buf_ptr = nodemask;
326 for ( i = 0; i < mic_ctx->micpm_ctx.nodemask.len; i++) {
327 temp_buf_ptr = nodemask + i;
328 for (j = 0; j < 8; j++) {
329 if (*temp_buf_ptr & (1ULL << j))
330 seq_printf(s, "%d ", j + (i * 8));
331 }
332 }
333 seq_printf(s, "\n");
334 kfree(nodemask);
335 return 0;
336}
337
338static int deactive_set_open(struct inode *inode, struct file *file)
339{
340 return single_open(file, deactive_set_show, inode->i_private);
341}
342
343static struct file_operations deactivation_set_file_ops = {
344 .owner = THIS_MODULE,
345 .open = deactive_set_open,
346 .read = seq_read,
347 .llseek = seq_lseek,
348 .release = single_release
349};
350
351static int ospm_restart_show(struct seq_file *s, void *pos) {
352
353 uint64_t bid = (uint64_t)s->private;
354 mic_ctx_t *mic_ctx;
355 int err;
356
357 mic_ctx = get_per_dev_ctx(bid);
358 if (!mic_ctx) {
359 return -EINVAL;
360 }
361
362 err = pm_stop_device(mic_ctx);
363 if(err) {
364 seq_printf(s, "%s:%d\n", "Error calling pm_stop_device.", err);
365 return err;
366 }
367
368 err = pm_start_device(mic_ctx);
369 if(err) {
370 seq_printf(s, "%s:%d\n", "Error calling pm_start_device.", err);
371 return err;
372 }
373
374 return 0;
375}
376
377static int ospm_restart_open(struct inode *inode, struct file *file)
378{
379 return single_open(file, ospm_restart_show, inode->i_private);
380}
381
382static struct file_operations ospm_restart_file_ops = {
383 .owner = THIS_MODULE,
384 .open = ospm_restart_open,
385 .read = seq_read,
386 .llseek = seq_lseek,
387 .release = single_release
388};
389
390static int testmsg_set(void *data, uint64_t value)
391{
392 uint64_t bid;
393 mic_ctx_t *mic_ctx;
394 int err;
395
396 bid = (uint64_t)data;
397 if (bid >= mic_data.dd_numdevs) {
398 return -EINVAL;
399 }
400
401 mic_ctx = get_per_dev_ctx(bid);
402 if (!mic_ctx) {
403 return -EINVAL;
404 }
405
406 if (value == 0) {
407 return -EINVAL;
408 }
409
410 err = mic_pm_send_msg(mic_ctx ,PM_MESSAGE_TEST, PM_TEST_MSG_BODY, sizeof(PM_TEST_MSG_BODY));
411 return err;
412}
413
414static int testmsg_get(void *data, uint64_t *value)
415{
416 uint64_t bid;
417 mic_ctx_t *mic_ctx;
418 int err;
419
420 bid = (uint64_t)data;
421 if (bid >= mic_data.dd_numdevs) {
422 return -EINVAL;
423 }
424
425 mic_ctx = get_per_dev_ctx(bid);
426 if (!mic_ctx) {
427 return -EINVAL;
428 }
429
430 err = check_test_msg(mic_ctx,PM_TEST_MSG_BODY, sizeof(PM_TEST_MSG_BODY));
431 *value = err;
432
433 return err;
434}
435DEFINE_SIMPLE_ATTRIBUTE(testmsg_fops, testmsg_get, testmsg_set, "%llu");
436
437int
438micpm_dbg_init(mic_ctx_t *mic_ctx)
439{
440 /* directory name will be in format micpmXXXXX
441 * so assuming the name string wont excceed 12 characters */
442 const uint32_t DBG_DIRNAME_LENGTH = 12;
443 char pmdbg_dir_name[DBG_DIRNAME_LENGTH];
444 micpm_ctx_t *micpm_ctx = &mic_ctx->micpm_ctx;
445 struct dentry *mmiodir;
446 uint64_t id = mic_ctx->bi_id;
447
448
449 if(!mic_data.dd_pm.pmdbgparent_dir) {
450 printk(KERN_ERR "%s: %d Parent debugfs directory does not exist.\n"
451 "debugfs may not be supported in kernel", __func__, __LINE__);
452 return -EOPNOTSUPP;
453 }
454
455 snprintf(pmdbg_dir_name, sizeof(pmdbg_dir_name), "micpm%d", mic_ctx->bi_id);
456 micpm_ctx->pmdbg_dir = debugfs_create_dir
457 (pmdbg_dir_name, mic_data.dd_pm.pmdbgparent_dir);
458 if (!micpm_ctx->pmdbg_dir) {
459 printk(KERN_ERR "%s: %d Failed in creating debugfs directory\n"
460 "debugfs may noe be supported in kernel", __func__, __LINE__);
461 return -EOPNOTSUPP;
462 }
463
464 /* Create debugfs entry to get/set idle state of the card known by host*/
465 debugfs_create_u32("idle_state", S_IRUGO | S_IWUSR, micpm_ctx->pmdbg_dir, &micpm_ctx->idle_state);
466
467 /*
468 * Create debugfs entry for sending PM_TEST_MESSAGE for testing communication to card
469 * set value = PM_MESSAGE_TEST to send the message to card
470 * get value to verfy that message was successfully sent, looped back by card and received.(0 = success)
471 */
472 debugfs_create_file("testmsg", S_IRUGO | S_IWUSR, micpm_ctx->pmdbg_dir, (void*)id, &testmsg_fops);
473
474 /* Create debugfs entry for showing for each node 'i' , all nodes 'j' i is dependent on */
475 debugfs_create_file("depgraph_i2j",
476 S_IRUGO,
477 micpm_ctx->pmdbg_dir,
478 (void*)id,
479 &depgraph_i2j_file_ops);
480
481 /* Create debugfs entry for showing for each node 'i', all nodes 'j' which are dependent on 'i' */
482 debugfs_create_file("depgraph_j2i",
483 S_IRUGO,
484 micpm_ctx->pmdbg_dir,
485 (void*)id,
486 &depgraph_j2i_file_ops);
487
488 /* Create debugfs entry for showing connection info for a node */
489 debugfs_create_file("connection_info",
490 S_IRUGO,
491 micpm_ctx->pmdbg_dir,
492 (void*)id,
493 &connection_info_file_ops);
494
495 /* Create debugfs entry to initiate OSPM restart for a node */
496 debugfs_create_file("ospm_restart",
497 S_IRUGO,
498 micpm_ctx->pmdbg_dir,
499 (void*)id,
500 &ospm_restart_file_ops);
501
502 /* Create debugfs entry to display activation set for a node */
503 debugfs_create_file("activation_set",
504 S_IRUGO,
505 micpm_ctx->pmdbg_dir,
506 (void*)id,
507 &activation_set_file_ops);
508
509 /* Create debugfs entry to display de-activation set for a node */
510 debugfs_create_file("deactivation_set",
511 S_IRUGO,
512 micpm_ctx->pmdbg_dir,
513 (void*)id,
514 &deactivation_set_file_ops);
515
516 /* Create debugfs entries for reading power management status/control register value in MMIO region */
517 mmiodir = debugfs_create_dir("mmio", micpm_ctx->pmdbg_dir);
518 if (!mmiodir) {
519 printk(KERN_ERR "%s: %d Failed in creating debugfs directory\n"
520 "debugfs may noe be supported in kernel", __func__, __LINE__);
521 return -EOPNOTSUPP;
522 }
523 debugfs_create_file("svidctrl", S_IRUGO, mmiodir,(void*)id, &fops_svidctrl);
524 debugfs_create_file("pcuctrl", S_IRUGO, mmiodir,(void*)id, &fops_pcuctrl);
525 debugfs_create_file("hoststate", S_IRUGO, mmiodir,(void*)id, &fops_hoststate);
526 debugfs_create_file("cardstate", S_IRUGO, mmiodir,(void*)id, &fops_cardstate);
527 debugfs_create_file("wtimer", S_IRUGO, mmiodir,(void*)id, &fops_wtimer);
528 debugfs_create_file("gpmctrl", S_IRUGO, mmiodir,(void*)id, &fops_gpmctrl);
529 debugfs_create_file("core_volt", S_IRUGO, mmiodir,(void*)id, &fops_core_volt);
530 debugfs_create_file("uos_pcuctrl", S_IRUGO, mmiodir,(void*)id, &fops_uos_pcuctrl);
531
532 return 0;
533}
534
535void micpm_dbg_parent_init(void) {
536 mic_data.dd_pm.pmdbgparent_dir = debugfs_create_dir("micpm", NULL);
537 if (!mic_data.dd_pm.pmdbgparent_dir) {
538 PM_DEBUG("%s: %d Failed in creating debugfs directory\n"
539 "debugfs may not be supported in kernel", __func__, __LINE__);
540 }
541
542 debugfs_create_u32("enable_pm_logging", S_IRUGO | S_IWUSR,
543 mic_data.dd_pm.pmdbgparent_dir, &mic_data.dd_pm.enable_pm_logging);
544
545 return;
546}
547
548
549/*
550 * Test message is looped back to driver and lives in the message list.
551 * This function retrieves the message and send it to user space which
552 * can check if its the same message as that was sent.
553 */
554static int
555check_test_msg(mic_ctx_t *mic_ctx, void *buf, uint32_t len)
556{
557 int err = -EINVAL;
558 pm_recv_msg_t *recv_msg = NULL;
559 struct list_head *pos = NULL, *tmpq = NULL;
560 bool msg_found = false;
561
562 if(len != sizeof(pm_msg_unit_test)) {
563 pr_debug("Invalid Args: Size of buffer\n");
564 return -EINVAL;
565 }
566
567 mutex_lock(&mic_ctx->micpm_ctx.msg_mutex);
568 if(!list_empty_careful(&mic_ctx->micpm_ctx.msg_list)) {
569 list_for_each_safe(pos, tmpq, &mic_ctx->micpm_ctx.msg_list) {
570 recv_msg = list_entry(pos, pm_recv_msg_t, msg);
571 /*Do not touch the message if its not a test message */
572 if (recv_msg->msg_header.opcode == PM_MESSAGE_TEST) {
573 list_del(&recv_msg->msg);
574 msg_found = true;
575 break;
576 }
577 }
578 } else {
579 pr_debug("empty message list \n");
580 goto no_msg;
581 }
582
583 if (msg_found == false) {
584 pr_debug("Test msg not found \n");
585 goto no_msg;
586 }
587
588 if(recv_msg->msg_body == NULL) {
589 pr_debug("Invalid source buffer\n");
590 goto list_free;
591 }
592
593 err = strncmp((char*)recv_msg->msg_body, (char*)buf, len);
594 kfree(recv_msg->msg_body);
595
596list_free:
597 kfree(recv_msg);
598
599no_msg:
600 mutex_unlock(&mic_ctx->micpm_ctx.msg_mutex);
601 return err;
602
603}