429 lines
11 KiB
C
429 lines
11 KiB
C
/*
|
|
* arch/e2k/kernel/procsic.c
|
|
*
|
|
* This file contains implementation of functions to read and write SIC
|
|
* registers through proc fs.
|
|
*
|
|
* Copyright (C) 2010-2014 Pavel V. Panteleev (panteleev_p@mcst.ru)
|
|
*/
|
|
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/module.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
#include <asm/sic_regs.h>
|
|
#include <asm/sic_regs_access.h>
|
|
|
|
#define SICREGS_FILENAME "sicregs"
|
|
static struct proc_dir_entry *sicregs_entry;
|
|
|
|
#define SICWRITE_FILENAME "sicwrite"
|
|
static struct proc_dir_entry *sicwrite_entry;
|
|
|
|
#define SICWRITE_STR_MAX_SIZE 32
|
|
|
|
int sicwrite_regs[] = {0x414, 0x454, 0x494};
|
|
|
|
static void sicregs_print_MC_ECC(struct seq_file *s, int node)
|
|
{
|
|
e2k_mc_ecc_struct_t ecc;
|
|
int i;
|
|
|
|
for (i = 0; i < SIC_MC_COUNT; i++) {
|
|
ecc.E2K_MC_ECC_reg = sic_get_mc_ecc(node, i);
|
|
|
|
seq_printf(s, "\tMC%d_ECC=0x%x (ee=%d dmode=%d of=%d "
|
|
"ue=%d secnt=%d)\n",
|
|
i, ecc.E2K_MC_ECC_reg, ecc.E2K_MC_ECC_ee,
|
|
ecc.E2K_MC_ECC_dmode, ecc.E2K_MC_ECC_of,
|
|
ecc.E2K_MC_ECC_ue, ecc.E2K_MC_ECC_secnt);
|
|
}
|
|
}
|
|
|
|
static void sicregs_print_MC_OPMB(struct seq_file *s, int node)
|
|
{
|
|
e2k_mc_opmb_struct_t opmb;
|
|
int i;
|
|
|
|
if (IS_MACHINE_E2S || IS_MACHINE_E8C) {
|
|
for (i = 0; i < SIC_MC_COUNT; i++) {
|
|
opmb.E2K_MC_OPMB_reg = sic_get_mc_opmb(node, i);
|
|
|
|
seq_printf(s, "\tMC%d_OPMB=0x%x (ct0=%d ct1=%d pbm0=%d "
|
|
"pbm1=%d rm=%d rdodt=%d wrodt=%d bl8int=%d "
|
|
"mi_fast=%d mt=%d il=%d rcven_del=%d mc_ps=%d "
|
|
"arp_en=%d flt_brop=%d flt_rdpr=%d flt_blk=%d "
|
|
"parerr=%d cmdpack=%d sldwr=%d sldrd=%d "
|
|
"mirr=%d twrwr=%d mcln=%d)\n",
|
|
i, opmb.E2K_MC_OPMB_reg, opmb.E2K_MC_OPMB_ct0,
|
|
opmb.E2K_MC_OPMB_ct1, opmb.E2K_MC_OPMB_pbm0,
|
|
opmb.E2K_MC_OPMB_pbm1, opmb.E2K_MC_OPMB_rm,
|
|
opmb.E2K_MC_OPMB_rdodt, opmb.E2K_MC_OPMB_wrodt,
|
|
opmb.E2K_MC_OPMB_bl8int,
|
|
opmb.E2K_MC_OPMB_mi_fast, opmb.E2K_MC_OPMB_mt,
|
|
opmb.E2K_MC_OPMB_il, opmb.E2K_MC_OPMB_rcven_del,
|
|
opmb.E2K_MC_OPMB_mc_ps, opmb.E2K_MC_OPMB_arp_en,
|
|
opmb.E2K_MC_OPMB_flt_brop,
|
|
opmb.E2K_MC_OPMB_flt_rdpr,
|
|
opmb.E2K_MC_OPMB_flt_blk,
|
|
opmb.E2K_MC_OPMB_parerr,
|
|
opmb.E2K_MC_OPMB_cmdpack,
|
|
opmb.E2K_MC_OPMB_sldwr,
|
|
opmb.E2K_MC_OPMB_sldrd, opmb.E2K_MC_OPMB_mirr,
|
|
opmb.E2K_MC_OPMB_twrwr, opmb.E2K_MC_OPMB_mcln);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void sicregs_print_IPCC_CSR(struct seq_file *s, int node)
|
|
{
|
|
e2k_ipcc_csr_struct_t ipcc_csr;
|
|
int i;
|
|
|
|
if (IS_MACHINE_E2S || IS_MACHINE_E8C) {
|
|
for (i = 1; i < SIC_IPCC_LINKS_COUNT + 1; i++) {
|
|
ipcc_csr.E2K_IPCC_CSR_reg = sic_get_ipcc_csr(node, i);
|
|
|
|
seq_printf(s, "\tIPCC_CSR%d=0x%x (link_scale=0x%x "
|
|
"cmd_code=0x%x cmd_active=%d terr_vc_num=0x%x "
|
|
"rx_oflw_uflw=%d event_imsk=0x%x "
|
|
"ltssm_state=0x%x cmd_cmpl_sts=0x%x "
|
|
"link_width=0x%x event_sts=0x%x "
|
|
"link_state=%d)\n",
|
|
i, ipcc_csr.E2K_IPCC_CSR_reg,
|
|
ipcc_csr.E2K_IPCC_CSR_link_scale,
|
|
ipcc_csr.E2K_IPCC_CSR_cmd_code,
|
|
ipcc_csr.E2K_IPCC_CSR_cmd_active,
|
|
ipcc_csr.E2K_IPCC_CSR_terr_vc_num,
|
|
ipcc_csr.E2K_IPCC_CSR_rx_oflw_uflw,
|
|
ipcc_csr.E2K_IPCC_CSR_event_imsk,
|
|
ipcc_csr.E2K_IPCC_CSR_ltssm_state,
|
|
ipcc_csr.E2K_IPCC_CSR_cmd_cmpl_sts,
|
|
ipcc_csr.E2K_IPCC_CSR_link_width,
|
|
ipcc_csr.E2K_IPCC_CSR_event_sts,
|
|
ipcc_csr.E2K_IPCC_CSR_link_state);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void sicregs_print_IPCC_PMR(struct seq_file *s, int node)
|
|
{
|
|
e2k_ipcc_pmr_struct_t ipcc_pmr;
|
|
int i;
|
|
|
|
if (IS_MACHINE_E2S || IS_MACHINE_E8C) {
|
|
for (i = 1; i < SIC_IPCC_LINKS_COUNT + 1; i++) {
|
|
ipcc_pmr.E2K_IPCC_PMR_reg = sic_get_ipcc_pmr(node, i);
|
|
|
|
seq_printf(s, "\tIPCC_PMR%d=0x%x (force_rxdet=%d "
|
|
"ctc_en=%d scramble=%d rcvr_tmrl=0x%x "
|
|
"phle_lmt=%d dlle_lmt=0x%x irqpp=0x%x "
|
|
"crqpp=0x%x drqpp=0x%x)\n",
|
|
i, ipcc_pmr.E2K_IPCC_PMR_reg,
|
|
ipcc_pmr.E2K_IPCC_PMR_force_rxdet,
|
|
ipcc_pmr.E2K_IPCC_PMR_ctc_en,
|
|
ipcc_pmr.E2K_IPCC_PMR_scramble,
|
|
ipcc_pmr.E2K_IPCC_PMR_rcvr_tmrl,
|
|
ipcc_pmr.E2K_IPCC_PMR_phle_lmt,
|
|
ipcc_pmr.E2K_IPCC_PMR_dlle_lmt,
|
|
ipcc_pmr.E2K_IPCC_PMR_irqpp,
|
|
ipcc_pmr.E2K_IPCC_PMR_crqpp,
|
|
ipcc_pmr.E2K_IPCC_PMR_drqpp);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void sicregs_print_IPCC_STR(struct seq_file *s, int node)
|
|
{
|
|
e2k_ipcc_str_struct_t ipcc_str;
|
|
int i;
|
|
|
|
if (IS_MACHINE_E2S || IS_MACHINE_E8C) {
|
|
for (i = 1; i < SIC_IPCC_LINKS_COUNT + 1; i++) {
|
|
ipcc_str.E2K_IPCC_STR_reg = sic_get_ipcc_str(node, i);
|
|
|
|
seq_printf(s, "\tIPCC_STR%d=0x%x (ecnt=0x%x eco=%d "
|
|
"ecf=0x%x)\n",
|
|
i, ipcc_str.E2K_IPCC_STR_reg,
|
|
ipcc_str.E2K_IPCC_STR_ecnt,
|
|
ipcc_str.E2K_IPCC_STR_eco,
|
|
ipcc_str.E2K_IPCC_STR_ecf);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void sicregs_print_IO_CSR(struct seq_file *s, int node)
|
|
{
|
|
e2k_io_csr_struct_t io_csr;
|
|
int i;
|
|
|
|
if (IS_MACHINE_E3S || IS_MACHINE_ES2 || IS_MACHINE_E2S ||
|
|
IS_MACHINE_E1CP) {
|
|
for (i = 0; i < SIC_IO_LINKS_COUNT; i++) {
|
|
io_csr.E2K_IO_CSR_reg = sic_get_io_csr(node, i);
|
|
|
|
if (!i)
|
|
seq_printf(s, "\tIO_CSR");
|
|
else if (IS_MACHINE_E2S)
|
|
seq_printf(s, "\tIO_CSR_HI");
|
|
else
|
|
seq_printf(s, "\tIO_CSR1");
|
|
|
|
seq_printf(s, "=0x%x (srst=%d bsy_ie=%d err_ie=%d "
|
|
"to_ie=%d lsc_ie=%d bsy_ev=%d err_ev=%d "
|
|
"to_ev=%d lsc_ev=%d link_tu=%d ch_on=%d)\n",
|
|
io_csr.E2K_IO_CSR_reg,
|
|
io_csr.E2K_IO_CSR_srst,
|
|
io_csr.E2K_IO_CSR_bsy_ie,
|
|
io_csr.E2K_IO_CSR_err_ie,
|
|
io_csr.E2K_IO_CSR_to_ie,
|
|
io_csr.E2K_IO_CSR_lsc_ie,
|
|
io_csr.E2K_IO_CSR_bsy_ev,
|
|
io_csr.E2K_IO_CSR_err_ev,
|
|
io_csr.E2K_IO_CSR_to_ev,
|
|
io_csr.E2K_IO_CSR_lsc_ev,
|
|
io_csr.E2K_IO_CSR_link_tu,
|
|
io_csr.E2K_IO_CSR_ch_on);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void sicregs_print_IO_TMR(struct seq_file *s, int node)
|
|
{
|
|
e2k_io_tmr_struct_t io_tmr;
|
|
int i;
|
|
|
|
if (IS_MACHINE_E3S || IS_MACHINE_ES2 || IS_MACHINE_E2S ||
|
|
IS_MACHINE_E1CP) {
|
|
for (i = 0; i < SIC_IO_LINKS_COUNT; i++) {
|
|
io_tmr.E2K_IO_TMR_reg = sic_get_io_tmr(node, i);
|
|
|
|
if (!i)
|
|
seq_printf(s, "\tIO_TMR");
|
|
else if (IS_MACHINE_E2S)
|
|
seq_printf(s, "\tIO_TMR_HI");
|
|
else
|
|
seq_printf(s, "\tIO_TMR1");
|
|
|
|
seq_printf(s, "=0x%x (ptocl=0x%x pbrn=0x%x)\n",
|
|
io_tmr.E2K_IO_TMR_reg,
|
|
io_tmr.E2K_IO_TMR_ptocl,
|
|
io_tmr.E2K_IO_TMR_pbrn);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void sicregs_print_IO_STR(struct seq_file *s, int node)
|
|
{
|
|
e2k_io_str_struct_t io_str;
|
|
int i;
|
|
|
|
if (IS_MACHINE_E3S || IS_MACHINE_ES2 || IS_MACHINE_E2S ||
|
|
IS_MACHINE_E1CP) {
|
|
for (i = 0; i < SIC_IO_LINKS_COUNT; i++) {
|
|
io_str.E2K_IO_STR_reg = sic_get_io_str(node, i);
|
|
|
|
if (!i)
|
|
seq_printf(s, "\tIO_STR");
|
|
else if (IS_MACHINE_E2S)
|
|
seq_printf(s, "\tIO_STR_HI");
|
|
else
|
|
seq_printf(s, "\tIO_STR1");
|
|
|
|
seq_printf(s, "=0x%x (rc=0x%x rcol=%d bsy_rce=%d "
|
|
"err_rce=%d to_rce=%d)\n",
|
|
io_str.E2K_IO_STR_reg,
|
|
io_str.E2K_IO_STR_rc,
|
|
io_str.E2K_IO_STR_rcol,
|
|
io_str.E2K_IO_STR_bsy_rce,
|
|
io_str.E2K_IO_STR_err_rce,
|
|
io_str.E2K_IO_STR_to_rce);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void sicregs_print_PL_CSR(struct seq_file *s, int node)
|
|
{
|
|
e2k_pl_csr_struct_t pl_csr;
|
|
int i;
|
|
|
|
if (IS_MACHINE_E3S || IS_MACHINE_ES2) {
|
|
for (i = 0; i < SIC_CPU_LINKS_COUNT; i++) {
|
|
pl_csr.E2K_PL_CSR_reg = sic_get_pl_csr(node, i);
|
|
|
|
seq_printf(s, "\tPL_CSR%d=0x%x (rc=0x%x rcol=%d rce=%d "
|
|
"link_tu=%d ch_on=%d lerr=%d srst=%d)\n",
|
|
i,
|
|
pl_csr.E2K_PL_CSR_reg,
|
|
pl_csr.E2K_PL_CSR_rc,
|
|
pl_csr.E2K_PL_CSR_rcol,
|
|
pl_csr.E2K_PL_CSR_rce,
|
|
pl_csr.E2K_PL_CSR_link_tu,
|
|
pl_csr.E2K_PL_CSR_ch_on,
|
|
pl_csr.E2K_PL_CSR_lerr,
|
|
pl_csr.E2K_PL_CSR_srst);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int sicregs_seq_show(struct seq_file *s, void *v)
|
|
{
|
|
int node = *((int *)v);
|
|
|
|
seq_printf(s, "node: %d\n", node);
|
|
|
|
sicregs_print_MC_ECC(s, node);
|
|
sicregs_print_MC_OPMB(s, node);
|
|
sicregs_print_IPCC_CSR(s, node);
|
|
sicregs_print_IPCC_PMR(s, node);
|
|
sicregs_print_IPCC_STR(s, node);
|
|
sicregs_print_IO_CSR(s, node);
|
|
sicregs_print_IO_TMR(s, node);
|
|
sicregs_print_IO_STR(s, node);
|
|
sicregs_print_PL_CSR(s, node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void *sicregs_seq_start(struct seq_file *s, loff_t *pos)
|
|
{
|
|
if (!node_online(*pos))
|
|
*pos = next_online_node(*pos);
|
|
if (*pos == MAX_NUMNODES)
|
|
return 0;
|
|
return (void *)pos;
|
|
}
|
|
|
|
static void *sicregs_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
{
|
|
*pos = next_online_node(*pos);
|
|
if (*pos == MAX_NUMNODES)
|
|
return 0;
|
|
return (void *)pos;
|
|
}
|
|
|
|
static void sicregs_seq_stop(struct seq_file *s, void *v)
|
|
{
|
|
}
|
|
|
|
static const struct seq_operations sicregs_seq_ops = {
|
|
.start = sicregs_seq_start,
|
|
.next = sicregs_seq_next,
|
|
.stop = sicregs_seq_stop,
|
|
.show = sicregs_seq_show
|
|
};
|
|
|
|
static int sicregs_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return seq_open(file, &sicregs_seq_ops);
|
|
}
|
|
|
|
static const struct file_operations sicregs_proc_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = sicregs_proc_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = seq_release
|
|
};
|
|
|
|
static inline int sicwrite_reg_possible(int reg)
|
|
{
|
|
int count = sizeof(sicwrite_regs) / sizeof(int);
|
|
int i = 0;
|
|
|
|
for (; i < count; i++)
|
|
if (sicwrite_regs[i] == reg)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static inline void sicwrite_write_reg(char *str)
|
|
{
|
|
int node, reg, val, res;
|
|
|
|
res = sscanf(str, "%d 0x%X 0x%X\n", &node, ®, &val);
|
|
if (res != 3) {
|
|
pr_err("Failed to write SIC register (invalid string).\n");
|
|
return;
|
|
} else if (!node_online(node)) {
|
|
pr_err("Failed to write SIC register (invalid node number).\n");
|
|
return;
|
|
} else if (!sicwrite_reg_possible(reg)) {
|
|
pr_err("Failed to write SIC register (invalid register).\n");
|
|
return;
|
|
}
|
|
|
|
sic_write_node_nbsr_reg(node, reg, val);
|
|
}
|
|
|
|
static ssize_t sicwrite_write(struct file *file, const char __user *buffer,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
char sicwrite_buffer[SICWRITE_STR_MAX_SIZE];
|
|
long ret;
|
|
|
|
memset(sicwrite_buffer, 0, sizeof(char) * SICWRITE_STR_MAX_SIZE);
|
|
|
|
if (count + 1 > SICWRITE_STR_MAX_SIZE) {
|
|
pr_err("Failed to write SIC register (too long string).\n");
|
|
ret = -EINVAL;
|
|
} else if (copy_from_user(sicwrite_buffer, buffer, count)) {
|
|
pr_err("Failed to write SIC register (kernel error).\n");
|
|
ret = -EFAULT;
|
|
} else {
|
|
sicwrite_write_reg(sicwrite_buffer);
|
|
ret = count;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int sicwrite_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int sicwrite_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, sicwrite_proc_show, NULL);
|
|
}
|
|
|
|
static const struct file_operations sicwrite_proc_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = sicwrite_proc_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.write = sicwrite_write,
|
|
.release = seq_release
|
|
};
|
|
|
|
static int __init init_procsic(void)
|
|
{
|
|
if (!HAS_MACHINE_L_SIC)
|
|
return 0;
|
|
|
|
sicregs_entry = proc_create(SICREGS_FILENAME, S_IRUGO,
|
|
NULL, &sicregs_proc_fops);
|
|
if (!sicregs_entry)
|
|
return -ENOMEM;
|
|
|
|
sicwrite_entry = proc_create(SICWRITE_FILENAME, S_IRUGO,
|
|
NULL, &sicwrite_proc_fops);
|
|
if (!sicwrite_entry)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit exit_procsic(void)
|
|
{
|
|
if (HAS_MACHINE_L_SIC) {
|
|
proc_remove(sicregs_entry);
|
|
proc_remove(sicwrite_entry);
|
|
}
|
|
}
|
|
|
|
module_init(init_procsic);
|
|
module_exit(exit_procsic);
|