at v5.2 4.9 kB view raw
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright 2010 Benjamin Herrenschmidt, IBM Corp 4 * <benh@kernel.crashing.org> 5 * and David Gibson, IBM Corporation. 6 */ 7 8#include <linux/kernel.h> 9#include <linux/slab.h> 10#include <linux/export.h> 11#include <asm/debugfs.h> 12#include <asm/prom.h> 13#include <asm/scom.h> 14#include <linux/uaccess.h> 15 16const struct scom_controller *scom_controller; 17EXPORT_SYMBOL_GPL(scom_controller); 18 19struct device_node *scom_find_parent(struct device_node *node) 20{ 21 struct device_node *par, *tmp; 22 const u32 *p; 23 24 for (par = of_node_get(node); par;) { 25 if (of_get_property(par, "scom-controller", NULL)) 26 break; 27 p = of_get_property(par, "scom-parent", NULL); 28 tmp = par; 29 if (p == NULL) 30 par = of_get_parent(par); 31 else 32 par = of_find_node_by_phandle(*p); 33 of_node_put(tmp); 34 } 35 return par; 36} 37EXPORT_SYMBOL_GPL(scom_find_parent); 38 39scom_map_t scom_map_device(struct device_node *dev, int index) 40{ 41 struct device_node *parent; 42 unsigned int cells, size; 43 const __be32 *prop, *sprop; 44 u64 reg, cnt; 45 scom_map_t ret; 46 47 parent = scom_find_parent(dev); 48 49 if (parent == NULL) 50 return NULL; 51 52 /* 53 * We support "scom-reg" properties for adding scom registers 54 * to a random device-tree node with an explicit scom-parent 55 * 56 * We also support the simple "reg" property if the device is 57 * a direct child of a scom controller. 58 * 59 * In case both exist, "scom-reg" takes precedence. 60 */ 61 prop = of_get_property(dev, "scom-reg", &size); 62 sprop = of_get_property(parent, "#scom-cells", NULL); 63 if (!prop && parent == dev->parent) { 64 prop = of_get_property(dev, "reg", &size); 65 sprop = of_get_property(parent, "#address-cells", NULL); 66 } 67 if (!prop) 68 return NULL; 69 cells = sprop ? be32_to_cpup(sprop) : 1; 70 size >>= 2; 71 72 if (index >= (size / (2*cells))) 73 return NULL; 74 75 reg = of_read_number(&prop[index * cells * 2], cells); 76 cnt = of_read_number(&prop[index * cells * 2 + cells], cells); 77 78 ret = scom_map(parent, reg, cnt); 79 of_node_put(parent); 80 81 return ret; 82} 83EXPORT_SYMBOL_GPL(scom_map_device); 84 85#ifdef CONFIG_SCOM_DEBUGFS 86struct scom_debug_entry { 87 struct device_node *dn; 88 struct debugfs_blob_wrapper path; 89 char name[16]; 90}; 91 92static ssize_t scom_debug_read(struct file *filp, char __user *ubuf, 93 size_t count, loff_t *ppos) 94{ 95 struct scom_debug_entry *ent = filp->private_data; 96 u64 __user *ubuf64 = (u64 __user *)ubuf; 97 loff_t off = *ppos; 98 ssize_t done = 0; 99 u64 reg, reg_cnt, val; 100 scom_map_t map; 101 int rc; 102 103 if (off < 0 || (off & 7) || (count & 7)) 104 return -EINVAL; 105 reg = off >> 3; 106 reg_cnt = count >> 3; 107 108 map = scom_map(ent->dn, reg, reg_cnt); 109 if (!scom_map_ok(map)) 110 return -ENXIO; 111 112 for (reg = 0; reg < reg_cnt; reg++) { 113 rc = scom_read(map, reg, &val); 114 if (!rc) 115 rc = put_user(val, ubuf64); 116 if (rc) { 117 if (!done) 118 done = rc; 119 break; 120 } 121 ubuf64++; 122 *ppos += 8; 123 done += 8; 124 } 125 scom_unmap(map); 126 return done; 127} 128 129static ssize_t scom_debug_write(struct file* filp, const char __user *ubuf, 130 size_t count, loff_t *ppos) 131{ 132 struct scom_debug_entry *ent = filp->private_data; 133 u64 __user *ubuf64 = (u64 __user *)ubuf; 134 loff_t off = *ppos; 135 ssize_t done = 0; 136 u64 reg, reg_cnt, val; 137 scom_map_t map; 138 int rc; 139 140 if (off < 0 || (off & 7) || (count & 7)) 141 return -EINVAL; 142 reg = off >> 3; 143 reg_cnt = count >> 3; 144 145 map = scom_map(ent->dn, reg, reg_cnt); 146 if (!scom_map_ok(map)) 147 return -ENXIO; 148 149 for (reg = 0; reg < reg_cnt; reg++) { 150 rc = get_user(val, ubuf64); 151 if (!rc) 152 rc = scom_write(map, reg, val); 153 if (rc) { 154 if (!done) 155 done = rc; 156 break; 157 } 158 ubuf64++; 159 done += 8; 160 } 161 scom_unmap(map); 162 return done; 163} 164 165static const struct file_operations scom_debug_fops = { 166 .read = scom_debug_read, 167 .write = scom_debug_write, 168 .open = simple_open, 169 .llseek = default_llseek, 170}; 171 172static int scom_debug_init_one(struct dentry *root, struct device_node *dn, 173 int i) 174{ 175 struct scom_debug_entry *ent; 176 struct dentry *dir; 177 178 ent = kzalloc(sizeof(*ent), GFP_KERNEL); 179 if (!ent) 180 return -ENOMEM; 181 182 ent->dn = of_node_get(dn); 183 snprintf(ent->name, 16, "%08x", i); 184 ent->path.data = (void*)kasprintf(GFP_KERNEL, "%pOF", dn); 185 ent->path.size = strlen((char *)ent->path.data); 186 187 dir = debugfs_create_dir(ent->name, root); 188 if (!dir) { 189 of_node_put(dn); 190 kfree(ent->path.data); 191 kfree(ent); 192 return -1; 193 } 194 195 debugfs_create_blob("devspec", 0400, dir, &ent->path); 196 debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops); 197 198 return 0; 199} 200 201static int scom_debug_init(void) 202{ 203 struct device_node *dn; 204 struct dentry *root; 205 int i, rc; 206 207 root = debugfs_create_dir("scom", powerpc_debugfs_root); 208 if (!root) 209 return -1; 210 211 i = rc = 0; 212 for_each_node_with_property(dn, "scom-controller") { 213 int id = of_get_ibm_chip_id(dn); 214 if (id == -1) 215 id = i; 216 rc |= scom_debug_init_one(root, dn, id); 217 i++; 218 } 219 220 return rc; 221} 222device_initcall(scom_debug_init); 223#endif /* CONFIG_SCOM_DEBUGFS */