* Copyright 2010-2017 Intel Corporation.
* 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.
* Disclaimer: The codes contained in these modules may be specific to
* the Intel Software Development Platform codenamed Knights Ferry,
* and the Intel product codenamed Knights Corner, and are not backward
* compatible with other Intel products. Additionally, Intel will NOT
* support the codes or instruction set in future products.
* Intel offers no warranty of any kind regarding the code. This code is
* licensed on an "AS IS" basis and Intel is not obligated to provide
* any support, assistance, installation, training, or other services
* of any kind. Intel is also not obligated to provide any updates,
* enhancements or extensions. Intel specifically disclaims any warranty
* of merchantability, non-infringement, fitness for any particular
* purpose, and any other warranty.
* Further, Intel disclaims all liability of any kind, including but
* not limited to liability for infringement of any proprietary rights,
* relating to the use of the code, even if Intel is notified of the
* possibility of such liability. Except as expressly stated in an Intel
* license agreement provided with this code and agreed upon with Intel,
* no license, express or implied, by estoppel or otherwise, to any
* intellectual property rights is granted herein.
/* contains code to handle MIC IO control codes */
#include <linux/module.h>
/* helper methods for debugging/unit testing /*/
static int check_test_msg(mic_ctx_t *mic_ctx, void *buf, uint32_t len);
#define PM_MMIO_REGVALUE_GET(_name, _offset) \
int get_##_name(void *data, uint64_t *value) \
if (bid >= mic_data.dd_numdevs) { \
mic_ctx = get_per_dev_ctx(bid); \
*value = pm_reg_read(mic_ctx, _offset); \
DEFINE_SIMPLE_ATTRIBUTE(fops_##_name, get_##_name, NULL, "%llu"); \
static PM_MMIO_REGVALUE_GET(svidctrl, SBOX_SVID_CONTROL);
static PM_MMIO_REGVALUE_GET(pcuctrl, SBOX_PCU_CONTROL);
static PM_MMIO_REGVALUE_GET(hoststate,SBOX_HOST_PMSTATE);
static PM_MMIO_REGVALUE_GET(cardstate, SBOX_UOS_PMSTATE);
static PM_MMIO_REGVALUE_GET(wtimer, SBOX_C3WAKEUP_TIMER);
static PM_MMIO_REGVALUE_GET(gpmctrl, GBOX_PM_CTRL);
static PM_MMIO_REGVALUE_GET(core_volt, SBOX_COREVOLT);
static PM_MMIO_REGVALUE_GET(uos_pcuctrl, SBOX_UOS_PCUCONTROL);
static int depgraph_j2i_show(struct seq_file *s, void *pos)
uint64_t bid = (uint64_t)s->private;
mic_ctx = get_per_dev_ctx(bid);
seq_printf(s,"=================================================================\n");
seq_printf(s,"%-10s |%-25s\n", "Scif Node" , "dependent nodes");
seq_printf(s,"=================================================================\n");
for ( i = 0; i <= ms_info.mi_maxid; i++) {
seq_printf(s, "%-10d |", i);
for (j = 0; j <= ms_info.mi_maxid; j++) {
switch(ms_info.mi_depmtrx[j][i]) {
case DEP_STATE_DEPENDENT:
/* (A) - active dependency on node i */
seq_printf(s
, "%d(A),", j
);
case DEP_STATE_DISCONNECT_READY
:
/* (R) - node j has sent PC6 ready message to the host
* dependency is not active so node i can go idle
seq_printf(s
, "%d(R),", j
);
case DEP_STATE_DISCONNECTED
:
/* (D) - node j is in idle state.
* dependency is not active so node i can go idle
seq_printf(s
, "%d(D),", j
);
seq_printf(s
,"\n=================================================================\n");
static int depgraph_j2i_open(struct inode
*inode
, struct file
*file
)
return single_open(file
, depgraph_j2i_show
, inode
->i_private
);
static struct file_operations depgraph_j2i_file_ops
= {
.open
= depgraph_j2i_open
,
.release
= single_release
static int depgraph_i2j_show(struct seq_file
*s
, void *pos
)
uint64_t bid
= (uint64_t)s
->private;
mic_ctx
= get_per_dev_ctx(bid
);
seq_printf(s
,"=================================================================\n");
seq_printf(s
,"%-10s |%-25s\n", "Scif Node" , "is dependent on Nodes");
seq_printf(s
,"=================================================================\n");
for ( i
= 0; i
<= ms_info
.mi_maxid
; i
++) {
seq_printf(s
, "%-10d |", i
);
for (j
= 0; j
<= ms_info
.mi_maxid
; j
++) {
switch(ms_info
.mi_depmtrx
[i
][j
]) {
case DEP_STATE_DEPENDENT
:
/* (A) - active dependency on node j */
seq_printf(s
, "%d(A),", j
);
case DEP_STATE_DISCONNECT_READY
:
/* (R) - node j has sent PC6 ready message to the host */
seq_printf(s
, "%d(R),", j
);
case DEP_STATE_DISCONNECTED
:
/* (D) - node j is in idle state.
* This should not happen unless node i itself is in idle state
seq_printf(s
, "%d(D),", j
);
seq_printf(s
,"\n=================================================================\n");
static int depgraph_i2j_open(struct inode
*inode
, struct file
*file
)
return single_open(file
, depgraph_i2j_show
, inode
->i_private
);
static struct file_operations depgraph_i2j_file_ops
= {
.open
= depgraph_i2j_open
,
.release
= single_release
static int connection_info_show(struct seq_file
*s
, void *pos
) {
uint64_t bid
= (uint64_t)s
->private;
struct list_head
*position
, *tmpq
;
mic_ctx
= get_per_dev_ctx(bid
);
seq_printf(s
,"=========================================================================\n");
if(mic_ctx
->micpm_ctx
.pm_epd
!= NULL
) {
seq_printf(s
, "%-35s | %35d\n", "Local Node", mic_ctx
->micpm_ctx
.pm_epd
->port
.node
);
seq_printf(s
, "%-35s | %35d\n", "Local Port", mic_ctx
->micpm_ctx
.pm_epd
->port
.port
);
seq_printf(s
, "%-35s | %35d\n", "Remote Node", mic_ctx
->micpm_ctx
.pm_epd
->peer
.node
);
seq_printf(s
, "%-35s | %35d\n", "Remote Port", mic_ctx
->micpm_ctx
.pm_epd
->peer
.port
);
seq_printf(s
, "%-35s | %35d\n", "Connection state", mic_ctx
->micpm_ctx
.pm_epd
->state
);
if(!list_empty(&mic_ctx
->micpm_ctx
.msg_list
)) {
list_for_each_safe(position
, tmpq
, &mic_ctx
->micpm_ctx
.msg_list
) {
seq_printf(s
, "%-35s | %35d\n", "Messages in queue", count
);
seq_printf(s
, "%s\n", "No PM connection found");
seq_printf(s
,"=========================================================================\n");
static int connection_info_open(struct inode
*inode
, struct file
*file
)
return single_open(file
, connection_info_show
, inode
->i_private
);
static struct file_operations connection_info_file_ops
= {
.open
= connection_info_open
,
.release
= single_release
static int active_set_show(struct seq_file
*s
, void *pos
) {
uint64_t bid
= (uint64_t)s
->private;
mic_ctx
= get_per_dev_ctx(bid
);
nodemask
= (uint8_t*) kzalloc(mic_ctx
->micpm_ctx
.nodemask
.len
, GFP_KERNEL
);
seq_printf(s
, "%s\n", "Cannot allocate buffer");
if ((micscif_get_activeset(mic_ctx
->bi_id
+ 1, nodemask
))) {
seq_printf(s
, "%s\n", "Cannot calculate activation set");
seq_printf(s
, "%s\n", "Nodes in activation set:");
for ( i
= 0; i
< mic_ctx
->micpm_ctx
.nodemask
.len
; i
++) {
temp_buf_ptr
= nodemask
+ i
;
for (j
= 0; j
< 8; j
++) {
if (*temp_buf_ptr
& (1ULL << j
))
seq_printf(s
, "%d ", j
+ (i
* 8));
static int active_set_open(struct inode
*inode
, struct file
*file
)
return single_open(file
, active_set_show
, inode
->i_private
);
static struct file_operations activation_set_file_ops
= {
.release
= single_release
static int deactive_set_show(struct seq_file
*s
, void *pos
) {
uint64_t bid
= (uint64_t)s
->private;
mic_ctx
= get_per_dev_ctx(bid
);
nodemask
= (uint8_t*) kzalloc(mic_ctx
->micpm_ctx
.nodemask
.len
, GFP_KERNEL
);
seq_printf(s
, "%s\n", "Cannot allocate buffer");
if ((micscif_get_deactiveset(mic_ctx
->bi_id
+1, nodemask
, 1))) {
seq_printf(s
, "%s\n", "Cannot calculate activation set");
seq_printf(s
, "%s\n", "Nodes in deactivation set:");
for ( i
= 0; i
< mic_ctx
->micpm_ctx
.nodemask
.len
; i
++) {
temp_buf_ptr
= nodemask
+ i
;
for (j
= 0; j
< 8; j
++) {
if (*temp_buf_ptr
& (1ULL << j
))
seq_printf(s
, "%d ", j
+ (i
* 8));
static int deactive_set_open(struct inode
*inode
, struct file
*file
)
return single_open(file
, deactive_set_show
, inode
->i_private
);
static struct file_operations deactivation_set_file_ops
= {
.open
= deactive_set_open
,
.release
= single_release
static int ospm_restart_show(struct seq_file
*s
, void *pos
) {
uint64_t bid
= (uint64_t)s
->private;
mic_ctx
= get_per_dev_ctx(bid
);
err
= pm_stop_device(mic_ctx
);
seq_printf(s
, "%s:%d\n", "Error calling pm_stop_device.", err
);
err
= pm_start_device(mic_ctx
);
seq_printf(s
, "%s:%d\n", "Error calling pm_start_device.", err
);
static int ospm_restart_open(struct inode
*inode
, struct file
*file
)
return single_open(file
, ospm_restart_show
, inode
->i_private
);
static struct file_operations ospm_restart_file_ops
= {
.open
= ospm_restart_open
,
.release
= single_release
static int testmsg_set(void *data
, uint64_t value
)
if (bid
>= mic_data
.dd_numdevs
) {
mic_ctx
= get_per_dev_ctx(bid
);
err
= mic_pm_send_msg(mic_ctx
,PM_MESSAGE_TEST
, PM_TEST_MSG_BODY
, sizeof(PM_TEST_MSG_BODY
));
static int testmsg_get(void *data
, uint64_t *value
)
if (bid
>= mic_data
.dd_numdevs
) {
mic_ctx
= get_per_dev_ctx(bid
);
err
= check_test_msg(mic_ctx
,PM_TEST_MSG_BODY
, sizeof(PM_TEST_MSG_BODY
));
DEFINE_SIMPLE_ATTRIBUTE(testmsg_fops
, testmsg_get
, testmsg_set
, "%llu");
micpm_dbg_init(mic_ctx_t
*mic_ctx
)
/* directory name will be in format micpmXXXXX
* so assuming the name string wont excceed 12 characters */
const uint32_t DBG_DIRNAME_LENGTH
= 12;
char pmdbg_dir_name
[DBG_DIRNAME_LENGTH
];
micpm_ctx_t
*micpm_ctx
= &mic_ctx
->micpm_ctx
;
uint64_t id
= mic_ctx
->bi_id
;
if(!mic_data
.dd_pm
.pmdbgparent_dir
) {
printk(KERN_ERR
"%s: %d Parent debugfs directory does not exist.\n"
"debugfs may not be supported in kernel", __func__
, __LINE__
);
snprintf(pmdbg_dir_name
, sizeof(pmdbg_dir_name
), "micpm%d", mic_ctx
->bi_id
);
micpm_ctx
->pmdbg_dir
= debugfs_create_dir
(pmdbg_dir_name
, mic_data
.dd_pm
.pmdbgparent_dir
);
if (!micpm_ctx
->pmdbg_dir
) {
printk(KERN_ERR
"%s: %d Failed in creating debugfs directory\n"
"debugfs may noe be supported in kernel", __func__
, __LINE__
);
/* Create debugfs entry to get/set idle state of the card known by host*/
debugfs_create_u32("idle_state", S_IRUGO
| S_IWUSR
, micpm_ctx
->pmdbg_dir
, &micpm_ctx
->idle_state
);
* Create debugfs entry for sending PM_TEST_MESSAGE for testing communication to card
* set value = PM_MESSAGE_TEST to send the message to card
* get value to verfy that message was successfully sent, looped back by card and received.(0 = success)
debugfs_create_file("testmsg", S_IRUGO
| S_IWUSR
, micpm_ctx
->pmdbg_dir
, (void*)id
, &testmsg_fops
);
/* Create debugfs entry for showing for each node 'i' , all nodes 'j' i is dependent on */
debugfs_create_file("depgraph_i2j",
/* Create debugfs entry for showing for each node 'i', all nodes 'j' which are dependent on 'i' */
debugfs_create_file("depgraph_j2i",
/* Create debugfs entry for showing connection info for a node */
debugfs_create_file("connection_info",
&connection_info_file_ops
);
/* Create debugfs entry to initiate OSPM restart for a node */
debugfs_create_file("ospm_restart",
/* Create debugfs entry to display activation set for a node */
debugfs_create_file("activation_set",
&activation_set_file_ops
);
/* Create debugfs entry to display de-activation set for a node */
debugfs_create_file("deactivation_set",
&deactivation_set_file_ops
);
/* Create debugfs entries for reading power management status/control register value in MMIO region */
mmiodir
= debugfs_create_dir("mmio", micpm_ctx
->pmdbg_dir
);
printk(KERN_ERR
"%s: %d Failed in creating debugfs directory\n"
"debugfs may noe be supported in kernel", __func__
, __LINE__
);
debugfs_create_file("svidctrl", S_IRUGO
, mmiodir
,(void*)id
, &fops_svidctrl
);
debugfs_create_file("pcuctrl", S_IRUGO
, mmiodir
,(void*)id
, &fops_pcuctrl
);
debugfs_create_file("hoststate", S_IRUGO
, mmiodir
,(void*)id
, &fops_hoststate
);
debugfs_create_file("cardstate", S_IRUGO
, mmiodir
,(void*)id
, &fops_cardstate
);
debugfs_create_file("wtimer", S_IRUGO
, mmiodir
,(void*)id
, &fops_wtimer
);
debugfs_create_file("gpmctrl", S_IRUGO
, mmiodir
,(void*)id
, &fops_gpmctrl
);
debugfs_create_file("core_volt", S_IRUGO
, mmiodir
,(void*)id
, &fops_core_volt
);
debugfs_create_file("uos_pcuctrl", S_IRUGO
, mmiodir
,(void*)id
, &fops_uos_pcuctrl
);
void micpm_dbg_parent_init(void) {
mic_data
.dd_pm
.pmdbgparent_dir
= debugfs_create_dir("micpm", NULL
);
if (!mic_data
.dd_pm
.pmdbgparent_dir
) {
PM_DEBUG("%s: %d Failed in creating debugfs directory\n"
"debugfs may not be supported in kernel", __func__
, __LINE__
);
debugfs_create_u32("enable_pm_logging", S_IRUGO
| S_IWUSR
,
mic_data
.dd_pm
.pmdbgparent_dir
, &mic_data
.dd_pm
.enable_pm_logging
);
* Test message is looped back to driver and lives in the message list.
* This function retrieves the message and send it to user space which
* can check if its the same message as that was sent.
check_test_msg(mic_ctx_t
*mic_ctx
, void *buf
, uint32_t len
)
pm_recv_msg_t
*recv_msg
= NULL
;
struct list_head
*pos
= NULL
, *tmpq
= NULL
;
if(len
!= sizeof(pm_msg_unit_test
)) {
pr_debug("Invalid Args: Size of buffer\n");
mutex_lock(&mic_ctx
->micpm_ctx
.msg_mutex
);
if(!list_empty_careful(&mic_ctx
->micpm_ctx
.msg_list
)) {
list_for_each_safe(pos
, tmpq
, &mic_ctx
->micpm_ctx
.msg_list
) {
recv_msg
= list_entry(pos
, pm_recv_msg_t
, msg
);
/*Do not touch the message if its not a test message */
if (recv_msg
->msg_header
.opcode
== PM_MESSAGE_TEST
) {
list_del(&recv_msg
->msg
);
pr_debug("empty message list \n");
if (msg_found
== false) {
pr_debug("Test msg not found \n");
if(recv_msg
->msg_body
== NULL
) {
pr_debug("Invalid source buffer\n");
err
= strncmp((char*)recv_msg
->msg_body
, (char*)buf
, len
);
kfree(recv_msg
->msg_body
);
mutex_unlock(&mic_ctx
->micpm_ctx
.msg_mutex
);