Merge tag 'mips_6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux
[sfrench/cifs-2.6.git] / arch / powerpc / platforms / powernv / opal-xscom.c
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 #include <linux/debugfs.h>
18
19 #include <asm/machdep.h>
20 #include <asm/firmware.h>
21 #include <asm/opal.h>
22 #include <asm/prom.h>
23
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
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
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
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
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
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 (IS_ERR(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
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", arch_debugfs_dir);
193         if (IS_ERR(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);