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 | |
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>
-rw-r--r-- | arch/powerpc/kernel/vio.c | 171 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/setup.c | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/vio.c | 70 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/vio.c | 147 | ||||
-rw-r--r-- | include/asm-powerpc/vio.h | 9 |
5 files changed, 169 insertions, 232 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); | ||
diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c index 4862b8e7c78c..901acbcc0888 100644 --- a/arch/powerpc/platforms/iseries/setup.c +++ b/arch/powerpc/platforms/iseries/setup.c | |||
@@ -951,6 +951,7 @@ void dt_vdevices(struct iseries_flat_dt *dt) | |||
951 | dt_prop_str(dt, "device_type", "vlan"); | 951 | dt_prop_str(dt, "device_type", "vlan"); |
952 | dt_prop_empty(dt, "compatible"); | 952 | dt_prop_empty(dt, "compatible"); |
953 | dt_prop_u32(dt, "reg", reg + i); | 953 | dt_prop_u32(dt, "reg", reg + i); |
954 | dt_prop_u32(dt, "linux,unit_address", i); | ||
954 | 955 | ||
955 | mac_addr[0] = 0x02; | 956 | mac_addr[0] = 0x02; |
956 | mac_addr[1] = 0x01; | 957 | mac_addr[1] = 0x01; |
@@ -971,6 +972,7 @@ void dt_vdevices(struct iseries_flat_dt *dt) | |||
971 | dt_prop_str(dt, "device_type", "viodasd"); | 972 | dt_prop_str(dt, "device_type", "viodasd"); |
972 | dt_prop_empty(dt, "compatible"); | 973 | dt_prop_empty(dt, "compatible"); |
973 | dt_prop_u32(dt, "reg", reg + i); | 974 | dt_prop_u32(dt, "reg", reg + i); |
975 | dt_prop_u32(dt, "linux,unit_address", i); | ||
974 | dt_end_node(dt); | 976 | dt_end_node(dt); |
975 | } | 977 | } |
976 | reg += HVMAXARCHITECTEDVIRTUALDISKS; | 978 | reg += HVMAXARCHITECTEDVIRTUALDISKS; |
@@ -980,6 +982,7 @@ void dt_vdevices(struct iseries_flat_dt *dt) | |||
980 | dt_prop_str(dt, "device_type", "viocd"); | 982 | dt_prop_str(dt, "device_type", "viocd"); |
981 | dt_prop_empty(dt, "compatible"); | 983 | dt_prop_empty(dt, "compatible"); |
982 | dt_prop_u32(dt, "reg", reg + i); | 984 | dt_prop_u32(dt, "reg", reg + i); |
985 | dt_prop_u32(dt, "linux,unit_address", i); | ||
983 | dt_end_node(dt); | 986 | dt_end_node(dt); |
984 | } | 987 | } |
985 | reg += HVMAXARCHITECTEDVIRTUALCDROMS; | 988 | reg += HVMAXARCHITECTEDVIRTUALCDROMS; |
@@ -989,6 +992,7 @@ void dt_vdevices(struct iseries_flat_dt *dt) | |||
989 | dt_prop_str(dt, "device_type", "viotape"); | 992 | dt_prop_str(dt, "device_type", "viotape"); |
990 | dt_prop_empty(dt, "compatible"); | 993 | dt_prop_empty(dt, "compatible"); |
991 | dt_prop_u32(dt, "reg", reg + i); | 994 | dt_prop_u32(dt, "reg", reg + i); |
995 | dt_prop_u32(dt, "linux,unit_address", i); | ||
992 | dt_end_node(dt); | 996 | dt_end_node(dt); |
993 | } | 997 | } |
994 | 998 | ||
diff --git a/arch/powerpc/platforms/iseries/vio.c b/arch/powerpc/platforms/iseries/vio.c index 22045a2829a9..a689da6f2b07 100644 --- a/arch/powerpc/platforms/iseries/vio.c +++ b/arch/powerpc/platforms/iseries/vio.c | |||
@@ -43,58 +43,11 @@ static void __init iommu_vio_init(void) | |||
43 | printk("Virtual Bus VIO TCE table failed.\n"); | 43 | printk("Virtual Bus VIO TCE table failed.\n"); |
44 | } | 44 | } |
45 | 45 | ||
46 | /** | 46 | static struct iommu_table *vio_build_iommu_table(struct vio_dev *dev) |
47 | * vio_register_device_iseries: - Register a new iSeries vio device. | ||
48 | * @voidev: The device to register. | ||
49 | */ | ||
50 | static struct vio_dev *__init vio_register_device_iseries(char *type, | ||
51 | uint32_t unit_num) | ||
52 | { | 47 | { |
53 | struct vio_dev *viodev; | 48 | if (strcmp(dev->type, "vlan") == 0) |
54 | 49 | return &veth_iommu_table; | |
55 | /* allocate a vio_dev for this device */ | 50 | return &vio_iommu_table; |
56 | viodev = kmalloc(sizeof(struct vio_dev), GFP_KERNEL); | ||
57 | if (!viodev) | ||
58 | return NULL; | ||
59 | memset(viodev, 0, sizeof(struct vio_dev)); | ||
60 | |||
61 | snprintf(viodev->dev.bus_id, BUS_ID_SIZE, "%s%d", type, unit_num); | ||
62 | |||
63 | viodev->name = viodev->dev.bus_id; | ||
64 | viodev->type = type; | ||
65 | viodev->unit_address = unit_num; | ||
66 | viodev->iommu_table = &vio_iommu_table; | ||
67 | if (vio_register_device(viodev) == NULL) { | ||
68 | kfree(viodev); | ||
69 | return NULL; | ||
70 | } | ||
71 | return viodev; | ||
72 | } | ||
73 | |||
74 | static void __init probe_bus_iseries(void) | ||
75 | { | ||
76 | HvLpIndexMap vlan_map; | ||
77 | struct vio_dev *viodev; | ||
78 | int i; | ||
79 | |||
80 | /* there is only one of each of these */ | ||
81 | vio_register_device_iseries("viocons", 0); | ||
82 | vio_register_device_iseries("vscsi", 0); | ||
83 | |||
84 | vlan_map = HvLpConfig_getVirtualLanIndexMap(); | ||
85 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) { | ||
86 | if ((vlan_map & (0x8000 >> i)) == 0) | ||
87 | continue; | ||
88 | viodev = vio_register_device_iseries("vlan", i); | ||
89 | /* veth is special and has it own iommu_table */ | ||
90 | viodev->iommu_table = &veth_iommu_table; | ||
91 | } | ||
92 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALDISKS; i++) | ||
93 | vio_register_device_iseries("viodasd", i); | ||
94 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALCDROMS; i++) | ||
95 | vio_register_device_iseries("viocd", i); | ||
96 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALTAPES; i++) | ||
97 | vio_register_device_iseries("viotape", i); | ||
98 | } | 51 | } |
99 | 52 | ||
100 | /** | 53 | /** |
@@ -109,6 +62,7 @@ static int vio_match_device_iseries(const struct vio_device_id *id, | |||
109 | 62 | ||
110 | static struct vio_bus_ops vio_bus_ops_iseries = { | 63 | static struct vio_bus_ops vio_bus_ops_iseries = { |
111 | .match = vio_match_device_iseries, | 64 | .match = vio_match_device_iseries, |
65 | .build_iommu_table = vio_build_iommu_table, | ||
112 | }; | 66 | }; |
113 | 67 | ||
114 | /** | 68 | /** |
@@ -116,16 +70,10 @@ static struct vio_bus_ops vio_bus_ops_iseries = { | |||
116 | */ | 70 | */ |
117 | static int __init vio_bus_init_iseries(void) | 71 | static int __init vio_bus_init_iseries(void) |
118 | { | 72 | { |
119 | int err; | 73 | iommu_vio_init(); |
120 | 74 | vio_bus_device.iommu_table = &vio_iommu_table; | |
121 | err = vio_bus_init(&vio_bus_ops_iseries); | 75 | iSeries_vio_dev = &vio_bus_device.dev; |
122 | if (err == 0) { | 76 | return vio_bus_init(&vio_bus_ops_iseries); |
123 | iommu_vio_init(); | ||
124 | vio_bus_device.iommu_table = &vio_iommu_table; | ||
125 | iSeries_vio_dev = &vio_bus_device.dev; | ||
126 | probe_bus_iseries(); | ||
127 | } | ||
128 | return err; | ||
129 | } | 77 | } |
130 | 78 | ||
131 | __initcall(vio_bus_init_iseries); | 79 | __initcall(vio_bus_init_iseries); |
diff --git a/arch/powerpc/platforms/pseries/vio.c b/arch/powerpc/platforms/pseries/vio.c index 8e53e04ada8b..b3925aefdac6 100644 --- a/arch/powerpc/platforms/pseries/vio.c +++ b/arch/powerpc/platforms/pseries/vio.c | |||
@@ -26,26 +26,6 @@ | |||
26 | 26 | ||
27 | extern struct subsystem devices_subsys; /* needed for vio_find_name() */ | 27 | extern struct subsystem devices_subsys; /* needed for vio_find_name() */ |
28 | 28 | ||
29 | static void probe_bus_pseries(void) | ||
30 | { | ||
31 | struct device_node *node_vroot, *of_node; | ||
32 | |||
33 | node_vroot = find_devices("vdevice"); | ||
34 | if ((node_vroot == NULL) || (node_vroot->child == NULL)) | ||
35 | /* this machine doesn't do virtual IO, and that's ok */ | ||
36 | return; | ||
37 | |||
38 | /* | ||
39 | * Create struct vio_devices for each virtual device in the device tree. | ||
40 | * Drivers will associate with them later. | ||
41 | */ | ||
42 | for (of_node = node_vroot->child; of_node != NULL; | ||
43 | of_node = of_node->sibling) { | ||
44 | printk(KERN_DEBUG "%s: processing %p\n", __FUNCTION__, of_node); | ||
45 | vio_register_device_node(of_node); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | /** | 29 | /** |
50 | * vio_match_device_pseries: - Tell if a pSeries VIO device matches a | 30 | * vio_match_device_pseries: - Tell if a pSeries VIO device matches a |
51 | * vio_device_id | 31 | * vio_device_id |
@@ -57,47 +37,6 @@ static int vio_match_device_pseries(const struct vio_device_id *id, | |||
57 | device_is_compatible(dev->dev.platform_data, id->compat); | 37 | device_is_compatible(dev->dev.platform_data, id->compat); |
58 | } | 38 | } |
59 | 39 | ||
60 | static void vio_release_device_pseries(struct device *dev) | ||
61 | { | ||
62 | /* XXX free TCE table */ | ||
63 | of_node_put(dev->platform_data); | ||
64 | } | ||
65 | |||
66 | static ssize_t viodev_show_devspec(struct device *dev, | ||
67 | struct device_attribute *attr, char *buf) | ||
68 | { | ||
69 | struct device_node *of_node = dev->platform_data; | ||
70 | |||
71 | return sprintf(buf, "%s\n", of_node->full_name); | ||
72 | } | ||
73 | DEVICE_ATTR(devspec, S_IRUSR | S_IRGRP | S_IROTH, viodev_show_devspec, NULL); | ||
74 | |||
75 | static void vio_unregister_device_pseries(struct vio_dev *viodev) | ||
76 | { | ||
77 | device_remove_file(&viodev->dev, &dev_attr_devspec); | ||
78 | } | ||
79 | |||
80 | static struct vio_bus_ops vio_bus_ops_pseries = { | ||
81 | .match = vio_match_device_pseries, | ||
82 | .unregister_device = vio_unregister_device_pseries, | ||
83 | .release_device = vio_release_device_pseries, | ||
84 | }; | ||
85 | |||
86 | /** | ||
87 | * vio_bus_init_pseries: - Initialize the pSeries virtual IO bus | ||
88 | */ | ||
89 | static int __init vio_bus_init_pseries(void) | ||
90 | { | ||
91 | int err; | ||
92 | |||
93 | err = vio_bus_init(&vio_bus_ops_pseries); | ||
94 | if (err == 0) | ||
95 | probe_bus_pseries(); | ||
96 | return err; | ||
97 | } | ||
98 | |||
99 | __initcall(vio_bus_init_pseries); | ||
100 | |||
101 | /** | 40 | /** |
102 | * vio_build_iommu_table: - gets the dma information from OF and | 41 | * vio_build_iommu_table: - gets the dma information from OF and |
103 | * builds the TCE tree. | 42 | * builds the TCE tree. |
@@ -136,88 +75,20 @@ static struct iommu_table *vio_build_iommu_table(struct vio_dev *dev) | |||
136 | return iommu_init_table(newTceTable); | 75 | return iommu_init_table(newTceTable); |
137 | } | 76 | } |
138 | 77 | ||
78 | static struct vio_bus_ops vio_bus_ops_pseries = { | ||
79 | .match = vio_match_device_pseries, | ||
80 | .build_iommu_table = vio_build_iommu_table, | ||
81 | }; | ||
82 | |||
139 | /** | 83 | /** |
140 | * vio_register_device_node: - Register a new vio device. | 84 | * vio_bus_init_pseries: - Initialize the pSeries virtual IO bus |
141 | * @of_node: The OF node for this device. | ||
142 | * | ||
143 | * Creates and initializes a vio_dev structure from the data in | ||
144 | * of_node (dev.platform_data) and adds it to the list of virtual devices. | ||
145 | * Returns a pointer to the created vio_dev or NULL if node has | ||
146 | * NULL device_type or compatible fields. | ||
147 | */ | 85 | */ |
148 | struct vio_dev * __devinit vio_register_device_node(struct device_node *of_node) | 86 | static int __init vio_bus_init_pseries(void) |
149 | { | 87 | { |
150 | struct vio_dev *viodev; | 88 | return vio_bus_init(&vio_bus_ops_pseries); |
151 | unsigned int *unit_address; | ||
152 | unsigned int *irq_p; | ||
153 | |||
154 | /* we need the 'device_type' property, in order to match with drivers */ | ||
155 | if ((NULL == of_node->type)) { | ||
156 | printk(KERN_WARNING | ||
157 | "%s: node %s missing 'device_type'\n", __FUNCTION__, | ||
158 | of_node->name ? of_node->name : "<unknown>"); | ||
159 | return NULL; | ||
160 | } | ||
161 | |||
162 | unit_address = (unsigned int *)get_property(of_node, "reg", NULL); | ||
163 | if (!unit_address) { | ||
164 | printk(KERN_WARNING "%s: node %s missing 'reg'\n", __FUNCTION__, | ||
165 | of_node->name ? of_node->name : "<unknown>"); | ||
166 | return NULL; | ||
167 | } | ||
168 | |||
169 | /* allocate a vio_dev for this node */ | ||
170 | viodev = kmalloc(sizeof(struct vio_dev), GFP_KERNEL); | ||
171 | if (!viodev) { | ||
172 | return NULL; | ||
173 | } | ||
174 | memset(viodev, 0, sizeof(struct vio_dev)); | ||
175 | |||
176 | viodev->dev.platform_data = of_node_get(of_node); | ||
177 | |||
178 | viodev->irq = NO_IRQ; | ||
179 | irq_p = (unsigned int *)get_property(of_node, "interrupts", NULL); | ||
180 | if (irq_p) { | ||
181 | int virq = virt_irq_create_mapping(*irq_p); | ||
182 | if (virq == NO_IRQ) { | ||
183 | printk(KERN_ERR "Unable to allocate interrupt " | ||
184 | "number for %s\n", of_node->full_name); | ||
185 | } else | ||
186 | viodev->irq = irq_offset_up(virq); | ||
187 | } | ||
188 | |||
189 | snprintf(viodev->dev.bus_id, BUS_ID_SIZE, "%x", *unit_address); | ||
190 | viodev->name = of_node->name; | ||
191 | viodev->type = of_node->type; | ||
192 | viodev->unit_address = *unit_address; | ||
193 | viodev->iommu_table = vio_build_iommu_table(viodev); | ||
194 | |||
195 | /* register with generic device framework */ | ||
196 | if (vio_register_device(viodev) == NULL) { | ||
197 | /* XXX free TCE table */ | ||
198 | kfree(viodev); | ||
199 | return NULL; | ||
200 | } | ||
201 | device_create_file(&viodev->dev, &dev_attr_devspec); | ||
202 | |||
203 | return viodev; | ||
204 | } | 89 | } |
205 | EXPORT_SYMBOL(vio_register_device_node); | ||
206 | 90 | ||
207 | /** | 91 | __initcall(vio_bus_init_pseries); |
208 | * vio_get_attribute: - get attribute for virtual device | ||
209 | * @vdev: The vio device to get property. | ||
210 | * @which: The property/attribute to be extracted. | ||
211 | * @length: Pointer to length of returned data size (unused if NULL). | ||
212 | * | ||
213 | * Calls prom.c's get_property() to return the value of the | ||
214 | * attribute specified by the preprocessor constant @which | ||
215 | */ | ||
216 | const void * vio_get_attribute(struct vio_dev *vdev, void* which, int* length) | ||
217 | { | ||
218 | return get_property(vdev->dev.platform_data, (char*)which, length); | ||
219 | } | ||
220 | EXPORT_SYMBOL(vio_get_attribute); | ||
221 | 92 | ||
222 | /* vio_find_name() - internal because only vio.c knows how we formatted the | 93 | /* vio_find_name() - internal because only vio.c knows how we formatted the |
223 | * kobject name | 94 | * kobject name |
diff --git a/include/asm-powerpc/vio.h b/include/asm-powerpc/vio.h index 0544ece51761..0055d8e6102d 100644 --- a/include/asm-powerpc/vio.h +++ b/include/asm-powerpc/vio.h | |||
@@ -66,8 +66,7 @@ struct vio_driver { | |||
66 | 66 | ||
67 | struct vio_bus_ops { | 67 | struct vio_bus_ops { |
68 | int (*match)(const struct vio_device_id *id, const struct vio_dev *dev); | 68 | int (*match)(const struct vio_device_id *id, const struct vio_dev *dev); |
69 | void (*unregister_device)(struct vio_dev *); | 69 | struct iommu_table *(*build_iommu_table)(struct vio_dev *dev); |
70 | void (*release_device)(struct device *); | ||
71 | }; | 70 | }; |
72 | 71 | ||
73 | extern struct dma_mapping_ops vio_dma_ops; | 72 | extern struct dma_mapping_ops vio_dma_ops; |
@@ -82,14 +81,14 @@ extern void __devinit vio_unregister_device(struct vio_dev *dev); | |||
82 | 81 | ||
83 | extern int vio_bus_init(struct vio_bus_ops *); | 82 | extern int vio_bus_init(struct vio_bus_ops *); |
84 | 83 | ||
85 | #ifdef CONFIG_PPC_PSERIES | ||
86 | struct device_node; | 84 | struct device_node; |
87 | 85 | ||
88 | extern struct vio_dev * __devinit vio_register_device_node( | 86 | extern struct vio_dev * __devinit vio_register_device_node( |
89 | struct device_node *node_vdev); | 87 | struct device_node *node_vdev); |
90 | extern struct vio_dev *vio_find_node(struct device_node *vnode); | 88 | extern const void *vio_get_attribute(struct vio_dev *vdev, char *which, |
91 | extern const void *vio_get_attribute(struct vio_dev *vdev, void *which, | ||
92 | int *length); | 89 | int *length); |
90 | #ifdef CONFIG_PPC_PSERIES | ||
91 | extern struct vio_dev *vio_find_node(struct device_node *vnode); | ||
93 | extern int vio_enable_interrupts(struct vio_dev *dev); | 92 | extern int vio_enable_interrupts(struct vio_dev *dev); |
94 | extern int vio_disable_interrupts(struct vio_dev *dev); | 93 | extern int vio_disable_interrupts(struct vio_dev *dev); |
95 | #endif | 94 | #endif |