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