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