1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * PowerNV SCOM bus debugfs interface
5 * Copyright 2010 Benjamin Herrenschmidt, IBM Corp
6 * <benh@kernel.crashing.org>
7 * and David Gibson, IBM Corporation.
8 * Copyright 2013 IBM Corp.
11 #include <linux/kernel.h>
13 #include <linux/bug.h>
14 #include <linux/gfp.h>
15 #include <linux/slab.h>
16 #include <linux/uaccess.h>
17 #include <linux/debugfs.h>
19 #include <asm/machdep.h>
20 #include <asm/firmware.h>
24 static u64 opal_scom_unmangle(u64 addr)
29 * XSCOM addresses use the top nibble to set indirect mode and
30 * its form. Bits 4-11 are always 0.
32 * Because the debugfs interface uses signed offsets and shifts
33 * the address left by 3, we basically cannot use the top 4 bits
34 * of the 64-bit address, and thus cannot use the indirect bit.
36 * To deal with that, we support the indirect bits being in
37 * bits 4-7 (IBM notation) instead of bit 0-3 in this API, we
38 * do the conversion here.
40 * For in-kernel use, we don't need to do this mangling. In
41 * kernel won't have bits 4-7 set.
44 * debugfs will always set 0-3 = 0 and clear 4-7
45 * kernel will always clear 0-3 = 0 and set 4-7
48 tmp &= 0x0f00000000000000;
49 addr &= 0xf0ffffffffffffff;
55 static int opal_scom_read(uint32_t chip, uint64_t addr, u64 reg, u64 *value)
60 reg = opal_scom_unmangle(addr + reg);
61 rc = opal_xscom_read(chip, reg, (__be64 *)__pa(&v));
63 *value = 0xfffffffffffffffful;
66 *value = be64_to_cpu(v);
70 static int opal_scom_write(uint32_t chip, uint64_t addr, u64 reg, u64 value)
74 reg = opal_scom_unmangle(addr + reg);
75 rc = opal_xscom_write(chip, reg, value);
81 struct scom_debug_entry {
83 struct debugfs_blob_wrapper path;
87 static ssize_t scom_debug_read(struct file *filp, char __user *ubuf,
88 size_t count, loff_t *ppos)
90 struct scom_debug_entry *ent = filp->private_data;
91 u64 __user *ubuf64 = (u64 __user *)ubuf;
94 u64 reg, reg_base, reg_cnt, val;
97 if (off < 0 || (off & 7) || (count & 7))
100 reg_cnt = count >> 3;
102 for (reg = 0; reg < reg_cnt; reg++) {
103 rc = opal_scom_read(ent->chip, reg_base, reg, &val);
105 rc = put_user(val, ubuf64);
118 static ssize_t scom_debug_write(struct file *filp, const char __user *ubuf,
119 size_t count, loff_t *ppos)
121 struct scom_debug_entry *ent = filp->private_data;
122 u64 __user *ubuf64 = (u64 __user *)ubuf;
125 u64 reg, reg_base, reg_cnt, val;
128 if (off < 0 || (off & 7) || (count & 7))
131 reg_cnt = count >> 3;
133 for (reg = 0; reg < reg_cnt; reg++) {
134 rc = get_user(val, ubuf64);
136 rc = opal_scom_write(ent->chip, reg_base, reg, val);
148 static const struct file_operations scom_debug_fops = {
149 .read = scom_debug_read,
150 .write = scom_debug_write,
152 .llseek = default_llseek,
155 static int scom_debug_init_one(struct dentry *root, struct device_node *dn,
158 struct scom_debug_entry *ent;
161 ent = kzalloc(sizeof(*ent), GFP_KERNEL);
166 snprintf(ent->name, 16, "%08x", chip);
167 ent->path.data = (void *)kasprintf(GFP_KERNEL, "%pOF", dn);
168 if (!ent->path.data) {
173 ent->path.size = strlen((char *)ent->path.data);
175 dir = debugfs_create_dir(ent->name, root);
177 kfree(ent->path.data);
182 debugfs_create_blob("devspec", 0400, dir, &ent->path);
183 debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops);
188 static int scom_debug_init(void)
190 struct device_node *dn;
194 if (!firmware_has_feature(FW_FEATURE_OPAL))
197 root = debugfs_create_dir("scom", arch_debugfs_dir);
202 for_each_node_with_property(dn, "scom-controller") {
203 chip = of_get_ibm_chip_id(dn);
205 rc |= scom_debug_init_one(root, dn, chip);
210 device_initcall(scom_debug_init);