diff options
author | Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com> | 2007-06-21 10:14:22 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-06-28 05:19:21 -0400 |
commit | a5c631b174e23cab773cf422c1f39b28e7224602 (patch) | |
tree | bfe9756b88557b12bc61e505d31abd50458fa22f /arch/powerpc/platforms | |
parent | 80071802cb9c622dbd44bc6ba292f0683891ef44 (diff) |
[POWERPC] PS3: Storage device registration routines
Add support for storage devices to the device probe code.
Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r-- | arch/powerpc/platforms/ps3/device-init.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c index 864f313be8de..825ebb2cbc2a 100644 --- a/arch/powerpc/platforms/ps3/device-init.c +++ b/arch/powerpc/platforms/ps3/device-init.c | |||
@@ -26,6 +26,7 @@ | |||
26 | 26 | ||
27 | #include <asm/firmware.h> | 27 | #include <asm/firmware.h> |
28 | #include <asm/lv1call.h> | 28 | #include <asm/lv1call.h> |
29 | #include <asm/ps3stor.h> | ||
29 | 30 | ||
30 | #include "platform.h" | 31 | #include "platform.h" |
31 | 32 | ||
@@ -237,6 +238,262 @@ static int __init ps3_setup_vuart_device(enum ps3_match_id match_id, | |||
237 | return result; | 238 | return result; |
238 | } | 239 | } |
239 | 240 | ||
241 | static int ps3stor_wait_for_completion(u64 dev_id, u64 tag, | ||
242 | unsigned int timeout) | ||
243 | { | ||
244 | int result = -1; | ||
245 | unsigned int retries = 0; | ||
246 | u64 status; | ||
247 | |||
248 | for (retries = 0; retries < timeout; retries++) { | ||
249 | result = lv1_storage_check_async_status(dev_id, tag, &status); | ||
250 | if (!result) | ||
251 | break; | ||
252 | |||
253 | msleep(1); | ||
254 | } | ||
255 | |||
256 | if (result) | ||
257 | pr_debug("%s:%u: check_async_status: %s, status %lx\n", | ||
258 | __func__, __LINE__, ps3_result(result), status); | ||
259 | |||
260 | return result; | ||
261 | } | ||
262 | |||
263 | /** | ||
264 | * ps3_storage_wait_for_device - Wait for a storage device to become ready. | ||
265 | * @repo: The repository device to wait for. | ||
266 | * | ||
267 | * Uses the hypervisor's storage device notification mechanism to wait until | ||
268 | * a storage device is ready. The device notification mechanism uses a | ||
269 | * psuedo device (id = -1) to asynchronously notify the guest when storage | ||
270 | * devices become ready. The notification device has a block size of 512 | ||
271 | * bytes. | ||
272 | */ | ||
273 | |||
274 | static int ps3_storage_wait_for_device(const struct ps3_repository_device *repo) | ||
275 | { | ||
276 | int result; | ||
277 | const u64 notification_dev_id = (u64)-1LL; | ||
278 | const unsigned int timeout = HZ; | ||
279 | u64 lpar; | ||
280 | u64 tag; | ||
281 | struct { | ||
282 | u64 operation_code; /* must be zero */ | ||
283 | u64 event_mask; /* 1 = device ready */ | ||
284 | } *notify_cmd; | ||
285 | struct { | ||
286 | u64 event_type; /* notify_device_ready */ | ||
287 | u64 bus_id; | ||
288 | u64 dev_id; | ||
289 | u64 dev_type; | ||
290 | u64 dev_port; | ||
291 | } *notify_event; | ||
292 | enum { | ||
293 | notify_device_ready = 1 | ||
294 | }; | ||
295 | |||
296 | pr_debug(" -> %s:%u: bus_id %u, dev_id %u, dev_type %u\n", __func__, | ||
297 | __LINE__, repo->bus_id, repo->dev_id, repo->dev_type); | ||
298 | |||
299 | notify_cmd = kzalloc(512, GFP_KERNEL); | ||
300 | notify_event = (void *)notify_cmd; | ||
301 | if (!notify_cmd) | ||
302 | return -ENOMEM; | ||
303 | |||
304 | lpar = ps3_mm_phys_to_lpar(__pa(notify_cmd)); | ||
305 | |||
306 | result = lv1_open_device(repo->bus_id, notification_dev_id, 0); | ||
307 | if (result) { | ||
308 | printk(KERN_ERR "%s:%u: lv1_open_device %s\n", __func__, | ||
309 | __LINE__, ps3_result(result)); | ||
310 | result = -ENODEV; | ||
311 | goto fail_free; | ||
312 | } | ||
313 | |||
314 | /* Setup and write the request for device notification. */ | ||
315 | |||
316 | notify_cmd->operation_code = 0; /* must be zero */ | ||
317 | notify_cmd->event_mask = 0x01; /* device ready */ | ||
318 | |||
319 | result = lv1_storage_write(notification_dev_id, 0, 0, 1, 0, lpar, | ||
320 | &tag); | ||
321 | if (result) { | ||
322 | printk(KERN_ERR "%s:%u: write failed %s\n", __func__, __LINE__, | ||
323 | ps3_result(result)); | ||
324 | result = -ENODEV; | ||
325 | goto fail_close; | ||
326 | } | ||
327 | |||
328 | /* Wait for the write completion */ | ||
329 | |||
330 | result = ps3stor_wait_for_completion(notification_dev_id, tag, | ||
331 | timeout); | ||
332 | if (result) { | ||
333 | printk(KERN_ERR "%s:%u: write not completed %s\n", __func__, | ||
334 | __LINE__, ps3_result(result)); | ||
335 | result = -ENODEV; | ||
336 | goto fail_close; | ||
337 | } | ||
338 | |||
339 | /* Loop here processing the requested notification events. */ | ||
340 | |||
341 | result = -ENODEV; | ||
342 | while (1) { | ||
343 | memset(notify_event, 0, sizeof(*notify_event)); | ||
344 | |||
345 | result = lv1_storage_read(notification_dev_id, 0, 0, 1, 0, | ||
346 | lpar, &tag); | ||
347 | if (result) { | ||
348 | printk(KERN_ERR "%s:%u: write failed %s\n", __func__, | ||
349 | __LINE__, ps3_result(result)); | ||
350 | break; | ||
351 | } | ||
352 | |||
353 | result = ps3stor_wait_for_completion(notification_dev_id, tag, | ||
354 | timeout); | ||
355 | if (result) { | ||
356 | printk(KERN_ERR "%s:%u: read not completed %s\n", | ||
357 | __func__, __LINE__, ps3_result(result)); | ||
358 | break; | ||
359 | } | ||
360 | |||
361 | if (notify_event->event_type != notify_device_ready || | ||
362 | notify_event->bus_id != repo->bus_id) { | ||
363 | pr_debug("%s:%u: bad notify_event: event %lu, " | ||
364 | "dev_id %lu, dev_type %lu\n", | ||
365 | __func__, __LINE__, notify_event->event_type, | ||
366 | notify_event->dev_id, notify_event->dev_type); | ||
367 | break; | ||
368 | } | ||
369 | |||
370 | if (notify_event->dev_id == repo->dev_id && | ||
371 | notify_event->dev_type == repo->dev_type) { | ||
372 | pr_debug("%s:%u: device ready: dev_id %u\n", __func__, | ||
373 | __LINE__, repo->dev_id); | ||
374 | result = 0; | ||
375 | break; | ||
376 | } | ||
377 | |||
378 | if (notify_event->dev_id == repo->dev_id && | ||
379 | notify_event->dev_type == PS3_DEV_TYPE_NOACCESS) { | ||
380 | pr_debug("%s:%u: no access: dev_id %u\n", __func__, | ||
381 | __LINE__, repo->dev_id); | ||
382 | break; | ||
383 | } | ||
384 | } | ||
385 | |||
386 | fail_close: | ||
387 | lv1_close_device(repo->bus_id, notification_dev_id); | ||
388 | fail_free: | ||
389 | kfree(notify_cmd); | ||
390 | pr_debug(" <- %s:%u\n", __func__, __LINE__); | ||
391 | return result; | ||
392 | } | ||
393 | |||
394 | static int ps3_setup_storage_dev(const struct ps3_repository_device *repo, | ||
395 | enum ps3_match_id match_id) | ||
396 | { | ||
397 | int result; | ||
398 | struct ps3_storage_device *p; | ||
399 | u64 port, blk_size, num_blocks; | ||
400 | unsigned int num_regions, i; | ||
401 | |||
402 | pr_debug(" -> %s:%u: match_id %u\n", __func__, __LINE__, match_id); | ||
403 | |||
404 | result = ps3_repository_read_stor_dev_info(repo->bus_index, | ||
405 | repo->dev_index, &port, | ||
406 | &blk_size, &num_blocks, | ||
407 | &num_regions); | ||
408 | if (result) { | ||
409 | printk(KERN_ERR "%s:%u: _read_stor_dev_info failed %d\n", | ||
410 | __func__, __LINE__, result); | ||
411 | return -ENODEV; | ||
412 | } | ||
413 | |||
414 | pr_debug("%s:%u: index %u:%u: port %lu blk_size %lu num_blocks %lu " | ||
415 | "num_regions %u\n", __func__, __LINE__, repo->bus_index, | ||
416 | repo->dev_index, port, blk_size, num_blocks, num_regions); | ||
417 | |||
418 | p = kzalloc(sizeof(struct ps3_storage_device) + | ||
419 | num_regions * sizeof(struct ps3_storage_region), | ||
420 | GFP_KERNEL); | ||
421 | if (!p) { | ||
422 | result = -ENOMEM; | ||
423 | goto fail_malloc; | ||
424 | } | ||
425 | |||
426 | p->sbd.match_id = match_id; | ||
427 | p->sbd.dev_type = PS3_DEVICE_TYPE_SB; | ||
428 | p->sbd.bus_id = repo->bus_id; | ||
429 | p->sbd.dev_id = repo->dev_id; | ||
430 | p->sbd.d_region = &p->dma_region; | ||
431 | p->blk_size = blk_size; | ||
432 | p->num_regions = num_regions; | ||
433 | |||
434 | result = ps3_repository_find_interrupt(repo, | ||
435 | PS3_INTERRUPT_TYPE_EVENT_PORT, | ||
436 | &p->sbd.interrupt_id); | ||
437 | if (result) { | ||
438 | printk(KERN_ERR "%s:%u: find_interrupt failed %d\n", __func__, | ||
439 | __LINE__, result); | ||
440 | result = -ENODEV; | ||
441 | goto fail_find_interrupt; | ||
442 | } | ||
443 | |||
444 | /* FIXME: Arrange to only do this on a 'cold' boot */ | ||
445 | |||
446 | result = ps3_storage_wait_for_device(repo); | ||
447 | if (result) { | ||
448 | printk(KERN_ERR "%s:%u: storage_notification failed %d\n", | ||
449 | __func__, __LINE__, result); | ||
450 | result = -ENODEV; | ||
451 | goto fail_probe_notification; | ||
452 | } | ||
453 | |||
454 | for (i = 0; i < num_regions; i++) { | ||
455 | unsigned int id; | ||
456 | u64 start, size; | ||
457 | |||
458 | result = ps3_repository_read_stor_dev_region(repo->bus_index, | ||
459 | repo->dev_index, | ||
460 | i, &id, &start, | ||
461 | &size); | ||
462 | if (result) { | ||
463 | printk(KERN_ERR | ||
464 | "%s:%u: read_stor_dev_region failed %d\n", | ||
465 | __func__, __LINE__, result); | ||
466 | result = -ENODEV; | ||
467 | goto fail_read_region; | ||
468 | } | ||
469 | pr_debug("%s:%u: region %u: id %u start %lu size %lu\n", | ||
470 | __func__, __LINE__, i, id, start, size); | ||
471 | |||
472 | p->regions[i].id = id; | ||
473 | p->regions[i].start = start; | ||
474 | p->regions[i].size = size; | ||
475 | } | ||
476 | |||
477 | result = ps3_system_bus_device_register(&p->sbd); | ||
478 | if (result) { | ||
479 | pr_debug("%s:%u ps3_system_bus_device_register failed\n", | ||
480 | __func__, __LINE__); | ||
481 | goto fail_device_register; | ||
482 | } | ||
483 | |||
484 | pr_debug(" <- %s:%u\n", __func__, __LINE__); | ||
485 | return 0; | ||
486 | |||
487 | fail_device_register: | ||
488 | fail_read_region: | ||
489 | fail_probe_notification: | ||
490 | fail_find_interrupt: | ||
491 | kfree(p); | ||
492 | fail_malloc: | ||
493 | pr_debug(" <- %s:%u: fail.\n", __func__, __LINE__); | ||
494 | return result; | ||
495 | } | ||
496 | |||
240 | static int __init ps3_register_vuart_devices(void) | 497 | static int __init ps3_register_vuart_devices(void) |
241 | { | 498 | { |
242 | int result; | 499 | int result; |
@@ -356,6 +613,35 @@ static int ps3_register_repository_device( | |||
356 | __func__, __LINE__); | 613 | __func__, __LINE__); |
357 | } | 614 | } |
358 | break; | 615 | break; |
616 | case PS3_DEV_TYPE_STOR_DISK: | ||
617 | result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_DISK); | ||
618 | |||
619 | /* Some devices are not accessable from the Other OS lpar. */ | ||
620 | if (result == -ENODEV) { | ||
621 | result = 0; | ||
622 | pr_debug("%s:%u: not accessable\n", __func__, | ||
623 | __LINE__); | ||
624 | } | ||
625 | |||
626 | if (result) | ||
627 | pr_debug("%s:%u ps3_setup_storage_dev failed\n", | ||
628 | __func__, __LINE__); | ||
629 | break; | ||
630 | |||
631 | case PS3_DEV_TYPE_STOR_ROM: | ||
632 | result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_ROM); | ||
633 | if (result) | ||
634 | pr_debug("%s:%u ps3_setup_storage_dev failed\n", | ||
635 | __func__, __LINE__); | ||
636 | break; | ||
637 | |||
638 | case PS3_DEV_TYPE_STOR_FLASH: | ||
639 | result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_FLASH); | ||
640 | if (result) | ||
641 | pr_debug("%s:%u ps3_setup_storage_dev failed\n", | ||
642 | __func__, __LINE__); | ||
643 | break; | ||
644 | |||
359 | default: | 645 | default: |
360 | result = 0; | 646 | result = 0; |
361 | pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__, | 647 | pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__, |