xref: /OK3568_Linux_fs/kernel/arch/powerpc/platforms/powernv/opal-xscom.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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