linux/arch/powerpc/platforms/powernv/opal-xscom.c
<<
>>
Prefs
   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
  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        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
 183static 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}
 205device_initcall(scom_debug_init);
 206