Path: blob/master/arch/powerpc/platforms/powernv/opal-xscom.c
26481 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* PowerNV SCOM bus debugfs interface3*4* Copyright 2010 Benjamin Herrenschmidt, IBM Corp5* <[email protected]>6* and David Gibson, IBM Corporation.7* Copyright 2013 IBM Corp.8*/910#include <linux/kernel.h>11#include <linux/of.h>12#include <linux/bug.h>13#include <linux/gfp.h>14#include <linux/slab.h>15#include <linux/uaccess.h>16#include <linux/debugfs.h>1718#include <asm/machdep.h>19#include <asm/firmware.h>20#include <asm/opal.h>21#include <asm/prom.h>2223static u64 opal_scom_unmangle(u64 addr)24{25u64 tmp;2627/*28* XSCOM addresses use the top nibble to set indirect mode and29* its form. Bits 4-11 are always 0.30*31* Because the debugfs interface uses signed offsets and shifts32* the address left by 3, we basically cannot use the top 4 bits33* of the 64-bit address, and thus cannot use the indirect bit.34*35* To deal with that, we support the indirect bits being in36* bits 4-7 (IBM notation) instead of bit 0-3 in this API, we37* do the conversion here.38*39* For in-kernel use, we don't need to do this mangling. In40* kernel won't have bits 4-7 set.41*42* So:43* debugfs will always set 0-3 = 0 and clear 4-744* kernel will always clear 0-3 = 0 and set 4-745*/46tmp = addr;47tmp &= 0x0f00000000000000;48addr &= 0xf0ffffffffffffff;49addr |= tmp << 4;5051return addr;52}5354static int opal_scom_read(uint32_t chip, uint64_t addr, u64 reg, u64 *value)55{56int64_t rc;57__be64 v;5859reg = opal_scom_unmangle(addr + reg);60rc = opal_xscom_read(chip, reg, (__be64 *)__pa(&v));61if (rc) {62*value = 0xfffffffffffffffful;63return -EIO;64}65*value = be64_to_cpu(v);66return 0;67}6869static int opal_scom_write(uint32_t chip, uint64_t addr, u64 reg, u64 value)70{71int64_t rc;7273reg = opal_scom_unmangle(addr + reg);74rc = opal_xscom_write(chip, reg, value);75if (rc)76return -EIO;77return 0;78}7980struct scom_debug_entry {81u32 chip;82struct debugfs_blob_wrapper path;83char name[16];84};8586static ssize_t scom_debug_read(struct file *filp, char __user *ubuf,87size_t count, loff_t *ppos)88{89struct scom_debug_entry *ent = filp->private_data;90u64 __user *ubuf64 = (u64 __user *)ubuf;91loff_t off = *ppos;92ssize_t done = 0;93u64 reg, reg_base, reg_cnt, val;94int rc;9596if (off < 0 || (off & 7) || (count & 7))97return -EINVAL;98reg_base = off >> 3;99reg_cnt = count >> 3;100101for (reg = 0; reg < reg_cnt; reg++) {102rc = opal_scom_read(ent->chip, reg_base, reg, &val);103if (!rc)104rc = put_user(val, ubuf64);105if (rc) {106if (!done)107done = rc;108break;109}110ubuf64++;111*ppos += 8;112done += 8;113}114return done;115}116117static ssize_t scom_debug_write(struct file *filp, const char __user *ubuf,118size_t count, loff_t *ppos)119{120struct scom_debug_entry *ent = filp->private_data;121u64 __user *ubuf64 = (u64 __user *)ubuf;122loff_t off = *ppos;123ssize_t done = 0;124u64 reg, reg_base, reg_cnt, val;125int rc;126127if (off < 0 || (off & 7) || (count & 7))128return -EINVAL;129reg_base = off >> 3;130reg_cnt = count >> 3;131132for (reg = 0; reg < reg_cnt; reg++) {133rc = get_user(val, ubuf64);134if (!rc)135rc = opal_scom_write(ent->chip, reg_base, reg, val);136if (rc) {137if (!done)138done = rc;139break;140}141ubuf64++;142done += 8;143}144return done;145}146147static const struct file_operations scom_debug_fops = {148.read = scom_debug_read,149.write = scom_debug_write,150.open = simple_open,151.llseek = default_llseek,152};153154static int scom_debug_init_one(struct dentry *root, struct device_node *dn,155int chip)156{157struct scom_debug_entry *ent;158struct dentry *dir;159160ent = kzalloc(sizeof(*ent), GFP_KERNEL);161if (!ent)162return -ENOMEM;163164ent->chip = chip;165snprintf(ent->name, 16, "%08x", chip);166ent->path.data = (void *)kasprintf(GFP_KERNEL, "%pOF", dn);167if (!ent->path.data) {168kfree(ent);169return -ENOMEM;170}171172ent->path.size = strlen((char *)ent->path.data);173174dir = debugfs_create_dir(ent->name, root);175if (IS_ERR(dir)) {176kfree(ent->path.data);177kfree(ent);178return -1;179}180181debugfs_create_blob("devspec", 0400, dir, &ent->path);182debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops);183184return 0;185}186187static int scom_debug_init(void)188{189struct device_node *dn;190struct dentry *root;191int chip, rc;192193if (!firmware_has_feature(FW_FEATURE_OPAL))194return 0;195196root = debugfs_create_dir("scom", arch_debugfs_dir);197if (IS_ERR(root))198return -1;199200rc = 0;201for_each_node_with_property(dn, "scom-controller") {202chip = of_get_ibm_chip_id(dn);203WARN_ON(chip == -1);204rc |= scom_debug_init_one(root, dn, chip);205}206207return rc;208}209device_initcall(scom_debug_init);210211212