Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * PowerNV SCOM bus debugfs interface
0004  *
0005  * Copyright 2010 Benjamin Herrenschmidt, IBM Corp
0006  *                <benh@kernel.crashing.org>
0007  *     and        David Gibson, IBM Corporation.
0008  * Copyright 2013 IBM Corp.
0009  */
0010 
0011 #include <linux/kernel.h>
0012 #include <linux/of.h>
0013 #include <linux/bug.h>
0014 #include <linux/gfp.h>
0015 #include <linux/slab.h>
0016 #include <linux/uaccess.h>
0017 #include <linux/debugfs.h>
0018 
0019 #include <asm/machdep.h>
0020 #include <asm/firmware.h>
0021 #include <asm/opal.h>
0022 #include <asm/prom.h>
0023 
0024 static u64 opal_scom_unmangle(u64 addr)
0025 {
0026     u64 tmp;
0027 
0028     /*
0029      * XSCOM addresses use the top nibble to set indirect mode and
0030      * its form.  Bits 4-11 are always 0.
0031      *
0032      * Because the debugfs interface uses signed offsets and shifts
0033      * the address left by 3, we basically cannot use the top 4 bits
0034      * of the 64-bit address, and thus cannot use the indirect bit.
0035      *
0036      * To deal with that, we support the indirect bits being in
0037      * bits 4-7 (IBM notation) instead of bit 0-3 in this API, we
0038      * do the conversion here.
0039      *
0040      * For in-kernel use, we don't need to do this mangling.  In
0041      * kernel won't have bits 4-7 set.
0042      *
0043      * So:
0044      *   debugfs will always   set 0-3 = 0 and clear 4-7
0045      *    kernel will always clear 0-3 = 0 and   set 4-7
0046      */
0047     tmp = addr;
0048     tmp  &= 0x0f00000000000000;
0049     addr &= 0xf0ffffffffffffff;
0050     addr |= tmp << 4;
0051 
0052     return addr;
0053 }
0054 
0055 static int opal_scom_read(uint32_t chip, uint64_t addr, u64 reg, u64 *value)
0056 {
0057     int64_t rc;
0058     __be64 v;
0059 
0060     reg = opal_scom_unmangle(addr + reg);
0061     rc = opal_xscom_read(chip, reg, (__be64 *)__pa(&v));
0062     if (rc) {
0063         *value = 0xfffffffffffffffful;
0064         return -EIO;
0065     }
0066     *value = be64_to_cpu(v);
0067     return 0;
0068 }
0069 
0070 static int opal_scom_write(uint32_t chip, uint64_t addr, u64 reg, u64 value)
0071 {
0072     int64_t rc;
0073 
0074     reg = opal_scom_unmangle(addr + reg);
0075     rc = opal_xscom_write(chip, reg, value);
0076     if (rc)
0077         return -EIO;
0078     return 0;
0079 }
0080 
0081 struct scom_debug_entry {
0082     u32 chip;
0083     struct debugfs_blob_wrapper path;
0084     char name[16];
0085 };
0086 
0087 static ssize_t scom_debug_read(struct file *filp, char __user *ubuf,
0088                    size_t count, loff_t *ppos)
0089 {
0090     struct scom_debug_entry *ent = filp->private_data;
0091     u64 __user *ubuf64 = (u64 __user *)ubuf;
0092     loff_t off = *ppos;
0093     ssize_t done = 0;
0094     u64 reg, reg_base, reg_cnt, val;
0095     int rc;
0096 
0097     if (off < 0 || (off & 7) || (count & 7))
0098         return -EINVAL;
0099     reg_base = off >> 3;
0100     reg_cnt = count >> 3;
0101 
0102     for (reg = 0; reg < reg_cnt; reg++) {
0103         rc = opal_scom_read(ent->chip, reg_base, reg, &val);
0104         if (!rc)
0105             rc = put_user(val, ubuf64);
0106         if (rc) {
0107             if (!done)
0108                 done = rc;
0109             break;
0110         }
0111         ubuf64++;
0112         *ppos += 8;
0113         done += 8;
0114     }
0115     return done;
0116 }
0117 
0118 static ssize_t scom_debug_write(struct file *filp, const char __user *ubuf,
0119                 size_t count, loff_t *ppos)
0120 {
0121     struct scom_debug_entry *ent = filp->private_data;
0122     u64 __user *ubuf64 = (u64 __user *)ubuf;
0123     loff_t off = *ppos;
0124     ssize_t done = 0;
0125     u64 reg, reg_base, reg_cnt, val;
0126     int rc;
0127 
0128     if (off < 0 || (off & 7) || (count & 7))
0129         return -EINVAL;
0130     reg_base = off >> 3;
0131     reg_cnt = count >> 3;
0132 
0133     for (reg = 0; reg < reg_cnt; reg++) {
0134         rc = get_user(val, ubuf64);
0135         if (!rc)
0136             rc = opal_scom_write(ent->chip, reg_base, reg,  val);
0137         if (rc) {
0138             if (!done)
0139                 done = rc;
0140             break;
0141         }
0142         ubuf64++;
0143         done += 8;
0144     }
0145     return done;
0146 }
0147 
0148 static const struct file_operations scom_debug_fops = {
0149     .read =     scom_debug_read,
0150     .write =    scom_debug_write,
0151     .open =     simple_open,
0152     .llseek =   default_llseek,
0153 };
0154 
0155 static int scom_debug_init_one(struct dentry *root, struct device_node *dn,
0156                    int chip)
0157 {
0158     struct scom_debug_entry *ent;
0159     struct dentry *dir;
0160 
0161     ent = kzalloc(sizeof(*ent), GFP_KERNEL);
0162     if (!ent)
0163         return -ENOMEM;
0164 
0165     ent->chip = chip;
0166     snprintf(ent->name, 16, "%08x", chip);
0167     ent->path.data = (void *)kasprintf(GFP_KERNEL, "%pOF", dn);
0168     ent->path.size = strlen((char *)ent->path.data);
0169 
0170     dir = debugfs_create_dir(ent->name, root);
0171     if (!dir) {
0172         kfree(ent->path.data);
0173         kfree(ent);
0174         return -1;
0175     }
0176 
0177     debugfs_create_blob("devspec", 0400, dir, &ent->path);
0178     debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops);
0179 
0180     return 0;
0181 }
0182 
0183 static int scom_debug_init(void)
0184 {
0185     struct device_node *dn;
0186     struct dentry *root;
0187     int chip, rc;
0188 
0189     if (!firmware_has_feature(FW_FEATURE_OPAL))
0190         return 0;
0191 
0192     root = debugfs_create_dir("scom", arch_debugfs_dir);
0193     if (!root)
0194         return -1;
0195 
0196     rc = 0;
0197     for_each_node_with_property(dn, "scom-controller") {
0198         chip = of_get_ibm_chip_id(dn);
0199         WARN_ON(chip == -1);
0200         rc |= scom_debug_init_one(root, dn, chip);
0201     }
0202 
0203     return rc;
0204 }
0205 device_initcall(scom_debug_init);