123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * PowerNV SCOM bus debugfs interface
- *
- * Copyright 2010 Benjamin Herrenschmidt, IBM Corp
- * <[email protected]>
- * and David Gibson, IBM Corporation.
- * Copyright 2013 IBM Corp.
- */
- #include <linux/kernel.h>
- #include <linux/of.h>
- #include <linux/bug.h>
- #include <linux/gfp.h>
- #include <linux/slab.h>
- #include <linux/uaccess.h>
- #include <linux/debugfs.h>
- #include <asm/machdep.h>
- #include <asm/firmware.h>
- #include <asm/opal.h>
- #include <asm/prom.h>
- static u64 opal_scom_unmangle(u64 addr)
- {
- u64 tmp;
- /*
- * XSCOM addresses use the top nibble to set indirect mode and
- * its form. Bits 4-11 are always 0.
- *
- * Because the debugfs interface uses signed offsets and shifts
- * the address left by 3, we basically cannot use the top 4 bits
- * of the 64-bit address, and thus cannot use the indirect bit.
- *
- * To deal with that, we support the indirect bits being in
- * bits 4-7 (IBM notation) instead of bit 0-3 in this API, we
- * do the conversion here.
- *
- * For in-kernel use, we don't need to do this mangling. In
- * kernel won't have bits 4-7 set.
- *
- * So:
- * debugfs will always set 0-3 = 0 and clear 4-7
- * kernel will always clear 0-3 = 0 and set 4-7
- */
- tmp = addr;
- tmp &= 0x0f00000000000000;
- addr &= 0xf0ffffffffffffff;
- addr |= tmp << 4;
- return addr;
- }
- static int opal_scom_read(uint32_t chip, uint64_t addr, u64 reg, u64 *value)
- {
- int64_t rc;
- __be64 v;
- reg = opal_scom_unmangle(addr + reg);
- rc = opal_xscom_read(chip, reg, (__be64 *)__pa(&v));
- if (rc) {
- *value = 0xfffffffffffffffful;
- return -EIO;
- }
- *value = be64_to_cpu(v);
- return 0;
- }
- static int opal_scom_write(uint32_t chip, uint64_t addr, u64 reg, u64 value)
- {
- int64_t rc;
- reg = opal_scom_unmangle(addr + reg);
- rc = opal_xscom_write(chip, reg, value);
- if (rc)
- return -EIO;
- return 0;
- }
- struct scom_debug_entry {
- u32 chip;
- struct debugfs_blob_wrapper path;
- char name[16];
- };
- static ssize_t scom_debug_read(struct file *filp, char __user *ubuf,
- size_t count, loff_t *ppos)
- {
- struct scom_debug_entry *ent = filp->private_data;
- u64 __user *ubuf64 = (u64 __user *)ubuf;
- loff_t off = *ppos;
- ssize_t done = 0;
- u64 reg, reg_base, reg_cnt, val;
- int rc;
- if (off < 0 || (off & 7) || (count & 7))
- return -EINVAL;
- reg_base = off >> 3;
- reg_cnt = count >> 3;
- for (reg = 0; reg < reg_cnt; reg++) {
- rc = opal_scom_read(ent->chip, reg_base, reg, &val);
- if (!rc)
- rc = put_user(val, ubuf64);
- if (rc) {
- if (!done)
- done = rc;
- break;
- }
- ubuf64++;
- *ppos += 8;
- done += 8;
- }
- return done;
- }
- static ssize_t scom_debug_write(struct file *filp, const char __user *ubuf,
- size_t count, loff_t *ppos)
- {
- struct scom_debug_entry *ent = filp->private_data;
- u64 __user *ubuf64 = (u64 __user *)ubuf;
- loff_t off = *ppos;
- ssize_t done = 0;
- u64 reg, reg_base, reg_cnt, val;
- int rc;
- if (off < 0 || (off & 7) || (count & 7))
- return -EINVAL;
- reg_base = off >> 3;
- reg_cnt = count >> 3;
- for (reg = 0; reg < reg_cnt; reg++) {
- rc = get_user(val, ubuf64);
- if (!rc)
- rc = opal_scom_write(ent->chip, reg_base, reg, val);
- if (rc) {
- if (!done)
- done = rc;
- break;
- }
- ubuf64++;
- done += 8;
- }
- return done;
- }
- static const struct file_operations scom_debug_fops = {
- .read = scom_debug_read,
- .write = scom_debug_write,
- .open = simple_open,
- .llseek = default_llseek,
- };
- static int scom_debug_init_one(struct dentry *root, struct device_node *dn,
- int chip)
- {
- struct scom_debug_entry *ent;
- struct dentry *dir;
- ent = kzalloc(sizeof(*ent), GFP_KERNEL);
- if (!ent)
- return -ENOMEM;
- ent->chip = chip;
- snprintf(ent->name, 16, "%08x", chip);
- ent->path.data = (void *)kasprintf(GFP_KERNEL, "%pOF", dn);
- ent->path.size = strlen((char *)ent->path.data);
- dir = debugfs_create_dir(ent->name, root);
- if (!dir) {
- kfree(ent->path.data);
- kfree(ent);
- return -1;
- }
- debugfs_create_blob("devspec", 0400, dir, &ent->path);
- debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops);
- return 0;
- }
- static int scom_debug_init(void)
- {
- struct device_node *dn;
- struct dentry *root;
- int chip, rc;
- if (!firmware_has_feature(FW_FEATURE_OPAL))
- return 0;
- root = debugfs_create_dir("scom", arch_debugfs_dir);
- if (!root)
- return -1;
- rc = 0;
- for_each_node_with_property(dn, "scom-controller") {
- chip = of_get_ibm_chip_id(dn);
- WARN_ON(chip == -1);
- rc |= scom_debug_init_one(root, dn, chip);
- }
- return rc;
- }
- device_initcall(scom_debug_init);
|