1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * PowerNV SCOM bus debugfs interface
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright 2010 Benjamin Herrenschmidt, IBM Corp
6*4882a593Smuzhiyun * <benh@kernel.crashing.org>
7*4882a593Smuzhiyun * and David Gibson, IBM Corporation.
8*4882a593Smuzhiyun * Copyright 2013 IBM Corp.
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/kernel.h>
12*4882a593Smuzhiyun #include <linux/of.h>
13*4882a593Smuzhiyun #include <linux/bug.h>
14*4882a593Smuzhiyun #include <linux/gfp.h>
15*4882a593Smuzhiyun #include <linux/slab.h>
16*4882a593Smuzhiyun #include <linux/uaccess.h>
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun #include <asm/machdep.h>
19*4882a593Smuzhiyun #include <asm/firmware.h>
20*4882a593Smuzhiyun #include <asm/opal.h>
21*4882a593Smuzhiyun #include <asm/debugfs.h>
22*4882a593Smuzhiyun #include <asm/prom.h>
23*4882a593Smuzhiyun
opal_scom_unmangle(u64 addr)24*4882a593Smuzhiyun static u64 opal_scom_unmangle(u64 addr)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun u64 tmp;
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun /*
29*4882a593Smuzhiyun * XSCOM addresses use the top nibble to set indirect mode and
30*4882a593Smuzhiyun * its form. Bits 4-11 are always 0.
31*4882a593Smuzhiyun *
32*4882a593Smuzhiyun * Because the debugfs interface uses signed offsets and shifts
33*4882a593Smuzhiyun * the address left by 3, we basically cannot use the top 4 bits
34*4882a593Smuzhiyun * of the 64-bit address, and thus cannot use the indirect bit.
35*4882a593Smuzhiyun *
36*4882a593Smuzhiyun * To deal with that, we support the indirect bits being in
37*4882a593Smuzhiyun * bits 4-7 (IBM notation) instead of bit 0-3 in this API, we
38*4882a593Smuzhiyun * do the conversion here.
39*4882a593Smuzhiyun *
40*4882a593Smuzhiyun * For in-kernel use, we don't need to do this mangling. In
41*4882a593Smuzhiyun * kernel won't have bits 4-7 set.
42*4882a593Smuzhiyun *
43*4882a593Smuzhiyun * So:
44*4882a593Smuzhiyun * debugfs will always set 0-3 = 0 and clear 4-7
45*4882a593Smuzhiyun * kernel will always clear 0-3 = 0 and set 4-7
46*4882a593Smuzhiyun */
47*4882a593Smuzhiyun tmp = addr;
48*4882a593Smuzhiyun tmp &= 0x0f00000000000000;
49*4882a593Smuzhiyun addr &= 0xf0ffffffffffffff;
50*4882a593Smuzhiyun addr |= tmp << 4;
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun return addr;
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun
opal_scom_read(uint32_t chip,uint64_t addr,u64 reg,u64 * value)55*4882a593Smuzhiyun static int opal_scom_read(uint32_t chip, uint64_t addr, u64 reg, u64 *value)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun int64_t rc;
58*4882a593Smuzhiyun __be64 v;
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun reg = opal_scom_unmangle(addr + reg);
61*4882a593Smuzhiyun rc = opal_xscom_read(chip, reg, (__be64 *)__pa(&v));
62*4882a593Smuzhiyun if (rc) {
63*4882a593Smuzhiyun *value = 0xfffffffffffffffful;
64*4882a593Smuzhiyun return -EIO;
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun *value = be64_to_cpu(v);
67*4882a593Smuzhiyun return 0;
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun
opal_scom_write(uint32_t chip,uint64_t addr,u64 reg,u64 value)70*4882a593Smuzhiyun static int opal_scom_write(uint32_t chip, uint64_t addr, u64 reg, u64 value)
71*4882a593Smuzhiyun {
72*4882a593Smuzhiyun int64_t rc;
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun reg = opal_scom_unmangle(addr + reg);
75*4882a593Smuzhiyun rc = opal_xscom_write(chip, reg, value);
76*4882a593Smuzhiyun if (rc)
77*4882a593Smuzhiyun return -EIO;
78*4882a593Smuzhiyun return 0;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun struct scom_debug_entry {
82*4882a593Smuzhiyun u32 chip;
83*4882a593Smuzhiyun struct debugfs_blob_wrapper path;
84*4882a593Smuzhiyun char name[16];
85*4882a593Smuzhiyun };
86*4882a593Smuzhiyun
scom_debug_read(struct file * filp,char __user * ubuf,size_t count,loff_t * ppos)87*4882a593Smuzhiyun static ssize_t scom_debug_read(struct file *filp, char __user *ubuf,
88*4882a593Smuzhiyun size_t count, loff_t *ppos)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun struct scom_debug_entry *ent = filp->private_data;
91*4882a593Smuzhiyun u64 __user *ubuf64 = (u64 __user *)ubuf;
92*4882a593Smuzhiyun loff_t off = *ppos;
93*4882a593Smuzhiyun ssize_t done = 0;
94*4882a593Smuzhiyun u64 reg, reg_base, reg_cnt, val;
95*4882a593Smuzhiyun int rc;
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun if (off < 0 || (off & 7) || (count & 7))
98*4882a593Smuzhiyun return -EINVAL;
99*4882a593Smuzhiyun reg_base = off >> 3;
100*4882a593Smuzhiyun reg_cnt = count >> 3;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun for (reg = 0; reg < reg_cnt; reg++) {
103*4882a593Smuzhiyun rc = opal_scom_read(ent->chip, reg_base, reg, &val);
104*4882a593Smuzhiyun if (!rc)
105*4882a593Smuzhiyun rc = put_user(val, ubuf64);
106*4882a593Smuzhiyun if (rc) {
107*4882a593Smuzhiyun if (!done)
108*4882a593Smuzhiyun done = rc;
109*4882a593Smuzhiyun break;
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun ubuf64++;
112*4882a593Smuzhiyun *ppos += 8;
113*4882a593Smuzhiyun done += 8;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun return done;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun
scom_debug_write(struct file * filp,const char __user * ubuf,size_t count,loff_t * ppos)118*4882a593Smuzhiyun static ssize_t scom_debug_write(struct file *filp, const char __user *ubuf,
119*4882a593Smuzhiyun size_t count, loff_t *ppos)
120*4882a593Smuzhiyun {
121*4882a593Smuzhiyun struct scom_debug_entry *ent = filp->private_data;
122*4882a593Smuzhiyun u64 __user *ubuf64 = (u64 __user *)ubuf;
123*4882a593Smuzhiyun loff_t off = *ppos;
124*4882a593Smuzhiyun ssize_t done = 0;
125*4882a593Smuzhiyun u64 reg, reg_base, reg_cnt, val;
126*4882a593Smuzhiyun int rc;
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun if (off < 0 || (off & 7) || (count & 7))
129*4882a593Smuzhiyun return -EINVAL;
130*4882a593Smuzhiyun reg_base = off >> 3;
131*4882a593Smuzhiyun reg_cnt = count >> 3;
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun for (reg = 0; reg < reg_cnt; reg++) {
134*4882a593Smuzhiyun rc = get_user(val, ubuf64);
135*4882a593Smuzhiyun if (!rc)
136*4882a593Smuzhiyun rc = opal_scom_write(ent->chip, reg_base, reg, val);
137*4882a593Smuzhiyun if (rc) {
138*4882a593Smuzhiyun if (!done)
139*4882a593Smuzhiyun done = rc;
140*4882a593Smuzhiyun break;
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun ubuf64++;
143*4882a593Smuzhiyun done += 8;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun return done;
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun static const struct file_operations scom_debug_fops = {
149*4882a593Smuzhiyun .read = scom_debug_read,
150*4882a593Smuzhiyun .write = scom_debug_write,
151*4882a593Smuzhiyun .open = simple_open,
152*4882a593Smuzhiyun .llseek = default_llseek,
153*4882a593Smuzhiyun };
154*4882a593Smuzhiyun
scom_debug_init_one(struct dentry * root,struct device_node * dn,int chip)155*4882a593Smuzhiyun static int scom_debug_init_one(struct dentry *root, struct device_node *dn,
156*4882a593Smuzhiyun int chip)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun struct scom_debug_entry *ent;
159*4882a593Smuzhiyun struct dentry *dir;
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun ent = kzalloc(sizeof(*ent), GFP_KERNEL);
162*4882a593Smuzhiyun if (!ent)
163*4882a593Smuzhiyun return -ENOMEM;
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun ent->chip = chip;
166*4882a593Smuzhiyun snprintf(ent->name, 16, "%08x", chip);
167*4882a593Smuzhiyun ent->path.data = (void *)kasprintf(GFP_KERNEL, "%pOF", dn);
168*4882a593Smuzhiyun ent->path.size = strlen((char *)ent->path.data);
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun dir = debugfs_create_dir(ent->name, root);
171*4882a593Smuzhiyun if (!dir) {
172*4882a593Smuzhiyun kfree(ent->path.data);
173*4882a593Smuzhiyun kfree(ent);
174*4882a593Smuzhiyun return -1;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun debugfs_create_blob("devspec", 0400, dir, &ent->path);
178*4882a593Smuzhiyun debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops);
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun return 0;
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun
scom_debug_init(void)183*4882a593Smuzhiyun static int scom_debug_init(void)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun struct device_node *dn;
186*4882a593Smuzhiyun struct dentry *root;
187*4882a593Smuzhiyun int chip, rc;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun if (!firmware_has_feature(FW_FEATURE_OPAL))
190*4882a593Smuzhiyun return 0;
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun root = debugfs_create_dir("scom", powerpc_debugfs_root);
193*4882a593Smuzhiyun if (!root)
194*4882a593Smuzhiyun return -1;
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun rc = 0;
197*4882a593Smuzhiyun for_each_node_with_property(dn, "scom-controller") {
198*4882a593Smuzhiyun chip = of_get_ibm_chip_id(dn);
199*4882a593Smuzhiyun WARN_ON(chip == -1);
200*4882a593Smuzhiyun rc |= scom_debug_init_one(root, dn, chip);
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun return rc;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun device_initcall(scom_debug_init);
206