diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2007-10-11 00:59:54 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-10-11 06:40:48 -0400 |
commit | 8251b4c481bca72568e9c1042ea11189838e5f6d (patch) | |
tree | 41991f93aec12592885ac33a5312dcf4024ebe5f /arch/powerpc | |
parent | 7465ce0db310d2fa29f721da7e3aacd1dad7090f (diff) |
[POWERPC] iSeries: Move viodasd probing
This way we only have entries in the device tree for disks that actually
exist. A slight complication is that disks may be attached to LPARs
at runtime.
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Acked-by: Jens Axboe <jens.axboe@oracle.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/platforms/iseries/dt.c | 6 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/vio.c | 301 |
2 files changed, 210 insertions, 97 deletions
diff --git a/arch/powerpc/platforms/iseries/dt.c b/arch/powerpc/platforms/iseries/dt.c index 2e4ad6b34506..4543c4bc3a56 100644 --- a/arch/powerpc/platforms/iseries/dt.c +++ b/arch/powerpc/platforms/iseries/dt.c | |||
@@ -72,7 +72,6 @@ static char __initdata device_type_cpu[] = "cpu"; | |||
72 | static char __initdata device_type_memory[] = "memory"; | 72 | static char __initdata device_type_memory[] = "memory"; |
73 | static char __initdata device_type_serial[] = "serial"; | 73 | static char __initdata device_type_serial[] = "serial"; |
74 | static char __initdata device_type_network[] = "network"; | 74 | static char __initdata device_type_network[] = "network"; |
75 | static char __initdata device_type_block[] = "block"; | ||
76 | static char __initdata device_type_pci[] = "pci"; | 75 | static char __initdata device_type_pci[] = "pci"; |
77 | static char __initdata device_type_vdevice[] = "vdevice"; | 76 | static char __initdata device_type_vdevice[] = "vdevice"; |
78 | static char __initdata device_type_vscsi[] = "vscsi"; | 77 | static char __initdata device_type_vscsi[] = "vscsi"; |
@@ -374,11 +373,6 @@ static void __init dt_vdevices(struct iseries_flat_dt *dt) | |||
374 | 373 | ||
375 | dt_end_node(dt); | 374 | dt_end_node(dt); |
376 | } | 375 | } |
377 | reg += HVMAXARCHITECTEDVIRTUALLANS; | ||
378 | |||
379 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALDISKS; i++) | ||
380 | dt_do_vdevice(dt, "viodasd", reg, i, device_type_block, | ||
381 | "IBM,iSeries-viodasd", 1); | ||
382 | 376 | ||
383 | dt_end_node(dt); | 377 | dt_end_node(dt); |
384 | } | 378 | } |
diff --git a/arch/powerpc/platforms/iseries/vio.c b/arch/powerpc/platforms/iseries/vio.c index a4cc990a26a0..910b00b4703e 100644 --- a/arch/powerpc/platforms/iseries/vio.c +++ b/arch/powerpc/platforms/iseries/vio.c | |||
@@ -25,8 +25,10 @@ | |||
25 | #include <linux/gfp.h> | 25 | #include <linux/gfp.h> |
26 | #include <linux/completion.h> | 26 | #include <linux/completion.h> |
27 | #include <linux/proc_fs.h> | 27 | #include <linux/proc_fs.h> |
28 | #include <linux/module.h> | ||
28 | 29 | ||
29 | #include <asm/firmware.h> | 30 | #include <asm/firmware.h> |
31 | #include <asm/vio.h> | ||
30 | #include <asm/iseries/vio.h> | 32 | #include <asm/iseries/vio.h> |
31 | #include <asm/iseries/iommu.h> | 33 | #include <asm/iseries/iommu.h> |
32 | #include <asm/iseries/hv_types.h> | 34 | #include <asm/iseries/hv_types.h> |
@@ -57,7 +59,7 @@ struct vio_resource { | |||
57 | char model[3]; | 59 | char model[3]; |
58 | }; | 60 | }; |
59 | 61 | ||
60 | static struct property * __init new_property(const char *name, int length, | 62 | static struct property *new_property(const char *name, int length, |
61 | const void *value) | 63 | const void *value) |
62 | { | 64 | { |
63 | struct property *np = kzalloc(sizeof(*np) + strlen(name) + 1 + length, | 65 | struct property *np = kzalloc(sizeof(*np) + strlen(name) + 1 + length, |
@@ -78,7 +80,7 @@ static void __init free_property(struct property *np) | |||
78 | kfree(np); | 80 | kfree(np); |
79 | } | 81 | } |
80 | 82 | ||
81 | static struct device_node * __init new_node(const char *path, | 83 | static struct device_node *new_node(const char *path, |
82 | struct device_node *parent) | 84 | struct device_node *parent) |
83 | { | 85 | { |
84 | struct device_node *np = kzalloc(sizeof(*np), GFP_KERNEL); | 86 | struct device_node *np = kzalloc(sizeof(*np), GFP_KERNEL); |
@@ -97,7 +99,7 @@ static struct device_node * __init new_node(const char *path, | |||
97 | return np; | 99 | return np; |
98 | } | 100 | } |
99 | 101 | ||
100 | static void __init free_node(struct device_node *np) | 102 | static void free_node(struct device_node *np) |
101 | { | 103 | { |
102 | struct property *next; | 104 | struct property *next; |
103 | struct property *prop; | 105 | struct property *prop; |
@@ -113,7 +115,7 @@ static void __init free_node(struct device_node *np) | |||
113 | kfree(np); | 115 | kfree(np); |
114 | } | 116 | } |
115 | 117 | ||
116 | static int __init add_string_property(struct device_node *np, const char *name, | 118 | static int add_string_property(struct device_node *np, const char *name, |
117 | const char *value) | 119 | const char *value) |
118 | { | 120 | { |
119 | struct property *nprop = new_property(name, strlen(value) + 1, value); | 121 | struct property *nprop = new_property(name, strlen(value) + 1, value); |
@@ -124,7 +126,7 @@ static int __init add_string_property(struct device_node *np, const char *name, | |||
124 | return 1; | 126 | return 1; |
125 | } | 127 | } |
126 | 128 | ||
127 | static int __init add_raw_property(struct device_node *np, const char *name, | 129 | static int add_raw_property(struct device_node *np, const char *name, |
128 | int length, const void *value) | 130 | int length, const void *value) |
129 | { | 131 | { |
130 | struct property *nprop = new_property(name, length, value); | 132 | struct property *nprop = new_property(name, length, value); |
@@ -135,6 +137,201 @@ static int __init add_raw_property(struct device_node *np, const char *name, | |||
135 | return 1; | 137 | return 1; |
136 | } | 138 | } |
137 | 139 | ||
140 | static struct device_node *do_device_node(struct device_node *parent, | ||
141 | const char *name, u32 reg, u32 unit, const char *type, | ||
142 | const char *compat, struct vio_resource *res) | ||
143 | { | ||
144 | struct device_node *np; | ||
145 | char path[32]; | ||
146 | |||
147 | snprintf(path, sizeof(path), "/vdevice/%s@%08x", name, reg); | ||
148 | np = new_node(path, parent); | ||
149 | if (!np) | ||
150 | return NULL; | ||
151 | if (!add_string_property(np, "name", name) || | ||
152 | !add_string_property(np, "device_type", type) || | ||
153 | !add_string_property(np, "compatible", compat) || | ||
154 | !add_raw_property(np, "reg", sizeof(reg), ®) || | ||
155 | !add_raw_property(np, "linux,unit_address", | ||
156 | sizeof(unit), &unit)) { | ||
157 | goto node_free; | ||
158 | } | ||
159 | if (res) { | ||
160 | if (!add_raw_property(np, "linux,vio_rsrcname", | ||
161 | sizeof(res->rsrcname), res->rsrcname) || | ||
162 | !add_raw_property(np, "linux,vio_type", | ||
163 | sizeof(res->type), res->type) || | ||
164 | !add_raw_property(np, "linux,vio_model", | ||
165 | sizeof(res->model), res->model)) | ||
166 | goto node_free; | ||
167 | } | ||
168 | np->name = of_get_property(np, "name", NULL); | ||
169 | np->type = of_get_property(np, "device_type", NULL); | ||
170 | of_attach_node(np); | ||
171 | #ifdef CONFIG_PROC_DEVICETREE | ||
172 | if (parent->pde) { | ||
173 | struct proc_dir_entry *ent; | ||
174 | |||
175 | ent = proc_mkdir(strrchr(np->full_name, '/') + 1, parent->pde); | ||
176 | if (ent) | ||
177 | proc_device_tree_add_node(np, ent); | ||
178 | } | ||
179 | #endif | ||
180 | return np; | ||
181 | |||
182 | node_free: | ||
183 | free_node(np); | ||
184 | return NULL; | ||
185 | } | ||
186 | |||
187 | /* | ||
188 | * This is here so that we can dynamically add viodasd | ||
189 | * devices without exposing all the above infrastructure. | ||
190 | */ | ||
191 | struct vio_dev *vio_create_viodasd(u32 unit) | ||
192 | { | ||
193 | struct device_node *vio_root; | ||
194 | struct device_node *np; | ||
195 | struct vio_dev *vdev = NULL; | ||
196 | |||
197 | vio_root = of_find_node_by_path("/vdevice"); | ||
198 | if (!vio_root) | ||
199 | return NULL; | ||
200 | np = do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit, | ||
201 | "block", "IBM,iSeries-viodasd", NULL); | ||
202 | of_node_put(vio_root); | ||
203 | if (np) { | ||
204 | vdev = vio_register_device_node(np); | ||
205 | if (!vdev) | ||
206 | free_node(np); | ||
207 | } | ||
208 | return vdev; | ||
209 | } | ||
210 | EXPORT_SYMBOL_GPL(vio_create_viodasd); | ||
211 | |||
212 | static void __init handle_block_event(struct HvLpEvent *event) | ||
213 | { | ||
214 | struct vioblocklpevent *bevent = (struct vioblocklpevent *)event; | ||
215 | struct vio_waitevent *pwe; | ||
216 | |||
217 | if (event == NULL) | ||
218 | /* Notification that a partition went away! */ | ||
219 | return; | ||
220 | /* First, we should NEVER get an int here...only acks */ | ||
221 | if (hvlpevent_is_int(event)) { | ||
222 | printk(KERN_WARNING "handle_viod_request: " | ||
223 | "Yikes! got an int in viodasd event handler!\n"); | ||
224 | if (hvlpevent_need_ack(event)) { | ||
225 | event->xRc = HvLpEvent_Rc_InvalidSubtype; | ||
226 | HvCallEvent_ackLpEvent(event); | ||
227 | } | ||
228 | return; | ||
229 | } | ||
230 | |||
231 | switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { | ||
232 | case vioblockopen: | ||
233 | /* | ||
234 | * Handle a response to an open request. We get all the | ||
235 | * disk information in the response, so update it. The | ||
236 | * correlation token contains a pointer to a waitevent | ||
237 | * structure that has a completion in it. update the | ||
238 | * return code in the waitevent structure and post the | ||
239 | * completion to wake up the guy who sent the request | ||
240 | */ | ||
241 | pwe = (struct vio_waitevent *)event->xCorrelationToken; | ||
242 | pwe->rc = event->xRc; | ||
243 | pwe->sub_result = bevent->sub_result; | ||
244 | complete(&pwe->com); | ||
245 | break; | ||
246 | case vioblockclose: | ||
247 | break; | ||
248 | default: | ||
249 | printk(KERN_WARNING "handle_viod_request: unexpected subtype!"); | ||
250 | if (hvlpevent_need_ack(event)) { | ||
251 | event->xRc = HvLpEvent_Rc_InvalidSubtype; | ||
252 | HvCallEvent_ackLpEvent(event); | ||
253 | } | ||
254 | } | ||
255 | } | ||
256 | |||
257 | static void __init probe_disk(struct device_node *vio_root, u32 unit) | ||
258 | { | ||
259 | HvLpEvent_Rc hvrc; | ||
260 | struct vio_waitevent we; | ||
261 | u16 flags = 0; | ||
262 | |||
263 | retry: | ||
264 | init_completion(&we.com); | ||
265 | |||
266 | /* Send the open event to OS/400 */ | ||
267 | hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | ||
268 | HvLpEvent_Type_VirtualIo, | ||
269 | viomajorsubtype_blockio | vioblockopen, | ||
270 | HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | ||
271 | viopath_sourceinst(viopath_hostLp), | ||
272 | viopath_targetinst(viopath_hostLp), | ||
273 | (u64)(unsigned long)&we, VIOVERSION << 16, | ||
274 | ((u64)unit << 48) | ((u64)flags<< 32), | ||
275 | 0, 0, 0); | ||
276 | if (hvrc != 0) { | ||
277 | printk(KERN_WARNING "probe_disk: bad rc on HV open %d\n", | ||
278 | (int)hvrc); | ||
279 | return; | ||
280 | } | ||
281 | |||
282 | wait_for_completion(&we.com); | ||
283 | |||
284 | if (we.rc != 0) { | ||
285 | if (flags != 0) | ||
286 | return; | ||
287 | /* try again with read only flag set */ | ||
288 | flags = vioblockflags_ro; | ||
289 | goto retry; | ||
290 | } | ||
291 | |||
292 | /* Send the close event to OS/400. We DON'T expect a response */ | ||
293 | hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | ||
294 | HvLpEvent_Type_VirtualIo, | ||
295 | viomajorsubtype_blockio | vioblockclose, | ||
296 | HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck, | ||
297 | viopath_sourceinst(viopath_hostLp), | ||
298 | viopath_targetinst(viopath_hostLp), | ||
299 | 0, VIOVERSION << 16, | ||
300 | ((u64)unit << 48) | ((u64)flags << 32), | ||
301 | 0, 0, 0); | ||
302 | if (hvrc != 0) { | ||
303 | printk(KERN_WARNING "probe_disk: " | ||
304 | "bad rc sending event to OS/400 %d\n", (int)hvrc); | ||
305 | return; | ||
306 | } | ||
307 | |||
308 | do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit, | ||
309 | "block", "IBM,iSeries-viodasd", NULL); | ||
310 | } | ||
311 | |||
312 | static void __init get_viodasd_info(struct device_node *vio_root) | ||
313 | { | ||
314 | int rc; | ||
315 | u32 unit; | ||
316 | |||
317 | rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, 2); | ||
318 | if (rc) { | ||
319 | printk(KERN_WARNING "get_viodasd_info: " | ||
320 | "error opening path to host partition %d\n", | ||
321 | viopath_hostLp); | ||
322 | return; | ||
323 | } | ||
324 | |||
325 | /* Initialize our request handler */ | ||
326 | vio_setHandler(viomajorsubtype_blockio, handle_block_event); | ||
327 | |||
328 | for (unit = 0; unit < HVMAXARCHITECTEDVIRTUALDISKS; unit++) | ||
329 | probe_disk(vio_root, unit); | ||
330 | |||
331 | vio_clearHandler(viomajorsubtype_blockio); | ||
332 | viopath_close(viopath_hostLp, viomajorsubtype_blockio, 2); | ||
333 | } | ||
334 | |||
138 | static void __init handle_cd_event(struct HvLpEvent *event) | 335 | static void __init handle_cd_event(struct HvLpEvent *event) |
139 | { | 336 | { |
140 | struct viocdlpevent *bevent; | 337 | struct viocdlpevent *bevent; |
@@ -233,49 +430,9 @@ static void __init get_viocd_info(struct device_node *vio_root) | |||
233 | 430 | ||
234 | for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALCDROMS) && | 431 | for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALCDROMS) && |
235 | unitinfo[unit].rsrcname[0]; unit++) { | 432 | unitinfo[unit].rsrcname[0]; unit++) { |
236 | struct device_node *np; | 433 | if (!do_device_node(vio_root, "viocd", FIRST_VIOCD + unit, unit, |
237 | char name[64]; | 434 | "block", "IBM,iSeries-viocd", &unitinfo[unit])) |
238 | u32 reg = FIRST_VIOCD + unit; | 435 | break; |
239 | |||
240 | snprintf(name, sizeof(name), "/vdevice/viocd@%08x", reg); | ||
241 | np = new_node(name, vio_root); | ||
242 | if (!np) | ||
243 | goto hv_free; | ||
244 | if (!add_string_property(np, "name", "viocd") || | ||
245 | !add_string_property(np, "device_type", "block") || | ||
246 | !add_string_property(np, "compatible", | ||
247 | "IBM,iSeries-viocd") || | ||
248 | !add_raw_property(np, "reg", sizeof(reg), ®) || | ||
249 | !add_raw_property(np, "linux,unit_address", | ||
250 | sizeof(unit), &unit) || | ||
251 | !add_raw_property(np, "linux,vio_rsrcname", | ||
252 | sizeof(unitinfo[unit].rsrcname), | ||
253 | unitinfo[unit].rsrcname) || | ||
254 | !add_raw_property(np, "linux,vio_type", | ||
255 | sizeof(unitinfo[unit].type), | ||
256 | unitinfo[unit].type) || | ||
257 | !add_raw_property(np, "linux,vio_model", | ||
258 | sizeof(unitinfo[unit].model), | ||
259 | unitinfo[unit].model)) | ||
260 | goto node_free; | ||
261 | np->name = of_get_property(np, "name", NULL); | ||
262 | np->type = of_get_property(np, "device_type", NULL); | ||
263 | of_attach_node(np); | ||
264 | #ifdef CONFIG_PROC_DEVICETREE | ||
265 | if (vio_root->pde) { | ||
266 | struct proc_dir_entry *ent; | ||
267 | |||
268 | ent = proc_mkdir(strrchr(np->full_name, '/') + 1, | ||
269 | vio_root->pde); | ||
270 | if (ent) | ||
271 | proc_device_tree_add_node(np, ent); | ||
272 | } | ||
273 | #endif | ||
274 | continue; | ||
275 | |||
276 | node_free: | ||
277 | free_node(np); | ||
278 | break; | ||
279 | } | 436 | } |
280 | 437 | ||
281 | hv_free: | 438 | hv_free: |
@@ -350,49 +507,10 @@ static void __init get_viotape_info(struct device_node *vio_root) | |||
350 | 507 | ||
351 | for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALTAPES) && | 508 | for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALTAPES) && |
352 | unitinfo[unit].rsrcname[0]; unit++) { | 509 | unitinfo[unit].rsrcname[0]; unit++) { |
353 | struct device_node *np; | 510 | if (!do_device_node(vio_root, "viotape", FIRST_VIOTAPE + unit, |
354 | char name[64]; | 511 | unit, "byte", "IBM,iSeries-viotape", |
355 | u32 reg = FIRST_VIOTAPE + unit; | 512 | &unitinfo[unit])) |
356 | 513 | break; | |
357 | snprintf(name, sizeof(name), "/vdevice/viotape@%08x", reg); | ||
358 | np = new_node(name, vio_root); | ||
359 | if (!np) | ||
360 | goto hv_free; | ||
361 | if (!add_string_property(np, "name", "viotape") || | ||
362 | !add_string_property(np, "device_type", "byte") || | ||
363 | !add_string_property(np, "compatible", | ||
364 | "IBM,iSeries-viotape") || | ||
365 | !add_raw_property(np, "reg", sizeof(reg), ®) || | ||
366 | !add_raw_property(np, "linux,unit_address", | ||
367 | sizeof(unit), &unit) || | ||
368 | !add_raw_property(np, "linux,vio_rsrcname", | ||
369 | sizeof(unitinfo[unit].rsrcname), | ||
370 | unitinfo[unit].rsrcname) || | ||
371 | !add_raw_property(np, "linux,vio_type", | ||
372 | sizeof(unitinfo[unit].type), | ||
373 | unitinfo[unit].type) || | ||
374 | !add_raw_property(np, "linux,vio_model", | ||
375 | sizeof(unitinfo[unit].model), | ||
376 | unitinfo[unit].model)) | ||
377 | goto node_free; | ||
378 | np->name = of_get_property(np, "name", NULL); | ||
379 | np->type = of_get_property(np, "device_type", NULL); | ||
380 | of_attach_node(np); | ||
381 | #ifdef CONFIG_PROC_DEVICETREE | ||
382 | if (vio_root->pde) { | ||
383 | struct proc_dir_entry *ent; | ||
384 | |||
385 | ent = proc_mkdir(strrchr(np->full_name, '/') + 1, | ||
386 | vio_root->pde); | ||
387 | if (ent) | ||
388 | proc_device_tree_add_node(np, ent); | ||
389 | } | ||
390 | #endif | ||
391 | continue; | ||
392 | |||
393 | node_free: | ||
394 | free_node(np); | ||
395 | break; | ||
396 | } | 514 | } |
397 | 515 | ||
398 | hv_free: | 516 | hv_free: |
@@ -422,6 +540,7 @@ static int __init iseries_vio_init(void) | |||
422 | goto put_node; | 540 | goto put_node; |
423 | } | 541 | } |
424 | 542 | ||
543 | get_viodasd_info(vio_root); | ||
425 | get_viocd_info(vio_root); | 544 | get_viocd_info(vio_root); |
426 | get_viotape_info(vio_root); | 545 | get_viotape_info(vio_root); |
427 | 546 | ||