12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28adae0c8SBenjamin Herrenschmidt /*
3bfd2f0d4SAndrew Donnellan * PowerNV SCOM bus debugfs interface
48adae0c8SBenjamin Herrenschmidt *
5bfd2f0d4SAndrew Donnellan * Copyright 2010 Benjamin Herrenschmidt, IBM Corp
6bfd2f0d4SAndrew Donnellan * <benh@kernel.crashing.org>
7bfd2f0d4SAndrew Donnellan * and David Gibson, IBM Corporation.
88adae0c8SBenjamin Herrenschmidt * Copyright 2013 IBM Corp.
98adae0c8SBenjamin Herrenschmidt */
108adae0c8SBenjamin Herrenschmidt
118adae0c8SBenjamin Herrenschmidt #include <linux/kernel.h>
128adae0c8SBenjamin Herrenschmidt #include <linux/of.h>
138adae0c8SBenjamin Herrenschmidt #include <linux/bug.h>
148adae0c8SBenjamin Herrenschmidt #include <linux/gfp.h>
158adae0c8SBenjamin Herrenschmidt #include <linux/slab.h>
16bfd2f0d4SAndrew Donnellan #include <linux/uaccess.h>
17dbf77fedSAneesh Kumar K.V #include <linux/debugfs.h>
188adae0c8SBenjamin Herrenschmidt
198adae0c8SBenjamin Herrenschmidt #include <asm/machdep.h>
208adae0c8SBenjamin Herrenschmidt #include <asm/firmware.h>
218adae0c8SBenjamin Herrenschmidt #include <asm/opal.h>
22bfd2f0d4SAndrew Donnellan #include <asm/prom.h>
238adae0c8SBenjamin Herrenschmidt
opal_scom_unmangle(u64 addr)24e0cf9576SBenjamin Herrenschmidt static u64 opal_scom_unmangle(u64 addr)
2580546ac5SBenjamin Herrenschmidt {
26517c2757SMichael Neuling u64 tmp;
27517c2757SMichael Neuling
2880546ac5SBenjamin Herrenschmidt /*
29517c2757SMichael Neuling * XSCOM addresses use the top nibble to set indirect mode and
30517c2757SMichael Neuling * its form. Bits 4-11 are always 0.
3180546ac5SBenjamin Herrenschmidt *
3280546ac5SBenjamin Herrenschmidt * Because the debugfs interface uses signed offsets and shifts
3380546ac5SBenjamin Herrenschmidt * the address left by 3, we basically cannot use the top 4 bits
3480546ac5SBenjamin Herrenschmidt * of the 64-bit address, and thus cannot use the indirect bit.
3580546ac5SBenjamin Herrenschmidt *
36517c2757SMichael Neuling * To deal with that, we support the indirect bits being in
37517c2757SMichael Neuling * bits 4-7 (IBM notation) instead of bit 0-3 in this API, we
38517c2757SMichael Neuling * do the conversion here.
3980546ac5SBenjamin Herrenschmidt *
40517c2757SMichael Neuling * For in-kernel use, we don't need to do this mangling. In
41517c2757SMichael Neuling * kernel won't have bits 4-7 set.
42e0cf9576SBenjamin Herrenschmidt *
43517c2757SMichael Neuling * So:
44517c2757SMichael Neuling * debugfs will always set 0-3 = 0 and clear 4-7
45517c2757SMichael Neuling * kernel will always clear 0-3 = 0 and set 4-7
4680546ac5SBenjamin Herrenschmidt */
47517c2757SMichael Neuling tmp = addr;
48517c2757SMichael Neuling tmp &= 0x0f00000000000000;
49517c2757SMichael Neuling addr &= 0xf0ffffffffffffff;
50517c2757SMichael Neuling addr |= tmp << 4;
51517c2757SMichael Neuling
52e0cf9576SBenjamin Herrenschmidt return addr;
5380546ac5SBenjamin Herrenschmidt }
5480546ac5SBenjamin Herrenschmidt
opal_scom_read(uint32_t chip,uint64_t addr,u64 reg,u64 * value)55bfd2f0d4SAndrew Donnellan static int opal_scom_read(uint32_t chip, uint64_t addr, u64 reg, u64 *value)
568adae0c8SBenjamin Herrenschmidt {
578adae0c8SBenjamin Herrenschmidt int64_t rc;
5801a9dbccSAnton Blanchard __be64 v;
598adae0c8SBenjamin Herrenschmidt
60bfd2f0d4SAndrew Donnellan reg = opal_scom_unmangle(addr + reg);
61bfd2f0d4SAndrew Donnellan rc = opal_xscom_read(chip, reg, (__be64 *)__pa(&v));
62bfd2f0d4SAndrew Donnellan if (rc) {
63bfd2f0d4SAndrew Donnellan *value = 0xfffffffffffffffful;
64bfd2f0d4SAndrew Donnellan return -EIO;
65bfd2f0d4SAndrew Donnellan }
6601a9dbccSAnton Blanchard *value = be64_to_cpu(v);
678adae0c8SBenjamin Herrenschmidt return 0;
688adae0c8SBenjamin Herrenschmidt }
69bfd2f0d4SAndrew Donnellan
opal_scom_write(uint32_t chip,uint64_t addr,u64 reg,u64 value)70bfd2f0d4SAndrew Donnellan static int opal_scom_write(uint32_t chip, uint64_t addr, u64 reg, u64 value)
71bfd2f0d4SAndrew Donnellan {
72bfd2f0d4SAndrew Donnellan int64_t rc;
73bfd2f0d4SAndrew Donnellan
74bfd2f0d4SAndrew Donnellan reg = opal_scom_unmangle(addr + reg);
75bfd2f0d4SAndrew Donnellan rc = opal_xscom_write(chip, reg, value);
76bfd2f0d4SAndrew Donnellan if (rc)
77bfd2f0d4SAndrew Donnellan return -EIO;
78bfd2f0d4SAndrew Donnellan return 0;
79bfd2f0d4SAndrew Donnellan }
80bfd2f0d4SAndrew Donnellan
81bfd2f0d4SAndrew Donnellan struct scom_debug_entry {
82bfd2f0d4SAndrew Donnellan u32 chip;
83bfd2f0d4SAndrew Donnellan struct debugfs_blob_wrapper path;
84bfd2f0d4SAndrew Donnellan char name[16];
85bfd2f0d4SAndrew Donnellan };
86bfd2f0d4SAndrew Donnellan
scom_debug_read(struct file * filp,char __user * ubuf,size_t count,loff_t * ppos)87bfd2f0d4SAndrew Donnellan static ssize_t scom_debug_read(struct file *filp, char __user *ubuf,
88bfd2f0d4SAndrew Donnellan size_t count, loff_t *ppos)
89bfd2f0d4SAndrew Donnellan {
90bfd2f0d4SAndrew Donnellan struct scom_debug_entry *ent = filp->private_data;
91bfd2f0d4SAndrew Donnellan u64 __user *ubuf64 = (u64 __user *)ubuf;
92bfd2f0d4SAndrew Donnellan loff_t off = *ppos;
93bfd2f0d4SAndrew Donnellan ssize_t done = 0;
94bfd2f0d4SAndrew Donnellan u64 reg, reg_base, reg_cnt, val;
95bfd2f0d4SAndrew Donnellan int rc;
96bfd2f0d4SAndrew Donnellan
97bfd2f0d4SAndrew Donnellan if (off < 0 || (off & 7) || (count & 7))
98bfd2f0d4SAndrew Donnellan return -EINVAL;
99bfd2f0d4SAndrew Donnellan reg_base = off >> 3;
100bfd2f0d4SAndrew Donnellan reg_cnt = count >> 3;
101bfd2f0d4SAndrew Donnellan
102bfd2f0d4SAndrew Donnellan for (reg = 0; reg < reg_cnt; reg++) {
103bfd2f0d4SAndrew Donnellan rc = opal_scom_read(ent->chip, reg_base, reg, &val);
104bfd2f0d4SAndrew Donnellan if (!rc)
105bfd2f0d4SAndrew Donnellan rc = put_user(val, ubuf64);
106bfd2f0d4SAndrew Donnellan if (rc) {
107bfd2f0d4SAndrew Donnellan if (!done)
108bfd2f0d4SAndrew Donnellan done = rc;
109bfd2f0d4SAndrew Donnellan break;
110bfd2f0d4SAndrew Donnellan }
111bfd2f0d4SAndrew Donnellan ubuf64++;
112bfd2f0d4SAndrew Donnellan *ppos += 8;
113bfd2f0d4SAndrew Donnellan done += 8;
114bfd2f0d4SAndrew Donnellan }
115bfd2f0d4SAndrew Donnellan return done;
116bfd2f0d4SAndrew Donnellan }
117bfd2f0d4SAndrew Donnellan
scom_debug_write(struct file * filp,const char __user * ubuf,size_t count,loff_t * ppos)118bfd2f0d4SAndrew Donnellan static ssize_t scom_debug_write(struct file *filp, const char __user *ubuf,
119bfd2f0d4SAndrew Donnellan size_t count, loff_t *ppos)
120bfd2f0d4SAndrew Donnellan {
121bfd2f0d4SAndrew Donnellan struct scom_debug_entry *ent = filp->private_data;
122bfd2f0d4SAndrew Donnellan u64 __user *ubuf64 = (u64 __user *)ubuf;
123bfd2f0d4SAndrew Donnellan loff_t off = *ppos;
124bfd2f0d4SAndrew Donnellan ssize_t done = 0;
125bfd2f0d4SAndrew Donnellan u64 reg, reg_base, reg_cnt, val;
126bfd2f0d4SAndrew Donnellan int rc;
127bfd2f0d4SAndrew Donnellan
128bfd2f0d4SAndrew Donnellan if (off < 0 || (off & 7) || (count & 7))
129bfd2f0d4SAndrew Donnellan return -EINVAL;
130bfd2f0d4SAndrew Donnellan reg_base = off >> 3;
131bfd2f0d4SAndrew Donnellan reg_cnt = count >> 3;
132bfd2f0d4SAndrew Donnellan
133bfd2f0d4SAndrew Donnellan for (reg = 0; reg < reg_cnt; reg++) {
134bfd2f0d4SAndrew Donnellan rc = get_user(val, ubuf64);
135bfd2f0d4SAndrew Donnellan if (!rc)
136bfd2f0d4SAndrew Donnellan rc = opal_scom_write(ent->chip, reg_base, reg, val);
137bfd2f0d4SAndrew Donnellan if (rc) {
138bfd2f0d4SAndrew Donnellan if (!done)
139bfd2f0d4SAndrew Donnellan done = rc;
140bfd2f0d4SAndrew Donnellan break;
141bfd2f0d4SAndrew Donnellan }
142bfd2f0d4SAndrew Donnellan ubuf64++;
143bfd2f0d4SAndrew Donnellan done += 8;
144bfd2f0d4SAndrew Donnellan }
145bfd2f0d4SAndrew Donnellan return done;
146bfd2f0d4SAndrew Donnellan }
147bfd2f0d4SAndrew Donnellan
148bfd2f0d4SAndrew Donnellan static const struct file_operations scom_debug_fops = {
149bfd2f0d4SAndrew Donnellan .read = scom_debug_read,
150bfd2f0d4SAndrew Donnellan .write = scom_debug_write,
151bfd2f0d4SAndrew Donnellan .open = simple_open,
152bfd2f0d4SAndrew Donnellan .llseek = default_llseek,
153bfd2f0d4SAndrew Donnellan };
154bfd2f0d4SAndrew Donnellan
scom_debug_init_one(struct dentry * root,struct device_node * dn,int chip)155bfd2f0d4SAndrew Donnellan static int scom_debug_init_one(struct dentry *root, struct device_node *dn,
156bfd2f0d4SAndrew Donnellan int chip)
157bfd2f0d4SAndrew Donnellan {
158bfd2f0d4SAndrew Donnellan struct scom_debug_entry *ent;
159bfd2f0d4SAndrew Donnellan struct dentry *dir;
160bfd2f0d4SAndrew Donnellan
161bfd2f0d4SAndrew Donnellan ent = kzalloc(sizeof(*ent), GFP_KERNEL);
162bfd2f0d4SAndrew Donnellan if (!ent)
163bfd2f0d4SAndrew Donnellan return -ENOMEM;
164bfd2f0d4SAndrew Donnellan
165bfd2f0d4SAndrew Donnellan ent->chip = chip;
166bfd2f0d4SAndrew Donnellan snprintf(ent->name, 16, "%08x", chip);
167bfd2f0d4SAndrew Donnellan ent->path.data = (void *)kasprintf(GFP_KERNEL, "%pOF", dn);
168*dd8422ffSKunwu Chan if (!ent->path.data) {
169*dd8422ffSKunwu Chan kfree(ent);
170*dd8422ffSKunwu Chan return -ENOMEM;
171*dd8422ffSKunwu Chan }
172*dd8422ffSKunwu Chan
173bfd2f0d4SAndrew Donnellan ent->path.size = strlen((char *)ent->path.data);
174bfd2f0d4SAndrew Donnellan
175bfd2f0d4SAndrew Donnellan dir = debugfs_create_dir(ent->name, root);
176429356faSImmad Mir if (IS_ERR(dir)) {
177bfd2f0d4SAndrew Donnellan kfree(ent->path.data);
178bfd2f0d4SAndrew Donnellan kfree(ent);
179bfd2f0d4SAndrew Donnellan return -1;
180bfd2f0d4SAndrew Donnellan }
181bfd2f0d4SAndrew Donnellan
182bfd2f0d4SAndrew Donnellan debugfs_create_blob("devspec", 0400, dir, &ent->path);
183bfd2f0d4SAndrew Donnellan debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops);
184bfd2f0d4SAndrew Donnellan
185bfd2f0d4SAndrew Donnellan return 0;
186bfd2f0d4SAndrew Donnellan }
187bfd2f0d4SAndrew Donnellan
scom_debug_init(void)188bfd2f0d4SAndrew Donnellan static int scom_debug_init(void)
189bfd2f0d4SAndrew Donnellan {
190bfd2f0d4SAndrew Donnellan struct device_node *dn;
191bfd2f0d4SAndrew Donnellan struct dentry *root;
192bfd2f0d4SAndrew Donnellan int chip, rc;
193bfd2f0d4SAndrew Donnellan
194bfd2f0d4SAndrew Donnellan if (!firmware_has_feature(FW_FEATURE_OPAL))
195bfd2f0d4SAndrew Donnellan return 0;
196bfd2f0d4SAndrew Donnellan
197dbf77fedSAneesh Kumar K.V root = debugfs_create_dir("scom", arch_debugfs_dir);
198429356faSImmad Mir if (IS_ERR(root))
199bfd2f0d4SAndrew Donnellan return -1;
200bfd2f0d4SAndrew Donnellan
201bfd2f0d4SAndrew Donnellan rc = 0;
202bfd2f0d4SAndrew Donnellan for_each_node_with_property(dn, "scom-controller") {
203bfd2f0d4SAndrew Donnellan chip = of_get_ibm_chip_id(dn);
204bfd2f0d4SAndrew Donnellan WARN_ON(chip == -1);
205bfd2f0d4SAndrew Donnellan rc |= scom_debug_init_one(root, dn, chip);
206bfd2f0d4SAndrew Donnellan }
207bfd2f0d4SAndrew Donnellan
208bfd2f0d4SAndrew Donnellan return rc;
209bfd2f0d4SAndrew Donnellan }
210bfd2f0d4SAndrew Donnellan device_initcall(scom_debug_init);
211