mirror of
https://github.com/torvalds/linux.git
synced 2024-11-19 10:31:48 +00:00
eb3ae0aa38
Based on 1 normalized pattern(s): this program is free software you can redistribute it and or modify it under the terms of the gnu general public license version 2 only 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 extracted by the scancode license scanner the SPDX license identifier GPL-2.0-only has been chosen to replace the boilerplate/reference in 8 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Alexios Zavras <alexios.zavras@intel.com> Reviewed-by: Allison Randal <allison@lohutok.net> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190529141901.300923465@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
484 lines
11 KiB
C
484 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/**
|
|
* IBM Accelerator Family 'GenWQE'
|
|
*
|
|
* (C) Copyright IBM Corp. 2013
|
|
*
|
|
* Author: Frank Haverkamp <haver@linux.vnet.ibm.com>
|
|
* Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com>
|
|
* Author: Michael Jung <mijung@gmx.net>
|
|
* Author: Michael Ruettger <michael@ibmra.de>
|
|
*/
|
|
|
|
/*
|
|
* Debugfs interfaces for the GenWQE card. Help to debug potential
|
|
* problems. Dump internal chip state for debugging and failure
|
|
* determination.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
#include "card_base.h"
|
|
#include "card_ddcb.h"
|
|
|
|
static void dbg_uidn_show(struct seq_file *s, struct genwqe_reg *regs,
|
|
int entries)
|
|
{
|
|
unsigned int i;
|
|
u32 v_hi, v_lo;
|
|
|
|
for (i = 0; i < entries; i++) {
|
|
v_hi = (regs[i].val >> 32) & 0xffffffff;
|
|
v_lo = (regs[i].val) & 0xffffffff;
|
|
|
|
seq_printf(s, " 0x%08x 0x%08x 0x%08x 0x%08x EXT_ERR_REC\n",
|
|
regs[i].addr, regs[i].idx, v_hi, v_lo);
|
|
}
|
|
}
|
|
|
|
static int curr_dbg_uidn_show(struct seq_file *s, void *unused, int uid)
|
|
{
|
|
struct genwqe_dev *cd = s->private;
|
|
int entries;
|
|
struct genwqe_reg *regs;
|
|
|
|
entries = genwqe_ffdc_buff_size(cd, uid);
|
|
if (entries < 0)
|
|
return -EINVAL;
|
|
|
|
if (entries == 0)
|
|
return 0;
|
|
|
|
regs = kcalloc(entries, sizeof(*regs), GFP_KERNEL);
|
|
if (regs == NULL)
|
|
return -ENOMEM;
|
|
|
|
genwqe_stop_traps(cd); /* halt the traps while dumping data */
|
|
genwqe_ffdc_buff_read(cd, uid, regs, entries);
|
|
genwqe_start_traps(cd);
|
|
|
|
dbg_uidn_show(s, regs, entries);
|
|
kfree(regs);
|
|
return 0;
|
|
}
|
|
|
|
static int curr_dbg_uid0_show(struct seq_file *s, void *unused)
|
|
{
|
|
return curr_dbg_uidn_show(s, unused, 0);
|
|
}
|
|
|
|
DEFINE_SHOW_ATTRIBUTE(curr_dbg_uid0);
|
|
|
|
static int curr_dbg_uid1_show(struct seq_file *s, void *unused)
|
|
{
|
|
return curr_dbg_uidn_show(s, unused, 1);
|
|
}
|
|
|
|
DEFINE_SHOW_ATTRIBUTE(curr_dbg_uid1);
|
|
|
|
static int curr_dbg_uid2_show(struct seq_file *s, void *unused)
|
|
{
|
|
return curr_dbg_uidn_show(s, unused, 2);
|
|
}
|
|
|
|
DEFINE_SHOW_ATTRIBUTE(curr_dbg_uid2);
|
|
|
|
static int prev_dbg_uidn_show(struct seq_file *s, void *unused, int uid)
|
|
{
|
|
struct genwqe_dev *cd = s->private;
|
|
|
|
dbg_uidn_show(s, cd->ffdc[uid].regs, cd->ffdc[uid].entries);
|
|
return 0;
|
|
}
|
|
|
|
static int prev_dbg_uid0_show(struct seq_file *s, void *unused)
|
|
{
|
|
return prev_dbg_uidn_show(s, unused, 0);
|
|
}
|
|
|
|
DEFINE_SHOW_ATTRIBUTE(prev_dbg_uid0);
|
|
|
|
static int prev_dbg_uid1_show(struct seq_file *s, void *unused)
|
|
{
|
|
return prev_dbg_uidn_show(s, unused, 1);
|
|
}
|
|
|
|
DEFINE_SHOW_ATTRIBUTE(prev_dbg_uid1);
|
|
|
|
static int prev_dbg_uid2_show(struct seq_file *s, void *unused)
|
|
{
|
|
return prev_dbg_uidn_show(s, unused, 2);
|
|
}
|
|
|
|
DEFINE_SHOW_ATTRIBUTE(prev_dbg_uid2);
|
|
|
|
static int curr_regs_show(struct seq_file *s, void *unused)
|
|
{
|
|
struct genwqe_dev *cd = s->private;
|
|
unsigned int i;
|
|
struct genwqe_reg *regs;
|
|
|
|
regs = kcalloc(GENWQE_FFDC_REGS, sizeof(*regs), GFP_KERNEL);
|
|
if (regs == NULL)
|
|
return -ENOMEM;
|
|
|
|
genwqe_stop_traps(cd);
|
|
genwqe_read_ffdc_regs(cd, regs, GENWQE_FFDC_REGS, 1);
|
|
genwqe_start_traps(cd);
|
|
|
|
for (i = 0; i < GENWQE_FFDC_REGS; i++) {
|
|
if (regs[i].addr == 0xffffffff)
|
|
break; /* invalid entries */
|
|
|
|
if (regs[i].val == 0x0ull)
|
|
continue; /* do not print 0x0 FIRs */
|
|
|
|
seq_printf(s, " 0x%08x 0x%016llx\n",
|
|
regs[i].addr, regs[i].val);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_SHOW_ATTRIBUTE(curr_regs);
|
|
|
|
static int prev_regs_show(struct seq_file *s, void *unused)
|
|
{
|
|
struct genwqe_dev *cd = s->private;
|
|
unsigned int i;
|
|
struct genwqe_reg *regs = cd->ffdc[GENWQE_DBG_REGS].regs;
|
|
|
|
if (regs == NULL)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < GENWQE_FFDC_REGS; i++) {
|
|
if (regs[i].addr == 0xffffffff)
|
|
break; /* invalid entries */
|
|
|
|
if (regs[i].val == 0x0ull)
|
|
continue; /* do not print 0x0 FIRs */
|
|
|
|
seq_printf(s, " 0x%08x 0x%016llx\n",
|
|
regs[i].addr, regs[i].val);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_SHOW_ATTRIBUTE(prev_regs);
|
|
|
|
static int jtimer_show(struct seq_file *s, void *unused)
|
|
{
|
|
struct genwqe_dev *cd = s->private;
|
|
unsigned int vf_num;
|
|
u64 jtimer;
|
|
|
|
jtimer = genwqe_read_vreg(cd, IO_SLC_VF_APPJOB_TIMEOUT, 0);
|
|
seq_printf(s, " PF 0x%016llx %d msec\n", jtimer,
|
|
GENWQE_PF_JOBTIMEOUT_MSEC);
|
|
|
|
for (vf_num = 0; vf_num < cd->num_vfs; vf_num++) {
|
|
jtimer = genwqe_read_vreg(cd, IO_SLC_VF_APPJOB_TIMEOUT,
|
|
vf_num + 1);
|
|
seq_printf(s, " VF%-2d 0x%016llx %d msec\n", vf_num, jtimer,
|
|
cd->vf_jobtimeout_msec[vf_num]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_SHOW_ATTRIBUTE(jtimer);
|
|
|
|
static int queue_working_time_show(struct seq_file *s, void *unused)
|
|
{
|
|
struct genwqe_dev *cd = s->private;
|
|
unsigned int vf_num;
|
|
u64 t;
|
|
|
|
t = genwqe_read_vreg(cd, IO_SLC_VF_QUEUE_WTIME, 0);
|
|
seq_printf(s, " PF 0x%016llx\n", t);
|
|
|
|
for (vf_num = 0; vf_num < cd->num_vfs; vf_num++) {
|
|
t = genwqe_read_vreg(cd, IO_SLC_VF_QUEUE_WTIME, vf_num + 1);
|
|
seq_printf(s, " VF%-2d 0x%016llx\n", vf_num, t);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_SHOW_ATTRIBUTE(queue_working_time);
|
|
|
|
static int ddcb_info_show(struct seq_file *s, void *unused)
|
|
{
|
|
struct genwqe_dev *cd = s->private;
|
|
unsigned int i;
|
|
struct ddcb_queue *queue;
|
|
struct ddcb *pddcb;
|
|
|
|
queue = &cd->queue;
|
|
seq_puts(s, "DDCB QUEUE:\n");
|
|
seq_printf(s, " ddcb_max: %d\n"
|
|
" ddcb_daddr: %016llx - %016llx\n"
|
|
" ddcb_vaddr: %p\n"
|
|
" ddcbs_in_flight: %u\n"
|
|
" ddcbs_max_in_flight: %u\n"
|
|
" ddcbs_completed: %u\n"
|
|
" return_on_busy: %u\n"
|
|
" wait_on_busy: %u\n"
|
|
" irqs_processed: %u\n",
|
|
queue->ddcb_max, (long long)queue->ddcb_daddr,
|
|
(long long)queue->ddcb_daddr +
|
|
(queue->ddcb_max * DDCB_LENGTH),
|
|
queue->ddcb_vaddr, queue->ddcbs_in_flight,
|
|
queue->ddcbs_max_in_flight, queue->ddcbs_completed,
|
|
queue->return_on_busy, queue->wait_on_busy,
|
|
cd->irqs_processed);
|
|
|
|
/* Hardware State */
|
|
seq_printf(s, " 0x%08x 0x%016llx IO_QUEUE_CONFIG\n"
|
|
" 0x%08x 0x%016llx IO_QUEUE_STATUS\n"
|
|
" 0x%08x 0x%016llx IO_QUEUE_SEGMENT\n"
|
|
" 0x%08x 0x%016llx IO_QUEUE_INITSQN\n"
|
|
" 0x%08x 0x%016llx IO_QUEUE_WRAP\n"
|
|
" 0x%08x 0x%016llx IO_QUEUE_OFFSET\n"
|
|
" 0x%08x 0x%016llx IO_QUEUE_WTIME\n"
|
|
" 0x%08x 0x%016llx IO_QUEUE_ERRCNTS\n"
|
|
" 0x%08x 0x%016llx IO_QUEUE_LRW\n",
|
|
queue->IO_QUEUE_CONFIG,
|
|
__genwqe_readq(cd, queue->IO_QUEUE_CONFIG),
|
|
queue->IO_QUEUE_STATUS,
|
|
__genwqe_readq(cd, queue->IO_QUEUE_STATUS),
|
|
queue->IO_QUEUE_SEGMENT,
|
|
__genwqe_readq(cd, queue->IO_QUEUE_SEGMENT),
|
|
queue->IO_QUEUE_INITSQN,
|
|
__genwqe_readq(cd, queue->IO_QUEUE_INITSQN),
|
|
queue->IO_QUEUE_WRAP,
|
|
__genwqe_readq(cd, queue->IO_QUEUE_WRAP),
|
|
queue->IO_QUEUE_OFFSET,
|
|
__genwqe_readq(cd, queue->IO_QUEUE_OFFSET),
|
|
queue->IO_QUEUE_WTIME,
|
|
__genwqe_readq(cd, queue->IO_QUEUE_WTIME),
|
|
queue->IO_QUEUE_ERRCNTS,
|
|
__genwqe_readq(cd, queue->IO_QUEUE_ERRCNTS),
|
|
queue->IO_QUEUE_LRW,
|
|
__genwqe_readq(cd, queue->IO_QUEUE_LRW));
|
|
|
|
seq_printf(s, "DDCB list (ddcb_act=%d/ddcb_next=%d):\n",
|
|
queue->ddcb_act, queue->ddcb_next);
|
|
|
|
pddcb = queue->ddcb_vaddr;
|
|
for (i = 0; i < queue->ddcb_max; i++) {
|
|
seq_printf(s, " %-3d: RETC=%03x SEQ=%04x HSI/SHI=%02x/%02x ",
|
|
i, be16_to_cpu(pddcb->retc_16),
|
|
be16_to_cpu(pddcb->seqnum_16),
|
|
pddcb->hsi, pddcb->shi);
|
|
seq_printf(s, "PRIV=%06llx CMD=%02x\n",
|
|
be64_to_cpu(pddcb->priv_64), pddcb->cmd);
|
|
pddcb++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_SHOW_ATTRIBUTE(ddcb_info);
|
|
|
|
static int info_show(struct seq_file *s, void *unused)
|
|
{
|
|
struct genwqe_dev *cd = s->private;
|
|
u64 app_id, slu_id, bitstream = -1;
|
|
struct pci_dev *pci_dev = cd->pci_dev;
|
|
|
|
slu_id = __genwqe_readq(cd, IO_SLU_UNITCFG);
|
|
app_id = __genwqe_readq(cd, IO_APP_UNITCFG);
|
|
|
|
if (genwqe_is_privileged(cd))
|
|
bitstream = __genwqe_readq(cd, IO_SLU_BITSTREAM);
|
|
|
|
seq_printf(s, "%s driver version: %s\n"
|
|
" Device Name/Type: %s %s CardIdx: %d\n"
|
|
" SLU/APP Config : 0x%016llx/0x%016llx\n"
|
|
" Build Date : %u/%x/%u\n"
|
|
" Base Clock : %u MHz\n"
|
|
" Arch/SVN Release: %u/%llx\n"
|
|
" Bitstream : %llx\n",
|
|
GENWQE_DEVNAME, DRV_VERSION, dev_name(&pci_dev->dev),
|
|
genwqe_is_privileged(cd) ?
|
|
"Physical" : "Virtual or no SR-IOV",
|
|
cd->card_idx, slu_id, app_id,
|
|
(u16)((slu_id >> 12) & 0x0fLLU), /* month */
|
|
(u16)((slu_id >> 4) & 0xffLLU), /* day */
|
|
(u16)((slu_id >> 16) & 0x0fLLU) + 2010, /* year */
|
|
genwqe_base_clock_frequency(cd),
|
|
(u16)((slu_id >> 32) & 0xffLLU), slu_id >> 40,
|
|
bitstream);
|
|
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_SHOW_ATTRIBUTE(info);
|
|
|
|
int genwqe_init_debugfs(struct genwqe_dev *cd)
|
|
{
|
|
struct dentry *root;
|
|
struct dentry *file;
|
|
int ret;
|
|
char card_name[64];
|
|
char name[64];
|
|
unsigned int i;
|
|
|
|
sprintf(card_name, "%s%d_card", GENWQE_DEVNAME, cd->card_idx);
|
|
|
|
root = debugfs_create_dir(card_name, cd->debugfs_genwqe);
|
|
if (!root) {
|
|
ret = -ENOMEM;
|
|
goto err0;
|
|
}
|
|
|
|
/* non privileged interfaces are done here */
|
|
file = debugfs_create_file("ddcb_info", S_IRUGO, root, cd,
|
|
&ddcb_info_fops);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
file = debugfs_create_file("info", S_IRUGO, root, cd,
|
|
&info_fops);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
file = debugfs_create_x64("err_inject", 0666, root, &cd->err_inject);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
file = debugfs_create_u32("ddcb_software_timeout", 0666, root,
|
|
&cd->ddcb_software_timeout);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
file = debugfs_create_u32("kill_timeout", 0666, root,
|
|
&cd->kill_timeout);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
/* privileged interfaces follow here */
|
|
if (!genwqe_is_privileged(cd)) {
|
|
cd->debugfs_root = root;
|
|
return 0;
|
|
}
|
|
|
|
file = debugfs_create_file("curr_regs", S_IRUGO, root, cd,
|
|
&curr_regs_fops);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
file = debugfs_create_file("curr_dbg_uid0", S_IRUGO, root, cd,
|
|
&curr_dbg_uid0_fops);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
file = debugfs_create_file("curr_dbg_uid1", S_IRUGO, root, cd,
|
|
&curr_dbg_uid1_fops);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
file = debugfs_create_file("curr_dbg_uid2", S_IRUGO, root, cd,
|
|
&curr_dbg_uid2_fops);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
file = debugfs_create_file("prev_regs", S_IRUGO, root, cd,
|
|
&prev_regs_fops);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
file = debugfs_create_file("prev_dbg_uid0", S_IRUGO, root, cd,
|
|
&prev_dbg_uid0_fops);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
file = debugfs_create_file("prev_dbg_uid1", S_IRUGO, root, cd,
|
|
&prev_dbg_uid1_fops);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
file = debugfs_create_file("prev_dbg_uid2", S_IRUGO, root, cd,
|
|
&prev_dbg_uid2_fops);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
for (i = 0; i < GENWQE_MAX_VFS; i++) {
|
|
sprintf(name, "vf%u_jobtimeout_msec", i);
|
|
|
|
file = debugfs_create_u32(name, 0666, root,
|
|
&cd->vf_jobtimeout_msec[i]);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
}
|
|
|
|
file = debugfs_create_file("jobtimer", S_IRUGO, root, cd,
|
|
&jtimer_fops);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
file = debugfs_create_file("queue_working_time", S_IRUGO, root, cd,
|
|
&queue_working_time_fops);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
file = debugfs_create_u32("skip_recovery", 0666, root,
|
|
&cd->skip_recovery);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
file = debugfs_create_u32("use_platform_recovery", 0666, root,
|
|
&cd->use_platform_recovery);
|
|
if (!file) {
|
|
ret = -ENOMEM;
|
|
goto err1;
|
|
}
|
|
|
|
cd->debugfs_root = root;
|
|
return 0;
|
|
err1:
|
|
debugfs_remove_recursive(root);
|
|
err0:
|
|
return ret;
|
|
}
|
|
|
|
void genqwe_exit_debugfs(struct genwqe_dev *cd)
|
|
{
|
|
debugfs_remove_recursive(cd->debugfs_root);
|
|
}
|