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