18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright IBM Corp. 2012,2015
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Author(s):
68c2ecf20Sopenharmony_ci *    Jan Glauber <jang@linux.vnet.ibm.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "zpci"
108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
148c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
158c2ecf20Sopenharmony_ci#include <linux/export.h>
168c2ecf20Sopenharmony_ci#include <linux/pci.h>
178c2ecf20Sopenharmony_ci#include <asm/debug.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <asm/pci_dma.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic struct dentry *debugfs_root;
228c2ecf20Sopenharmony_cidebug_info_t *pci_debug_msg_id;
238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_debug_msg_id);
248c2ecf20Sopenharmony_cidebug_info_t *pci_debug_err_id;
258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_debug_err_id);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic char *pci_common_names[] = {
288c2ecf20Sopenharmony_ci	"Load operations",
298c2ecf20Sopenharmony_ci	"Store operations",
308c2ecf20Sopenharmony_ci	"Store block operations",
318c2ecf20Sopenharmony_ci	"Refresh operations",
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic char *pci_fmt0_names[] = {
358c2ecf20Sopenharmony_ci	"DMA read bytes",
368c2ecf20Sopenharmony_ci	"DMA write bytes",
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic char *pci_fmt1_names[] = {
408c2ecf20Sopenharmony_ci	"Received bytes",
418c2ecf20Sopenharmony_ci	"Received packets",
428c2ecf20Sopenharmony_ci	"Transmitted bytes",
438c2ecf20Sopenharmony_ci	"Transmitted packets",
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic char *pci_fmt2_names[] = {
478c2ecf20Sopenharmony_ci	"Consumed work units",
488c2ecf20Sopenharmony_ci	"Maximum work units",
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic char *pci_fmt3_names[] = {
528c2ecf20Sopenharmony_ci	"Transmitted bytes",
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic char *pci_sw_names[] = {
568c2ecf20Sopenharmony_ci	"Allocated pages",
578c2ecf20Sopenharmony_ci	"Mapped pages",
588c2ecf20Sopenharmony_ci	"Unmapped pages",
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic void pci_fmb_show(struct seq_file *m, char *name[], int length,
628c2ecf20Sopenharmony_ci			 u64 *data)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	int i;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	for (i = 0; i < length; i++, data++)
678c2ecf20Sopenharmony_ci		seq_printf(m, "%26s:\t%llu\n", name[i], *data);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic void pci_sw_counter_show(struct seq_file *m)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct zpci_dev *zdev = m->private;
738c2ecf20Sopenharmony_ci	atomic64_t *counter = &zdev->allocated_pages;
748c2ecf20Sopenharmony_ci	int i;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pci_sw_names); i++, counter++)
778c2ecf20Sopenharmony_ci		seq_printf(m, "%26s:\t%llu\n", pci_sw_names[i],
788c2ecf20Sopenharmony_ci			   atomic64_read(counter));
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic int pci_perf_show(struct seq_file *m, void *v)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct zpci_dev *zdev = m->private;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (!zdev)
868c2ecf20Sopenharmony_ci		return 0;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	mutex_lock(&zdev->lock);
898c2ecf20Sopenharmony_ci	if (!zdev->fmb) {
908c2ecf20Sopenharmony_ci		mutex_unlock(&zdev->lock);
918c2ecf20Sopenharmony_ci		seq_puts(m, "FMB statistics disabled\n");
928c2ecf20Sopenharmony_ci		return 0;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/* header */
968c2ecf20Sopenharmony_ci	seq_printf(m, "Update interval: %u ms\n", zdev->fmb_update);
978c2ecf20Sopenharmony_ci	seq_printf(m, "Samples: %u\n", zdev->fmb->samples);
988c2ecf20Sopenharmony_ci	seq_printf(m, "Last update TOD: %Lx\n", zdev->fmb->last_update);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	pci_fmb_show(m, pci_common_names, ARRAY_SIZE(pci_common_names),
1018c2ecf20Sopenharmony_ci		     &zdev->fmb->ld_ops);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	switch (zdev->fmb->format) {
1048c2ecf20Sopenharmony_ci	case 0:
1058c2ecf20Sopenharmony_ci		if (!(zdev->fmb->fmt_ind & ZPCI_FMB_DMA_COUNTER_VALID))
1068c2ecf20Sopenharmony_ci			break;
1078c2ecf20Sopenharmony_ci		pci_fmb_show(m, pci_fmt0_names, ARRAY_SIZE(pci_fmt0_names),
1088c2ecf20Sopenharmony_ci			     &zdev->fmb->fmt0.dma_rbytes);
1098c2ecf20Sopenharmony_ci		break;
1108c2ecf20Sopenharmony_ci	case 1:
1118c2ecf20Sopenharmony_ci		pci_fmb_show(m, pci_fmt1_names, ARRAY_SIZE(pci_fmt1_names),
1128c2ecf20Sopenharmony_ci			     &zdev->fmb->fmt1.rx_bytes);
1138c2ecf20Sopenharmony_ci		break;
1148c2ecf20Sopenharmony_ci	case 2:
1158c2ecf20Sopenharmony_ci		pci_fmb_show(m, pci_fmt2_names, ARRAY_SIZE(pci_fmt2_names),
1168c2ecf20Sopenharmony_ci			     &zdev->fmb->fmt2.consumed_work_units);
1178c2ecf20Sopenharmony_ci		break;
1188c2ecf20Sopenharmony_ci	case 3:
1198c2ecf20Sopenharmony_ci		pci_fmb_show(m, pci_fmt3_names, ARRAY_SIZE(pci_fmt3_names),
1208c2ecf20Sopenharmony_ci			     &zdev->fmb->fmt3.tx_bytes);
1218c2ecf20Sopenharmony_ci		break;
1228c2ecf20Sopenharmony_ci	default:
1238c2ecf20Sopenharmony_ci		seq_puts(m, "Unknown format\n");
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	pci_sw_counter_show(m);
1278c2ecf20Sopenharmony_ci	mutex_unlock(&zdev->lock);
1288c2ecf20Sopenharmony_ci	return 0;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic ssize_t pci_perf_seq_write(struct file *file, const char __user *ubuf,
1328c2ecf20Sopenharmony_ci				  size_t count, loff_t *off)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct zpci_dev *zdev = ((struct seq_file *) file->private_data)->private;
1358c2ecf20Sopenharmony_ci	unsigned long val;
1368c2ecf20Sopenharmony_ci	int rc;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (!zdev)
1398c2ecf20Sopenharmony_ci		return 0;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	rc = kstrtoul_from_user(ubuf, count, 10, &val);
1428c2ecf20Sopenharmony_ci	if (rc)
1438c2ecf20Sopenharmony_ci		return rc;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	mutex_lock(&zdev->lock);
1468c2ecf20Sopenharmony_ci	switch (val) {
1478c2ecf20Sopenharmony_ci	case 0:
1488c2ecf20Sopenharmony_ci		rc = zpci_fmb_disable_device(zdev);
1498c2ecf20Sopenharmony_ci		break;
1508c2ecf20Sopenharmony_ci	case 1:
1518c2ecf20Sopenharmony_ci		rc = zpci_fmb_enable_device(zdev);
1528c2ecf20Sopenharmony_ci		break;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci	mutex_unlock(&zdev->lock);
1558c2ecf20Sopenharmony_ci	return rc ? rc : count;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int pci_perf_seq_open(struct inode *inode, struct file *filp)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	return single_open(filp, pci_perf_show,
1618c2ecf20Sopenharmony_ci			   file_inode(filp)->i_private);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic const struct file_operations debugfs_pci_perf_fops = {
1658c2ecf20Sopenharmony_ci	.open	 = pci_perf_seq_open,
1668c2ecf20Sopenharmony_ci	.read	 = seq_read,
1678c2ecf20Sopenharmony_ci	.write	 = pci_perf_seq_write,
1688c2ecf20Sopenharmony_ci	.llseek  = seq_lseek,
1698c2ecf20Sopenharmony_ci	.release = single_release,
1708c2ecf20Sopenharmony_ci};
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_civoid zpci_debug_init_device(struct zpci_dev *zdev, const char *name)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	zdev->debugfs_dev = debugfs_create_dir(name, debugfs_root);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	debugfs_create_file("statistics", S_IFREG | S_IRUGO | S_IWUSR,
1778c2ecf20Sopenharmony_ci			    zdev->debugfs_dev, zdev, &debugfs_pci_perf_fops);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_civoid zpci_debug_exit_device(struct zpci_dev *zdev)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	debugfs_remove_recursive(zdev->debugfs_dev);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ciint __init zpci_debug_init(void)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	/* event trace buffer */
1888c2ecf20Sopenharmony_ci	pci_debug_msg_id = debug_register("pci_msg", 8, 1, 8 * sizeof(long));
1898c2ecf20Sopenharmony_ci	if (!pci_debug_msg_id)
1908c2ecf20Sopenharmony_ci		return -EINVAL;
1918c2ecf20Sopenharmony_ci	debug_register_view(pci_debug_msg_id, &debug_sprintf_view);
1928c2ecf20Sopenharmony_ci	debug_set_level(pci_debug_msg_id, 3);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	/* error log */
1958c2ecf20Sopenharmony_ci	pci_debug_err_id = debug_register("pci_error", 2, 1, 16);
1968c2ecf20Sopenharmony_ci	if (!pci_debug_err_id)
1978c2ecf20Sopenharmony_ci		return -EINVAL;
1988c2ecf20Sopenharmony_ci	debug_register_view(pci_debug_err_id, &debug_hex_ascii_view);
1998c2ecf20Sopenharmony_ci	debug_set_level(pci_debug_err_id, 6);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	debugfs_root = debugfs_create_dir("pci", NULL);
2028c2ecf20Sopenharmony_ci	return 0;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_civoid zpci_debug_exit(void)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	debug_unregister(pci_debug_msg_id);
2088c2ecf20Sopenharmony_ci	debug_unregister(pci_debug_err_id);
2098c2ecf20Sopenharmony_ci	debugfs_remove(debugfs_root);
2108c2ecf20Sopenharmony_ci}
211