diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2006-04-27 03:18:21 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-04-29 04:02:00 -0400 |
commit | e10fa77368dff31140451fac04d78d9f51f0f3ac (patch) | |
tree | 20f3645859dbd995ded831f5a40b78e823e4fc6e /arch/powerpc/kernel/vio.c | |
parent | 95a1ca6cd8e702a19ee56efae522a5816a56a205 (diff) |
[PATCH] powerpc: use the device tree for the iSeries vio bus probe
As an added bonus, since every vio_dev now has a device_node
associated with it, hotplug now works.
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/kernel/vio.c')
-rw-r--r-- | arch/powerpc/kernel/vio.c | 171 |
1 files changed, 143 insertions, 28 deletions
diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index 971020cf3f7d..9b46eed5e637 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c | |||
@@ -22,9 +22,7 @@ | |||
22 | #include <asm/dma.h> | 22 | #include <asm/dma.h> |
23 | #include <asm/vio.h> | 23 | #include <asm/vio.h> |
24 | #include <asm/prom.h> | 24 | #include <asm/prom.h> |
25 | 25 | #include <asm/firmware.h> | |
26 | static const struct vio_device_id *vio_match_device( | ||
27 | const struct vio_device_id *, const struct vio_dev *); | ||
28 | 26 | ||
29 | struct vio_dev vio_bus_device = { /* fake "parent" device */ | 27 | struct vio_dev vio_bus_device = { /* fake "parent" device */ |
30 | .name = vio_bus_device.dev.bus_id, | 28 | .name = vio_bus_device.dev.bus_id, |
@@ -35,6 +33,27 @@ struct vio_dev vio_bus_device = { /* fake "parent" device */ | |||
35 | 33 | ||
36 | static struct vio_bus_ops vio_bus_ops; | 34 | static struct vio_bus_ops vio_bus_ops; |
37 | 35 | ||
36 | /** | ||
37 | * vio_match_device: - Tell if a VIO device has a matching | ||
38 | * VIO device id structure. | ||
39 | * @ids: array of VIO device id structures to search in | ||
40 | * @dev: the VIO device structure to match against | ||
41 | * | ||
42 | * Used by a driver to check whether a VIO device present in the | ||
43 | * system is in its list of supported devices. Returns the matching | ||
44 | * vio_device_id structure or NULL if there is no match. | ||
45 | */ | ||
46 | static const struct vio_device_id *vio_match_device( | ||
47 | const struct vio_device_id *ids, const struct vio_dev *dev) | ||
48 | { | ||
49 | while (ids->type[0] != '\0') { | ||
50 | if (vio_bus_ops.match(ids, dev)) | ||
51 | return ids; | ||
52 | ids++; | ||
53 | } | ||
54 | return NULL; | ||
55 | } | ||
56 | |||
38 | /* | 57 | /* |
39 | * Convert from struct device to struct vio_dev and pass to driver. | 58 | * Convert from struct device to struct vio_dev and pass to driver. |
40 | * dev->driver has already been set by generic code because vio_bus_match | 59 | * dev->driver has already been set by generic code because vio_bus_match |
@@ -107,25 +126,76 @@ void vio_unregister_driver(struct vio_driver *viodrv) | |||
107 | EXPORT_SYMBOL(vio_unregister_driver); | 126 | EXPORT_SYMBOL(vio_unregister_driver); |
108 | 127 | ||
109 | /** | 128 | /** |
110 | * vio_match_device: - Tell if a VIO device has a matching | 129 | * vio_register_device_node: - Register a new vio device. |
111 | * VIO device id structure. | 130 | * @of_node: The OF node for this device. |
112 | * @ids: array of VIO device id structures to search in | ||
113 | * @dev: the VIO device structure to match against | ||
114 | * | 131 | * |
115 | * Used by a driver to check whether a VIO device present in the | 132 | * Creates and initializes a vio_dev structure from the data in |
116 | * system is in its list of supported devices. Returns the matching | 133 | * of_node (dev.platform_data) and adds it to the list of virtual devices. |
117 | * vio_device_id structure or NULL if there is no match. | 134 | * Returns a pointer to the created vio_dev or NULL if node has |
135 | * NULL device_type or compatible fields. | ||
118 | */ | 136 | */ |
119 | static const struct vio_device_id *vio_match_device( | 137 | struct vio_dev * __devinit vio_register_device_node(struct device_node *of_node) |
120 | const struct vio_device_id *ids, const struct vio_dev *dev) | ||
121 | { | 138 | { |
122 | while (ids->type[0] != '\0') { | 139 | struct vio_dev *viodev; |
123 | if (vio_bus_ops.match(ids, dev)) | 140 | unsigned int *unit_address; |
124 | return ids; | 141 | unsigned int *irq_p; |
125 | ids++; | 142 | |
143 | /* we need the 'device_type' property, in order to match with drivers */ | ||
144 | if (of_node->type == NULL) { | ||
145 | printk(KERN_WARNING "%s: node %s missing 'device_type'\n", | ||
146 | __FUNCTION__, | ||
147 | of_node->name ? of_node->name : "<unknown>"); | ||
148 | return NULL; | ||
126 | } | 149 | } |
127 | return NULL; | 150 | |
151 | unit_address = (unsigned int *)get_property(of_node, "reg", NULL); | ||
152 | if (unit_address == NULL) { | ||
153 | printk(KERN_WARNING "%s: node %s missing 'reg'\n", | ||
154 | __FUNCTION__, | ||
155 | of_node->name ? of_node->name : "<unknown>"); | ||
156 | return NULL; | ||
157 | } | ||
158 | |||
159 | /* allocate a vio_dev for this node */ | ||
160 | viodev = kzalloc(sizeof(struct vio_dev), GFP_KERNEL); | ||
161 | if (viodev == NULL) | ||
162 | return NULL; | ||
163 | |||
164 | viodev->dev.platform_data = of_node_get(of_node); | ||
165 | |||
166 | viodev->irq = NO_IRQ; | ||
167 | irq_p = (unsigned int *)get_property(of_node, "interrupts", NULL); | ||
168 | if (irq_p) { | ||
169 | int virq = virt_irq_create_mapping(*irq_p); | ||
170 | if (virq == NO_IRQ) { | ||
171 | printk(KERN_ERR "Unable to allocate interrupt " | ||
172 | "number for %s\n", of_node->full_name); | ||
173 | } else | ||
174 | viodev->irq = irq_offset_up(virq); | ||
175 | } | ||
176 | |||
177 | snprintf(viodev->dev.bus_id, BUS_ID_SIZE, "%x", *unit_address); | ||
178 | viodev->name = of_node->name; | ||
179 | viodev->type = of_node->type; | ||
180 | viodev->unit_address = *unit_address; | ||
181 | if (firmware_has_feature(FW_FEATURE_ISERIES)) { | ||
182 | unit_address = (unsigned int *)get_property(of_node, | ||
183 | "linux,unit_address", NULL); | ||
184 | if (unit_address != NULL) | ||
185 | viodev->unit_address = *unit_address; | ||
186 | } | ||
187 | viodev->iommu_table = vio_bus_ops.build_iommu_table(viodev); | ||
188 | |||
189 | /* register with generic device framework */ | ||
190 | if (vio_register_device(viodev) == NULL) { | ||
191 | /* XXX free TCE table */ | ||
192 | kfree(viodev); | ||
193 | return NULL; | ||
194 | } | ||
195 | |||
196 | return viodev; | ||
128 | } | 197 | } |
198 | EXPORT_SYMBOL(vio_register_device_node); | ||
129 | 199 | ||
130 | /** | 200 | /** |
131 | * vio_bus_init: - Initialize the virtual IO bus | 201 | * vio_bus_init: - Initialize the virtual IO bus |
@@ -133,6 +203,7 @@ static const struct vio_device_id *vio_match_device( | |||
133 | int __init vio_bus_init(struct vio_bus_ops *ops) | 203 | int __init vio_bus_init(struct vio_bus_ops *ops) |
134 | { | 204 | { |
135 | int err; | 205 | int err; |
206 | struct device_node *node_vroot; | ||
136 | 207 | ||
137 | vio_bus_ops = *ops; | 208 | vio_bus_ops = *ops; |
138 | 209 | ||
@@ -153,23 +224,54 @@ int __init vio_bus_init(struct vio_bus_ops *ops) | |||
153 | return err; | 224 | return err; |
154 | } | 225 | } |
155 | 226 | ||
227 | node_vroot = find_devices("vdevice"); | ||
228 | if (node_vroot) { | ||
229 | struct device_node *of_node; | ||
230 | |||
231 | /* | ||
232 | * Create struct vio_devices for each virtual device in | ||
233 | * the device tree. Drivers will associate with them later. | ||
234 | */ | ||
235 | for (of_node = node_vroot->child; of_node != NULL; | ||
236 | of_node = of_node->sibling) { | ||
237 | printk(KERN_DEBUG "%s: processing %p\n", | ||
238 | __FUNCTION__, of_node); | ||
239 | vio_register_device_node(of_node); | ||
240 | } | ||
241 | } | ||
242 | |||
156 | return 0; | 243 | return 0; |
157 | } | 244 | } |
158 | 245 | ||
159 | /* vio_dev refcount hit 0 */ | 246 | /* vio_dev refcount hit 0 */ |
160 | static void __devinit vio_dev_release(struct device *dev) | 247 | static void __devinit vio_dev_release(struct device *dev) |
161 | { | 248 | { |
162 | if (vio_bus_ops.release_device) | 249 | if (dev->platform_data) { |
163 | vio_bus_ops.release_device(dev); | 250 | /* XXX free TCE table */ |
251 | of_node_put(dev->platform_data); | ||
252 | } | ||
164 | kfree(to_vio_dev(dev)); | 253 | kfree(to_vio_dev(dev)); |
165 | } | 254 | } |
166 | 255 | ||
167 | static ssize_t viodev_show_name(struct device *dev, | 256 | static ssize_t name_show(struct device *dev, |
168 | struct device_attribute *attr, char *buf) | 257 | struct device_attribute *attr, char *buf) |
169 | { | 258 | { |
170 | return sprintf(buf, "%s\n", to_vio_dev(dev)->name); | 259 | return sprintf(buf, "%s\n", to_vio_dev(dev)->name); |
171 | } | 260 | } |
172 | DEVICE_ATTR(name, S_IRUSR | S_IRGRP | S_IROTH, viodev_show_name, NULL); | 261 | |
262 | static ssize_t devspec_show(struct device *dev, | ||
263 | struct device_attribute *attr, char *buf) | ||
264 | { | ||
265 | struct device_node *of_node = dev->platform_data; | ||
266 | |||
267 | return sprintf(buf, "%s\n", of_node ? of_node->full_name : "none"); | ||
268 | } | ||
269 | |||
270 | static struct device_attribute vio_dev_attrs[] = { | ||
271 | __ATTR_RO(name), | ||
272 | __ATTR_RO(devspec), | ||
273 | __ATTR_NULL | ||
274 | }; | ||
173 | 275 | ||
174 | struct vio_dev * __devinit vio_register_device(struct vio_dev *viodev) | 276 | struct vio_dev * __devinit vio_register_device(struct vio_dev *viodev) |
175 | { | 277 | { |
@@ -184,16 +286,12 @@ struct vio_dev * __devinit vio_register_device(struct vio_dev *viodev) | |||
184 | __FUNCTION__, viodev->dev.bus_id); | 286 | __FUNCTION__, viodev->dev.bus_id); |
185 | return NULL; | 287 | return NULL; |
186 | } | 288 | } |
187 | device_create_file(&viodev->dev, &dev_attr_name); | ||
188 | 289 | ||
189 | return viodev; | 290 | return viodev; |
190 | } | 291 | } |
191 | 292 | ||
192 | void __devinit vio_unregister_device(struct vio_dev *viodev) | 293 | void __devinit vio_unregister_device(struct vio_dev *viodev) |
193 | { | 294 | { |
194 | if (vio_bus_ops.unregister_device) | ||
195 | vio_bus_ops.unregister_device(viodev); | ||
196 | device_remove_file(&viodev->dev, &dev_attr_name); | ||
197 | device_unregister(&viodev->dev); | 295 | device_unregister(&viodev->dev); |
198 | } | 296 | } |
199 | EXPORT_SYMBOL(vio_unregister_device); | 297 | EXPORT_SYMBOL(vio_unregister_device); |
@@ -267,22 +365,23 @@ static int vio_hotplug(struct device *dev, char **envp, int num_envp, | |||
267 | char *buffer, int buffer_size) | 365 | char *buffer, int buffer_size) |
268 | { | 366 | { |
269 | const struct vio_dev *vio_dev = to_vio_dev(dev); | 367 | const struct vio_dev *vio_dev = to_vio_dev(dev); |
368 | struct device_node *dn = dev->platform_data; | ||
270 | char *cp; | 369 | char *cp; |
271 | int length; | 370 | int length; |
272 | 371 | ||
273 | if (!num_envp) | 372 | if (!num_envp) |
274 | return -ENOMEM; | 373 | return -ENOMEM; |
275 | 374 | ||
276 | if (!vio_dev->dev.platform_data) | 375 | if (!dn) |
277 | return -ENODEV; | 376 | return -ENODEV; |
278 | cp = (char *)get_property(vio_dev->dev.platform_data, "compatible", &length); | 377 | cp = (char *)get_property(dn, "compatible", &length); |
279 | if (!cp) | 378 | if (!cp) |
280 | return -ENODEV; | 379 | return -ENODEV; |
281 | 380 | ||
282 | envp[0] = buffer; | 381 | envp[0] = buffer; |
283 | length = scnprintf(buffer, buffer_size, "MODALIAS=vio:T%sS%s", | 382 | length = scnprintf(buffer, buffer_size, "MODALIAS=vio:T%sS%s", |
284 | vio_dev->type, cp); | 383 | vio_dev->type, cp); |
285 | if (buffer_size - length <= 0) | 384 | if ((buffer_size - length) <= 0) |
286 | return -ENOMEM; | 385 | return -ENOMEM; |
287 | envp[1] = NULL; | 386 | envp[1] = NULL; |
288 | return 0; | 387 | return 0; |
@@ -290,9 +389,25 @@ static int vio_hotplug(struct device *dev, char **envp, int num_envp, | |||
290 | 389 | ||
291 | struct bus_type vio_bus_type = { | 390 | struct bus_type vio_bus_type = { |
292 | .name = "vio", | 391 | .name = "vio", |
392 | .dev_attrs = vio_dev_attrs, | ||
293 | .uevent = vio_hotplug, | 393 | .uevent = vio_hotplug, |
294 | .match = vio_bus_match, | 394 | .match = vio_bus_match, |
295 | .probe = vio_bus_probe, | 395 | .probe = vio_bus_probe, |
296 | .remove = vio_bus_remove, | 396 | .remove = vio_bus_remove, |
297 | .shutdown = vio_bus_shutdown, | 397 | .shutdown = vio_bus_shutdown, |
298 | }; | 398 | }; |
399 | |||
400 | /** | ||
401 | * vio_get_attribute: - get attribute for virtual device | ||
402 | * @vdev: The vio device to get property. | ||
403 | * @which: The property/attribute to be extracted. | ||
404 | * @length: Pointer to length of returned data size (unused if NULL). | ||
405 | * | ||
406 | * Calls prom.c's get_property() to return the value of the | ||
407 | * attribute specified by @which | ||
408 | */ | ||
409 | const void *vio_get_attribute(struct vio_dev *vdev, char *which, int *length) | ||
410 | { | ||
411 | return get_property(vdev->dev.platform_data, which, length); | ||
412 | } | ||
413 | EXPORT_SYMBOL(vio_get_attribute); | ||