at v3.12 4.5 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/debugfs.h> 23#include <linux/slab.h> 24#include <linux/export.h> 25#include <asm/debug.h> 26#include <asm/prom.h> 27#include <asm/scom.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 u32 *prop; 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 prop = of_get_property(parent, "#scom-cells", NULL); 66 cells = prop ? *prop : 1; 67 68 prop = of_get_property(dev, "scom-reg", &size); 69 if (!prop) 70 return 0; 71 size >>= 2; 72 73 if (index >= (size / (2*cells))) 74 return 0; 75 76 reg = of_read_number(&prop[index * cells * 2], cells); 77 cnt = of_read_number(&prop[index * cells * 2 + cells], cells); 78 79 ret = scom_map(parent, reg, cnt); 80 of_node_put(parent); 81 82 return ret; 83} 84EXPORT_SYMBOL_GPL(scom_map_device); 85 86#ifdef CONFIG_SCOM_DEBUGFS 87struct scom_debug_entry { 88 struct device_node *dn; 89 unsigned long addr; 90 scom_map_t map; 91 spinlock_t lock; 92 char name[8]; 93 struct debugfs_blob_wrapper blob; 94}; 95 96static int scom_addr_set(void *data, u64 val) 97{ 98 struct scom_debug_entry *ent = data; 99 100 ent->addr = 0; 101 scom_unmap(ent->map); 102 103 ent->map = scom_map(ent->dn, val, 1); 104 if (scom_map_ok(ent->map)) 105 ent->addr = val; 106 else 107 return -EFAULT; 108 109 return 0; 110} 111 112static int scom_addr_get(void *data, u64 *val) 113{ 114 struct scom_debug_entry *ent = data; 115 *val = ent->addr; 116 return 0; 117} 118DEFINE_SIMPLE_ATTRIBUTE(scom_addr_fops, scom_addr_get, scom_addr_set, 119 "0x%llx\n"); 120 121static int scom_val_set(void *data, u64 val) 122{ 123 struct scom_debug_entry *ent = data; 124 125 if (!scom_map_ok(ent->map)) 126 return -EFAULT; 127 128 scom_write(ent->map, 0, val); 129 130 return 0; 131} 132 133static int scom_val_get(void *data, u64 *val) 134{ 135 struct scom_debug_entry *ent = data; 136 137 if (!scom_map_ok(ent->map)) 138 return -EFAULT; 139 140 *val = scom_read(ent->map, 0); 141 return 0; 142} 143DEFINE_SIMPLE_ATTRIBUTE(scom_val_fops, scom_val_get, scom_val_set, 144 "0x%llx\n"); 145 146static int scom_debug_init_one(struct dentry *root, struct device_node *dn, 147 int i) 148{ 149 struct scom_debug_entry *ent; 150 struct dentry *dir; 151 152 ent = kzalloc(sizeof(*ent), GFP_KERNEL); 153 if (!ent) 154 return -ENOMEM; 155 156 ent->dn = of_node_get(dn); 157 ent->map = SCOM_MAP_INVALID; 158 spin_lock_init(&ent->lock); 159 snprintf(ent->name, 8, "scom%d", i); 160 ent->blob.data = (void*) dn->full_name; 161 ent->blob.size = strlen(dn->full_name); 162 163 dir = debugfs_create_dir(ent->name, root); 164 if (!dir) { 165 of_node_put(dn); 166 kfree(ent); 167 return -1; 168 } 169 170 debugfs_create_file("addr", 0600, dir, ent, &scom_addr_fops); 171 debugfs_create_file("value", 0600, dir, ent, &scom_val_fops); 172 debugfs_create_blob("path", 0400, dir, &ent->blob); 173 174 return 0; 175} 176 177static int scom_debug_init(void) 178{ 179 struct device_node *dn; 180 struct dentry *root; 181 int i, rc; 182 183 root = debugfs_create_dir("scom", powerpc_debugfs_root); 184 if (!root) 185 return -1; 186 187 i = rc = 0; 188 for_each_node_with_property(dn, "scom-controller") 189 rc |= scom_debug_init_one(root, dn, i++); 190 191 return rc; 192} 193device_initcall(scom_debug_init); 194#endif /* CONFIG_SCOM_DEBUGFS */