[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>
This commit is contained in:
parent
80071802cb
commit
a5c631b174
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#include <asm/firmware.h>
|
#include <asm/firmware.h>
|
||||||
#include <asm/lv1call.h>
|
#include <asm/lv1call.h>
|
||||||
|
#include <asm/ps3stor.h>
|
||||||
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
|
@ -237,6 +238,262 @@ static int __init ps3_setup_vuart_device(enum ps3_match_id match_id,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ps3stor_wait_for_completion(u64 dev_id, u64 tag,
|
||||||
|
unsigned int timeout)
|
||||||
|
{
|
||||||
|
int result = -1;
|
||||||
|
unsigned int retries = 0;
|
||||||
|
u64 status;
|
||||||
|
|
||||||
|
for (retries = 0; retries < timeout; retries++) {
|
||||||
|
result = lv1_storage_check_async_status(dev_id, tag, &status);
|
||||||
|
if (!result)
|
||||||
|
break;
|
||||||
|
|
||||||
|
msleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
pr_debug("%s:%u: check_async_status: %s, status %lx\n",
|
||||||
|
__func__, __LINE__, ps3_result(result), status);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ps3_storage_wait_for_device - Wait for a storage device to become ready.
|
||||||
|
* @repo: The repository device to wait for.
|
||||||
|
*
|
||||||
|
* Uses the hypervisor's storage device notification mechanism to wait until
|
||||||
|
* a storage device is ready. The device notification mechanism uses a
|
||||||
|
* psuedo device (id = -1) to asynchronously notify the guest when storage
|
||||||
|
* devices become ready. The notification device has a block size of 512
|
||||||
|
* bytes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int ps3_storage_wait_for_device(const struct ps3_repository_device *repo)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
const u64 notification_dev_id = (u64)-1LL;
|
||||||
|
const unsigned int timeout = HZ;
|
||||||
|
u64 lpar;
|
||||||
|
u64 tag;
|
||||||
|
struct {
|
||||||
|
u64 operation_code; /* must be zero */
|
||||||
|
u64 event_mask; /* 1 = device ready */
|
||||||
|
} *notify_cmd;
|
||||||
|
struct {
|
||||||
|
u64 event_type; /* notify_device_ready */
|
||||||
|
u64 bus_id;
|
||||||
|
u64 dev_id;
|
||||||
|
u64 dev_type;
|
||||||
|
u64 dev_port;
|
||||||
|
} *notify_event;
|
||||||
|
enum {
|
||||||
|
notify_device_ready = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
pr_debug(" -> %s:%u: bus_id %u, dev_id %u, dev_type %u\n", __func__,
|
||||||
|
__LINE__, repo->bus_id, repo->dev_id, repo->dev_type);
|
||||||
|
|
||||||
|
notify_cmd = kzalloc(512, GFP_KERNEL);
|
||||||
|
notify_event = (void *)notify_cmd;
|
||||||
|
if (!notify_cmd)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
lpar = ps3_mm_phys_to_lpar(__pa(notify_cmd));
|
||||||
|
|
||||||
|
result = lv1_open_device(repo->bus_id, notification_dev_id, 0);
|
||||||
|
if (result) {
|
||||||
|
printk(KERN_ERR "%s:%u: lv1_open_device %s\n", __func__,
|
||||||
|
__LINE__, ps3_result(result));
|
||||||
|
result = -ENODEV;
|
||||||
|
goto fail_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup and write the request for device notification. */
|
||||||
|
|
||||||
|
notify_cmd->operation_code = 0; /* must be zero */
|
||||||
|
notify_cmd->event_mask = 0x01; /* device ready */
|
||||||
|
|
||||||
|
result = lv1_storage_write(notification_dev_id, 0, 0, 1, 0, lpar,
|
||||||
|
&tag);
|
||||||
|
if (result) {
|
||||||
|
printk(KERN_ERR "%s:%u: write failed %s\n", __func__, __LINE__,
|
||||||
|
ps3_result(result));
|
||||||
|
result = -ENODEV;
|
||||||
|
goto fail_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for the write completion */
|
||||||
|
|
||||||
|
result = ps3stor_wait_for_completion(notification_dev_id, tag,
|
||||||
|
timeout);
|
||||||
|
if (result) {
|
||||||
|
printk(KERN_ERR "%s:%u: write not completed %s\n", __func__,
|
||||||
|
__LINE__, ps3_result(result));
|
||||||
|
result = -ENODEV;
|
||||||
|
goto fail_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop here processing the requested notification events. */
|
||||||
|
|
||||||
|
result = -ENODEV;
|
||||||
|
while (1) {
|
||||||
|
memset(notify_event, 0, sizeof(*notify_event));
|
||||||
|
|
||||||
|
result = lv1_storage_read(notification_dev_id, 0, 0, 1, 0,
|
||||||
|
lpar, &tag);
|
||||||
|
if (result) {
|
||||||
|
printk(KERN_ERR "%s:%u: write failed %s\n", __func__,
|
||||||
|
__LINE__, ps3_result(result));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = ps3stor_wait_for_completion(notification_dev_id, tag,
|
||||||
|
timeout);
|
||||||
|
if (result) {
|
||||||
|
printk(KERN_ERR "%s:%u: read not completed %s\n",
|
||||||
|
__func__, __LINE__, ps3_result(result));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notify_event->event_type != notify_device_ready ||
|
||||||
|
notify_event->bus_id != repo->bus_id) {
|
||||||
|
pr_debug("%s:%u: bad notify_event: event %lu, "
|
||||||
|
"dev_id %lu, dev_type %lu\n",
|
||||||
|
__func__, __LINE__, notify_event->event_type,
|
||||||
|
notify_event->dev_id, notify_event->dev_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notify_event->dev_id == repo->dev_id &&
|
||||||
|
notify_event->dev_type == repo->dev_type) {
|
||||||
|
pr_debug("%s:%u: device ready: dev_id %u\n", __func__,
|
||||||
|
__LINE__, repo->dev_id);
|
||||||
|
result = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notify_event->dev_id == repo->dev_id &&
|
||||||
|
notify_event->dev_type == PS3_DEV_TYPE_NOACCESS) {
|
||||||
|
pr_debug("%s:%u: no access: dev_id %u\n", __func__,
|
||||||
|
__LINE__, repo->dev_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail_close:
|
||||||
|
lv1_close_device(repo->bus_id, notification_dev_id);
|
||||||
|
fail_free:
|
||||||
|
kfree(notify_cmd);
|
||||||
|
pr_debug(" <- %s:%u\n", __func__, __LINE__);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ps3_setup_storage_dev(const struct ps3_repository_device *repo,
|
||||||
|
enum ps3_match_id match_id)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
struct ps3_storage_device *p;
|
||||||
|
u64 port, blk_size, num_blocks;
|
||||||
|
unsigned int num_regions, i;
|
||||||
|
|
||||||
|
pr_debug(" -> %s:%u: match_id %u\n", __func__, __LINE__, match_id);
|
||||||
|
|
||||||
|
result = ps3_repository_read_stor_dev_info(repo->bus_index,
|
||||||
|
repo->dev_index, &port,
|
||||||
|
&blk_size, &num_blocks,
|
||||||
|
&num_regions);
|
||||||
|
if (result) {
|
||||||
|
printk(KERN_ERR "%s:%u: _read_stor_dev_info failed %d\n",
|
||||||
|
__func__, __LINE__, result);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_debug("%s:%u: index %u:%u: port %lu blk_size %lu num_blocks %lu "
|
||||||
|
"num_regions %u\n", __func__, __LINE__, repo->bus_index,
|
||||||
|
repo->dev_index, port, blk_size, num_blocks, num_regions);
|
||||||
|
|
||||||
|
p = kzalloc(sizeof(struct ps3_storage_device) +
|
||||||
|
num_regions * sizeof(struct ps3_storage_region),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!p) {
|
||||||
|
result = -ENOMEM;
|
||||||
|
goto fail_malloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->sbd.match_id = match_id;
|
||||||
|
p->sbd.dev_type = PS3_DEVICE_TYPE_SB;
|
||||||
|
p->sbd.bus_id = repo->bus_id;
|
||||||
|
p->sbd.dev_id = repo->dev_id;
|
||||||
|
p->sbd.d_region = &p->dma_region;
|
||||||
|
p->blk_size = blk_size;
|
||||||
|
p->num_regions = num_regions;
|
||||||
|
|
||||||
|
result = ps3_repository_find_interrupt(repo,
|
||||||
|
PS3_INTERRUPT_TYPE_EVENT_PORT,
|
||||||
|
&p->sbd.interrupt_id);
|
||||||
|
if (result) {
|
||||||
|
printk(KERN_ERR "%s:%u: find_interrupt failed %d\n", __func__,
|
||||||
|
__LINE__, result);
|
||||||
|
result = -ENODEV;
|
||||||
|
goto fail_find_interrupt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: Arrange to only do this on a 'cold' boot */
|
||||||
|
|
||||||
|
result = ps3_storage_wait_for_device(repo);
|
||||||
|
if (result) {
|
||||||
|
printk(KERN_ERR "%s:%u: storage_notification failed %d\n",
|
||||||
|
__func__, __LINE__, result);
|
||||||
|
result = -ENODEV;
|
||||||
|
goto fail_probe_notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < num_regions; i++) {
|
||||||
|
unsigned int id;
|
||||||
|
u64 start, size;
|
||||||
|
|
||||||
|
result = ps3_repository_read_stor_dev_region(repo->bus_index,
|
||||||
|
repo->dev_index,
|
||||||
|
i, &id, &start,
|
||||||
|
&size);
|
||||||
|
if (result) {
|
||||||
|
printk(KERN_ERR
|
||||||
|
"%s:%u: read_stor_dev_region failed %d\n",
|
||||||
|
__func__, __LINE__, result);
|
||||||
|
result = -ENODEV;
|
||||||
|
goto fail_read_region;
|
||||||
|
}
|
||||||
|
pr_debug("%s:%u: region %u: id %u start %lu size %lu\n",
|
||||||
|
__func__, __LINE__, i, id, start, size);
|
||||||
|
|
||||||
|
p->regions[i].id = id;
|
||||||
|
p->regions[i].start = start;
|
||||||
|
p->regions[i].size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = ps3_system_bus_device_register(&p->sbd);
|
||||||
|
if (result) {
|
||||||
|
pr_debug("%s:%u ps3_system_bus_device_register failed\n",
|
||||||
|
__func__, __LINE__);
|
||||||
|
goto fail_device_register;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_debug(" <- %s:%u\n", __func__, __LINE__);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail_device_register:
|
||||||
|
fail_read_region:
|
||||||
|
fail_probe_notification:
|
||||||
|
fail_find_interrupt:
|
||||||
|
kfree(p);
|
||||||
|
fail_malloc:
|
||||||
|
pr_debug(" <- %s:%u: fail.\n", __func__, __LINE__);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static int __init ps3_register_vuart_devices(void)
|
static int __init ps3_register_vuart_devices(void)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
|
@ -356,6 +613,35 @@ static int ps3_register_repository_device(
|
||||||
__func__, __LINE__);
|
__func__, __LINE__);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case PS3_DEV_TYPE_STOR_DISK:
|
||||||
|
result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_DISK);
|
||||||
|
|
||||||
|
/* Some devices are not accessable from the Other OS lpar. */
|
||||||
|
if (result == -ENODEV) {
|
||||||
|
result = 0;
|
||||||
|
pr_debug("%s:%u: not accessable\n", __func__,
|
||||||
|
__LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
pr_debug("%s:%u ps3_setup_storage_dev failed\n",
|
||||||
|
__func__, __LINE__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PS3_DEV_TYPE_STOR_ROM:
|
||||||
|
result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_ROM);
|
||||||
|
if (result)
|
||||||
|
pr_debug("%s:%u ps3_setup_storage_dev failed\n",
|
||||||
|
__func__, __LINE__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PS3_DEV_TYPE_STOR_FLASH:
|
||||||
|
result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_FLASH);
|
||||||
|
if (result)
|
||||||
|
pr_debug("%s:%u ps3_setup_storage_dev failed\n",
|
||||||
|
__func__, __LINE__);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
result = 0;
|
result = 0;
|
||||||
pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__,
|
pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__,
|
||||||
|
|
Loading…
Reference in New Issue