Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  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 */