root/arch/powerpc/platforms/powernv/opal-xscom.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. opal_scom_unmangle
  2. opal_scom_read
  3. opal_scom_write
  4. scom_debug_read
  5. scom_debug_write
  6. scom_debug_init_one
  7. scom_debug_init

   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 
  18 #include <asm/machdep.h>
  19 #include <asm/firmware.h>
  20 #include <asm/opal.h>
  21 #include <asm/debugfs.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 (!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", powerpc_debugfs_root);
 193         if (!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);

/* [<][>][^][v][top][bottom][index][help] */