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