File Index
Files and directories
Sign in
FILE

opal-xscom.c

File preview with syntax highlighting.

linux-6.19.14/arch/powerpc/platforms/powernv/opal-xscom.c 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
 24static 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
 55static 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
 70static 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
 81struct scom_debug_entry {
 82	u32 chip;
 83	struct debugfs_blob_wrapper path;
 84	char name[16];
 85};
 86
 87static 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
118static 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
148static 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
155static 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
188static 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}
210device_initcall(scom_debug_init);
~/ Read-only file preview.