Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/platforms/powernv/opal-xscom.c
26481 views
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
* <[email protected]>
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
if (!ent->path.data) {
169
kfree(ent);
170
return -ENOMEM;
171
}
172
173
ent->path.size = strlen((char *)ent->path.data);
174
175
dir = debugfs_create_dir(ent->name, root);
176
if (IS_ERR(dir)) {
177
kfree(ent->path.data);
178
kfree(ent);
179
return -1;
180
}
181
182
debugfs_create_blob("devspec", 0400, dir, &ent->path);
183
debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops);
184
185
return 0;
186
}
187
188
static int scom_debug_init(void)
189
{
190
struct device_node *dn;
191
struct dentry *root;
192
int chip, rc;
193
194
if (!firmware_has_feature(FW_FEATURE_OPAL))
195
return 0;
196
197
root = debugfs_create_dir("scom", arch_debugfs_dir);
198
if (IS_ERR(root))
199
return -1;
200
201
rc = 0;
202
for_each_node_with_property(dn, "scom-controller") {
203
chip = of_get_ibm_chip_id(dn);
204
WARN_ON(chip == -1);
205
rc |= scom_debug_init_one(root, dn, chip);
206
}
207
208
return rc;
209
}
210
device_initcall(scom_debug_init);
211
212