opal-xscom.c (4525B)
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#include <linux/debugfs.h> 18 19#include <asm/machdep.h> 20#include <asm/firmware.h> 21#include <asm/opal.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", arch_debugfs_dir); 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);