aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel/vio.c
diff options
context:
space:
mode:
authorSam Ravnborg <sam@ravnborg.org>2008-12-03 06:11:52 -0500
committerDavid S. Miller <davem@davemloft.net>2008-12-04 12:17:21 -0500
commita88b5ba8bd8ac18aad65ee6c6a254e2e74876db3 (patch)
treeeb3d0ffaf53c3f7ec6083752c2097cecd1cb892a /arch/sparc/kernel/vio.c
parentd670bd4f803c8b646acd20f3ba21e65458293faf (diff)
sparc,sparc64: unify kernel/
o Move all files from sparc64/kernel/ to sparc/kernel - rename as appropriate o Update sparc/Makefile to the changes o Update sparc/kernel/Makefile to include the sparc64 files NOTE: This commit changes link order on sparc64! Link order had to change for either of sparc32 and sparc64. And assuming sparc64 see more testing than sparc32 change link order on sparc64 where issues will be caught faster. Signed-off-by: Sam Ravnborg <sam@ravnborg.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc/kernel/vio.c')
-rw-r--r--arch/sparc/kernel/vio.c451
1 files changed, 451 insertions, 0 deletions
diff --git a/arch/sparc/kernel/vio.c b/arch/sparc/kernel/vio.c
new file mode 100644
index 000000000000..92b1f8ec01de
--- /dev/null
+++ b/arch/sparc/kernel/vio.c
@@ -0,0 +1,451 @@
1/* vio.c: Virtual I/O channel devices probing infrastructure.
2 *
3 * Copyright (c) 2003-2005 IBM Corp.
4 * Dave Engebretsen engebret@us.ibm.com
5 * Santiago Leon santil@us.ibm.com
6 * Hollis Blanchard <hollisb@us.ibm.com>
7 * Stephen Rothwell
8 *
9 * Adapted to sparc64 by David S. Miller davem@davemloft.net
10 */
11
12#include <linux/kernel.h>
13#include <linux/irq.h>
14#include <linux/init.h>
15
16#include <asm/mdesc.h>
17#include <asm/vio.h>
18
19static const struct vio_device_id *vio_match_device(
20 const struct vio_device_id *matches,
21 const struct vio_dev *dev)
22{
23 const char *type, *compat;
24 int len;
25
26 type = dev->type;
27 compat = dev->compat;
28 len = dev->compat_len;
29
30 while (matches->type[0] || matches->compat[0]) {
31 int match = 1;
32 if (matches->type[0])
33 match &= !strcmp(matches->type, type);
34
35 if (matches->compat[0]) {
36 match &= len &&
37 of_find_in_proplist(compat, matches->compat, len);
38 }
39 if (match)
40 return matches;
41 matches++;
42 }
43 return NULL;
44}
45
46static int vio_bus_match(struct device *dev, struct device_driver *drv)
47{
48 struct vio_dev *vio_dev = to_vio_dev(dev);
49 struct vio_driver *vio_drv = to_vio_driver(drv);
50 const struct vio_device_id *matches = vio_drv->id_table;
51
52 if (!matches)
53 return 0;
54
55 return vio_match_device(matches, vio_dev) != NULL;
56}
57
58static int vio_device_probe(struct device *dev)
59{
60 struct vio_dev *vdev = to_vio_dev(dev);
61 struct vio_driver *drv = to_vio_driver(dev->driver);
62 const struct vio_device_id *id;
63 int error = -ENODEV;
64
65 if (drv->probe) {
66 id = vio_match_device(drv->id_table, vdev);
67 if (id)
68 error = drv->probe(vdev, id);
69 }
70
71 return error;
72}
73
74static int vio_device_remove(struct device *dev)
75{
76 struct vio_dev *vdev = to_vio_dev(dev);
77 struct vio_driver *drv = to_vio_driver(dev->driver);
78
79 if (drv->remove)
80 return drv->remove(vdev);
81
82 return 1;
83}
84
85static ssize_t devspec_show(struct device *dev,
86 struct device_attribute *attr, char *buf)
87{
88 struct vio_dev *vdev = to_vio_dev(dev);
89 const char *str = "none";
90
91 if (!strcmp(vdev->type, "vnet-port"))
92 str = "vnet";
93 else if (!strcmp(vdev->type, "vdc-port"))
94 str = "vdisk";
95
96 return sprintf(buf, "%s\n", str);
97}
98
99static ssize_t type_show(struct device *dev,
100 struct device_attribute *attr, char *buf)
101{
102 struct vio_dev *vdev = to_vio_dev(dev);
103 return sprintf(buf, "%s\n", vdev->type);
104}
105
106static struct device_attribute vio_dev_attrs[] = {
107 __ATTR_RO(devspec),
108 __ATTR_RO(type),
109 __ATTR_NULL
110};
111
112static struct bus_type vio_bus_type = {
113 .name = "vio",
114 .dev_attrs = vio_dev_attrs,
115 .match = vio_bus_match,
116 .probe = vio_device_probe,
117 .remove = vio_device_remove,
118};
119
120int vio_register_driver(struct vio_driver *viodrv)
121{
122 viodrv->driver.bus = &vio_bus_type;
123
124 return driver_register(&viodrv->driver);
125}
126EXPORT_SYMBOL(vio_register_driver);
127
128void vio_unregister_driver(struct vio_driver *viodrv)
129{
130 driver_unregister(&viodrv->driver);
131}
132EXPORT_SYMBOL(vio_unregister_driver);
133
134static void vio_dev_release(struct device *dev)
135{
136 kfree(to_vio_dev(dev));
137}
138
139static ssize_t
140show_pciobppath_attr(struct device *dev, struct device_attribute *attr,
141 char *buf)
142{
143 struct vio_dev *vdev;
144 struct device_node *dp;
145
146 vdev = to_vio_dev(dev);
147 dp = vdev->dp;
148
149 return snprintf (buf, PAGE_SIZE, "%s\n", dp->full_name);
150}
151
152static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH,
153 show_pciobppath_attr, NULL);
154
155static struct device_node *cdev_node;
156
157static struct vio_dev *root_vdev;
158static u64 cdev_cfg_handle;
159
160static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp,
161 struct vio_dev *vdev)
162{
163 u64 a;
164
165 mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) {
166 const u64 *chan_id;
167 const u64 *irq;
168 u64 target;
169
170 target = mdesc_arc_target(hp, a);
171
172 irq = mdesc_get_property(hp, target, "tx-ino", NULL);
173 if (irq)
174 vdev->tx_irq = sun4v_build_virq(cdev_cfg_handle, *irq);
175
176 irq = mdesc_get_property(hp, target, "rx-ino", NULL);
177 if (irq)
178 vdev->rx_irq = sun4v_build_virq(cdev_cfg_handle, *irq);
179
180 chan_id = mdesc_get_property(hp, target, "id", NULL);
181 if (chan_id)
182 vdev->channel_id = *chan_id;
183 }
184}
185
186static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
187 struct device *parent)
188{
189 const char *type, *compat, *bus_id_name;
190 struct device_node *dp;
191 struct vio_dev *vdev;
192 int err, tlen, clen;
193 const u64 *id, *cfg_handle;
194 u64 a;
195
196 type = mdesc_get_property(hp, mp, "device-type", &tlen);
197 if (!type) {
198 type = mdesc_get_property(hp, mp, "name", &tlen);
199 if (!type) {
200 type = mdesc_node_name(hp, mp);
201 tlen = strlen(type) + 1;
202 }
203 }
204 if (tlen > VIO_MAX_TYPE_LEN) {
205 printk(KERN_ERR "VIO: Type string [%s] is too long.\n",
206 type);
207 return NULL;
208 }
209
210 id = mdesc_get_property(hp, mp, "id", NULL);
211
212 cfg_handle = NULL;
213 mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) {
214 u64 target;
215
216 target = mdesc_arc_target(hp, a);
217 cfg_handle = mdesc_get_property(hp, target,
218 "cfg-handle", NULL);
219 if (cfg_handle)
220 break;
221 }
222
223 bus_id_name = type;
224 if (!strcmp(type, "domain-services-port"))
225 bus_id_name = "ds";
226
227 if (strlen(bus_id_name) >= BUS_ID_SIZE - 4) {
228 printk(KERN_ERR "VIO: bus_id_name [%s] is too long.\n",
229 bus_id_name);
230 return NULL;
231 }
232
233 compat = mdesc_get_property(hp, mp, "device-type", &clen);
234 if (!compat) {
235 clen = 0;
236 } else if (clen > VIO_MAX_COMPAT_LEN) {
237 printk(KERN_ERR "VIO: Compat len %d for [%s] is too long.\n",
238 clen, type);
239 return NULL;
240 }
241
242 vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
243 if (!vdev) {
244 printk(KERN_ERR "VIO: Could not allocate vio_dev\n");
245 return NULL;
246 }
247
248 vdev->mp = mp;
249 memcpy(vdev->type, type, tlen);
250 if (compat)
251 memcpy(vdev->compat, compat, clen);
252 else
253 memset(vdev->compat, 0, sizeof(vdev->compat));
254 vdev->compat_len = clen;
255
256 vdev->channel_id = ~0UL;
257 vdev->tx_irq = ~0;
258 vdev->rx_irq = ~0;
259
260 vio_fill_channel_info(hp, mp, vdev);
261
262 if (!id) {
263 dev_set_name(&vdev->dev, "%s", bus_id_name);
264 vdev->dev_no = ~(u64)0;
265 } else if (!cfg_handle) {
266 dev_set_name(&vdev->dev, "%s-%lu", bus_id_name, *id);
267 vdev->dev_no = *id;
268 } else {
269 dev_set_name(&vdev->dev, "%s-%lu-%lu", bus_id_name,
270 *cfg_handle, *id);
271 vdev->dev_no = *cfg_handle;
272 }
273
274 vdev->dev.parent = parent;
275 vdev->dev.bus = &vio_bus_type;
276 vdev->dev.release = vio_dev_release;
277
278 if (parent == NULL) {
279 dp = cdev_node;
280 } else if (to_vio_dev(parent) == root_vdev) {
281 dp = of_get_next_child(cdev_node, NULL);
282 while (dp) {
283 if (!strcmp(dp->type, type))
284 break;
285
286 dp = of_get_next_child(cdev_node, dp);
287 }
288 } else {
289 dp = to_vio_dev(parent)->dp;
290 }
291 vdev->dp = dp;
292
293 printk(KERN_INFO "VIO: Adding device %s\n", dev_name(&vdev->dev));
294
295 err = device_register(&vdev->dev);
296 if (err) {
297 printk(KERN_ERR "VIO: Could not register device %s, err=%d\n",
298 dev_name(&vdev->dev), err);
299 kfree(vdev);
300 return NULL;
301 }
302 if (vdev->dp)
303 err = sysfs_create_file(&vdev->dev.kobj,
304 &dev_attr_obppath.attr);
305
306 return vdev;
307}
308
309static void vio_add(struct mdesc_handle *hp, u64 node)
310{
311 (void) vio_create_one(hp, node, &root_vdev->dev);
312}
313
314static int vio_md_node_match(struct device *dev, void *arg)
315{
316 struct vio_dev *vdev = to_vio_dev(dev);
317
318 if (vdev->mp == (u64) arg)
319 return 1;
320
321 return 0;
322}
323
324static void vio_remove(struct mdesc_handle *hp, u64 node)
325{
326 struct device *dev;
327
328 dev = device_find_child(&root_vdev->dev, (void *) node,
329 vio_md_node_match);
330 if (dev) {
331 printk(KERN_INFO "VIO: Removing device %s\n", dev_name(dev));
332
333 device_unregister(dev);
334 }
335}
336
337static struct mdesc_notifier_client vio_device_notifier = {
338 .add = vio_add,
339 .remove = vio_remove,
340 .node_name = "virtual-device-port",
341};
342
343/* We are only interested in domain service ports under the
344 * "domain-services" node. On control nodes there is another port
345 * under "openboot" that we should not mess with as aparently that is
346 * reserved exclusively for OBP use.
347 */
348static void vio_add_ds(struct mdesc_handle *hp, u64 node)
349{
350 int found;
351 u64 a;
352
353 found = 0;
354 mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) {
355 u64 target = mdesc_arc_target(hp, a);
356 const char *name = mdesc_node_name(hp, target);
357
358 if (!strcmp(name, "domain-services")) {
359 found = 1;
360 break;
361 }
362 }
363
364 if (found)
365 (void) vio_create_one(hp, node, &root_vdev->dev);
366}
367
368static struct mdesc_notifier_client vio_ds_notifier = {
369 .add = vio_add_ds,
370 .remove = vio_remove,
371 .node_name = "domain-services-port",
372};
373
374static const char *channel_devices_node = "channel-devices";
375static const char *channel_devices_compat = "SUNW,sun4v-channel-devices";
376static const char *cfg_handle_prop = "cfg-handle";
377
378static int __init vio_init(void)
379{
380 struct mdesc_handle *hp;
381 const char *compat;
382 const u64 *cfg_handle;
383 int err, len;
384 u64 root;
385
386 err = bus_register(&vio_bus_type);
387 if (err) {
388 printk(KERN_ERR "VIO: Could not register bus type err=%d\n",
389 err);
390 return err;
391 }
392
393 hp = mdesc_grab();
394 if (!hp)
395 return 0;
396
397 root = mdesc_node_by_name(hp, MDESC_NODE_NULL, channel_devices_node);
398 if (root == MDESC_NODE_NULL) {
399 printk(KERN_INFO "VIO: No channel-devices MDESC node.\n");
400 mdesc_release(hp);
401 return 0;
402 }
403
404 cdev_node = of_find_node_by_name(NULL, "channel-devices");
405 err = -ENODEV;
406 if (!cdev_node) {
407 printk(KERN_INFO "VIO: No channel-devices OBP node.\n");
408 goto out_release;
409 }
410
411 compat = mdesc_get_property(hp, root, "compatible", &len);
412 if (!compat) {
413 printk(KERN_ERR "VIO: Channel devices lacks compatible "
414 "property\n");
415 goto out_release;
416 }
417 if (!of_find_in_proplist(compat, channel_devices_compat, len)) {
418 printk(KERN_ERR "VIO: Channel devices node lacks (%s) "
419 "compat entry.\n", channel_devices_compat);
420 goto out_release;
421 }
422
423 cfg_handle = mdesc_get_property(hp, root, cfg_handle_prop, NULL);
424 if (!cfg_handle) {
425 printk(KERN_ERR "VIO: Channel devices lacks %s property\n",
426 cfg_handle_prop);
427 goto out_release;
428 }
429
430 cdev_cfg_handle = *cfg_handle;
431
432 root_vdev = vio_create_one(hp, root, NULL);
433 err = -ENODEV;
434 if (!root_vdev) {
435 printk(KERN_ERR "VIO: Coult not create root device.\n");
436 goto out_release;
437 }
438
439 mdesc_register_notifier(&vio_device_notifier);
440 mdesc_register_notifier(&vio_ds_notifier);
441
442 mdesc_release(hp);
443
444 return err;
445
446out_release:
447 mdesc_release(hp);
448 return err;
449}
450
451postcore_initcall(vio_init);