12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 28adae0c8SBenjamin Herrenschmidt /* 3bfd2f0d4SAndrew Donnellan * PowerNV SCOM bus debugfs interface 48adae0c8SBenjamin Herrenschmidt * 5bfd2f0d4SAndrew Donnellan * Copyright 2010 Benjamin Herrenschmidt, IBM Corp 6bfd2f0d4SAndrew Donnellan * <benh@kernel.crashing.org> 7bfd2f0d4SAndrew Donnellan * and David Gibson, IBM Corporation. 88adae0c8SBenjamin Herrenschmidt * Copyright 2013 IBM Corp. 98adae0c8SBenjamin Herrenschmidt */ 108adae0c8SBenjamin Herrenschmidt 118adae0c8SBenjamin Herrenschmidt #include <linux/kernel.h> 128adae0c8SBenjamin Herrenschmidt #include <linux/of.h> 138adae0c8SBenjamin Herrenschmidt #include <linux/bug.h> 148adae0c8SBenjamin Herrenschmidt #include <linux/gfp.h> 158adae0c8SBenjamin Herrenschmidt #include <linux/slab.h> 16bfd2f0d4SAndrew Donnellan #include <linux/uaccess.h> 17*dbf77fedSAneesh Kumar K.V #include <linux/debugfs.h> 188adae0c8SBenjamin Herrenschmidt 198adae0c8SBenjamin Herrenschmidt #include <asm/machdep.h> 208adae0c8SBenjamin Herrenschmidt #include <asm/firmware.h> 218adae0c8SBenjamin Herrenschmidt #include <asm/opal.h> 22bfd2f0d4SAndrew Donnellan #include <asm/prom.h> 238adae0c8SBenjamin Herrenschmidt 24e0cf9576SBenjamin Herrenschmidt static u64 opal_scom_unmangle(u64 addr) 2580546ac5SBenjamin Herrenschmidt { 26517c2757SMichael Neuling u64 tmp; 27517c2757SMichael Neuling 2880546ac5SBenjamin Herrenschmidt /* 29517c2757SMichael Neuling * XSCOM addresses use the top nibble to set indirect mode and 30517c2757SMichael Neuling * its form. Bits 4-11 are always 0. 3180546ac5SBenjamin Herrenschmidt * 3280546ac5SBenjamin Herrenschmidt * Because the debugfs interface uses signed offsets and shifts 3380546ac5SBenjamin Herrenschmidt * the address left by 3, we basically cannot use the top 4 bits 3480546ac5SBenjamin Herrenschmidt * of the 64-bit address, and thus cannot use the indirect bit. 3580546ac5SBenjamin Herrenschmidt * 36517c2757SMichael Neuling * To deal with that, we support the indirect bits being in 37517c2757SMichael Neuling * bits 4-7 (IBM notation) instead of bit 0-3 in this API, we 38517c2757SMichael Neuling * do the conversion here. 3980546ac5SBenjamin Herrenschmidt * 40517c2757SMichael Neuling * For in-kernel use, we don't need to do this mangling. In 41517c2757SMichael Neuling * kernel won't have bits 4-7 set. 42e0cf9576SBenjamin Herrenschmidt * 43517c2757SMichael Neuling * So: 44517c2757SMichael Neuling * debugfs will always set 0-3 = 0 and clear 4-7 45517c2757SMichael Neuling * kernel will always clear 0-3 = 0 and set 4-7 4680546ac5SBenjamin Herrenschmidt */ 47517c2757SMichael Neuling tmp = addr; 48517c2757SMichael Neuling tmp &= 0x0f00000000000000; 49517c2757SMichael Neuling addr &= 0xf0ffffffffffffff; 50517c2757SMichael Neuling addr |= tmp << 4; 51517c2757SMichael Neuling 52e0cf9576SBenjamin Herrenschmidt return addr; 5380546ac5SBenjamin Herrenschmidt } 5480546ac5SBenjamin Herrenschmidt 55bfd2f0d4SAndrew Donnellan static int opal_scom_read(uint32_t chip, uint64_t addr, u64 reg, u64 *value) 568adae0c8SBenjamin Herrenschmidt { 578adae0c8SBenjamin Herrenschmidt int64_t rc; 5801a9dbccSAnton Blanchard __be64 v; 598adae0c8SBenjamin Herrenschmidt 60bfd2f0d4SAndrew Donnellan reg = opal_scom_unmangle(addr + reg); 61bfd2f0d4SAndrew Donnellan rc = opal_xscom_read(chip, reg, (__be64 *)__pa(&v)); 62bfd2f0d4SAndrew Donnellan if (rc) { 63bfd2f0d4SAndrew Donnellan *value = 0xfffffffffffffffful; 64bfd2f0d4SAndrew Donnellan return -EIO; 65bfd2f0d4SAndrew Donnellan } 6601a9dbccSAnton Blanchard *value = be64_to_cpu(v); 678adae0c8SBenjamin Herrenschmidt return 0; 688adae0c8SBenjamin Herrenschmidt } 69bfd2f0d4SAndrew Donnellan 70bfd2f0d4SAndrew Donnellan static int opal_scom_write(uint32_t chip, uint64_t addr, u64 reg, u64 value) 71bfd2f0d4SAndrew Donnellan { 72bfd2f0d4SAndrew Donnellan int64_t rc; 73bfd2f0d4SAndrew Donnellan 74bfd2f0d4SAndrew Donnellan reg = opal_scom_unmangle(addr + reg); 75bfd2f0d4SAndrew Donnellan rc = opal_xscom_write(chip, reg, value); 76bfd2f0d4SAndrew Donnellan if (rc) 77bfd2f0d4SAndrew Donnellan return -EIO; 78bfd2f0d4SAndrew Donnellan return 0; 79bfd2f0d4SAndrew Donnellan } 80bfd2f0d4SAndrew Donnellan 81bfd2f0d4SAndrew Donnellan struct scom_debug_entry { 82bfd2f0d4SAndrew Donnellan u32 chip; 83bfd2f0d4SAndrew Donnellan struct debugfs_blob_wrapper path; 84bfd2f0d4SAndrew Donnellan char name[16]; 85bfd2f0d4SAndrew Donnellan }; 86bfd2f0d4SAndrew Donnellan 87bfd2f0d4SAndrew Donnellan static ssize_t scom_debug_read(struct file *filp, char __user *ubuf, 88bfd2f0d4SAndrew Donnellan size_t count, loff_t *ppos) 89bfd2f0d4SAndrew Donnellan { 90bfd2f0d4SAndrew Donnellan struct scom_debug_entry *ent = filp->private_data; 91bfd2f0d4SAndrew Donnellan u64 __user *ubuf64 = (u64 __user *)ubuf; 92bfd2f0d4SAndrew Donnellan loff_t off = *ppos; 93bfd2f0d4SAndrew Donnellan ssize_t done = 0; 94bfd2f0d4SAndrew Donnellan u64 reg, reg_base, reg_cnt, val; 95bfd2f0d4SAndrew Donnellan int rc; 96bfd2f0d4SAndrew Donnellan 97bfd2f0d4SAndrew Donnellan if (off < 0 || (off & 7) || (count & 7)) 98bfd2f0d4SAndrew Donnellan return -EINVAL; 99bfd2f0d4SAndrew Donnellan reg_base = off >> 3; 100bfd2f0d4SAndrew Donnellan reg_cnt = count >> 3; 101bfd2f0d4SAndrew Donnellan 102bfd2f0d4SAndrew Donnellan for (reg = 0; reg < reg_cnt; reg++) { 103bfd2f0d4SAndrew Donnellan rc = opal_scom_read(ent->chip, reg_base, reg, &val); 104bfd2f0d4SAndrew Donnellan if (!rc) 105bfd2f0d4SAndrew Donnellan rc = put_user(val, ubuf64); 106bfd2f0d4SAndrew Donnellan if (rc) { 107bfd2f0d4SAndrew Donnellan if (!done) 108bfd2f0d4SAndrew Donnellan done = rc; 109bfd2f0d4SAndrew Donnellan break; 110bfd2f0d4SAndrew Donnellan } 111bfd2f0d4SAndrew Donnellan ubuf64++; 112bfd2f0d4SAndrew Donnellan *ppos += 8; 113bfd2f0d4SAndrew Donnellan done += 8; 114bfd2f0d4SAndrew Donnellan } 115bfd2f0d4SAndrew Donnellan return done; 116bfd2f0d4SAndrew Donnellan } 117bfd2f0d4SAndrew Donnellan 118bfd2f0d4SAndrew Donnellan static ssize_t scom_debug_write(struct file *filp, const char __user *ubuf, 119bfd2f0d4SAndrew Donnellan size_t count, loff_t *ppos) 120bfd2f0d4SAndrew Donnellan { 121bfd2f0d4SAndrew Donnellan struct scom_debug_entry *ent = filp->private_data; 122bfd2f0d4SAndrew Donnellan u64 __user *ubuf64 = (u64 __user *)ubuf; 123bfd2f0d4SAndrew Donnellan loff_t off = *ppos; 124bfd2f0d4SAndrew Donnellan ssize_t done = 0; 125bfd2f0d4SAndrew Donnellan u64 reg, reg_base, reg_cnt, val; 126bfd2f0d4SAndrew Donnellan int rc; 127bfd2f0d4SAndrew Donnellan 128bfd2f0d4SAndrew Donnellan if (off < 0 || (off & 7) || (count & 7)) 129bfd2f0d4SAndrew Donnellan return -EINVAL; 130bfd2f0d4SAndrew Donnellan reg_base = off >> 3; 131bfd2f0d4SAndrew Donnellan reg_cnt = count >> 3; 132bfd2f0d4SAndrew Donnellan 133bfd2f0d4SAndrew Donnellan for (reg = 0; reg < reg_cnt; reg++) { 134bfd2f0d4SAndrew Donnellan rc = get_user(val, ubuf64); 135bfd2f0d4SAndrew Donnellan if (!rc) 136bfd2f0d4SAndrew Donnellan rc = opal_scom_write(ent->chip, reg_base, reg, val); 137bfd2f0d4SAndrew Donnellan if (rc) { 138bfd2f0d4SAndrew Donnellan if (!done) 139bfd2f0d4SAndrew Donnellan done = rc; 140bfd2f0d4SAndrew Donnellan break; 141bfd2f0d4SAndrew Donnellan } 142bfd2f0d4SAndrew Donnellan ubuf64++; 143bfd2f0d4SAndrew Donnellan done += 8; 144bfd2f0d4SAndrew Donnellan } 145bfd2f0d4SAndrew Donnellan return done; 146bfd2f0d4SAndrew Donnellan } 147bfd2f0d4SAndrew Donnellan 148bfd2f0d4SAndrew Donnellan static const struct file_operations scom_debug_fops = { 149bfd2f0d4SAndrew Donnellan .read = scom_debug_read, 150bfd2f0d4SAndrew Donnellan .write = scom_debug_write, 151bfd2f0d4SAndrew Donnellan .open = simple_open, 152bfd2f0d4SAndrew Donnellan .llseek = default_llseek, 153bfd2f0d4SAndrew Donnellan }; 154bfd2f0d4SAndrew Donnellan 155bfd2f0d4SAndrew Donnellan static int scom_debug_init_one(struct dentry *root, struct device_node *dn, 156bfd2f0d4SAndrew Donnellan int chip) 157bfd2f0d4SAndrew Donnellan { 158bfd2f0d4SAndrew Donnellan struct scom_debug_entry *ent; 159bfd2f0d4SAndrew Donnellan struct dentry *dir; 160bfd2f0d4SAndrew Donnellan 161bfd2f0d4SAndrew Donnellan ent = kzalloc(sizeof(*ent), GFP_KERNEL); 162bfd2f0d4SAndrew Donnellan if (!ent) 163bfd2f0d4SAndrew Donnellan return -ENOMEM; 164bfd2f0d4SAndrew Donnellan 165bfd2f0d4SAndrew Donnellan ent->chip = chip; 166bfd2f0d4SAndrew Donnellan snprintf(ent->name, 16, "%08x", chip); 167bfd2f0d4SAndrew Donnellan ent->path.data = (void *)kasprintf(GFP_KERNEL, "%pOF", dn); 168bfd2f0d4SAndrew Donnellan ent->path.size = strlen((char *)ent->path.data); 169bfd2f0d4SAndrew Donnellan 170bfd2f0d4SAndrew Donnellan dir = debugfs_create_dir(ent->name, root); 171bfd2f0d4SAndrew Donnellan if (!dir) { 172bfd2f0d4SAndrew Donnellan kfree(ent->path.data); 173bfd2f0d4SAndrew Donnellan kfree(ent); 174bfd2f0d4SAndrew Donnellan return -1; 175bfd2f0d4SAndrew Donnellan } 176bfd2f0d4SAndrew Donnellan 177bfd2f0d4SAndrew Donnellan debugfs_create_blob("devspec", 0400, dir, &ent->path); 178bfd2f0d4SAndrew Donnellan debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops); 179bfd2f0d4SAndrew Donnellan 180bfd2f0d4SAndrew Donnellan return 0; 181bfd2f0d4SAndrew Donnellan } 182bfd2f0d4SAndrew Donnellan 183bfd2f0d4SAndrew Donnellan static int scom_debug_init(void) 184bfd2f0d4SAndrew Donnellan { 185bfd2f0d4SAndrew Donnellan struct device_node *dn; 186bfd2f0d4SAndrew Donnellan struct dentry *root; 187bfd2f0d4SAndrew Donnellan int chip, rc; 188bfd2f0d4SAndrew Donnellan 189bfd2f0d4SAndrew Donnellan if (!firmware_has_feature(FW_FEATURE_OPAL)) 190bfd2f0d4SAndrew Donnellan return 0; 191bfd2f0d4SAndrew Donnellan 192*dbf77fedSAneesh Kumar K.V root = debugfs_create_dir("scom", arch_debugfs_dir); 193bfd2f0d4SAndrew Donnellan if (!root) 194bfd2f0d4SAndrew Donnellan return -1; 195bfd2f0d4SAndrew Donnellan 196bfd2f0d4SAndrew Donnellan rc = 0; 197bfd2f0d4SAndrew Donnellan for_each_node_with_property(dn, "scom-controller") { 198bfd2f0d4SAndrew Donnellan chip = of_get_ibm_chip_id(dn); 199bfd2f0d4SAndrew Donnellan WARN_ON(chip == -1); 200bfd2f0d4SAndrew Donnellan rc |= scom_debug_init_one(root, dn, chip); 201bfd2f0d4SAndrew Donnellan } 202bfd2f0d4SAndrew Donnellan 203bfd2f0d4SAndrew Donnellan return rc; 204bfd2f0d4SAndrew Donnellan } 205bfd2f0d4SAndrew Donnellan device_initcall(scom_debug_init); 206