diff options
Diffstat (limited to 'arch/powerpc/platforms/ps3')
| -rw-r--r-- | arch/powerpc/platforms/ps3/Kconfig | 72 | ||||
| -rw-r--r-- | arch/powerpc/platforms/ps3/Makefile | 1 | ||||
| -rw-r--r-- | arch/powerpc/platforms/ps3/device-init.c | 677 | ||||
| -rw-r--r-- | arch/powerpc/platforms/ps3/exports.c | 2 | ||||
| -rw-r--r-- | arch/powerpc/platforms/ps3/gelic_udbg.c | 273 | ||||
| -rw-r--r-- | arch/powerpc/platforms/ps3/htab.c | 281 | ||||
| -rw-r--r-- | arch/powerpc/platforms/ps3/interrupt.c | 227 | ||||
| -rw-r--r-- | arch/powerpc/platforms/ps3/mm.c | 209 | ||||
| -rw-r--r-- | arch/powerpc/platforms/ps3/os-area.c | 692 | ||||
| -rw-r--r-- | arch/powerpc/platforms/ps3/platform.h | 69 | ||||
| -rw-r--r-- | arch/powerpc/platforms/ps3/repository.c | 651 | ||||
| -rw-r--r-- | arch/powerpc/platforms/ps3/setup.c | 46 | ||||
| -rw-r--r-- | arch/powerpc/platforms/ps3/smp.c | 95 | ||||
| -rw-r--r-- | arch/powerpc/platforms/ps3/spu.c | 70 | ||||
| -rw-r--r-- | arch/powerpc/platforms/ps3/system-bus.c | 197 | ||||
| -rw-r--r-- | arch/powerpc/platforms/ps3/time.c | 36 |
16 files changed, 2503 insertions, 1095 deletions
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig index 67144d1d140..56f274064d6 100644 --- a/arch/powerpc/platforms/ps3/Kconfig +++ b/arch/powerpc/platforms/ps3/Kconfig @@ -1,13 +1,11 @@ config PPC_PS3 bool "Sony PS3" - depends on PPC_MULTIPLATFORM && PPC64 + depends on PPC64 && PPC_BOOK3S select PPC_CELL - select USB_ARCH_HAS_OHCI select USB_OHCI_LITTLE_ENDIAN select USB_OHCI_BIG_ENDIAN_MMIO - select USB_ARCH_HAS_EHCI select USB_EHCI_BIG_ENDIAN_MMIO - select MEMORY_HOTPLUG + select PPC_PCI_CHOICE help This option enables support for the Sony PS3 game console and other platforms using the PS3 hypervisor. Enabling this @@ -48,7 +46,7 @@ config PS3_HTAB_SIZE system will have optimal runtime performance. config PS3_DYNAMIC_DMA - depends on PPC_PS3 && EXPERIMENTAL + depends on PPC_PS3 bool "PS3 Platform dynamic DMA page table management" default n help @@ -61,17 +59,6 @@ config PS3_DYNAMIC_DMA This support is mainly for Linux kernel development. If unsure, say N. -config PS3_USE_LPAR_ADDR - depends on PPC_PS3 && EXPERIMENTAL - bool "PS3 use lpar address space" - default y - help - This option is solely for experimentation by experts. Disables - translation of lpar addresses. SPE support currently won't work - without this set to y. - - If you have any doubt, choose the default y. - config PS3_VUART depends on PPC_PS3 tristate @@ -84,20 +71,33 @@ config PS3_PS3AV help Include support for the PS3 AV Settings driver. - This support is required for graphics and sound. In + This support is required for PS3 graphics and sound. In general, all users will say Y or M. config PS3_SYS_MANAGER depends on PPC_PS3 tristate "PS3 System Manager driver" if PS3_ADVANCED select PS3_VUART - default m + default y help Include support for the PS3 System Manager. - This support is required for system control. In + This support is required for PS3 system control. In general, all users will say Y or M. +config PS3_REPOSITORY_WRITE + bool "PS3 Repository write support" if PS3_ADVANCED + depends on PPC_PS3 + default n + help + Enables support for writing to the PS3 System Repository. + + This support is intended for bootloaders that need to store data + in the repository for later boot stages. + + If in doubt, say N here and reduce the size of the kernel by a + small amount. + config PS3_STORAGE depends on PPC_PS3 tristate @@ -132,10 +132,42 @@ config PS3_FLASH This support is required to access the PS3 FLASH ROM, which contains the boot loader and some boot options. - In general, all users will say Y or M. + In general, PS3 OtherOS users will say Y or M. As this driver needs a fixed buffer of 256 KiB of memory, it can be disabled on the kernel command line using "ps3flash=off", to not allocate this fixed buffer. +config PS3_VRAM + tristate "PS3 Video RAM Storage Driver" + depends on FB_PS3=y && BLOCK && m + help + This driver allows you to use excess PS3 video RAM as volatile + storage or system swap. + +config PS3_LPM + tristate "PS3 Logical Performance Monitor support" + depends on PPC_PS3 + help + Include support for the PS3 Logical Performance Monitor. + + This support is required to use the logical performance monitor + of the PS3's LV1 hypervisor. + + If you intend to use the advanced performance monitoring and + profiling support of the Cell processor with programs like + oprofile and perfmon2, then say Y or M, otherwise say N. + +config PS3GELIC_UDBG + bool "PS3 udbg output via UDP broadcasts on Ethernet" + depends on PPC_PS3 + help + Enables udbg early debugging output by sending broadcast UDP + via the Ethernet port (UDP port number 18194). + + This driver uses a trivial implementation and is independent + from the main PS3 gelic network driver. + + If in doubt, say N here. + endmenu diff --git a/arch/powerpc/platforms/ps3/Makefile b/arch/powerpc/platforms/ps3/Makefile index ac1bdf844ec..02b9e636dab 100644 --- a/arch/powerpc/platforms/ps3/Makefile +++ b/arch/powerpc/platforms/ps3/Makefile @@ -2,6 +2,7 @@ obj-y += setup.o mm.o time.o hvcall.o htab.o repository.o obj-y += interrupt.o exports.o os-area.o obj-y += system-bus.o +obj-$(CONFIG_PS3GELIC_UDBG) += gelic_udbg.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SPU_BASE) += spu.o obj-y += device-init.o diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c index ce15cada88d..3f175e8aedb 100644 --- a/arch/powerpc/platforms/ps3/device-init.c +++ b/arch/powerpc/platforms/ps3/device-init.c @@ -23,6 +23,8 @@ #include <linux/kernel.h> #include <linux/kthread.h> #include <linux/init.h> +#include <linux/slab.h> +#include <linux/reboot.h> #include <asm/firmware.h> #include <asm/lv1call.h> @@ -30,6 +32,89 @@ #include "platform.h" +static int __init ps3_register_lpm_devices(void) +{ + int result; + u64 tmp1; + u64 tmp2; + struct ps3_system_bus_device *dev; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->match_id = PS3_MATCH_ID_LPM; + dev->dev_type = PS3_DEVICE_TYPE_LPM; + + /* The current lpm driver only supports a single BE processor. */ + + result = ps3_repository_read_be_node_id(0, &dev->lpm.node_id); + + if (result) { + pr_debug("%s:%d: ps3_repository_read_be_node_id failed \n", + __func__, __LINE__); + goto fail_read_repo; + } + + result = ps3_repository_read_lpm_privileges(dev->lpm.node_id, &tmp1, + &dev->lpm.rights); + + if (result) { + pr_debug("%s:%d: ps3_repository_read_lpm_privleges failed \n", + __func__, __LINE__); + goto fail_read_repo; + } + + lv1_get_logical_partition_id(&tmp2); + + if (tmp1 != tmp2) { + pr_debug("%s:%d: wrong lpar\n", + __func__, __LINE__); + result = -ENODEV; + goto fail_rights; + } + + if (!(dev->lpm.rights & PS3_LPM_RIGHTS_USE_LPM)) { + pr_debug("%s:%d: don't have rights to use lpm\n", + __func__, __LINE__); + result = -EPERM; + goto fail_rights; + } + + pr_debug("%s:%d: pu_id %llu, rights %llu(%llxh)\n", + __func__, __LINE__, dev->lpm.pu_id, dev->lpm.rights, + dev->lpm.rights); + + result = ps3_repository_read_pu_id(0, &dev->lpm.pu_id); + + if (result) { + pr_debug("%s:%d: ps3_repository_read_pu_id failed \n", + __func__, __LINE__); + goto fail_read_repo; + } + + result = ps3_system_bus_device_register(dev); + + if (result) { + pr_debug("%s:%d ps3_system_bus_device_register failed\n", + __func__, __LINE__); + goto fail_register; + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; + + +fail_register: +fail_rights: +fail_read_repo: + kfree(dev); + pr_debug(" <- %s:%d: failed\n", __func__, __LINE__); + return result; +} + /** * ps3_setup_gelic_device - Setup and register a gelic device instance. * @@ -230,168 +315,20 @@ static int __init ps3_setup_vuart_device(enum ps3_match_id match_id, result = ps3_system_bus_device_register(&p->dev); - if (result) + if (result) { pr_debug("%s:%d ps3_system_bus_device_register failed\n", __func__, __LINE__); - - pr_debug(" <- %s:%d\n", __func__, __LINE__); - 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); + goto fail_device_register; } + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; - if (result) - pr_debug("%s:%u: check_async_status: %s, status %lx\n", - __func__, __LINE__, ps3_result(result), status); - +fail_device_register: + kfree(p); + pr_debug(" <- %s:%d fail\n", __func__, __LINE__); 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 error = -ENODEV; - int result; - const u64 notification_dev_id = (u64)-1LL; - const unsigned int timeout = HZ; - u64 lpar; - u64 tag; - void *buf; - enum ps3_notify_type { - notify_device_ready = 0, - notify_region_probe = 1, - notify_region_update = 2, - }; - struct { - u64 operation_code; /* must be zero */ - u64 event_mask; /* OR of 1UL << enum ps3_notify_type */ - } *notify_cmd; - struct { - u64 event_type; /* enum ps3_notify_type */ - u64 bus_id; - u64 dev_id; - u64 dev_type; - u64 dev_port; - } *notify_event; - - 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); - - buf = kzalloc(512, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - lpar = ps3_mm_phys_to_lpar(__pa(buf)); - notify_cmd = buf; - notify_event = buf; - - 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)); - goto fail_free; - } - - /* Setup and write the request for device notification. */ - - notify_cmd->operation_code = 0; /* must be zero */ - notify_cmd->event_mask = 1UL << notify_region_probe; - - 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)); - 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)); - goto fail_close; - } - - /* Loop here processing the requested notification events. */ - - 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_region_probe || - 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); - error = 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(buf); - pr_debug(" <- %s:%u\n", __func__, __LINE__); - return error; -} - static int ps3_setup_storage_dev(const struct ps3_repository_device *repo, enum ps3_match_id match_id) { @@ -412,9 +349,10 @@ static int ps3_setup_storage_dev(const struct ps3_repository_device *repo, return -ENODEV; } - pr_debug("%s:%u: index %u:%u: port %lu blk_size %lu num_blocks %lu " + pr_debug("%s:%u: (%u:%u:%u): port %llu blk_size %llu num_blocks %llu " "num_regions %u\n", __func__, __LINE__, repo->bus_index, - repo->dev_index, port, blk_size, num_blocks, num_regions); + repo->dev_index, repo->dev_type, port, blk_size, num_blocks, + num_regions); p = kzalloc(sizeof(struct ps3_storage_device) + num_regions * sizeof(struct ps3_storage_region), @@ -442,16 +380,6 @@ static int ps3_setup_storage_dev(const struct ps3_repository_device *repo, 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; @@ -467,7 +395,7 @@ static int ps3_setup_storage_dev(const struct ps3_repository_device *repo, result = -ENODEV; goto fail_read_region; } - pr_debug("%s:%u: region %u: id %u start %lu size %lu\n", + pr_debug("%s:%u: region %u: id %u start %llu size %llu\n", __func__, __LINE__, i, id, start, size); p->regions[i].id = id; @@ -487,7 +415,6 @@ static int ps3_setup_storage_dev(const struct ps3_repository_device *repo, fail_device_register: fail_read_region: -fail_probe_notification: fail_find_interrupt: kfree(p); fail_malloc: @@ -543,11 +470,17 @@ static int __init ps3_register_sound_devices(void) result = ps3_system_bus_device_register(&p->dev); - if (result) + if (result) { pr_debug("%s:%d ps3_system_bus_device_register failed\n", __func__, __LINE__); - + goto fail_device_register; + } pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; + +fail_device_register: + kfree(p); + pr_debug(" <- %s:%d failed\n", __func__, __LINE__); return result; } @@ -565,62 +498,78 @@ static int __init ps3_register_graphics_devices(void) if (!p) return -ENOMEM; - p->dev.match_id = PS3_MATCH_ID_GRAPHICS; + p->dev.match_id = PS3_MATCH_ID_GPU; + p->dev.match_sub_id = PS3_MATCH_SUB_ID_GPU_FB; p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; result = ps3_system_bus_device_register(&p->dev); - if (result) + if (result) { pr_debug("%s:%d ps3_system_bus_device_register failed\n", __func__, __LINE__); + goto fail_device_register; + } pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; + +fail_device_register: + kfree(p); + pr_debug(" <- %s:%d failed\n", __func__, __LINE__); return result; } -/** - * ps3_register_repository_device - Register a device from the repositiory info. - * - */ - -static int ps3_register_repository_device( - const struct ps3_repository_device *repo) +static int __init ps3_register_ramdisk_device(void) { int result; + struct layout { + struct ps3_system_bus_device dev; + } *p; - switch (repo->dev_type) { - case PS3_DEV_TYPE_SB_GELIC: - result = ps3_setup_gelic_device(repo); - if (result) { - pr_debug("%s:%d ps3_setup_gelic_device failed\n", - __func__, __LINE__); - } - break; - case PS3_DEV_TYPE_SB_USB: + pr_debug(" -> %s:%d\n", __func__, __LINE__); - /* Each USB device has both an EHCI and an OHCI HC */ + p = kzalloc(sizeof(struct layout), GFP_KERNEL); - result = ps3_setup_ehci_device(repo); + if (!p) + return -ENOMEM; - if (result) { - pr_debug("%s:%d ps3_setup_ehci_device failed\n", - __func__, __LINE__); - } + p->dev.match_id = PS3_MATCH_ID_GPU; + p->dev.match_sub_id = PS3_MATCH_SUB_ID_GPU_RAMDISK; + p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; - result = ps3_setup_ohci_device(repo); + result = ps3_system_bus_device_register(&p->dev); - if (result) { - pr_debug("%s:%d ps3_setup_ohci_device failed\n", - __func__, __LINE__); - } - break; + if (result) { + pr_debug("%s:%d ps3_system_bus_device_register failed\n", + __func__, __LINE__); + goto fail_device_register; + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; + +fail_device_register: + kfree(p); + pr_debug(" <- %s:%d failed\n", __func__, __LINE__); + return result; +} + +/** + * ps3_setup_dynamic_device - Setup a dynamic device from the repository + */ + +static int ps3_setup_dynamic_device(const struct ps3_repository_device *repo) +{ + int result; + + switch (repo->dev_type) { 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. */ + /* Some devices are not accessible from the Other OS lpar. */ if (result == -ENODEV) { result = 0; - pr_debug("%s:%u: not accessable\n", __func__, + pr_debug("%s:%u: not accessible\n", __func__, __LINE__); } @@ -653,60 +602,309 @@ static int ps3_register_repository_device( } /** + * ps3_setup_static_device - Setup a static device from the repository + */ + +static int __init ps3_setup_static_device(const struct ps3_repository_device *repo) +{ + int result; + + switch (repo->dev_type) { + case PS3_DEV_TYPE_SB_GELIC: + result = ps3_setup_gelic_device(repo); + if (result) { + pr_debug("%s:%d ps3_setup_gelic_device failed\n", + __func__, __LINE__); + } + break; + case PS3_DEV_TYPE_SB_USB: + + /* Each USB device has both an EHCI and an OHCI HC */ + + result = ps3_setup_ehci_device(repo); + + if (result) { + pr_debug("%s:%d ps3_setup_ehci_device failed\n", + __func__, __LINE__); + } + + result = ps3_setup_ohci_device(repo); + + if (result) { + pr_debug("%s:%d ps3_setup_ohci_device failed\n", + __func__, __LINE__); + } + break; + + default: + return ps3_setup_dynamic_device(repo); + } + + return result; +} + +static void ps3_find_and_add_device(u64 bus_id, u64 dev_id) +{ + struct ps3_repository_device repo; + int res; + unsigned int retries; + unsigned long rem; + + /* + * On some firmware versions (e.g. 1.90), the device may not show up + * in the repository immediately + */ + for (retries = 0; retries < 10; retries++) { + res = ps3_repository_find_device_by_id(&repo, bus_id, dev_id); + if (!res) + goto found; + + rem = msleep_interruptible(100); + if (rem) + break; + } + pr_warning("%s:%u: device %llu:%llu not found\n", __func__, __LINE__, + bus_id, dev_id); + return; + +found: + if (retries) + pr_debug("%s:%u: device %llu:%llu found after %u retries\n", + __func__, __LINE__, bus_id, dev_id, retries); + + ps3_setup_dynamic_device(&repo); + return; +} + +#define PS3_NOTIFICATION_DEV_ID ULONG_MAX +#define PS3_NOTIFICATION_INTERRUPT_ID 0 + +struct ps3_notification_device { + struct ps3_system_bus_device sbd; + spinlock_t lock; + u64 tag; + u64 lv1_status; + struct completion done; +}; + +enum ps3_notify_type { + notify_device_ready = 0, + notify_region_probe = 1, + notify_region_update = 2, +}; + +struct ps3_notify_cmd { + u64 operation_code; /* must be zero */ + u64 event_mask; /* OR of 1UL << enum ps3_notify_type */ +}; + +struct ps3_notify_event { + u64 event_type; /* enum ps3_notify_type */ + u64 bus_id; + u64 dev_id; + u64 dev_type; + u64 dev_port; +}; + +static irqreturn_t ps3_notification_interrupt(int irq, void *data) +{ + struct ps3_notification_device *dev = data; + int res; + u64 tag, status; + + spin_lock(&dev->lock); + res = lv1_storage_get_async_status(PS3_NOTIFICATION_DEV_ID, &tag, + &status); + if (tag != dev->tag) + pr_err("%s:%u: tag mismatch, got %llx, expected %llx\n", + __func__, __LINE__, tag, dev->tag); + + if (res) { + pr_err("%s:%u: res %d status 0x%llx\n", __func__, __LINE__, res, + status); + } else { + pr_debug("%s:%u: completed, status 0x%llx\n", __func__, + __LINE__, status); + dev->lv1_status = status; + complete(&dev->done); + } + spin_unlock(&dev->lock); + return IRQ_HANDLED; +} + +static int ps3_notification_read_write(struct ps3_notification_device *dev, + u64 lpar, int write) +{ + const char *op = write ? "write" : "read"; + unsigned long flags; + int res; + + init_completion(&dev->done); + spin_lock_irqsave(&dev->lock, flags); + res = write ? lv1_storage_write(dev->sbd.dev_id, 0, 0, 1, 0, lpar, + &dev->tag) + : lv1_storage_read(dev->sbd.dev_id, 0, 0, 1, 0, lpar, + &dev->tag); + spin_unlock_irqrestore(&dev->lock, flags); + if (res) { + pr_err("%s:%u: %s failed %d\n", __func__, __LINE__, op, res); + return -EPERM; + } + pr_debug("%s:%u: notification %s issued\n", __func__, __LINE__, op); + + res = wait_event_interruptible(dev->done.wait, + dev->done.done || kthread_should_stop()); + if (kthread_should_stop()) + res = -EINTR; + if (res) { + pr_debug("%s:%u: interrupted %s\n", __func__, __LINE__, op); + return res; + } + + if (dev->lv1_status) { + pr_err("%s:%u: %s not completed, status 0x%llx\n", __func__, + __LINE__, op, dev->lv1_status); + return -EIO; + } + pr_debug("%s:%u: notification %s completed\n", __func__, __LINE__, op); + + return 0; +} + +static struct task_struct *probe_task; + +/** * ps3_probe_thread - Background repository probing at system startup. * * This implementation only supports background probing on a single bus. + * It uses the hypervisor's storage device notification mechanism to wait until + * a storage device is ready. The device notification mechanism uses a + * pseudo device to asynchronously notify the guest when storage devices become + * ready. The notification device has a block size of 512 bytes. */ static int ps3_probe_thread(void *data) { - struct ps3_repository_device *repo = data; - int result; - unsigned int ms = 250; + struct ps3_notification_device dev; + int res; + unsigned int irq; + u64 lpar; + void *buf; + struct ps3_notify_cmd *notify_cmd; + struct ps3_notify_event *notify_event; pr_debug(" -> %s:%u: kthread started\n", __func__, __LINE__); + buf = kzalloc(512, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + lpar = ps3_mm_phys_to_lpar(__pa(buf)); + notify_cmd = buf; + notify_event = buf; + + /* dummy system bus device */ + dev.sbd.bus_id = (u64)data; + dev.sbd.dev_id = PS3_NOTIFICATION_DEV_ID; + dev.sbd.interrupt_id = PS3_NOTIFICATION_INTERRUPT_ID; + + res = lv1_open_device(dev.sbd.bus_id, dev.sbd.dev_id, 0); + if (res) { + pr_err("%s:%u: lv1_open_device failed %s\n", __func__, + __LINE__, ps3_result(res)); + goto fail_free; + } + + res = ps3_sb_event_receive_port_setup(&dev.sbd, PS3_BINDING_CPU_ANY, + &irq); + if (res) { + pr_err("%s:%u: ps3_sb_event_receive_port_setup failed %d\n", + __func__, __LINE__, res); + goto fail_close_device; + } + + spin_lock_init(&dev.lock); + + res = request_irq(irq, ps3_notification_interrupt, 0, + "ps3_notification", &dev); + if (res) { + pr_err("%s:%u: request_irq failed %d\n", __func__, __LINE__, + res); + goto fail_sb_event_receive_port_destroy; + } + + /* Setup and write the request for device notification. */ + notify_cmd->operation_code = 0; /* must be zero */ + notify_cmd->event_mask = 1UL << notify_region_probe; + + res = ps3_notification_read_write(&dev, lpar, 1); + if (res) + goto fail_free_irq; + + /* Loop here processing the requested notification events. */ do { try_to_freeze(); - pr_debug("%s:%u: probing...\n", __func__, __LINE__); - - do { - result = ps3_repository_find_device(repo); - - if (result == -ENODEV) - pr_debug("%s:%u: nothing new\n", __func__, - __LINE__); - else if (result) - pr_debug("%s:%u: find device error.\n", - __func__, __LINE__); - else { - pr_debug("%s:%u: found device\n", __func__, - __LINE__); - ps3_register_repository_device(repo); - ps3_repository_bump_device(repo); - ms = 250; - } - } while (!result); - - pr_debug("%s:%u: ms %u\n", __func__, __LINE__, ms); - - if ( ms > 60000) + memset(notify_event, 0, sizeof(*notify_event)); + + res = ps3_notification_read_write(&dev, lpar, 0); + if (res) break; - msleep_interruptible(ms); + pr_debug("%s:%u: notify event type 0x%llx bus id %llu dev id %llu" + " type %llu port %llu\n", __func__, __LINE__, + notify_event->event_type, notify_event->bus_id, + notify_event->dev_id, notify_event->dev_type, + notify_event->dev_port); - /* An exponential backoff. */ - ms <<= 1; + if (notify_event->event_type != notify_region_probe || + notify_event->bus_id != dev.sbd.bus_id) { + pr_warning("%s:%u: bad notify_event: event %llu, " + "dev_id %llu, dev_type %llu\n", + __func__, __LINE__, notify_event->event_type, + notify_event->dev_id, + notify_event->dev_type); + continue; + } + + ps3_find_and_add_device(dev.sbd.bus_id, notify_event->dev_id); } while (!kthread_should_stop()); +fail_free_irq: + free_irq(irq, &dev); +fail_sb_event_receive_port_destroy: + ps3_sb_event_receive_port_destroy(&dev.sbd, irq); +fail_close_device: + lv1_close_device(dev.sbd.bus_id, dev.sbd.dev_id); +fail_free: + kfree(buf); + + probe_task = NULL; + pr_debug(" <- %s:%u: kthread finished\n", __func__, __LINE__); return 0; } /** + * ps3_stop_probe_thread - Stops the background probe thread. + * + */ + +static int ps3_stop_probe_thread(struct notifier_block *nb, unsigned long code, + void *data) +{ + if (probe_task) + kthread_stop(probe_task); + return 0; +} + +static struct notifier_block nb = { + .notifier_call = ps3_stop_probe_thread +}; + +/** * ps3_start_probe_thread - Starts the background probe thread. * */ @@ -715,7 +913,7 @@ static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type) { int result; struct task_struct *task; - static struct ps3_repository_device repo; /* must be static */ + struct ps3_repository_device repo; pr_debug(" -> %s:%d\n", __func__, __LINE__); @@ -738,7 +936,8 @@ static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type) return -ENODEV; } - task = kthread_run(ps3_probe_thread, &repo, "ps3-probe-%u", bus_type); + task = kthread_run(ps3_probe_thread, (void *)repo.bus_id, + "ps3-probe-%u", bus_type); if (IS_ERR(task)) { result = PTR_ERR(task); @@ -747,6 +946,9 @@ static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type) return result; } + probe_task = task; + register_reboot_notifier(&nb); + pr_debug(" <- %s:%d\n", __func__, __LINE__); return 0; } @@ -774,11 +976,14 @@ static int __init ps3_register_devices(void) ps3_register_graphics_devices(); - ps3_repository_find_devices(PS3_BUS_TYPE_SB, - ps3_register_repository_device); + ps3_repository_find_devices(PS3_BUS_TYPE_SB, ps3_setup_static_device); ps3_register_sound_devices(); + ps3_register_lpm_devices(); + + ps3_register_ramdisk_device(); + pr_debug(" <- %s:%d\n", __func__, __LINE__); return 0; } diff --git a/arch/powerpc/platforms/ps3/exports.c b/arch/powerpc/platforms/ps3/exports.c index a7e8ffd24a6..7df5b7d8fc6 100644 --- a/arch/powerpc/platforms/ps3/exports.c +++ b/arch/powerpc/platforms/ps3/exports.c @@ -18,8 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <linux/module.h> - #define LV1_CALL(name, in, out, num) \ extern s64 _lv1_##name(LV1_##in##_IN_##out##_OUT_ARG_DECL); \ EXPORT_SYMBOL(_lv1_##name); diff --git a/arch/powerpc/platforms/ps3/gelic_udbg.c b/arch/powerpc/platforms/ps3/gelic_udbg.c new file mode 100644 index 00000000000..20b46a19a48 --- /dev/null +++ b/arch/powerpc/platforms/ps3/gelic_udbg.c @@ -0,0 +1,273 @@ +/* + * udbg debug output routine via GELIC UDP broadcasts + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2006, 2007 Sony Corporation + * Copyright (C) 2010 Hector Martin <hector@marcansoft.com> + * Copyright (C) 2011 Andre Heider <a.heider@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + */ + +#include <asm/io.h> +#include <asm/udbg.h> +#include <asm/lv1call.h> + +#define GELIC_BUS_ID 1 +#define GELIC_DEVICE_ID 0 +#define GELIC_DEBUG_PORT 18194 +#define GELIC_MAX_MESSAGE_SIZE 1000 + +#define GELIC_LV1_GET_MAC_ADDRESS 1 +#define GELIC_LV1_GET_VLAN_ID 4 +#define GELIC_LV1_VLAN_TX_ETHERNET_0 2 + +#define GELIC_DESCR_DMA_STAT_MASK 0xf0000000 +#define GELIC_DESCR_DMA_CARDOWNED 0xa0000000 + +#define GELIC_DESCR_TX_DMA_IKE 0x00080000 +#define GELIC_DESCR_TX_DMA_NO_CHKSUM 0x00000000 +#define GELIC_DESCR_TX_DMA_FRAME_TAIL 0x00040000 + +#define GELIC_DESCR_DMA_CMD_NO_CHKSUM (GELIC_DESCR_DMA_CARDOWNED | \ + GELIC_DESCR_TX_DMA_IKE | \ + GELIC_DESCR_TX_DMA_NO_CHKSUM) + +static u64 bus_addr; + +struct gelic_descr { + /* as defined by the hardware */ + __be32 buf_addr; + __be32 buf_size; + __be32 next_descr_addr; + __be32 dmac_cmd_status; + __be32 result_size; + __be32 valid_size; /* all zeroes for tx */ + __be32 data_status; + __be32 data_error; /* all zeroes for tx */ +} __attribute__((aligned(32))); + +struct debug_block { + struct gelic_descr descr; + u8 pkt[1520]; +} __packed; + +struct ethhdr { + u8 dest[6]; + u8 src[6]; + u16 type; +} __packed; + +struct vlantag { + u16 vlan; + u16 subtype; +} __packed; + +struct iphdr { + u8 ver_len; + u8 dscp_ecn; + u16 total_length; + u16 ident; + u16 frag_off_flags; + u8 ttl; + u8 proto; + u16 checksum; + u32 src; + u32 dest; +} __packed; + +struct udphdr { + u16 src; + u16 dest; + u16 len; + u16 checksum; +} __packed; + +static __iomem struct ethhdr *h_eth; +static __iomem struct vlantag *h_vlan; +static __iomem struct iphdr *h_ip; +static __iomem struct udphdr *h_udp; + +static __iomem char *pmsg; +static __iomem char *pmsgc; + +static __iomem struct debug_block dbg __attribute__((aligned(32))); + +static int header_size; + +static void map_dma_mem(int bus_id, int dev_id, void *start, size_t len, + u64 *real_bus_addr) +{ + s64 result; + u64 real_addr = ((u64)start) & 0x0fffffffffffffffUL; + u64 real_end = real_addr + len; + u64 map_start = real_addr & ~0xfff; + u64 map_end = (real_end + 0xfff) & ~0xfff; + u64 bus_addr = 0; + + u64 flags = 0xf800000000000000UL; + + result = lv1_allocate_device_dma_region(bus_id, dev_id, + map_end - map_start, 12, 0, + &bus_addr); + if (result) + lv1_panic(0); + + result = lv1_map_device_dma_region(bus_id, dev_id, map_start, + bus_addr, map_end - map_start, + flags); + if (result) + lv1_panic(0); + + *real_bus_addr = bus_addr + real_addr - map_start; +} + +static int unmap_dma_mem(int bus_id, int dev_id, u64 bus_addr, size_t len) +{ + s64 result; + u64 real_bus_addr; + + real_bus_addr = bus_addr & ~0xfff; + len += bus_addr - real_bus_addr; + len = (len + 0xfff) & ~0xfff; + + result = lv1_unmap_device_dma_region(bus_id, dev_id, real_bus_addr, + len); + if (result) + return result; + + return lv1_free_device_dma_region(bus_id, dev_id, real_bus_addr); +} + +static void gelic_debug_init(void) +{ + s64 result; + u64 v2; + u64 mac; + u64 vlan_id; + + result = lv1_open_device(GELIC_BUS_ID, GELIC_DEVICE_ID, 0); + if (result) + lv1_panic(0); + + map_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID, &dbg, sizeof(dbg), + &bus_addr); + + memset(&dbg, 0, sizeof(dbg)); + + dbg.descr.buf_addr = bus_addr + offsetof(struct debug_block, pkt); + + wmb(); + + result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID, + GELIC_LV1_GET_MAC_ADDRESS, 0, 0, 0, + &mac, &v2); + if (result) + lv1_panic(0); + + mac <<= 16; + + h_eth = (struct ethhdr *)dbg.pkt; + + memset(&h_eth->dest, 0xff, 6); + memcpy(&h_eth->src, &mac, 6); + + header_size = sizeof(struct ethhdr); + + result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID, + GELIC_LV1_GET_VLAN_ID, + GELIC_LV1_VLAN_TX_ETHERNET_0, 0, 0, + &vlan_id, &v2); + if (!result) { + h_eth->type = 0x8100; + + header_size += sizeof(struct vlantag); + h_vlan = (struct vlantag *)(h_eth + 1); + h_vlan->vlan = vlan_id; + h_vlan->subtype = 0x0800; + h_ip = (struct iphdr *)(h_vlan + 1); + } else { + h_eth->type = 0x0800; + h_ip = (struct iphdr *)(h_eth + 1); + } + + header_size += sizeof(struct iphdr); + h_ip->ver_len = 0x45; + h_ip->ttl = 10; + h_ip->proto = 0x11; + h_ip->src = 0x00000000; + h_ip->dest = 0xffffffff; + + header_size += sizeof(struct udphdr); + h_udp = (struct udphdr *)(h_ip + 1); + h_udp->src = GELIC_DEBUG_PORT; + h_udp->dest = GELIC_DEBUG_PORT; + + pmsgc = pmsg = (char *)(h_udp + 1); +} + +static void gelic_debug_shutdown(void) +{ + if (bus_addr) + unmap_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID, + bus_addr, sizeof(dbg)); + lv1_close_device(GELIC_BUS_ID, GELIC_DEVICE_ID); +} + +static void gelic_sendbuf(int msgsize) +{ + u16 *p; + u32 sum; + int i; + + dbg.descr.buf_size = header_size + msgsize; + h_ip->total_length = msgsize + sizeof(struct udphdr) + + sizeof(struct iphdr); + h_udp->len = msgsize + sizeof(struct udphdr); + + h_ip->checksum = 0; + sum = 0; + p = (u16 *)h_ip; + for (i = 0; i < 5; i++) + sum += *p++; + h_ip->checksum = ~(sum + (sum >> 16)); + + dbg.descr.dmac_cmd_status = GELIC_DESCR_DMA_CMD_NO_CHKSUM | + GELIC_DESCR_TX_DMA_FRAME_TAIL; + dbg.descr.result_size = 0; + dbg.descr.data_status = 0; + + wmb(); + + lv1_net_start_tx_dma(GELIC_BUS_ID, GELIC_DEVICE_ID, bus_addr, 0); + + while ((dbg.descr.dmac_cmd_status & GELIC_DESCR_DMA_STAT_MASK) == + GELIC_DESCR_DMA_CARDOWNED) + cpu_relax(); +} + +static void ps3gelic_udbg_putc(char ch) +{ + *pmsgc++ = ch; + if (ch == '\n' || (pmsgc-pmsg) >= GELIC_MAX_MESSAGE_SIZE) { + gelic_sendbuf(pmsgc-pmsg); + pmsgc = pmsg; + } +} + +void __init udbg_init_ps3gelic(void) +{ + gelic_debug_init(); + udbg_putc = ps3gelic_udbg_putc; +} + +void udbg_shutdown_ps3gelic(void) +{ + udbg_putc = NULL; + gelic_debug_shutdown(); +} +EXPORT_SYMBOL(udbg_shutdown_ps3gelic); diff --git a/arch/powerpc/platforms/ps3/htab.c b/arch/powerpc/platforms/ps3/htab.c index 5d2e176a1b1..3e270e3412a 100644 --- a/arch/powerpc/platforms/ps3/htab.c +++ b/arch/powerpc/platforms/ps3/htab.c @@ -19,145 +19,87 @@ */ #include <linux/kernel.h> +#include <linux/memblock.h> #include <asm/machdep.h> -#include <asm/lmb.h> +#include <asm/prom.h> #include <asm/udbg.h> #include <asm/lv1call.h> #include <asm/ps3fb.h> +#define PS3_VERBOSE_RESULT #include "platform.h" -#if defined(DEBUG) -#define DBG udbg_printf -#else -#define DBG pr_debug -#endif +/** + * enum lpar_vas_id - id of LPAR virtual address space. + * @lpar_vas_id_current: Current selected virtual address space + * + * Identify the target LPAR address space. + */ -static struct hash_pte *htab; -static unsigned long htab_addr; -static unsigned char *bolttab; -static unsigned char *inusetab; +enum ps3_lpar_vas_id { + PS3_LPAR_VAS_ID_CURRENT = 0, +}; -static DEFINE_SPINLOCK(ps3_bolttab_lock); -#define debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g) \ - _debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g, __func__, __LINE__) -static void _debug_dump_hpte(unsigned long pa, unsigned long va, - unsigned long group, unsigned long bitmap, struct hash_pte lhpte, - int psize, unsigned long slot, const char* func, int line) -{ - DBG("%s:%d: pa = %lxh\n", func, line, pa); - DBG("%s:%d: lpar = %lxh\n", func, line, - ps3_mm_phys_to_lpar(pa)); - DBG("%s:%d: va = %lxh\n", func, line, va); - DBG("%s:%d: group = %lxh\n", func, line, group); - DBG("%s:%d: bitmap = %lxh\n", func, line, bitmap); - DBG("%s:%d: hpte.v = %lxh\n", func, line, lhpte.v); - DBG("%s:%d: hpte.r = %lxh\n", func, line, lhpte.r); - DBG("%s:%d: psize = %xh\n", func, line, psize); - DBG("%s:%d: slot = %lxh\n", func, line, slot); -} +static DEFINE_SPINLOCK(ps3_htab_lock); -static long ps3_hpte_insert(unsigned long hpte_group, unsigned long va, - unsigned long pa, unsigned long rflags, unsigned long vflags, int psize) +static long ps3_hpte_insert(unsigned long hpte_group, unsigned long vpn, + unsigned long pa, unsigned long rflags, unsigned long vflags, + int psize, int apsize, int ssize) { - unsigned long slot; - struct hash_pte lhpte; - int secondary = 0; - unsigned long result; - unsigned long bitmap; + int result; + u64 hpte_v, hpte_r; + u64 inserted_index; + u64 evicted_v, evicted_r; + u64 hpte_v_array[4], hpte_rs; unsigned long flags; - unsigned long p_pteg, s_pteg, b_index, b_mask, cb, ci; - - vflags &= ~HPTE_V_SECONDARY; /* this bit is ignored */ + long ret = -1; - lhpte.v = hpte_encode_v(va, psize) | vflags | HPTE_V_VALID; - lhpte.r = hpte_encode_r(ps3_mm_phys_to_lpar(pa), psize) | rflags; - - p_pteg = hpte_group / HPTES_PER_GROUP; - s_pteg = ~p_pteg & htab_hash_mask; - - spin_lock_irqsave(&ps3_bolttab_lock, flags); + /* + * lv1_insert_htab_entry() will search for victim + * entry in both primary and secondary pte group + */ + vflags &= ~HPTE_V_SECONDARY; - BUG_ON(bolttab[p_pteg] == 0xff && bolttab[s_pteg] == 0xff); + hpte_v = hpte_encode_v(vpn, psize, apsize, ssize) | vflags | HPTE_V_VALID; + hpte_r = hpte_encode_r(ps3_mm_phys_to_lpar(pa), psize, apsize) | rflags; - bitmap = (inusetab[p_pteg] << 8) | inusetab[s_pteg]; + spin_lock_irqsave(&ps3_htab_lock, flags); - if (bitmap == 0xffff) { - /* - * PTEG is full. Search for victim. - */ - bitmap &= ~((bolttab[p_pteg] << 8) | bolttab[s_pteg]); - do { - ci = mftb() & 15; - cb = 0x8000UL >> ci; - } while ((cb & bitmap) == 0); - } else { - /* - * search free slot in hardware order - * [primary] 0, 2, 4, 6, 1, 3, 5, 7 - * [secondary] 0, 2, 4, 6, 1, 3, 5, 7 - */ - for (ci = 0; ci < HPTES_PER_GROUP; ci += 2) { - cb = 0x8000UL >> ci; - if ((cb & bitmap) == 0) - goto found; - } - for (ci = 1; ci < HPTES_PER_GROUP; ci += 2) { - cb = 0x8000UL >> ci; - if ((cb & bitmap) == 0) - goto found; - } - for (ci = HPTES_PER_GROUP; ci < HPTES_PER_GROUP*2; ci += 2) { - cb = 0x8000UL >> ci; - if ((cb & bitmap) == 0) - goto found; - } - for (ci = HPTES_PER_GROUP+1; ci < HPTES_PER_GROUP*2; ci += 2) { - cb = 0x8000UL >> ci; - if ((cb & bitmap) == 0) - goto found; - } - } - -found: - if (ci < HPTES_PER_GROUP) { - slot = p_pteg * HPTES_PER_GROUP + ci; - } else { - slot = s_pteg * HPTES_PER_GROUP + (ci & 7); - /* lhpte.dw0.dw0.h = 1; */ - vflags |= HPTE_V_SECONDARY; - lhpte.v |= HPTE_V_SECONDARY; - } - - result = lv1_write_htab_entry(0, slot, lhpte.v, lhpte.r); + /* talk hvc to replace entries BOLTED == 0 */ + result = lv1_insert_htab_entry(PS3_LPAR_VAS_ID_CURRENT, hpte_group, + hpte_v, hpte_r, + HPTE_V_BOLTED, 0, + &inserted_index, + &evicted_v, &evicted_r); if (result) { - debug_dump_hpte(pa, va, hpte_group, bitmap, lhpte, psize, slot); + /* all entries bolted !*/ + pr_info("%s:result=%s vpn=%lx pa=%lx ix=%lx v=%llx r=%llx\n", + __func__, ps3_result(result), vpn, pa, hpte_group, + hpte_v, hpte_r); BUG(); } /* - * If used slot is not in primary HPTE group, - * the slot should be in secondary HPTE group. + * see if the entry is inserted into secondary pteg */ + result = lv1_read_htab_entries(PS3_LPAR_VAS_ID_CURRENT, + inserted_index & ~0x3UL, + &hpte_v_array[0], &hpte_v_array[1], + &hpte_v_array[2], &hpte_v_array[3], + &hpte_rs); + BUG_ON(result); - if ((hpte_group ^ slot) & ~(HPTES_PER_GROUP - 1)) { - secondary = 1; - b_index = s_pteg; - } else { - secondary = 0; - b_index = p_pteg; - } + if (hpte_v_array[inserted_index % 4] & HPTE_V_SECONDARY) + ret = (inserted_index & 7) | (1 << 3); + else + ret = inserted_index & 7; - b_mask = (lhpte.v & HPTE_V_BOLTED) ? 1 << 7 : 0 << 7; - bolttab[b_index] |= b_mask >> (slot & 7); - b_mask = 1 << 7; - inusetab[b_index] |= b_mask >> (slot & 7); - spin_unlock_irqrestore(&ps3_bolttab_lock, flags); + spin_unlock_irqrestore(&ps3_htab_lock, flags); - return (slot & 7) | (secondary << 3); + return ret; } static long ps3_hpte_remove(unsigned long hpte_group) @@ -167,92 +109,92 @@ static long ps3_hpte_remove(unsigned long hpte_group) } static long ps3_hpte_updatepp(unsigned long slot, unsigned long newpp, - unsigned long va, int psize, int local) + unsigned long vpn, int psize, int apsize, + int ssize, int local) { + int result; + u64 hpte_v, want_v, hpte_rs; + u64 hpte_v_array[4]; unsigned long flags; - unsigned long result; - unsigned long pteg, bit; - unsigned long hpte_v, want_v; - - want_v = hpte_encode_v(va, psize); + long ret; - spin_lock_irqsave(&ps3_bolttab_lock, flags); - - hpte_v = htab[slot].v; - if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) { - spin_unlock_irqrestore(&ps3_bolttab_lock, flags); + want_v = hpte_encode_avpn(vpn, psize, ssize); - /* ps3_hpte_insert() will be used to update PTE */ - return -1; - } + spin_lock_irqsave(&ps3_htab_lock, flags); - result = lv1_write_htab_entry(0, slot, 0, 0); + result = lv1_read_htab_entries(PS3_LPAR_VAS_ID_CURRENT, slot & ~0x3UL, + &hpte_v_array[0], &hpte_v_array[1], + &hpte_v_array[2], &hpte_v_array[3], + &hpte_rs); if (result) { - DBG("%s: va=%lx slot=%lx psize=%d result = %ld (0x%lx)\n", - __func__, va, slot, psize, result, result); + pr_info("%s: result=%s read vpn=%lx slot=%lx psize=%d\n", + __func__, ps3_result(result), vpn, slot, psize); BUG(); } - pteg = slot / HPTES_PER_GROUP; - bit = slot % HPTES_PER_GROUP; - inusetab[pteg] &= ~(0x80 >> bit); + hpte_v = hpte_v_array[slot % 4]; - spin_unlock_irqrestore(&ps3_bolttab_lock, flags); + /* + * As lv1_read_htab_entries() does not give us the RPN, we can + * not synthesize the new hpte_r value here, and therefore can + * not update the hpte with lv1_insert_htab_entry(), so we + * instead invalidate it and ask the caller to update it via + * ps3_hpte_insert() by returning a -1 value. + */ + if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) { + /* not found */ + ret = -1; + } else { + /* entry found, just invalidate it */ + result = lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, + slot, 0, 0); + ret = -1; + } - /* ps3_hpte_insert() will be used to update PTE */ - return -1; + spin_unlock_irqrestore(&ps3_htab_lock, flags); + return ret; } static void ps3_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, - int psize) + int psize, int ssize) { panic("ps3_hpte_updateboltedpp() not implemented"); } -static void ps3_hpte_invalidate(unsigned long slot, unsigned long va, - int psize, int local) +static void ps3_hpte_invalidate(unsigned long slot, unsigned long vpn, + int psize, int apsize, int ssize, int local) { unsigned long flags; - unsigned long result; - unsigned long pteg, bit; + int result; + + spin_lock_irqsave(&ps3_htab_lock, flags); - spin_lock_irqsave(&ps3_bolttab_lock, flags); - result = lv1_write_htab_entry(0, slot, 0, 0); + result = lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, slot, 0, 0); if (result) { - DBG("%s: va=%lx slot=%lx psize=%d result = %ld (0x%lx)\n", - __func__, va, slot, psize, result, result); + pr_info("%s: result=%s vpn=%lx slot=%lx psize=%d\n", + __func__, ps3_result(result), vpn, slot, psize); BUG(); } - pteg = slot / HPTES_PER_GROUP; - bit = slot % HPTES_PER_GROUP; - inusetab[pteg] &= ~(0x80 >> bit); - spin_unlock_irqrestore(&ps3_bolttab_lock, flags); + spin_unlock_irqrestore(&ps3_htab_lock, flags); } static void ps3_hpte_clear(void) { - int result; + unsigned long hpte_count = (1UL << ppc64_pft_size) >> 4; + u64 i; - DBG(" -> %s:%d\n", __func__, __LINE__); - - result = lv1_unmap_htab(htab_addr); - BUG_ON(result); + for (i = 0; i < hpte_count; i++) + lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, i, 0, 0); ps3_mm_shutdown(); ps3_mm_vas_destroy(); - - DBG(" <- %s:%d\n", __func__, __LINE__); } void __init ps3_hpte_init(unsigned long htab_size) { - long bitmap_size; - - DBG(" -> %s:%d\n", __func__, __LINE__); - ppc_md.hpte_invalidate = ps3_hpte_invalidate; ppc_md.hpte_updatepp = ps3_hpte_updatepp; ppc_md.hpte_updateboltedpp = ps3_hpte_updateboltedpp; @@ -261,28 +203,5 @@ void __init ps3_hpte_init(unsigned long htab_size) ppc_md.hpte_clear_all = ps3_hpte_clear; ppc64_pft_size = __ilog2(htab_size); - - bitmap_size = htab_size / sizeof(struct hash_pte) / 8; - - bolttab = __va(lmb_alloc(bitmap_size, 1)); - inusetab = __va(lmb_alloc(bitmap_size, 1)); - - memset(bolttab, 0, bitmap_size); - memset(inusetab, 0, bitmap_size); - - DBG(" <- %s:%d\n", __func__, __LINE__); } -void __init ps3_map_htab(void) -{ - long result; - unsigned long htab_size = (1UL << ppc64_pft_size); - - result = lv1_map_htab(0, &htab_addr); - - htab = (__force struct hash_pte *)ioremap_flags(htab_addr, htab_size, - pgprot_val(PAGE_READONLY_X)); - - DBG("%s:%d: lpar %016lxh, virt %016lxh\n", __func__, __LINE__, - htab_addr, (unsigned long)htab); -} diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c index 67e32ec9b37..5f3b23220b8 100644 --- a/arch/powerpc/platforms/ps3/interrupt.c +++ b/arch/powerpc/platforms/ps3/interrupt.c @@ -19,7 +19,7 @@ */ #include <linux/kernel.h> -#include <linux/module.h> +#include <linux/export.h> #include <linux/irq.h> #include <asm/machdep.h> @@ -31,20 +31,20 @@ #if defined(DEBUG) #define DBG udbg_printf +#define FAIL udbg_printf #else -#define DBG pr_debug +#define DBG pr_devel +#define FAIL pr_debug #endif /** * struct ps3_bmp - a per cpu irq status and mask bitmap structure * @status: 256 bit status bitmap indexed by plug - * @unused_1: + * @unused_1: Alignment * @mask: 256 bit mask bitmap indexed by plug - * @unused_2: - * @lock: - * @ipi_debug_brk_mask: + * @unused_2: Alignment * - * The HV mantains per SMT thread mappings of HV outlet to HV plug on + * The HV maintains per SMT thread mappings of HV outlet to HV plug on * behalf of the guest. These mappings are implemented as 256 bit guest * supplied bitmaps indexed by plug number. The addresses of the bitmaps * are registered with the HV through lv1_configure_irq_state_bitmap(). @@ -60,6 +60,8 @@ * gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note * that there is no constraint on how many in this set an individual thread * can acquire. + * + * The mask is declared as unsigned long so we can use set/clear_bit on it. */ #define PS3_BMP_MINALIGN 64 @@ -68,24 +70,28 @@ struct ps3_bmp { struct { u64 status; u64 unused_1[3]; - u64 mask; + unsigned long mask; u64 unused_2[3]; }; - u64 ipi_debug_brk_mask; - spinlock_t lock; }; /** * struct ps3_private - a per cpu data structure * @bmp: ps3_bmp structure + * @bmp_lock: Syncronize access to bmp. + * @ipi_debug_brk_mask: Mask for debug break IPIs * @ppe_id: HV logical_ppe_id * @thread_id: HV thread_id + * @ipi_mask: Mask of IPI virqs */ struct ps3_private { struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN))); + spinlock_t bmp_lock; u64 ppe_id; u64 thread_id; + unsigned long ipi_debug_brk_mask; + unsigned long ipi_mask; }; static DEFINE_PER_CPU(struct ps3_private, ps3_private); @@ -97,16 +103,16 @@ static DEFINE_PER_CPU(struct ps3_private, ps3_private); * Sets ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). */ -static void ps3_chip_mask(unsigned int virq) +static void ps3_chip_mask(struct irq_data *d) { - struct ps3_private *pd = get_irq_chip_data(virq); + struct ps3_private *pd = irq_data_get_irq_chip_data(d); unsigned long flags; - pr_debug("%s:%d: thread_id %lu, virq %d\n", __func__, __LINE__, - pd->thread_id, virq); + DBG("%s:%d: thread_id %llu, virq %d\n", __func__, __LINE__, + pd->thread_id, d->irq); local_irq_save(flags); - clear_bit(63 - virq, &pd->bmp.mask); + clear_bit(63 - d->irq, &pd->bmp.mask); lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id); local_irq_restore(flags); } @@ -118,16 +124,16 @@ static void ps3_chip_mask(unsigned int virq) * Clears ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). */ -static void ps3_chip_unmask(unsigned int virq) +static void ps3_chip_unmask(struct irq_data *d) { - struct ps3_private *pd = get_irq_chip_data(virq); + struct ps3_private *pd = irq_data_get_irq_chip_data(d); unsigned long flags; - pr_debug("%s:%d: thread_id %lu, virq %d\n", __func__, __LINE__, - pd->thread_id, virq); + DBG("%s:%d: thread_id %llu, virq %d\n", __func__, __LINE__, + pd->thread_id, d->irq); local_irq_save(flags); - set_bit(63 - virq, &pd->bmp.mask); + set_bit(63 - d->irq, &pd->bmp.mask); lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id); local_irq_restore(flags); } @@ -139,10 +145,14 @@ static void ps3_chip_unmask(unsigned int virq) * Calls lv1_end_of_interrupt_ext(). */ -static void ps3_chip_eoi(unsigned int virq) +static void ps3_chip_eoi(struct irq_data *d) { - const struct ps3_private *pd = get_irq_chip_data(virq); - lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, virq); + const struct ps3_private *pd = irq_data_get_irq_chip_data(d); + + /* non-IPIs are EOIed here. */ + + if (!test_bit(63 - d->irq, &pd->ipi_mask)) + lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, d->irq); } /** @@ -150,10 +160,10 @@ static void ps3_chip_eoi(unsigned int virq) */ static struct irq_chip ps3_irq_chip = { - .typename = "ps3", - .mask = ps3_chip_mask, - .unmask = ps3_chip_unmask, - .eoi = ps3_chip_eoi, + .name = "ps3", + .irq_mask = ps3_chip_mask, + .irq_unmask = ps3_chip_unmask, + .irq_eoi = ps3_chip_eoi, }; /** @@ -167,8 +177,8 @@ static struct irq_chip ps3_irq_chip = { * ps3_private data. */ -int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, - unsigned int *virq) +static int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, + unsigned int *virq) { int result; struct ps3_private *pd; @@ -183,24 +193,24 @@ int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, *virq = irq_create_mapping(NULL, outlet); if (*virq == NO_IRQ) { - pr_debug("%s:%d: irq_create_mapping failed: outlet %lu\n", + FAIL("%s:%d: irq_create_mapping failed: outlet %lu\n", __func__, __LINE__, outlet); result = -ENOMEM; goto fail_create; } - pr_debug("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__, + DBG("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__, outlet, cpu, *virq); - result = set_irq_chip_data(*virq, pd); + result = irq_set_chip_data(*virq, pd); if (result) { - pr_debug("%s:%d: set_irq_chip_data failed\n", + FAIL("%s:%d: irq_set_chip_data failed\n", __func__, __LINE__); goto fail_set; } - ps3_chip_mask(*virq); + ps3_chip_mask(irq_get_irq_data(*virq)); return result; @@ -217,17 +227,17 @@ fail_create: * Clears chip data and calls irq_dispose_mapping() for the virq. */ -int ps3_virq_destroy(unsigned int virq) +static int ps3_virq_destroy(unsigned int virq) { - const struct ps3_private *pd = get_irq_chip_data(virq); + const struct ps3_private *pd = irq_get_chip_data(virq); - pr_debug("%s:%d: ppe_id %lu, thread_id %lu, virq %u\n", __func__, + DBG("%s:%d: ppe_id %llu, thread_id %llu, virq %u\n", __func__, __LINE__, pd->ppe_id, pd->thread_id, virq); - set_irq_chip_data(virq, NULL); + irq_set_chip_data(virq, NULL); irq_dispose_mapping(virq); - pr_debug("%s:%d <-\n", __func__, __LINE__); + DBG("%s:%d <-\n", __func__, __LINE__); return 0; } @@ -250,11 +260,11 @@ int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet, result = ps3_virq_setup(cpu, outlet, virq); if (result) { - pr_debug("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__); + FAIL("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__); goto fail_setup; } - pd = get_irq_chip_data(*virq); + pd = irq_get_chip_data(*virq); /* Binds outlet to cpu + virq. */ @@ -262,7 +272,7 @@ int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet, outlet, 0); if (result) { - pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", + FAIL("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", __func__, __LINE__, ps3_result(result)); result = -EPERM; goto fail_connect; @@ -289,17 +299,17 @@ EXPORT_SYMBOL_GPL(ps3_irq_plug_setup); int ps3_irq_plug_destroy(unsigned int virq) { int result; - const struct ps3_private *pd = get_irq_chip_data(virq); + const struct ps3_private *pd = irq_get_chip_data(virq); - pr_debug("%s:%d: ppe_id %lu, thread_id %lu, virq %u\n", __func__, + DBG("%s:%d: ppe_id %llu, thread_id %llu, virq %u\n", __func__, __LINE__, pd->ppe_id, pd->thread_id, virq); - ps3_chip_mask(virq); + ps3_chip_mask(irq_get_irq_data(virq)); result = lv1_disconnect_irq_plug_ext(pd->ppe_id, pd->thread_id, virq); if (result) - pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", + FAIL("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", __func__, __LINE__, ps3_result(result)); ps3_virq_destroy(virq); @@ -322,12 +332,12 @@ EXPORT_SYMBOL_GPL(ps3_irq_plug_destroy); int ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq) { int result; - unsigned long outlet; + u64 outlet; result = lv1_construct_event_receive_port(&outlet); if (result) { - pr_debug("%s:%d: lv1_construct_event_receive_port failed: %s\n", + FAIL("%s:%d: lv1_construct_event_receive_port failed: %s\n", __func__, __LINE__, ps3_result(result)); *virq = NO_IRQ; return result; @@ -353,14 +363,14 @@ int ps3_event_receive_port_destroy(unsigned int virq) { int result; - pr_debug(" -> %s:%d virq %u\n", __func__, __LINE__, virq); + DBG(" -> %s:%d virq %u\n", __func__, __LINE__, virq); - ps3_chip_mask(virq); + ps3_chip_mask(irq_get_irq_data(virq)); result = lv1_destruct_event_receive_port(virq_to_hw(virq)); if (result) - pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", + FAIL("%s:%d: lv1_destruct_event_receive_port failed: %s\n", __func__, __LINE__, ps3_result(result)); /* @@ -368,7 +378,7 @@ int ps3_event_receive_port_destroy(unsigned int virq) * calls from interrupt context (smp_call_function) when kexecing. */ - pr_debug(" <- %s:%d\n", __func__, __LINE__); + DBG(" <- %s:%d\n", __func__, __LINE__); return result; } @@ -404,7 +414,7 @@ int ps3_sb_event_receive_port_setup(struct ps3_system_bus_device *dev, dev->dev_id, virq_to_hw(*virq), dev->interrupt_id); if (result) { - pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port" + FAIL("%s:%d: lv1_connect_interrupt_event_receive_port" " failed: %s\n", __func__, __LINE__, ps3_result(result)); ps3_event_receive_port_destroy(*virq); @@ -412,7 +422,7 @@ int ps3_sb_event_receive_port_setup(struct ps3_system_bus_device *dev, return result; } - pr_debug("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, + DBG("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, dev->interrupt_id, *virq); return 0; @@ -426,14 +436,14 @@ int ps3_sb_event_receive_port_destroy(struct ps3_system_bus_device *dev, int result; - pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, + DBG(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, dev->interrupt_id, virq); result = lv1_disconnect_interrupt_event_receive_port(dev->bus_id, dev->dev_id, virq_to_hw(virq), dev->interrupt_id); if (result) - pr_debug("%s:%d: lv1_disconnect_interrupt_event_receive_port" + FAIL("%s:%d: lv1_disconnect_interrupt_event_receive_port" " failed: %s\n", __func__, __LINE__, ps3_result(result)); @@ -448,7 +458,7 @@ int ps3_sb_event_receive_port_destroy(struct ps3_system_bus_device *dev, result = ps3_virq_destroy(virq); BUG_ON(result); - pr_debug(" <- %s:%d\n", __func__, __LINE__); + DBG(" <- %s:%d\n", __func__, __LINE__); return result; } EXPORT_SYMBOL(ps3_sb_event_receive_port_destroy); @@ -468,12 +478,12 @@ int ps3_io_irq_setup(enum ps3_cpu_binding cpu, unsigned int interrupt_id, unsigned int *virq) { int result; - unsigned long outlet; + u64 outlet; result = lv1_construct_io_irq_outlet(interrupt_id, &outlet); if (result) { - pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n", + FAIL("%s:%d: lv1_construct_io_irq_outlet failed: %s\n", __func__, __LINE__, ps3_result(result)); return result; } @@ -490,7 +500,7 @@ int ps3_io_irq_destroy(unsigned int virq) int result; unsigned long outlet = virq_to_hw(virq); - ps3_chip_mask(virq); + ps3_chip_mask(irq_get_irq_data(virq)); /* * lv1_destruct_io_irq_outlet() will destroy the IRQ plug, @@ -503,7 +513,7 @@ int ps3_io_irq_destroy(unsigned int virq) result = lv1_destruct_io_irq_outlet(outlet); if (result) - pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", + FAIL("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", __func__, __LINE__, ps3_result(result)); return result; @@ -525,7 +535,7 @@ int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp, unsigned int *virq) { int result; - unsigned long outlet; + u64 outlet; u64 lpar_addr; BUG_ON(!is_kernel_addr((u64)virt_addr_bmp)); @@ -535,7 +545,7 @@ int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp, result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet); if (result) { - pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", + FAIL("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", __func__, __LINE__, ps3_result(result)); return result; } @@ -551,11 +561,11 @@ int ps3_vuart_irq_destroy(unsigned int virq) { int result; - ps3_chip_mask(virq); + ps3_chip_mask(irq_get_irq_data(virq)); result = lv1_deconfigure_virtual_uart_irq(); if (result) { - pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", + FAIL("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", __func__, __LINE__, ps3_result(result)); return result; } @@ -581,14 +591,14 @@ int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id, unsigned int class, unsigned int *virq) { int result; - unsigned long outlet; + u64 outlet; BUG_ON(class > 2); result = lv1_get_spe_irq_outlet(spe_id, class, &outlet); if (result) { - pr_debug("%s:%d: lv1_get_spe_irq_outlet failed: %s\n", + FAIL("%s:%d: lv1_get_spe_irq_outlet failed: %s\n", __func__, __LINE__, ps3_result(result)); return result; } @@ -603,7 +613,7 @@ int ps3_spe_irq_destroy(unsigned int virq) { int result; - ps3_chip_mask(virq); + ps3_chip_mask(irq_get_irq_data(virq)); result = ps3_irq_plug_destroy(virq); BUG_ON(result); @@ -619,7 +629,7 @@ int ps3_spe_irq_destroy(unsigned int virq) static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu, const char* func, int line) { - pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n", + pr_debug("%s:%d: %s %u {%04llx_%04llx_%04llx_%04llx}\n", func, line, header, cpu, *p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff, *p & 0xffff); @@ -628,7 +638,7 @@ static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu, static void __maybe_unused _dump_256_bmp(const char *header, const u64 *p, unsigned cpu, const char* func, int line) { - pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n", + pr_debug("%s:%d: %s %u {%016llx:%016llx:%016llx:%016llx}\n", func, line, header, cpu, p[0], p[1], p[2], p[3]); } @@ -637,10 +647,10 @@ static void _dump_bmp(struct ps3_private* pd, const char* func, int line) { unsigned long flags; - spin_lock_irqsave(&pd->bmp.lock, flags); + spin_lock_irqsave(&pd->bmp_lock, flags); _dump_64_bmp("stat", &pd->bmp.status, pd->thread_id, func, line); - _dump_64_bmp("mask", &pd->bmp.mask, pd->thread_id, func, line); - spin_unlock_irqrestore(&pd->bmp.lock, flags); + _dump_64_bmp("mask", (u64*)&pd->bmp.mask, pd->thread_id, func, line); + spin_unlock_irqrestore(&pd->bmp_lock, flags); } #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) @@ -649,43 +659,54 @@ static void __maybe_unused _dump_mask(struct ps3_private *pd, { unsigned long flags; - spin_lock_irqsave(&pd->bmp.lock, flags); - _dump_64_bmp("mask", &pd->bmp.mask, pd->thread_id, func, line); - spin_unlock_irqrestore(&pd->bmp.lock, flags); + spin_lock_irqsave(&pd->bmp_lock, flags); + _dump_64_bmp("mask", (u64*)&pd->bmp.mask, pd->thread_id, func, line); + spin_unlock_irqrestore(&pd->bmp_lock, flags); } #else static void dump_bmp(struct ps3_private* pd) {}; #endif /* defined(DEBUG) */ -static void ps3_host_unmap(struct irq_host *h, unsigned int virq) -{ - set_irq_chip_data(virq, NULL); -} - -static int ps3_host_map(struct irq_host *h, unsigned int virq, +static int ps3_host_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hwirq) { - pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq, + DBG("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq, virq); - set_irq_chip_and_handler(virq, &ps3_irq_chip, handle_fasteoi_irq); + irq_set_chip_and_handler(virq, &ps3_irq_chip, handle_fasteoi_irq); return 0; } -static struct irq_host_ops ps3_host_ops = { +static int ps3_host_match(struct irq_domain *h, struct device_node *np) +{ + /* Match all */ + return 1; +} + +static const struct irq_domain_ops ps3_host_ops = { .map = ps3_host_map, - .unmap = ps3_host_unmap, + .match = ps3_host_match, }; void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) { struct ps3_private *pd = &per_cpu(ps3_private, cpu); - pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq; + set_bit(63 - virq, &pd->ipi_debug_brk_mask); - pr_debug("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__, - cpu, virq, pd->bmp.ipi_debug_brk_mask); + DBG("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__, + cpu, virq, pd->ipi_debug_brk_mask); +} + +void __init ps3_register_ipi_irq(unsigned int cpu, unsigned int virq) +{ + struct ps3_private *pd = &per_cpu(ps3_private, cpu); + + set_bit(63 - virq, &pd->ipi_mask); + + DBG("%s:%d: cpu %u, virq %u, ipi_mask %lxh\n", __func__, __LINE__, + cpu, virq, pd->ipi_mask); } static unsigned int ps3_get_irq(void) @@ -696,14 +717,14 @@ static unsigned int ps3_get_irq(void) /* check for ipi break first to stop this cpu ASAP */ - if (x & pd->bmp.ipi_debug_brk_mask) - x &= pd->bmp.ipi_debug_brk_mask; + if (x & pd->ipi_debug_brk_mask) + x &= pd->ipi_debug_brk_mask; asm volatile("cntlzd %0,%1" : "=r" (plug) : "r" (x)); plug &= 0x3f; - if (unlikely(plug) == NO_IRQ) { - pr_debug("%s:%d: no plug found: thread_id %lu\n", __func__, + if (unlikely(plug == NO_IRQ)) { + DBG("%s:%d: no plug found: thread_id %llu\n", __func__, __LINE__, pd->thread_id); dump_bmp(&per_cpu(ps3_private, 0)); dump_bmp(&per_cpu(ps3_private, 1)); @@ -717,6 +738,12 @@ static unsigned int ps3_get_irq(void) BUG(); } #endif + + /* IPIs are EOIed here. */ + + if (test_bit(63 - plug, &pd->ipi_mask)) + lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, plug); + return plug; } @@ -724,21 +751,19 @@ void __init ps3_init_IRQ(void) { int result; unsigned cpu; - struct irq_host *host; + struct irq_domain *host; - host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &ps3_host_ops, - PS3_INVALID_OUTLET); + host = irq_domain_add_nomap(NULL, PS3_PLUG_MAX + 1, &ps3_host_ops, NULL); irq_set_default_host(host); - irq_set_virq_count(PS3_PLUG_MAX + 1); for_each_possible_cpu(cpu) { struct ps3_private *pd = &per_cpu(ps3_private, cpu); lv1_get_logical_ppe_id(&pd->ppe_id); pd->thread_id = get_hard_smp_processor_id(cpu); - spin_lock_init(&pd->bmp.lock); + spin_lock_init(&pd->bmp_lock); - pr_debug("%s:%d: ppe_id %lu, thread_id %lu, bmp %lxh\n", + DBG("%s:%d: ppe_id %llu, thread_id %llu, bmp %lxh\n", __func__, __LINE__, pd->ppe_id, pd->thread_id, ps3_mm_phys_to_lpar(__pa(&pd->bmp))); @@ -746,7 +771,7 @@ void __init ps3_init_IRQ(void) pd->thread_id, ps3_mm_phys_to_lpar(__pa(&pd->bmp))); if (result) - pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:" + FAIL("%s:%d: lv1_configure_irq_state_bitmap failed:" " %s\n", __func__, __LINE__, ps3_result(result)); } @@ -763,6 +788,6 @@ void ps3_shutdown_IRQ(int cpu) lv1_get_logical_ppe_id(&ppe_id); result = lv1_configure_irq_state_bitmap(ppe_id, thread_id, 0); - DBG("%s:%d: lv1_configure_irq_state_bitmap (%lu:%lu/%d) %s\n", __func__, + DBG("%s:%d: lv1_configure_irq_state_bitmap (%llu:%llu/%d) %s\n", __func__, __LINE__, ppe_id, thread_id, cpu, ps3_result(result)); } diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c index 7bb3e162097..0c9f643d9e2 100644 --- a/arch/powerpc/platforms/ps3/mm.c +++ b/arch/powerpc/platforms/ps3/mm.c @@ -19,28 +19,26 @@ */ #include <linux/kernel.h> -#include <linux/module.h> -#include <linux/memory_hotplug.h> +#include <linux/export.h> +#include <linux/memblock.h> +#include <linux/slab.h> +#include <asm/cell-regs.h> #include <asm/firmware.h> -#include <asm/lmb.h> +#include <asm/prom.h> #include <asm/udbg.h> #include <asm/lv1call.h> +#include <asm/setup.h> #include "platform.h" #if defined(DEBUG) #define DBG udbg_printf #else -#define DBG pr_debug +#define DBG pr_devel #endif enum { -#if defined(CONFIG_PS3_USE_LPAR_ADDR) - USE_LPAR_ADDR = 1, -#else - USE_LPAR_ADDR = 0, -#endif #if defined(CONFIG_PS3_DYNAMIC_DMA) USE_DYNAMIC_DMA = 1, #else @@ -80,12 +78,14 @@ enum { * @base: base address * @size: size in bytes * @offset: difference between base and rm.size + * @destroy: flag if region should be destroyed upon shutdown */ struct mem_region { - unsigned long base; - unsigned long size; + u64 base; + u64 size; unsigned long offset; + int destroy; }; /** @@ -97,7 +97,7 @@ struct mem_region { * The HV virtual address space (vas) allows for hotplug memory regions. * Memory regions can be created and destroyed in the vas at runtime. * @rm: real mode (bootmem) region - * @r1: hotplug memory region(s) + * @r1: highmem region(s) * * ps3 addresses * virt_addr: a cpu 'translated' effective address @@ -107,9 +107,9 @@ struct mem_region { */ struct map { - unsigned long total; - unsigned long vas_id; - unsigned long htab_size; + u64 total; + u64 vas_id; + u64 htab_size; struct mem_region rm; struct mem_region r1; }; @@ -118,13 +118,13 @@ struct map { static void __maybe_unused _debug_dump_map(const struct map *m, const char *func, int line) { - DBG("%s:%d: map.total = %lxh\n", func, line, m->total); - DBG("%s:%d: map.rm.size = %lxh\n", func, line, m->rm.size); - DBG("%s:%d: map.vas_id = %lu\n", func, line, m->vas_id); - DBG("%s:%d: map.htab_size = %lxh\n", func, line, m->htab_size); - DBG("%s:%d: map.r1.base = %lxh\n", func, line, m->r1.base); + DBG("%s:%d: map.total = %llxh\n", func, line, m->total); + DBG("%s:%d: map.rm.size = %llxh\n", func, line, m->rm.size); + DBG("%s:%d: map.vas_id = %llu\n", func, line, m->vas_id); + DBG("%s:%d: map.htab_size = %llxh\n", func, line, m->htab_size); + DBG("%s:%d: map.r1.base = %llxh\n", func, line, m->r1.base); DBG("%s:%d: map.r1.offset = %lxh\n", func, line, m->r1.offset); - DBG("%s:%d: map.r1.size = %lxh\n", func, line, m->r1.size); + DBG("%s:%d: map.r1.size = %llxh\n", func, line, m->r1.size); } static struct map map; @@ -137,11 +137,8 @@ static struct map map; unsigned long ps3_mm_phys_to_lpar(unsigned long phys_addr) { BUG_ON(is_kernel_addr(phys_addr)); - if (USE_LPAR_ADDR) - return phys_addr; - else - return (phys_addr < map.rm.size || phys_addr >= map.total) - ? phys_addr : phys_addr + map.r1.offset; + return (phys_addr < map.rm.size || phys_addr >= map.total) + ? phys_addr : phys_addr + map.r1.offset; } EXPORT_SYMBOL(ps3_mm_phys_to_lpar); @@ -153,11 +150,11 @@ EXPORT_SYMBOL(ps3_mm_phys_to_lpar); void __init ps3_mm_vas_create(unsigned long* htab_size) { int result; - unsigned long start_address; - unsigned long size; - unsigned long access_right; - unsigned long max_page_size; - unsigned long flags; + u64 start_address; + u64 size; + u64 access_right; + u64 max_page_size; + u64 flags; result = lv1_query_logical_partition_address_region_info(0, &start_address, &size, &access_right, &max_page_size, @@ -171,7 +168,7 @@ void __init ps3_mm_vas_create(unsigned long* htab_size) } if (max_page_size < PAGE_SHIFT_16M) { - DBG("%s:%d: bad max_page_size %lxh\n", __func__, __LINE__, + DBG("%s:%d: bad max_page_size %llxh\n", __func__, __LINE__, max_page_size); goto fail; } @@ -215,7 +212,7 @@ void ps3_mm_vas_destroy(void) { int result; - DBG("%s:%d: map.vas_id = %lu\n", __func__, __LINE__, map.vas_id); + DBG("%s:%d: map.vas_id = %llu\n", __func__, __LINE__, map.vas_id); if (map.vas_id) { result = lv1_select_virtual_address_space(0); @@ -226,10 +223,6 @@ void ps3_mm_vas_destroy(void) } } -/*============================================================================*/ -/* memory hotplug routines */ -/*============================================================================*/ - /** * ps3_mm_region_create - create a memory region in the vas * @r: pointer to a struct mem_region to accept initialized values @@ -242,15 +235,14 @@ void ps3_mm_vas_destroy(void) static int ps3_mm_region_create(struct mem_region *r, unsigned long size) { int result; - unsigned long muid; + u64 muid; r->size = _ALIGN_DOWN(size, 1 << PAGE_SHIFT_16M); DBG("%s:%d requested %lxh\n", __func__, __LINE__, size); - DBG("%s:%d actual %lxh\n", __func__, __LINE__, r->size); - DBG("%s:%d difference %lxh (%luMB)\n", __func__, __LINE__, - (unsigned long)(size - r->size), - (size - r->size) / 1024 / 1024); + DBG("%s:%d actual %llxh\n", __func__, __LINE__, r->size); + DBG("%s:%d difference %llxh (%lluMB)\n", __func__, __LINE__, + size - r->size, (size - r->size) / 1024 / 1024); if (r->size == 0) { DBG("%s:%d: size == 0\n", __func__, __LINE__); @@ -267,6 +259,7 @@ static int ps3_mm_region_create(struct mem_region *r, unsigned long size) goto zero_region; } + r->destroy = 1; r->offset = r->base - map.rm.size; return result; @@ -284,7 +277,14 @@ static void ps3_mm_region_destroy(struct mem_region *r) { int result; - DBG("%s:%d: r->base = %lxh\n", __func__, __LINE__, r->base); + if (!r->destroy) { + pr_info("%s:%d: Not destroying high region: %llxh %llxh\n", + __func__, __LINE__, r->base, r->size); + return; + } + + DBG("%s:%d: r->base = %llxh\n", __func__, __LINE__, r->base); + if (r->base) { result = lv1_release_memory(r->base); BUG_ON(result); @@ -293,48 +293,36 @@ static void ps3_mm_region_destroy(struct mem_region *r) } } -/** - * ps3_mm_add_memory - hot add memory - */ - -static int __init ps3_mm_add_memory(void) +static int ps3_mm_get_repository_highmem(struct mem_region *r) { int result; - unsigned long start_addr; - unsigned long start_pfn; - unsigned long nr_pages; - if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) - return -ENODEV; + /* Assume a single highmem region. */ - BUG_ON(!mem_init_done); + result = ps3_repository_read_highmem_info(0, &r->base, &r->size); - start_addr = USE_LPAR_ADDR ? map.r1.base : map.rm.size; - start_pfn = start_addr >> PAGE_SHIFT; - nr_pages = (map.r1.size + PAGE_SIZE - 1) >> PAGE_SHIFT; + if (result) + goto zero_region; - DBG("%s:%d: start_addr %lxh, start_pfn %lxh, nr_pages %lxh\n", - __func__, __LINE__, start_addr, start_pfn, nr_pages); + if (!r->base || !r->size) { + result = -1; + goto zero_region; + } - result = add_memory(0, start_addr, map.r1.size); + r->offset = r->base - map.rm.size; - if (result) { - DBG("%s:%d: add_memory failed: (%d)\n", - __func__, __LINE__, result); - return result; - } + DBG("%s:%d: Found high region in repository: %llxh %llxh\n", + __func__, __LINE__, r->base, r->size); - result = online_pages(start_pfn, nr_pages); + return 0; - if (result) - DBG("%s:%d: online_pages failed: (%d)\n", - __func__, __LINE__, result); +zero_region: + DBG("%s:%d: No high region in repository.\n", __func__, __LINE__); + r->size = r->base = r->offset = 0; return result; } -core_initcall(ps3_mm_add_memory); - /*============================================================================*/ /* dma routines */ /*============================================================================*/ @@ -359,7 +347,7 @@ static unsigned long dma_sb_lpar_to_bus(struct ps3_dma_region *r, static void __maybe_unused _dma_dump_region(const struct ps3_dma_region *r, const char *func, int line) { - DBG("%s:%d: dev %u:%u\n", func, line, r->dev->bus_id, + DBG("%s:%d: dev %llu:%llu\n", func, line, r->dev->bus_id, r->dev->dev_id); DBG("%s:%d: page_size %u\n", func, line, r->page_size); DBG("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr); @@ -394,7 +382,7 @@ struct dma_chunk { static void _dma_dump_chunk (const struct dma_chunk* c, const char* func, int line) { - DBG("%s:%d: r.dev %u:%u\n", func, line, + DBG("%s:%d: r.dev %llu:%llu\n", func, line, c->region->dev->bus_id, c->region->dev->dev_id); DBG("%s:%d: r.bus_addr %lxh\n", func, line, c->region->bus_addr); DBG("%s:%d: r.page_size %u\n", func, line, c->region->page_size); @@ -600,7 +588,7 @@ static int dma_ioc0_map_pages(struct ps3_dma_region *r, unsigned long phys_addr, /* build ioptes for the area */ pages = len >> r->page_size; - DBG("%s: pgsize=%#x len=%#lx pages=%#x iopteflag=%#lx\n", __func__, + DBG("%s: pgsize=%#x len=%#lx pages=%#x iopteflag=%#llx\n", __func__, r->page_size, r->len, pages, iopte_flag); for (iopage = 0; iopage < pages; iopage++) { offset = (1 << r->page_size) * iopage; @@ -610,9 +598,8 @@ static int dma_ioc0_map_pages(struct ps3_dma_region *r, unsigned long phys_addr, r->ioid, iopte_flag); if (result) { - printk(KERN_WARNING "%s:%d: lv1_map_device_dma_region " - "failed: %s\n", __func__, __LINE__, - ps3_result(result)); + pr_warning("%s:%d: lv1_put_iopte failed: %s\n", + __func__, __LINE__, ps3_result(result)); goto fail_map; } DBG("%s: pg=%d bus=%#lx, lpar=%#lx, ioid=%#x\n", __func__, @@ -652,13 +639,14 @@ fail_alloc: static int dma_sb_region_create(struct ps3_dma_region *r) { int result; + u64 bus_addr; - pr_info(" -> %s:%d:\n", __func__, __LINE__); + DBG(" -> %s:%d:\n", __func__, __LINE__); BUG_ON(!r); if (!r->dev->bus_id) { - pr_info("%s:%d: %u:%u no dma\n", __func__, __LINE__, + pr_info("%s:%d: %llu:%llu no dma\n", __func__, __LINE__, r->dev->bus_id, r->dev->dev_id); return 0; } @@ -675,7 +663,8 @@ static int dma_sb_region_create(struct ps3_dma_region *r) result = lv1_allocate_device_dma_region(r->dev->bus_id, r->dev->dev_id, roundup_pow_of_two(r->len), r->page_size, r->region_type, - &r->bus_addr); + &bus_addr); + r->bus_addr = bus_addr; if (result) { DBG("%s:%d: lv1_allocate_device_dma_region failed: %s\n", @@ -689,6 +678,7 @@ static int dma_sb_region_create(struct ps3_dma_region *r) static int dma_ioc0_region_create(struct ps3_dma_region *r) { int result; + u64 bus_addr; INIT_LIST_HEAD(&r->chunk_list.head); spin_lock_init(&r->chunk_list.lock); @@ -696,7 +686,8 @@ static int dma_ioc0_region_create(struct ps3_dma_region *r) result = lv1_allocate_io_segment(0, r->len, r->page_size, - &r->bus_addr); + &bus_addr); + r->bus_addr = bus_addr; if (result) { DBG("%s:%d: lv1_allocate_io_segment failed: %s\n", __func__, __LINE__, ps3_result(result)); @@ -724,7 +715,7 @@ static int dma_sb_region_free(struct ps3_dma_region *r) BUG_ON(!r); if (!r->dev->bus_id) { - pr_info("%s:%d: %u:%u no dma\n", __func__, __LINE__, + pr_info("%s:%d: %llu:%llu no dma\n", __func__, __LINE__, r->dev->bus_id, r->dev->dev_id); return 0; } @@ -781,7 +772,7 @@ static int dma_ioc0_region_free(struct ps3_dma_region *r) */ static int dma_sb_map_area(struct ps3_dma_region *r, unsigned long virt_addr, - unsigned long len, unsigned long *bus_addr, + unsigned long len, dma_addr_t *bus_addr, u64 iopte_flag) { int result; @@ -804,7 +795,7 @@ static int dma_sb_map_area(struct ps3_dma_region *r, unsigned long virt_addr, DBG("%s:%d lpar_addr %lxh\n", __func__, __LINE__, lpar_addr); DBG("%s:%d len %lxh\n", __func__, __LINE__, len); - DBG("%s:%d bus_addr %lxh (%lxh)\n", __func__, __LINE__, + DBG("%s:%d bus_addr %llxh (%lxh)\n", __func__, __LINE__, *bus_addr, len); } @@ -836,7 +827,7 @@ static int dma_sb_map_area(struct ps3_dma_region *r, unsigned long virt_addr, } static int dma_ioc0_map_area(struct ps3_dma_region *r, unsigned long virt_addr, - unsigned long len, unsigned long *bus_addr, + unsigned long len, dma_addr_t *bus_addr, u64 iopte_flag) { int result; @@ -876,7 +867,7 @@ static int dma_ioc0_map_area(struct ps3_dma_region *r, unsigned long virt_addr, return result; } *bus_addr = c->bus_addr + phys_addr - aligned_phys; - DBG("%s: va=%#lx pa=%#lx a_pa=%#lx bus=%#lx\n", __func__, + DBG("%s: va=%#lx pa=%#lx a_pa=%#lx bus=%#llx\n", __func__, virt_addr, phys_addr, aligned_phys, *bus_addr); c->usage_count = 1; @@ -893,7 +884,7 @@ static int dma_ioc0_map_area(struct ps3_dma_region *r, unsigned long virt_addr, * This is the common dma unmap routine. */ -static int dma_sb_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr, +static int dma_sb_unmap_area(struct ps3_dma_region *r, dma_addr_t bus_addr, unsigned long len) { unsigned long flags; @@ -907,7 +898,7 @@ static int dma_sb_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr, 1 << r->page_size); unsigned long aligned_len = _ALIGN_UP(len + bus_addr - aligned_bus, 1 << r->page_size); - DBG("%s:%d: not found: bus_addr %lxh\n", + DBG("%s:%d: not found: bus_addr %llxh\n", __func__, __LINE__, bus_addr); DBG("%s:%d: not found: len %lxh\n", __func__, __LINE__, len); @@ -930,12 +921,12 @@ static int dma_sb_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr, } static int dma_ioc0_unmap_area(struct ps3_dma_region *r, - unsigned long bus_addr, unsigned long len) + dma_addr_t bus_addr, unsigned long len) { unsigned long flags; struct dma_chunk *c; - DBG("%s: start a=%#lx l=%#lx\n", __func__, bus_addr, len); + DBG("%s: start a=%#llx l=%#lx\n", __func__, bus_addr, len); spin_lock_irqsave(&r->chunk_list.lock, flags); c = dma_find_chunk(r, bus_addr, len); @@ -945,7 +936,7 @@ static int dma_ioc0_unmap_area(struct ps3_dma_region *r, unsigned long aligned_len = _ALIGN_UP(len + bus_addr - aligned_bus, 1 << r->page_size); - DBG("%s:%d: not found: bus_addr %lxh\n", + DBG("%s:%d: not found: bus_addr %llxh\n", __func__, __LINE__, bus_addr); DBG("%s:%d: not found: len %lxh\n", __func__, __LINE__, len); @@ -979,7 +970,8 @@ static int dma_ioc0_unmap_area(struct ps3_dma_region *r, static int dma_sb_region_create_linear(struct ps3_dma_region *r) { int result; - unsigned long virt_addr, len, tmp; + unsigned long virt_addr, len; + dma_addr_t tmp; if (r->len > 16*1024*1024) { /* FIXME: need proper fix */ /* force 16M dma pages for linear mapping */ @@ -1001,20 +993,22 @@ static int dma_sb_region_create_linear(struct ps3_dma_region *r) if (len > r->len) len = r->len; result = dma_sb_map_area(r, virt_addr, len, &tmp, - IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M); + CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_SO_RW | + CBE_IOPTE_M); BUG_ON(result); } if (r->offset + r->len > map.rm.size) { /* Map (part of) 2nd RAM chunk */ - virt_addr = USE_LPAR_ADDR ? map.r1.base : map.rm.size; + virt_addr = map.rm.size; len = r->len; if (r->offset >= map.rm.size) virt_addr += r->offset - map.rm.size; else len -= map.rm.size - r->offset; result = dma_sb_map_area(r, virt_addr, len, &tmp, - IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M); + CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_SO_RW | + CBE_IOPTE_M); BUG_ON(result); } @@ -1031,7 +1025,8 @@ static int dma_sb_region_create_linear(struct ps3_dma_region *r) static int dma_sb_region_free_linear(struct ps3_dma_region *r) { int result; - unsigned long bus_addr, len, lpar_addr; + dma_addr_t bus_addr; + unsigned long len, lpar_addr; if (r->offset < map.rm.size) { /* Unmap (part of) 1st RAM chunk */ @@ -1076,7 +1071,7 @@ static int dma_sb_region_free_linear(struct ps3_dma_region *r) */ static int dma_sb_map_area_linear(struct ps3_dma_region *r, - unsigned long virt_addr, unsigned long len, unsigned long *bus_addr, + unsigned long virt_addr, unsigned long len, dma_addr_t *bus_addr, u64 iopte_flag) { unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr) @@ -1095,7 +1090,7 @@ static int dma_sb_map_area_linear(struct ps3_dma_region *r, */ static int dma_sb_unmap_area_linear(struct ps3_dma_region *r, - unsigned long bus_addr, unsigned long len) + dma_addr_t bus_addr, unsigned long len) { return 0; }; @@ -1173,13 +1168,13 @@ int ps3_dma_region_free(struct ps3_dma_region *r) EXPORT_SYMBOL(ps3_dma_region_free); int ps3_dma_map(struct ps3_dma_region *r, unsigned long virt_addr, - unsigned long len, unsigned long *bus_addr, + unsigned long len, dma_addr_t *bus_addr, u64 iopte_flag) { return r->region_ops->map(r, virt_addr, len, bus_addr, iopte_flag); } -int ps3_dma_unmap(struct ps3_dma_region *r, unsigned long bus_addr, +int ps3_dma_unmap(struct ps3_dma_region *r, dma_addr_t bus_addr, unsigned long len) { return r->region_ops->unmap(r, bus_addr, len); @@ -1213,13 +1208,23 @@ void __init ps3_mm_init(void) BUG_ON(map.rm.base); BUG_ON(!map.rm.size); + /* Check if we got the highmem region from an earlier boot step */ - /* arrange to do this in ps3_mm_add_memory */ - ps3_mm_region_create(&map.r1, map.total - map.rm.size); + if (ps3_mm_get_repository_highmem(&map.r1)) + ps3_mm_region_create(&map.r1, map.total - map.rm.size); /* correct map.total for the real total amount of memory we use */ map.total = map.rm.size + map.r1.size; + if (!map.r1.size) { + DBG("%s:%d: No highmem region found\n", __func__, __LINE__); + } else { + DBG("%s:%d: Adding highmem region: %llxh %llxh\n", + __func__, __LINE__, map.rm.size, + map.total - map.rm.size); + memblock_add(map.rm.size, map.total - map.rm.size); + } + DBG(" <- %s:%d\n", __func__, __LINE__); } diff --git a/arch/powerpc/platforms/ps3/os-area.c b/arch/powerpc/platforms/ps3/os-area.c index b70e474014f..09787139834 100644 --- a/arch/powerpc/platforms/ps3/os-area.c +++ b/arch/powerpc/platforms/ps3/os-area.c @@ -1,5 +1,5 @@ /* - * PS3 'Other OS' area data. + * PS3 flash memory os area. * * Copyright (C) 2006 Sony Computer Entertainment Inc. * Copyright 2006 Sony Corp. @@ -20,8 +20,16 @@ #include <linux/kernel.h> #include <linux/io.h> +#include <linux/workqueue.h> +#include <linux/fs.h> +#include <linux/syscalls.h> +#include <linux/export.h> +#include <linux/ctype.h> +#include <linux/memblock.h> +#include <linux/of.h> +#include <linux/slab.h> -#include <asm/lmb.h> +#include <asm/prom.h> #include "platform.h" @@ -29,16 +37,18 @@ enum { OS_AREA_SEGMENT_SIZE = 0X200, }; -enum { +enum os_area_ldr_format { HEADER_LDR_FORMAT_RAW = 0, HEADER_LDR_FORMAT_GZIP = 1, }; +#define OS_AREA_HEADER_MAGIC_NUM "cell_ext_os_area" + /** * struct os_area_header - os area header segment. * @magic_num: Always 'cell_ext_os_area'. * @hdr_version: Header format version number. - * @os_area_offset: Starting segment number of os image area. + * @db_area_offset: Starting segment number of other os database area. * @ldr_area_offset: Starting segment number of bootloader image area. * @ldr_format: HEADER_LDR_FORMAT flag. * @ldr_size: Size of bootloader image in bytes. @@ -50,9 +60,9 @@ enum { */ struct os_area_header { - s8 magic_num[16]; + u8 magic_num[16]; u32 hdr_version; - u32 os_area_offset; + u32 db_area_offset; u32 ldr_area_offset; u32 _reserved_1; u32 ldr_format; @@ -60,12 +70,12 @@ struct os_area_header { u32 _reserved_2[6]; }; -enum { +enum os_area_boot_flag { PARAM_BOOT_FLAG_GAME_OS = 0, PARAM_BOOT_FLAG_OTHER_OS = 1, }; -enum { +enum os_area_ctrl_button { PARAM_CTRL_BUTTON_O_IS_YES = 0, PARAM_CTRL_BUTTON_X_IS_YES = 1, }; @@ -84,6 +94,9 @@ enum { * @dns_primary: User preference of static primary dns server. * @dns_secondary: User preference of static secondary dns server. * + * The ps3 rtc maintains a read-only value that approximates seconds since + * 2000-01-01 00:00:00 UTC. + * * User preference of zero for static_ip_addr means use dhcp. */ @@ -108,45 +121,222 @@ struct os_area_params { u8 _reserved_5[8]; }; +#define OS_AREA_DB_MAGIC_NUM "-db-" + /** - * struct saved_params - Static working copies of data from the 'Other OS' area. + * struct os_area_db - Shared flash memory database. + * @magic_num: Always '-db-'. + * @version: os_area_db format version number. + * @index_64: byte offset of the database id index for 64 bit variables. + * @count_64: number of usable 64 bit index entries + * @index_32: byte offset of the database id index for 32 bit variables. + * @count_32: number of usable 32 bit index entries + * @index_16: byte offset of the database id index for 16 bit variables. + * @count_16: number of usable 16 bit index entries * - * For the convinience of the guest, the HV makes a copy of the 'Other OS' area - * in flash to a high address in the boot memory region and then puts that RAM - * address and the byte count into the repository for retreval by the guest. - * We copy the data we want into a static variable and allow the memory setup - * by the HV to be claimed by the lmb manager. + * Flash rom storage for exclusive use by guests running in the other os lpar. + * The current system configuration allocates 1K (two segments) for other os + * use. + */ + +struct os_area_db { + u8 magic_num[4]; + u16 version; + u16 _reserved_1; + u16 index_64; + u16 count_64; + u16 index_32; + u16 count_32; + u16 index_16; + u16 count_16; + u32 _reserved_2; + u8 _db_data[1000]; +}; + +/** + * enum os_area_db_owner - Data owners. + */ + +enum os_area_db_owner { + OS_AREA_DB_OWNER_ANY = -1, + OS_AREA_DB_OWNER_NONE = 0, + OS_AREA_DB_OWNER_PROTOTYPE = 1, + OS_AREA_DB_OWNER_LINUX = 2, + OS_AREA_DB_OWNER_PETITBOOT = 3, + OS_AREA_DB_OWNER_MAX = 32, +}; + +enum os_area_db_key { + OS_AREA_DB_KEY_ANY = -1, + OS_AREA_DB_KEY_NONE = 0, + OS_AREA_DB_KEY_RTC_DIFF = 1, + OS_AREA_DB_KEY_VIDEO_MODE = 2, + OS_AREA_DB_KEY_MAX = 8, +}; + +struct os_area_db_id { + int owner; + int key; +}; + +static const struct os_area_db_id os_area_db_id_empty = { + .owner = OS_AREA_DB_OWNER_NONE, + .key = OS_AREA_DB_KEY_NONE +}; + +static const struct os_area_db_id os_area_db_id_any = { + .owner = OS_AREA_DB_OWNER_ANY, + .key = OS_AREA_DB_KEY_ANY +}; + +static const struct os_area_db_id os_area_db_id_rtc_diff = { + .owner = OS_AREA_DB_OWNER_LINUX, + .key = OS_AREA_DB_KEY_RTC_DIFF +}; + +static const struct os_area_db_id os_area_db_id_video_mode = { + .owner = OS_AREA_DB_OWNER_LINUX, + .key = OS_AREA_DB_KEY_VIDEO_MODE +}; + +#define SECONDS_FROM_1970_TO_2000 946684800LL + +/** + * struct saved_params - Static working copies of data from the PS3 'os area'. + * + * The order of preference we use for the rtc_diff source: + * 1) The database value. + * 2) The game os value. + * 3) The number of seconds from 1970 to 2000. */ struct saved_params { - /* param 0 */ + unsigned int valid; s64 rtc_diff; unsigned int av_multi_out; - unsigned int ctrl_button; - /* param 1 */ - u8 static_ip_addr[4]; - u8 network_mask[4]; - u8 default_gateway[4]; - /* param 2 */ - u8 dns_primary[4]; - u8 dns_secondary[4]; } static saved_params; +static struct property property_rtc_diff = { + .name = "linux,rtc_diff", + .length = sizeof(saved_params.rtc_diff), + .value = &saved_params.rtc_diff, +}; + +static struct property property_av_multi_out = { + .name = "linux,av_multi_out", + .length = sizeof(saved_params.av_multi_out), + .value = &saved_params.av_multi_out, +}; + + +static DEFINE_MUTEX(os_area_flash_mutex); + +static const struct ps3_os_area_flash_ops *os_area_flash_ops; + +void ps3_os_area_flash_register(const struct ps3_os_area_flash_ops *ops) +{ + mutex_lock(&os_area_flash_mutex); + os_area_flash_ops = ops; + mutex_unlock(&os_area_flash_mutex); +} +EXPORT_SYMBOL_GPL(ps3_os_area_flash_register); + +static ssize_t os_area_flash_read(void *buf, size_t count, loff_t pos) +{ + ssize_t res = -ENODEV; + + mutex_lock(&os_area_flash_mutex); + if (os_area_flash_ops) + res = os_area_flash_ops->read(buf, count, pos); + mutex_unlock(&os_area_flash_mutex); + + return res; +} + +static ssize_t os_area_flash_write(const void *buf, size_t count, loff_t pos) +{ + ssize_t res = -ENODEV; + + mutex_lock(&os_area_flash_mutex); + if (os_area_flash_ops) + res = os_area_flash_ops->write(buf, count, pos); + mutex_unlock(&os_area_flash_mutex); + + return res; +} + + +/** + * os_area_set_property - Add or overwrite a saved_params value to the device tree. + * + * Overwrites an existing property. + */ + +static void os_area_set_property(struct device_node *node, + struct property *prop) +{ + int result; + struct property *tmp = of_find_property(node, prop->name, NULL); + + if (tmp) { + pr_debug("%s:%d found %s\n", __func__, __LINE__, prop->name); + of_remove_property(node, tmp); + } + + result = of_add_property(node, prop); + + if (result) + pr_debug("%s:%d of_set_property failed\n", __func__, + __LINE__); +} + +/** + * os_area_get_property - Get a saved_params value from the device tree. + * + */ + +static void __init os_area_get_property(struct device_node *node, + struct property *prop) +{ + const struct property *tmp = of_find_property(node, prop->name, NULL); + + if (tmp) { + BUG_ON(prop->length != tmp->length); + memcpy(prop->value, tmp->value, prop->length); + } else + pr_debug("%s:%d not found %s\n", __func__, __LINE__, + prop->name); +} + +static void dump_field(char *s, const u8 *field, int size_of_field) +{ +#if defined(DEBUG) + int i; + + for (i = 0; i < size_of_field; i++) + s[i] = isprint(field[i]) ? field[i] : '.'; + s[i] = 0; +#endif +} + #define dump_header(_a) _dump_header(_a, __func__, __LINE__) static void _dump_header(const struct os_area_header *h, const char *func, int line) { - pr_debug("%s:%d: h.magic_num: '%s'\n", func, line, - h->magic_num); - pr_debug("%s:%d: h.hdr_version: %u\n", func, line, + char str[sizeof(h->magic_num) + 1]; + + dump_field(str, h->magic_num, sizeof(h->magic_num)); + pr_debug("%s:%d: h.magic_num: '%s'\n", func, line, + str); + pr_debug("%s:%d: h.hdr_version: %u\n", func, line, h->hdr_version); - pr_debug("%s:%d: h.os_area_offset: %u\n", func, line, - h->os_area_offset); + pr_debug("%s:%d: h.db_area_offset: %u\n", func, line, + h->db_area_offset); pr_debug("%s:%d: h.ldr_area_offset: %u\n", func, line, h->ldr_area_offset); - pr_debug("%s:%d: h.ldr_format: %u\n", func, line, + pr_debug("%s:%d: h.ldr_format: %u\n", func, line, h->ldr_format); - pr_debug("%s:%d: h.ldr_size: %xh\n", func, line, + pr_debug("%s:%d: h.ldr_size: %xh\n", func, line, h->ldr_size); } @@ -156,7 +346,7 @@ static void _dump_params(const struct os_area_params *p, const char *func, { pr_debug("%s:%d: p.boot_flag: %u\n", func, line, p->boot_flag); pr_debug("%s:%d: p.num_params: %u\n", func, line, p->num_params); - pr_debug("%s:%d: p.rtc_diff %ld\n", func, line, p->rtc_diff); + pr_debug("%s:%d: p.rtc_diff %lld\n", func, line, p->rtc_diff); pr_debug("%s:%d: p.av_multi_out %u\n", func, line, p->av_multi_out); pr_debug("%s:%d: p.ctrl_button: %u\n", func, line, p->ctrl_button); pr_debug("%s:%d: p.static_ip_addr: %u.%u.%u.%u\n", func, line, @@ -176,9 +366,10 @@ static void _dump_params(const struct os_area_params *p, const char *func, p->dns_secondary[2], p->dns_secondary[3]); } -static int __init verify_header(const struct os_area_header *header) +static int verify_header(const struct os_area_header *header) { - if (memcmp(header->magic_num, "cell_ext_os_area", 16)) { + if (memcmp(header->magic_num, OS_AREA_HEADER_MAGIC_NUM, + sizeof(header->magic_num))) { pr_debug("%s:%d magic_num failed\n", __func__, __LINE__); return -1; } @@ -188,7 +379,7 @@ static int __init verify_header(const struct os_area_header *header) return -1; } - if (header->os_area_offset > header->ldr_area_offset) { + if (header->db_area_offset > header->ldr_area_offset) { pr_debug("%s:%d offsets failed\n", __func__, __LINE__); return -1; } @@ -196,59 +387,458 @@ static int __init verify_header(const struct os_area_header *header) return 0; } -int __init ps3_os_area_init(void) +static int db_verify(const struct os_area_db *db) +{ + if (memcmp(db->magic_num, OS_AREA_DB_MAGIC_NUM, + sizeof(db->magic_num))) { + pr_debug("%s:%d magic_num failed\n", __func__, __LINE__); + return -EINVAL; + } + + if (db->version != 1) { + pr_debug("%s:%d version failed\n", __func__, __LINE__); + return -EINVAL; + } + + return 0; +} + +struct db_index { + uint8_t owner:5; + uint8_t key:3; +}; + +struct db_iterator { + const struct os_area_db *db; + struct os_area_db_id match_id; + struct db_index *idx; + struct db_index *last_idx; + union { + uint64_t *value_64; + uint32_t *value_32; + uint16_t *value_16; + }; +}; + +static unsigned int db_align_up(unsigned int val, unsigned int size) +{ + return (val + (size - 1)) & (~(size - 1)); +} + +/** + * db_for_each_64 - Iterator for 64 bit entries. + * + * A NULL value for id can be used to match all entries. + * OS_AREA_DB_OWNER_ANY and OS_AREA_DB_KEY_ANY can be used to match all. + */ + +static int db_for_each_64(const struct os_area_db *db, + const struct os_area_db_id *match_id, struct db_iterator *i) +{ +next: + if (!i->db) { + i->db = db; + i->match_id = match_id ? *match_id : os_area_db_id_any; + i->idx = (void *)db + db->index_64; + i->last_idx = i->idx + db->count_64; + i->value_64 = (void *)db + db->index_64 + + db_align_up(db->count_64, 8); + } else { + i->idx++; + i->value_64++; + } + + if (i->idx >= i->last_idx) { + pr_debug("%s:%d: reached end\n", __func__, __LINE__); + return 0; + } + + if (i->match_id.owner != OS_AREA_DB_OWNER_ANY + && i->match_id.owner != (int)i->idx->owner) + goto next; + if (i->match_id.key != OS_AREA_DB_KEY_ANY + && i->match_id.key != (int)i->idx->key) + goto next; + + return 1; +} + +static int db_delete_64(struct os_area_db *db, const struct os_area_db_id *id) +{ + struct db_iterator i; + + for (i.db = NULL; db_for_each_64(db, id, &i); ) { + + pr_debug("%s:%d: got (%d:%d) %llxh\n", __func__, __LINE__, + i.idx->owner, i.idx->key, + (unsigned long long)*i.value_64); + + i.idx->owner = 0; + i.idx->key = 0; + *i.value_64 = 0; + } + return 0; +} + +static int db_set_64(struct os_area_db *db, const struct os_area_db_id *id, + uint64_t value) +{ + struct db_iterator i; + + pr_debug("%s:%d: (%d:%d) <= %llxh\n", __func__, __LINE__, + id->owner, id->key, (unsigned long long)value); + + if (!id->owner || id->owner == OS_AREA_DB_OWNER_ANY + || id->key == OS_AREA_DB_KEY_ANY) { + pr_debug("%s:%d: bad id: (%d:%d)\n", __func__, + __LINE__, id->owner, id->key); + return -1; + } + + db_delete_64(db, id); + + i.db = NULL; + if (db_for_each_64(db, &os_area_db_id_empty, &i)) { + + pr_debug("%s:%d: got (%d:%d) %llxh\n", __func__, __LINE__, + i.idx->owner, i.idx->key, + (unsigned long long)*i.value_64); + + i.idx->owner = id->owner; + i.idx->key = id->key; + *i.value_64 = value; + + pr_debug("%s:%d: set (%d:%d) <= %llxh\n", __func__, __LINE__, + i.idx->owner, i.idx->key, + (unsigned long long)*i.value_64); + return 0; + } + pr_debug("%s:%d: database full.\n", + __func__, __LINE__); + return -1; +} + +static int db_get_64(const struct os_area_db *db, + const struct os_area_db_id *id, uint64_t *value) +{ + struct db_iterator i; + + i.db = NULL; + if (db_for_each_64(db, id, &i)) { + *value = *i.value_64; + pr_debug("%s:%d: found %lld\n", __func__, __LINE__, + (long long int)*i.value_64); + return 0; + } + pr_debug("%s:%d: not found\n", __func__, __LINE__); + return -1; +} + +static int db_get_rtc_diff(const struct os_area_db *db, int64_t *rtc_diff) +{ + return db_get_64(db, &os_area_db_id_rtc_diff, (uint64_t*)rtc_diff); +} + +#define dump_db(a) _dump_db(a, __func__, __LINE__) +static void _dump_db(const struct os_area_db *db, const char *func, + int line) +{ + char str[sizeof(db->magic_num) + 1]; + + dump_field(str, db->magic_num, sizeof(db->magic_num)); + pr_debug("%s:%d: db.magic_num: '%s'\n", func, line, + str); + pr_debug("%s:%d: db.version: %u\n", func, line, + db->version); + pr_debug("%s:%d: db.index_64: %u\n", func, line, + db->index_64); + pr_debug("%s:%d: db.count_64: %u\n", func, line, + db->count_64); + pr_debug("%s:%d: db.index_32: %u\n", func, line, + db->index_32); + pr_debug("%s:%d: db.count_32: %u\n", func, line, + db->count_32); + pr_debug("%s:%d: db.index_16: %u\n", func, line, + db->index_16); + pr_debug("%s:%d: db.count_16: %u\n", func, line, + db->count_16); +} + +static void os_area_db_init(struct os_area_db *db) +{ + enum { + HEADER_SIZE = offsetof(struct os_area_db, _db_data), + INDEX_64_COUNT = 64, + VALUES_64_COUNT = 57, + INDEX_32_COUNT = 64, + VALUES_32_COUNT = 57, + INDEX_16_COUNT = 64, + VALUES_16_COUNT = 57, + }; + + memset(db, 0, sizeof(struct os_area_db)); + + memcpy(db->magic_num, OS_AREA_DB_MAGIC_NUM, sizeof(db->magic_num)); + db->version = 1; + db->index_64 = HEADER_SIZE; + db->count_64 = VALUES_64_COUNT; + db->index_32 = HEADER_SIZE + + INDEX_64_COUNT * sizeof(struct db_index) + + VALUES_64_COUNT * sizeof(u64); + db->count_32 = VALUES_32_COUNT; + db->index_16 = HEADER_SIZE + + INDEX_64_COUNT * sizeof(struct db_index) + + VALUES_64_COUNT * sizeof(u64) + + INDEX_32_COUNT * sizeof(struct db_index) + + VALUES_32_COUNT * sizeof(u32); + db->count_16 = VALUES_16_COUNT; + + /* Rules to check db layout. */ + + BUILD_BUG_ON(sizeof(struct db_index) != 1); + BUILD_BUG_ON(sizeof(struct os_area_db) != 2 * OS_AREA_SEGMENT_SIZE); + BUILD_BUG_ON(INDEX_64_COUNT & 0x7); + BUILD_BUG_ON(VALUES_64_COUNT > INDEX_64_COUNT); + BUILD_BUG_ON(INDEX_32_COUNT & 0x7); + BUILD_BUG_ON(VALUES_32_COUNT > INDEX_32_COUNT); + BUILD_BUG_ON(INDEX_16_COUNT & 0x7); + BUILD_BUG_ON(VALUES_16_COUNT > INDEX_16_COUNT); + BUILD_BUG_ON(HEADER_SIZE + + INDEX_64_COUNT * sizeof(struct db_index) + + VALUES_64_COUNT * sizeof(u64) + + INDEX_32_COUNT * sizeof(struct db_index) + + VALUES_32_COUNT * sizeof(u32) + + INDEX_16_COUNT * sizeof(struct db_index) + + VALUES_16_COUNT * sizeof(u16) + > sizeof(struct os_area_db)); +} + +/** + * update_flash_db - Helper for os_area_queue_work_handler. + * + */ + +static int update_flash_db(void) +{ + const unsigned int buf_len = 8 * OS_AREA_SEGMENT_SIZE; + struct os_area_header *header; + ssize_t count; + int error; + loff_t pos; + struct os_area_db* db; + + /* Read in header and db from flash. */ + + header = kmalloc(buf_len, GFP_KERNEL); + if (!header) { + pr_debug("%s: kmalloc failed\n", __func__); + return -ENOMEM; + } + + count = os_area_flash_read(header, buf_len, 0); + if (count < 0) { + pr_debug("%s: os_area_flash_read failed %zd\n", __func__, + count); + error = count; + goto fail; + } + + pos = header->db_area_offset * OS_AREA_SEGMENT_SIZE; + if (count < OS_AREA_SEGMENT_SIZE || verify_header(header) || + count < pos) { + pr_debug("%s: verify_header failed\n", __func__); + dump_header(header); + error = -EINVAL; + goto fail; + } + + /* Now got a good db offset and some maybe good db data. */ + + db = (void *)header + pos; + + error = db_verify(db); + if (error) { + pr_notice("%s: Verify of flash database failed, formatting.\n", + __func__); + dump_db(db); + os_area_db_init(db); + } + + /* Now got good db data. */ + + db_set_64(db, &os_area_db_id_rtc_diff, saved_params.rtc_diff); + + count = os_area_flash_write(db, sizeof(struct os_area_db), pos); + if (count < sizeof(struct os_area_db)) { + pr_debug("%s: os_area_flash_write failed %zd\n", __func__, + count); + error = count < 0 ? count : -EIO; + } + +fail: + kfree(header); + return error; +} + +/** + * os_area_queue_work_handler - Asynchronous write handler. + * + * An asynchronous write for flash memory and the device tree. Do not + * call directly, use os_area_queue_work(). + */ + +static void os_area_queue_work_handler(struct work_struct *work) +{ + struct device_node *node; + int error; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + node = of_find_node_by_path("/"); + if (node) { + os_area_set_property(node, &property_rtc_diff); + of_node_put(node); + } else + pr_debug("%s:%d of_find_node_by_path failed\n", + __func__, __LINE__); + + error = update_flash_db(); + if (error) + pr_warning("%s: Could not update FLASH ROM\n", __func__); + + pr_debug(" <- %s:%d\n", __func__, __LINE__); +} + +static void os_area_queue_work(void) +{ + static DECLARE_WORK(q, os_area_queue_work_handler); + + wmb(); + schedule_work(&q); +} + +/** + * ps3_os_area_save_params - Copy data from os area mirror to @saved_params. + * + * For the convenience of the guest the HV makes a copy of the os area in + * flash to a high address in the boot memory region and then puts that RAM + * address and the byte count into the repository for retrieval by the guest. + * We copy the data we want into a static variable and allow the memory setup + * by the HV to be claimed by the memblock manager. + * + * The os area mirror will not be available to a second stage kernel, and + * the header verify will fail. In this case, the saved_params values will + * be set from flash memory or the passed in device tree in ps3_os_area_init(). + */ + +void __init ps3_os_area_save_params(void) { int result; u64 lpar_addr; unsigned int size; struct os_area_header *header; struct os_area_params *params; + struct os_area_db *db; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); result = ps3_repository_read_boot_dat_info(&lpar_addr, &size); if (result) { pr_debug("%s:%d ps3_repository_read_boot_dat_info failed\n", __func__, __LINE__); - return result; + return; } header = (struct os_area_header *)__va(lpar_addr); - params = (struct os_area_params *)__va(lpar_addr + OS_AREA_SEGMENT_SIZE); + params = (struct os_area_params *)__va(lpar_addr + + OS_AREA_SEGMENT_SIZE); result = verify_header(header); if (result) { + /* Second stage kernels exit here. */ pr_debug("%s:%d verify_header failed\n", __func__, __LINE__); dump_header(header); - return -EIO; + return; } + db = (struct os_area_db *)__va(lpar_addr + + header->db_area_offset * OS_AREA_SEGMENT_SIZE); + dump_header(header); dump_params(params); + dump_db(db); - saved_params.rtc_diff = params->rtc_diff; + result = db_verify(db) || db_get_rtc_diff(db, &saved_params.rtc_diff); + if (result) + saved_params.rtc_diff = params->rtc_diff ? params->rtc_diff + : SECONDS_FROM_1970_TO_2000; saved_params.av_multi_out = params->av_multi_out; - saved_params.ctrl_button = params->ctrl_button; - memcpy(saved_params.static_ip_addr, params->static_ip_addr, 4); - memcpy(saved_params.network_mask, params->network_mask, 4); - memcpy(saved_params.default_gateway, params->default_gateway, 4); - memcpy(saved_params.dns_secondary, params->dns_secondary, 4); + saved_params.valid = 1; + + memset(header, 0, sizeof(*header)); - return result; + pr_debug(" <- %s:%d\n", __func__, __LINE__); } /** - * ps3_os_area_rtc_diff - Returns the ps3 rtc diff value. + * ps3_os_area_init - Setup os area device tree properties as needed. + */ + +void __init ps3_os_area_init(void) +{ + struct device_node *node; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + node = of_find_node_by_path("/"); + + if (!saved_params.valid && node) { + /* Second stage kernels should have a dt entry. */ + os_area_get_property(node, &property_rtc_diff); + os_area_get_property(node, &property_av_multi_out); + } + + if(!saved_params.rtc_diff) + saved_params.rtc_diff = SECONDS_FROM_1970_TO_2000; + + if (node) { + os_area_set_property(node, &property_rtc_diff); + os_area_set_property(node, &property_av_multi_out); + of_node_put(node); + } else + pr_debug("%s:%d of_find_node_by_path failed\n", + __func__, __LINE__); + + pr_debug(" <- %s:%d\n", __func__, __LINE__); +} + +/** + * ps3_os_area_get_rtc_diff - Returns the rtc diff value. + */ + +u64 ps3_os_area_get_rtc_diff(void) +{ + return saved_params.rtc_diff; +} +EXPORT_SYMBOL_GPL(ps3_os_area_get_rtc_diff); + +/** + * ps3_os_area_set_rtc_diff - Set the rtc diff value. * - * The ps3 rtc maintains a value that approximates seconds since - * 2000-01-01 00:00:00 UTC. Returns the exact number of seconds from 1970 to - * 2000 when saved_params.rtc_diff has not been properly set up. + * An asynchronous write is needed to support writing updates from + * the timer interrupt context. */ -u64 ps3_os_area_rtc_diff(void) +void ps3_os_area_set_rtc_diff(u64 rtc_diff) { - return saved_params.rtc_diff ? saved_params.rtc_diff : 946684800UL; + if (saved_params.rtc_diff != rtc_diff) { + saved_params.rtc_diff = rtc_diff; + os_area_queue_work(); + } } +EXPORT_SYMBOL_GPL(ps3_os_area_set_rtc_diff); /** * ps3_os_area_get_av_multi_out - Returns the default video mode. diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h index 2eb8f92704b..d71329a8e32 100644 --- a/arch/powerpc/platforms/ps3/platform.h +++ b/arch/powerpc/platforms/ps3/platform.h @@ -43,11 +43,16 @@ void ps3_mm_shutdown(void); void ps3_init_IRQ(void); void ps3_shutdown_IRQ(int cpu); void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq); +void __init ps3_register_ipi_irq(unsigned int cpu, unsigned int virq); /* smp */ void smp_init_ps3(void); +#ifdef CONFIG_SMP void ps3_smp_cleanup_cpu(int cpu); +#else +static inline void ps3_smp_cleanup_cpu(int cpu) { } +#endif /* time */ @@ -58,8 +63,8 @@ int ps3_set_rtc_time(struct rtc_time *time); /* os area */ -int __init ps3_os_area_init(void); -u64 ps3_os_area_rtc_diff(void); +void __init ps3_os_area_save_params(void); +void __init ps3_os_area_init(void); /* spu */ @@ -83,13 +88,11 @@ enum ps3_dev_type { PS3_DEV_TYPE_STOR_ROM = TYPE_ROM, /* 5 */ PS3_DEV_TYPE_SB_GPIO = 6, PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC, /* 14 */ - PS3_DEV_TYPE_STOR_DUMMY = 32, - PS3_DEV_TYPE_NOACCESS = 255, }; int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str, u64 *value); -int ps3_repository_read_bus_id(unsigned int bus_index, unsigned int *bus_id); +int ps3_repository_read_bus_id(unsigned int bus_index, u64 *bus_id); int ps3_repository_read_bus_type(unsigned int bus_index, enum ps3_bus_type *bus_type); int ps3_repository_read_bus_num_dev(unsigned int bus_index, @@ -113,7 +116,7 @@ enum ps3_reg_type { int ps3_repository_read_dev_str(unsigned int bus_index, unsigned int dev_index, const char *dev_str, u64 *value); int ps3_repository_read_dev_id(unsigned int bus_index, unsigned int dev_index, - unsigned int *dev_id); + u64 *dev_id); int ps3_repository_read_dev_type(unsigned int bus_index, unsigned int dev_index, enum ps3_dev_type *dev_type); int ps3_repository_read_dev_intr(unsigned int bus_index, @@ -132,21 +135,17 @@ int ps3_repository_read_dev_reg(unsigned int bus_index, /* repository bus enumerators */ struct ps3_repository_device { - enum ps3_bus_type bus_type; unsigned int bus_index; - unsigned int bus_id; - enum ps3_dev_type dev_type; unsigned int dev_index; - unsigned int dev_id; + enum ps3_bus_type bus_type; + enum ps3_dev_type dev_type; + u64 bus_id; + u64 dev_id; }; -static inline struct ps3_repository_device *ps3_repository_bump_device( - struct ps3_repository_device *repo) -{ - repo->dev_index++; - return repo; -} int ps3_repository_find_device(struct ps3_repository_device *repo); +int ps3_repository_find_device_by_id(struct ps3_repository_device *repo, + u64 bus_id, u64 dev_id); int ps3_repository_find_devices(enum ps3_bus_type bus_type, int (*callback)(const struct ps3_repository_device *repo)); int ps3_repository_find_bus(enum ps3_bus_type bus_type, unsigned int from, @@ -180,23 +179,45 @@ int ps3_repository_read_stor_dev_region(unsigned int bus_index, unsigned int dev_index, unsigned int region_index, unsigned int *region_id, u64 *region_start, u64 *region_size); -/* repository pu and memory info */ +/* repository logical pu and memory info */ -int ps3_repository_read_num_pu(unsigned int *num_pu); -int ps3_repository_read_ppe_id(unsigned int *pu_index, unsigned int *ppe_id); +int ps3_repository_read_num_pu(u64 *num_pu); +int ps3_repository_read_pu_id(unsigned int pu_index, u64 *pu_id); int ps3_repository_read_rm_base(unsigned int ppe_id, u64 *rm_base); int ps3_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size); int ps3_repository_read_region_total(u64 *region_total); int ps3_repository_read_mm_info(u64 *rm_base, u64 *rm_size, u64 *region_total); +int ps3_repository_read_highmem_region_count(unsigned int *region_count); +int ps3_repository_read_highmem_base(unsigned int region_index, + u64 *highmem_base); +int ps3_repository_read_highmem_size(unsigned int region_index, + u64 *highmem_size); +int ps3_repository_read_highmem_info(unsigned int region_index, + u64 *highmem_base, u64 *highmem_size); + +int ps3_repository_write_highmem_region_count(unsigned int region_count); +int ps3_repository_write_highmem_base(unsigned int region_index, + u64 highmem_base); +int ps3_repository_write_highmem_size(unsigned int region_index, + u64 highmem_size); +int ps3_repository_write_highmem_info(unsigned int region_index, + u64 highmem_base, u64 highmem_size); +int ps3_repository_delete_highmem_info(unsigned int region_index); /* repository pme info */ int ps3_repository_read_num_be(unsigned int *num_be); int ps3_repository_read_be_node_id(unsigned int be_index, u64 *node_id); +int ps3_repository_read_be_id(u64 node_id, u64 *be_id); int ps3_repository_read_tb_freq(u64 node_id, u64 *tb_freq); int ps3_repository_read_be_tb_freq(unsigned int be_index, u64 *tb_freq); +/* repository performance monitor info */ + +int ps3_repository_read_lpm_privileges(unsigned int be_index, u64 *lpar, + u64 *rights); + /* repository 'Other OS' area */ int ps3_repository_read_boot_dat_addr(u64 *lpar_addr); @@ -228,14 +249,4 @@ int ps3_repository_read_spu_resource_id(unsigned int res_index, int ps3_repository_read_vuart_av_port(unsigned int *port); int ps3_repository_read_vuart_sysmgr_port(unsigned int *port); -/* Page table entries */ -#define IOPTE_PP_W 0x8000000000000000ul /* protection: write */ -#define IOPTE_PP_R 0x4000000000000000ul /* protection: read */ -#define IOPTE_M 0x2000000000000000ul /* coherency required */ -#define IOPTE_SO_R 0x1000000000000000ul /* ordering: writes */ -#define IOPTE_SO_RW 0x1800000000000000ul /* ordering: r & w */ -#define IOPTE_RPN_Mask 0x07fffffffffff000ul /* RPN */ -#define IOPTE_H 0x0000000000000800ul /* cache hint */ -#define IOPTE_IOID_Mask 0x00000000000007fful /* ioid */ - #endif diff --git a/arch/powerpc/platforms/ps3/repository.c b/arch/powerpc/platforms/ps3/repository.c index 1c94824f7b6..bfccdc7cb85 100644 --- a/arch/powerpc/platforms/ps3/repository.c +++ b/arch/powerpc/platforms/ps3/repository.c @@ -33,7 +33,7 @@ enum ps3_lpar_id { }; #define dump_field(_a, _b) _dump_field(_a, _b, __func__, __LINE__) -static void _dump_field(const char *hdr, u64 n, const char* func, int line) +static void _dump_field(const char *hdr, u64 n, const char *func, int line) { #if defined(DEBUG) char s[16]; @@ -44,16 +44,16 @@ static void _dump_field(const char *hdr, u64 n, const char* func, int line) s[i] = (in[i] <= 126 && in[i] >= 32) ? in[i] : '.'; s[i] = 0; - pr_debug("%s:%d: %s%016lx : %s\n", func, line, hdr, n, s); + pr_devel("%s:%d: %s%016llx : %s\n", func, line, hdr, n, s); #endif } #define dump_node_name(_a, _b, _c, _d, _e) \ _dump_node_name(_a, _b, _c, _d, _e, __func__, __LINE__) -static void _dump_node_name (unsigned int lpar_id, u64 n1, u64 n2, u64 n3, - u64 n4, const char* func, int line) +static void _dump_node_name(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, + u64 n4, const char *func, int line) { - pr_debug("%s:%d: lpar: %u\n", func, line, lpar_id); + pr_devel("%s:%d: lpar: %u\n", func, line, lpar_id); _dump_field("n1: ", n1, func, line); _dump_field("n2: ", n2, func, line); _dump_field("n3: ", n3, func, line); @@ -63,15 +63,15 @@ static void _dump_node_name (unsigned int lpar_id, u64 n1, u64 n2, u64 n3, #define dump_node(_a, _b, _c, _d, _e, _f, _g) \ _dump_node(_a, _b, _c, _d, _e, _f, _g, __func__, __LINE__) static void _dump_node(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, u64 n4, - u64 v1, u64 v2, const char* func, int line) + u64 v1, u64 v2, const char *func, int line) { - pr_debug("%s:%d: lpar: %u\n", func, line, lpar_id); + pr_devel("%s:%d: lpar: %u\n", func, line, lpar_id); _dump_field("n1: ", n1, func, line); _dump_field("n2: ", n2, func, line); _dump_field("n3: ", n3, func, line); _dump_field("n4: ", n4, func, line); - pr_debug("%s:%d: v1: %016lx\n", func, line, v1); - pr_debug("%s:%d: v2: %016lx\n", func, line, v2); + pr_devel("%s:%d: v1: %016llx\n", func, line, v1); + pr_devel("%s:%d: v2: %016llx\n", func, line, v2); } /** @@ -131,11 +131,11 @@ static int read_node(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, u64 n4, lpar_id = id; } - result = lv1_get_repository_node_value(lpar_id, n1, n2, n3, n4, &v1, + result = lv1_read_repository_node(lpar_id, n1, n2, n3, n4, &v1, &v2); if (result) { - pr_debug("%s:%d: lv1_get_repository_node_value failed: %s\n", + pr_warn("%s:%d: lv1_read_repository_node failed: %s\n", __func__, __LINE__, ps3_result(result)); dump_node_name(lpar_id, n1, n2, n3, n4); return -ENOENT; @@ -149,10 +149,10 @@ static int read_node(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, u64 n4, *_v2 = v2; if (v1 && !_v1) - pr_debug("%s:%d: warning: discarding non-zero v1: %016lx\n", + pr_devel("%s:%d: warning: discarding non-zero v1: %016llx\n", __func__, __LINE__, v1); if (v2 && !_v2) - pr_debug("%s:%d: warning: discarding non-zero v2: %016lx\n", + pr_devel("%s:%d: warning: discarding non-zero v2: %016llx\n", __func__, __LINE__, v2); return 0; @@ -165,21 +165,18 @@ int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str, make_first_field("bus", bus_index), make_field(bus_str, 0), 0, 0, - value, 0); + value, NULL); } -int ps3_repository_read_bus_id(unsigned int bus_index, unsigned int *bus_id) +int ps3_repository_read_bus_id(unsigned int bus_index, u64 *bus_id) { int result; - u64 v1; - u64 v2; /* unused */ result = read_node(PS3_LPAR_ID_PME, make_first_field("bus", bus_index), make_field("id", 0), 0, 0, - &v1, &v2); - *bus_id = v1; + bus_id, NULL); return result; } @@ -187,13 +184,13 @@ int ps3_repository_read_bus_type(unsigned int bus_index, enum ps3_bus_type *bus_type) { int result; - u64 v1; + u64 v1 = 0; result = read_node(PS3_LPAR_ID_PME, make_first_field("bus", bus_index), make_field("type", 0), 0, 0, - &v1, 0); + &v1, NULL); *bus_type = v1; return result; } @@ -202,13 +199,13 @@ int ps3_repository_read_bus_num_dev(unsigned int bus_index, unsigned int *num_dev) { int result; - u64 v1; + u64 v1 = 0; result = read_node(PS3_LPAR_ID_PME, make_first_field("bus", bus_index), make_field("num_dev", 0), 0, 0, - &v1, 0); + &v1, NULL); *num_dev = v1; return result; } @@ -221,22 +218,20 @@ int ps3_repository_read_dev_str(unsigned int bus_index, make_field("dev", dev_index), make_field(dev_str, 0), 0, - value, 0); + value, NULL); } int ps3_repository_read_dev_id(unsigned int bus_index, unsigned int dev_index, - unsigned int *dev_id) + u64 *dev_id) { int result; - u64 v1; result = read_node(PS3_LPAR_ID_PME, make_first_field("bus", bus_index), make_field("dev", dev_index), make_field("id", 0), 0, - &v1, 0); - *dev_id = v1; + dev_id, NULL); return result; } @@ -244,25 +239,25 @@ int ps3_repository_read_dev_type(unsigned int bus_index, unsigned int dev_index, enum ps3_dev_type *dev_type) { int result; - u64 v1; + u64 v1 = 0; result = read_node(PS3_LPAR_ID_PME, make_first_field("bus", bus_index), make_field("dev", dev_index), make_field("type", 0), 0, - &v1, 0); + &v1, NULL); *dev_type = v1; return result; } int ps3_repository_read_dev_intr(unsigned int bus_index, unsigned int dev_index, unsigned int intr_index, - enum ps3_interrupt_type *intr_type, unsigned int* interrupt_id) + enum ps3_interrupt_type *intr_type, unsigned int *interrupt_id) { int result; - u64 v1; - u64 v2; + u64 v1 = 0; + u64 v2 = 0; result = read_node(PS3_LPAR_ID_PME, make_first_field("bus", bus_index), @@ -280,14 +275,14 @@ int ps3_repository_read_dev_reg_type(unsigned int bus_index, enum ps3_reg_type *reg_type) { int result; - u64 v1; + u64 v1 = 0; result = read_node(PS3_LPAR_ID_PME, make_first_field("bus", bus_index), make_field("dev", dev_index), make_field("reg", reg_index), make_field("type", 0), - &v1, 0); + &v1, NULL); *reg_type = v1; return result; } @@ -328,16 +323,16 @@ int ps3_repository_find_device(struct ps3_repository_device *repo) result = ps3_repository_read_bus_num_dev(tmp.bus_index, &num_dev); if (result) { - pr_debug("%s:%d read_bus_num_dev failed\n", __func__, __LINE__); + pr_devel("%s:%d read_bus_num_dev failed\n", __func__, __LINE__); return result; } - pr_debug("%s:%d: bus_type %u, bus_index %u, bus_id %u, num_dev %u\n", + pr_devel("%s:%d: bus_type %u, bus_index %u, bus_id %llu, num_dev %u\n", __func__, __LINE__, tmp.bus_type, tmp.bus_index, tmp.bus_id, num_dev); if (tmp.dev_index >= num_dev) { - pr_debug("%s:%d: no device found\n", __func__, __LINE__); + pr_devel("%s:%d: no device found\n", __func__, __LINE__); return -ENODEV; } @@ -345,110 +340,142 @@ int ps3_repository_find_device(struct ps3_repository_device *repo) &tmp.dev_type); if (result) { - pr_debug("%s:%d read_dev_type failed\n", __func__, __LINE__); + pr_devel("%s:%d read_dev_type failed\n", __func__, __LINE__); return result; } - if (tmp.bus_type == PS3_BUS_TYPE_STORAGE) { - /* - * A storage device may show up in the repository before the - * hypervisor has finished probing its type and regions - */ - unsigned int num_regions; - - if (tmp.dev_type == PS3_DEV_TYPE_STOR_DUMMY) { - pr_debug("%s:%u storage device not ready\n", __func__, - __LINE__); - return -ENODEV; - } - - result = ps3_repository_read_stor_dev_num_regions(tmp.bus_index, - tmp.dev_index, - &num_regions); - if (result) { - pr_debug("%s:%d read_stor_dev_num_regions failed\n", - __func__, __LINE__); - return result; - } - - if (!num_regions) { - pr_debug("%s:%u storage device has no regions yet\n", - __func__, __LINE__); - return -ENODEV; - } - } - result = ps3_repository_read_dev_id(tmp.bus_index, tmp.dev_index, &tmp.dev_id); if (result) { - pr_debug("%s:%d ps3_repository_read_dev_id failed\n", __func__, + pr_devel("%s:%d ps3_repository_read_dev_id failed\n", __func__, __LINE__); return result; } - pr_debug("%s:%d: found: dev_type %u, dev_index %u, dev_id %u\n", + pr_devel("%s:%d: found: dev_type %u, dev_index %u, dev_id %llu\n", __func__, __LINE__, tmp.dev_type, tmp.dev_index, tmp.dev_id); *repo = tmp; return 0; } -int __devinit ps3_repository_find_devices(enum ps3_bus_type bus_type, - int (*callback)(const struct ps3_repository_device *repo)) +int ps3_repository_find_device_by_id(struct ps3_repository_device *repo, + u64 bus_id, u64 dev_id) { - int result = 0; - struct ps3_repository_device repo; - - pr_debug(" -> %s:%d: find bus_type %u\n", __func__, __LINE__, bus_type); - - for (repo.bus_index = 0; repo.bus_index < 10; repo.bus_index++) { + int result = -ENODEV; + struct ps3_repository_device tmp; + unsigned int num_dev; - result = ps3_repository_read_bus_type(repo.bus_index, - &repo.bus_type); + pr_devel(" -> %s:%u: find device by id %llu:%llu\n", __func__, __LINE__, + bus_id, dev_id); + for (tmp.bus_index = 0; tmp.bus_index < 10; tmp.bus_index++) { + result = ps3_repository_read_bus_id(tmp.bus_index, + &tmp.bus_id); if (result) { - pr_debug("%s:%d read_bus_type(%u) failed\n", - __func__, __LINE__, repo.bus_index); - break; + pr_devel("%s:%u read_bus_id(%u) failed\n", __func__, + __LINE__, tmp.bus_index); + return result; } - if (repo.bus_type != bus_type) { - pr_debug("%s:%d: skip, bus_type %u\n", __func__, - __LINE__, repo.bus_type); - continue; - } + if (tmp.bus_id == bus_id) + goto found_bus; - result = ps3_repository_read_bus_id(repo.bus_index, - &repo.bus_id); + pr_devel("%s:%u: skip, bus_id %llu\n", __func__, __LINE__, + tmp.bus_id); + } + pr_devel(" <- %s:%u: bus not found\n", __func__, __LINE__); + return result; + +found_bus: + result = ps3_repository_read_bus_type(tmp.bus_index, &tmp.bus_type); + if (result) { + pr_devel("%s:%u read_bus_type(%u) failed\n", __func__, + __LINE__, tmp.bus_index); + return result; + } + + result = ps3_repository_read_bus_num_dev(tmp.bus_index, &num_dev); + if (result) { + pr_devel("%s:%u read_bus_num_dev failed\n", __func__, + __LINE__); + return result; + } + for (tmp.dev_index = 0; tmp.dev_index < num_dev; tmp.dev_index++) { + result = ps3_repository_read_dev_id(tmp.bus_index, + tmp.dev_index, + &tmp.dev_id); if (result) { - pr_debug("%s:%d read_bus_id(%u) failed\n", - __func__, __LINE__, repo.bus_index); - continue; + pr_devel("%s:%u read_dev_id(%u:%u) failed\n", __func__, + __LINE__, tmp.bus_index, tmp.dev_index); + return result; } - for (repo.dev_index = 0; ; repo.dev_index++) { - result = ps3_repository_find_device(&repo); + if (tmp.dev_id == dev_id) + goto found_dev; - if (result == -ENODEV) { - result = 0; - break; - } else if (result) - break; + pr_devel("%s:%u: skip, dev_id %llu\n", __func__, __LINE__, + tmp.dev_id); + } + pr_devel(" <- %s:%u: dev not found\n", __func__, __LINE__); + return result; - result = callback(&repo); +found_dev: + result = ps3_repository_read_dev_type(tmp.bus_index, tmp.dev_index, + &tmp.dev_type); + if (result) { + pr_devel("%s:%u read_dev_type failed\n", __func__, __LINE__); + return result; + } - if (result) { - pr_debug("%s:%d: abort at callback\n", __func__, - __LINE__); - break; - } + pr_devel(" <- %s:%u: found: type (%u:%u) index (%u:%u) id (%llu:%llu)\n", + __func__, __LINE__, tmp.bus_type, tmp.dev_type, tmp.bus_index, + tmp.dev_index, tmp.bus_id, tmp.dev_id); + *repo = tmp; + return 0; +} + +int ps3_repository_find_devices(enum ps3_bus_type bus_type, + int (*callback)(const struct ps3_repository_device *repo)) +{ + int result = 0; + struct ps3_repository_device repo; + + pr_devel(" -> %s:%d: find bus_type %u\n", __func__, __LINE__, bus_type); + + repo.bus_type = bus_type; + result = ps3_repository_find_bus(repo.bus_type, 0, &repo.bus_index); + if (result) { + pr_devel(" <- %s:%u: bus not found\n", __func__, __LINE__); + return result; + } + + result = ps3_repository_read_bus_id(repo.bus_index, &repo.bus_id); + if (result) { + pr_devel("%s:%d read_bus_id(%u) failed\n", __func__, __LINE__, + repo.bus_index); + return result; + } + + for (repo.dev_index = 0; ; repo.dev_index++) { + result = ps3_repository_find_device(&repo); + if (result == -ENODEV) { + result = 0; + break; + } else if (result) + break; + + result = callback(&repo); + if (result) { + pr_devel("%s:%d: abort at callback\n", __func__, + __LINE__); + break; } - break; } - pr_debug(" <- %s:%d\n", __func__, __LINE__); + pr_devel(" <- %s:%d\n", __func__, __LINE__); return result; } @@ -462,7 +489,7 @@ int ps3_repository_find_bus(enum ps3_bus_type bus_type, unsigned int from, for (i = from; i < 10; i++) { error = ps3_repository_read_bus_type(i, &type); if (error) { - pr_debug("%s:%d read_bus_type failed\n", + pr_devel("%s:%d read_bus_type failed\n", __func__, __LINE__); *bus_index = UINT_MAX; return error; @@ -482,7 +509,7 @@ int ps3_repository_find_interrupt(const struct ps3_repository_device *repo, int result = 0; unsigned int res_index; - pr_debug("%s:%d: find intr_type %u\n", __func__, __LINE__, intr_type); + pr_devel("%s:%d: find intr_type %u\n", __func__, __LINE__, intr_type); *interrupt_id = UINT_MAX; @@ -494,7 +521,7 @@ int ps3_repository_find_interrupt(const struct ps3_repository_device *repo, repo->dev_index, res_index, &t, &id); if (result) { - pr_debug("%s:%d read_dev_intr failed\n", + pr_devel("%s:%d read_dev_intr failed\n", __func__, __LINE__); return result; } @@ -508,7 +535,7 @@ int ps3_repository_find_interrupt(const struct ps3_repository_device *repo, if (res_index == 10) return -ENODEV; - pr_debug("%s:%d: found intr_type %u at res_index %u\n", + pr_devel("%s:%d: found intr_type %u at res_index %u\n", __func__, __LINE__, intr_type, res_index); return result; @@ -520,7 +547,7 @@ int ps3_repository_find_reg(const struct ps3_repository_device *repo, int result = 0; unsigned int res_index; - pr_debug("%s:%d: find reg_type %u\n", __func__, __LINE__, reg_type); + pr_devel("%s:%d: find reg_type %u\n", __func__, __LINE__, reg_type); *bus_addr = *len = 0; @@ -533,7 +560,7 @@ int ps3_repository_find_reg(const struct ps3_repository_device *repo, repo->dev_index, res_index, &t, &a, &l); if (result) { - pr_debug("%s:%d read_dev_reg failed\n", + pr_devel("%s:%d read_dev_reg failed\n", __func__, __LINE__); return result; } @@ -548,7 +575,7 @@ int ps3_repository_find_reg(const struct ps3_repository_device *repo, if (res_index == 10) return -ENODEV; - pr_debug("%s:%d: found reg_type %u at res_index %u\n", + pr_devel("%s:%d: found reg_type %u at res_index %u\n", __func__, __LINE__, reg_type, res_index); return result; @@ -561,7 +588,7 @@ int ps3_repository_read_stor_dev_port(unsigned int bus_index, make_first_field("bus", bus_index), make_field("dev", dev_index), make_field("port", 0), - 0, port, 0); + 0, port, NULL); } int ps3_repository_read_stor_dev_blk_size(unsigned int bus_index, @@ -571,7 +598,7 @@ int ps3_repository_read_stor_dev_blk_size(unsigned int bus_index, make_first_field("bus", bus_index), make_field("dev", dev_index), make_field("blk_size", 0), - 0, blk_size, 0); + 0, blk_size, NULL); } int ps3_repository_read_stor_dev_num_blocks(unsigned int bus_index, @@ -581,20 +608,20 @@ int ps3_repository_read_stor_dev_num_blocks(unsigned int bus_index, make_first_field("bus", bus_index), make_field("dev", dev_index), make_field("n_blocks", 0), - 0, num_blocks, 0); + 0, num_blocks, NULL); } int ps3_repository_read_stor_dev_num_regions(unsigned int bus_index, unsigned int dev_index, unsigned int *num_regions) { int result; - u64 v1; + u64 v1 = 0; result = read_node(PS3_LPAR_ID_PME, make_first_field("bus", bus_index), make_field("dev", dev_index), make_field("n_regs", 0), - 0, &v1, 0); + 0, &v1, NULL); *num_regions = v1; return result; } @@ -604,14 +631,14 @@ int ps3_repository_read_stor_dev_region_id(unsigned int bus_index, unsigned int *region_id) { int result; - u64 v1; + u64 v1 = 0; result = read_node(PS3_LPAR_ID_PME, make_first_field("bus", bus_index), make_field("dev", dev_index), make_field("region", region_index), make_field("id", 0), - &v1, 0); + &v1, NULL); *region_id = v1; return result; } @@ -624,7 +651,7 @@ int ps3_repository_read_stor_dev_region_size(unsigned int bus_index, make_field("dev", dev_index), make_field("region", region_index), make_field("size", 0), - region_size, 0); + region_size, NULL); } int ps3_repository_read_stor_dev_region_start(unsigned int bus_index, @@ -635,7 +662,7 @@ int ps3_repository_read_stor_dev_region_start(unsigned int bus_index, make_field("dev", dev_index), make_field("region", region_index), make_field("start", 0), - region_start, 0); + region_start, NULL); } int ps3_repository_read_stor_dev_info(unsigned int bus_index, @@ -684,6 +711,35 @@ int ps3_repository_read_stor_dev_region(unsigned int bus_index, return result; } +/** + * ps3_repository_read_num_pu - Number of logical PU processors for this lpar. + */ + +int ps3_repository_read_num_pu(u64 *num_pu) +{ + *num_pu = 0; + return read_node(PS3_LPAR_ID_CURRENT, + make_first_field("bi", 0), + make_field("pun", 0), + 0, 0, + num_pu, NULL); +} + +/** + * ps3_repository_read_pu_id - Read the logical PU id. + * @pu_index: Zero based index. + * @pu_id: The logical PU id. + */ + +int ps3_repository_read_pu_id(unsigned int pu_index, u64 *pu_id) +{ + return read_node(PS3_LPAR_ID_CURRENT, + make_first_field("bi", 0), + make_field("pu", pu_index), + 0, 0, + pu_id, NULL); +} + int ps3_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size) { return read_node(PS3_LPAR_ID_CURRENT, @@ -691,7 +747,7 @@ int ps3_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size) make_field("pu", 0), ppe_id, make_field("rm_size", 0), - rm_size, 0); + rm_size, NULL); } int ps3_repository_read_region_total(u64 *region_total) @@ -700,7 +756,7 @@ int ps3_repository_read_region_total(u64 *region_total) make_first_field("bi", 0), make_field("rgntotal", 0), 0, 0, - region_total, 0); + region_total, NULL); } /** @@ -723,6 +779,72 @@ int ps3_repository_read_mm_info(u64 *rm_base, u64 *rm_size, u64 *region_total) } /** + * ps3_repository_read_highmem_region_count - Read the number of highmem regions + * + * Bootloaders must arrange the repository nodes such that regions are indexed + * with a region_index from 0 to region_count-1. + */ + +int ps3_repository_read_highmem_region_count(unsigned int *region_count) +{ + int result; + u64 v1 = 0; + + result = read_node(PS3_LPAR_ID_CURRENT, + make_first_field("highmem", 0), + make_field("region", 0), + make_field("count", 0), + 0, + &v1, NULL); + *region_count = v1; + return result; +} + + +int ps3_repository_read_highmem_base(unsigned int region_index, + u64 *highmem_base) +{ + return read_node(PS3_LPAR_ID_CURRENT, + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("base", 0), + 0, + highmem_base, NULL); +} + +int ps3_repository_read_highmem_size(unsigned int region_index, + u64 *highmem_size) +{ + return read_node(PS3_LPAR_ID_CURRENT, + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("size", 0), + 0, + highmem_size, NULL); +} + +/** + * ps3_repository_read_highmem_info - Read high memory region info + * @region_index: Region index, {0,..,region_count-1}. + * @highmem_base: High memory base address. + * @highmem_size: High memory size. + * + * Bootloaders that preallocate highmem regions must place the + * region info into the repository at these well known nodes. + */ + +int ps3_repository_read_highmem_info(unsigned int region_index, + u64 *highmem_base, u64 *highmem_size) +{ + int result; + + *highmem_base = 0; + result = ps3_repository_read_highmem_base(region_index, highmem_base); + return result ? result + : ps3_repository_read_highmem_size(region_index, highmem_size); +} + +/** * ps3_repository_read_num_spu_reserved - Number of physical spus reserved. * @num_spu: Number of physical spus. */ @@ -730,13 +852,13 @@ int ps3_repository_read_mm_info(u64 *rm_base, u64 *rm_size, u64 *region_total) int ps3_repository_read_num_spu_reserved(unsigned int *num_spu_reserved) { int result; - u64 v1; + u64 v1 = 0; result = read_node(PS3_LPAR_ID_CURRENT, make_first_field("bi", 0), make_field("spun", 0), 0, 0, - &v1, 0); + &v1, NULL); *num_spu_reserved = v1; return result; } @@ -749,13 +871,13 @@ int ps3_repository_read_num_spu_reserved(unsigned int *num_spu_reserved) int ps3_repository_read_num_spu_resource_id(unsigned int *num_resource_id) { int result; - u64 v1; + u64 v1 = 0; result = read_node(PS3_LPAR_ID_CURRENT, make_first_field("bi", 0), make_field("spursvn", 0), 0, 0, - &v1, 0); + &v1, NULL); *num_resource_id = v1; return result; } @@ -768,11 +890,11 @@ int ps3_repository_read_num_spu_resource_id(unsigned int *num_resource_id) */ int ps3_repository_read_spu_resource_id(unsigned int res_index, - enum ps3_spu_resource_type* resource_type, unsigned int *resource_id) + enum ps3_spu_resource_type *resource_type, unsigned int *resource_id) { int result; - u64 v1; - u64 v2; + u64 v1 = 0; + u64 v2 = 0; result = read_node(PS3_LPAR_ID_CURRENT, make_first_field("bi", 0), @@ -785,27 +907,27 @@ int ps3_repository_read_spu_resource_id(unsigned int res_index, return result; } -int ps3_repository_read_boot_dat_address(u64 *address) +static int ps3_repository_read_boot_dat_address(u64 *address) { return read_node(PS3_LPAR_ID_CURRENT, make_first_field("bi", 0), make_field("boot_dat", 0), make_field("address", 0), 0, - address, 0); + address, NULL); } int ps3_repository_read_boot_dat_size(unsigned int *size) { int result; - u64 v1; + u64 v1 = 0; result = read_node(PS3_LPAR_ID_CURRENT, make_first_field("bi", 0), make_field("boot_dat", 0), make_field("size", 0), 0, - &v1, 0); + &v1, NULL); *size = v1; return result; } @@ -813,14 +935,14 @@ int ps3_repository_read_boot_dat_size(unsigned int *size) int ps3_repository_read_vuart_av_port(unsigned int *port) { int result; - u64 v1; + u64 v1 = 0; result = read_node(PS3_LPAR_ID_CURRENT, make_first_field("bi", 0), make_field("vir_uart", 0), make_field("port", 0), make_field("avset", 0), - &v1, 0); + &v1, NULL); *port = v1; return result; } @@ -828,14 +950,14 @@ int ps3_repository_read_vuart_av_port(unsigned int *port) int ps3_repository_read_vuart_sysmgr_port(unsigned int *port) { int result; - u64 v1; + u64 v1 = 0; result = read_node(PS3_LPAR_ID_CURRENT, make_first_field("bi", 0), make_field("vir_uart", 0), make_field("port", 0), make_field("sysmgr", 0), - &v1, 0); + &v1, NULL); *port = v1; return result; } @@ -856,21 +978,31 @@ int ps3_repository_read_boot_dat_info(u64 *lpar_addr, unsigned int *size) : ps3_repository_read_boot_dat_size(size); } +/** + * ps3_repository_read_num_be - Number of physical BE processors in the system. + */ + int ps3_repository_read_num_be(unsigned int *num_be) { int result; - u64 v1; + u64 v1 = 0; result = read_node(PS3_LPAR_ID_PME, make_first_field("ben", 0), 0, 0, 0, - &v1, 0); + &v1, NULL); *num_be = v1; return result; } +/** + * ps3_repository_read_be_node_id - Read the physical BE processor node id. + * @be_index: Zero based index. + * @node_id: The BE processor node id. + */ + int ps3_repository_read_be_node_id(unsigned int be_index, u64 *node_id) { return read_node(PS3_LPAR_ID_PME, @@ -878,7 +1010,23 @@ int ps3_repository_read_be_node_id(unsigned int be_index, u64 *node_id) 0, 0, 0, - node_id, 0); + node_id, NULL); +} + +/** + * ps3_repository_read_be_id - Read the physical BE processor id. + * @node_id: The BE processor node id. + * @be_id: The BE processor id. + */ + +int ps3_repository_read_be_id(u64 node_id, u64 *be_id) +{ + return read_node(PS3_LPAR_ID_PME, + make_first_field("be", 0), + node_id, + 0, + 0, + be_id, NULL); } int ps3_repository_read_tb_freq(u64 node_id, u64 *tb_freq) @@ -888,7 +1036,7 @@ int ps3_repository_read_tb_freq(u64 node_id, u64 *tb_freq) node_id, make_field("clock", 0), 0, - tb_freq, 0); + tb_freq, NULL); } int ps3_repository_read_be_tb_freq(unsigned int be_index, u64 *tb_freq) @@ -897,11 +1045,161 @@ int ps3_repository_read_be_tb_freq(unsigned int be_index, u64 *tb_freq) u64 node_id; *tb_freq = 0; - result = ps3_repository_read_be_node_id(0, &node_id); + result = ps3_repository_read_be_node_id(be_index, &node_id); return result ? result : ps3_repository_read_tb_freq(node_id, tb_freq); } +int ps3_repository_read_lpm_privileges(unsigned int be_index, u64 *lpar, + u64 *rights) +{ + int result; + u64 node_id; + + *lpar = 0; + *rights = 0; + result = ps3_repository_read_be_node_id(be_index, &node_id); + return result ? result + : read_node(PS3_LPAR_ID_PME, + make_first_field("be", 0), + node_id, + make_field("lpm", 0), + make_field("priv", 0), + lpar, rights); +} + +#if defined(CONFIG_PS3_REPOSITORY_WRITE) + +static int create_node(u64 n1, u64 n2, u64 n3, u64 n4, u64 v1, u64 v2) +{ + int result; + + dump_node(0, n1, n2, n3, n4, v1, v2); + + result = lv1_create_repository_node(n1, n2, n3, n4, v1, v2); + + if (result) { + pr_devel("%s:%d: lv1_create_repository_node failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return -ENOENT; + } + + return 0; +} + +static int delete_node(u64 n1, u64 n2, u64 n3, u64 n4) +{ + int result; + + dump_node(0, n1, n2, n3, n4, 0, 0); + + result = lv1_delete_repository_node(n1, n2, n3, n4); + + if (result) { + pr_devel("%s:%d: lv1_delete_repository_node failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return -ENOENT; + } + + return 0; +} + +static int write_node(u64 n1, u64 n2, u64 n3, u64 n4, u64 v1, u64 v2) +{ + int result; + + result = create_node(n1, n2, n3, n4, v1, v2); + + if (!result) + return 0; + + result = lv1_write_repository_node(n1, n2, n3, n4, v1, v2); + + if (result) { + pr_devel("%s:%d: lv1_write_repository_node failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return -ENOENT; + } + + return 0; +} + +int ps3_repository_write_highmem_region_count(unsigned int region_count) +{ + int result; + u64 v1 = (u64)region_count; + + result = write_node( + make_first_field("highmem", 0), + make_field("region", 0), + make_field("count", 0), + 0, + v1, 0); + return result; +} + +int ps3_repository_write_highmem_base(unsigned int region_index, + u64 highmem_base) +{ + return write_node( + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("base", 0), + 0, + highmem_base, 0); +} + +int ps3_repository_write_highmem_size(unsigned int region_index, + u64 highmem_size) +{ + return write_node( + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("size", 0), + 0, + highmem_size, 0); +} + +int ps3_repository_write_highmem_info(unsigned int region_index, + u64 highmem_base, u64 highmem_size) +{ + int result; + + result = ps3_repository_write_highmem_base(region_index, highmem_base); + return result ? result + : ps3_repository_write_highmem_size(region_index, highmem_size); +} + +static int ps3_repository_delete_highmem_base(unsigned int region_index) +{ + return delete_node( + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("base", 0), + 0); +} + +static int ps3_repository_delete_highmem_size(unsigned int region_index) +{ + return delete_node( + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("size", 0), + 0); +} + +int ps3_repository_delete_highmem_info(unsigned int region_index) +{ + int result; + + result = ps3_repository_delete_highmem_base(region_index); + result += ps3_repository_delete_highmem_size(region_index); + + return result ? -1 : 0; +} + +#endif /* defined(CONFIG_PS3_WRITE_REPOSITORY) */ + #if defined(DEBUG) int ps3_repository_dump_resource_info(const struct ps3_repository_device *repo) @@ -909,7 +1207,7 @@ int ps3_repository_dump_resource_info(const struct ps3_repository_device *repo) int result = 0; unsigned int res_index; - pr_debug(" -> %s:%d: (%u:%u)\n", __func__, __LINE__, + pr_devel(" -> %s:%d: (%u:%u)\n", __func__, __LINE__, repo->bus_index, repo->dev_index); for (res_index = 0; res_index < 10; res_index++) { @@ -921,13 +1219,13 @@ int ps3_repository_dump_resource_info(const struct ps3_repository_device *repo) if (result) { if (result != LV1_NO_ENTRY) - pr_debug("%s:%d ps3_repository_read_dev_intr" + pr_devel("%s:%d ps3_repository_read_dev_intr" " (%u:%u) failed\n", __func__, __LINE__, repo->bus_index, repo->dev_index); break; } - pr_debug("%s:%d (%u:%u) intr_type %u, interrupt_id %u\n", + pr_devel("%s:%d (%u:%u) intr_type %u, interrupt_id %u\n", __func__, __LINE__, repo->bus_index, repo->dev_index, intr_type, interrupt_id); } @@ -942,18 +1240,18 @@ int ps3_repository_dump_resource_info(const struct ps3_repository_device *repo) if (result) { if (result != LV1_NO_ENTRY) - pr_debug("%s:%d ps3_repository_read_dev_reg" + pr_devel("%s:%d ps3_repository_read_dev_reg" " (%u:%u) failed\n", __func__, __LINE__, repo->bus_index, repo->dev_index); break; } - pr_debug("%s:%d (%u:%u) reg_type %u, bus_addr %lxh, len %lxh\n", + pr_devel("%s:%d (%u:%u) reg_type %u, bus_addr %llxh, len %llxh\n", __func__, __LINE__, repo->bus_index, repo->dev_index, reg_type, bus_addr, len); } - pr_debug(" <- %s:%d\n", __func__, __LINE__); + pr_devel(" <- %s:%d\n", __func__, __LINE__); return result; } @@ -963,22 +1261,22 @@ static int dump_stor_dev_info(struct ps3_repository_device *repo) unsigned int num_regions, region_index; u64 port, blk_size, num_blocks; - pr_debug(" -> %s:%d: (%u:%u)\n", __func__, __LINE__, + pr_devel(" -> %s:%d: (%u:%u)\n", __func__, __LINE__, repo->bus_index, repo->dev_index); result = ps3_repository_read_stor_dev_info(repo->bus_index, repo->dev_index, &port, &blk_size, &num_blocks, &num_regions); if (result) { - pr_debug("%s:%d ps3_repository_read_stor_dev_info" + pr_devel("%s:%d ps3_repository_read_stor_dev_info" " (%u:%u) failed\n", __func__, __LINE__, repo->bus_index, repo->dev_index); goto out; } - pr_debug("%s:%d (%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); + pr_devel("%s:%d (%u:%u): port %llu, blk_size %llu, num_blocks " + "%llu, num_regions %u\n", + __func__, __LINE__, repo->bus_index, repo->dev_index, + port, blk_size, num_blocks, num_regions); for (region_index = 0; region_index < num_regions; region_index++) { unsigned int region_id; @@ -988,19 +1286,20 @@ static int dump_stor_dev_info(struct ps3_repository_device *repo) repo->dev_index, region_index, ®ion_id, ®ion_start, ®ion_size); if (result) { - pr_debug("%s:%d ps3_repository_read_stor_dev_region" + pr_devel("%s:%d ps3_repository_read_stor_dev_region" " (%u:%u) failed\n", __func__, __LINE__, repo->bus_index, repo->dev_index); break; } - pr_debug("%s:%d (%u:%u) region_id %u, start %lxh, size %lxh\n", + pr_devel("%s:%d (%u:%u) region_id %u, start %lxh, size %lxh\n", __func__, __LINE__, repo->bus_index, repo->dev_index, - region_id, region_start, region_size); + region_id, (unsigned long)region_start, + (unsigned long)region_size); } out: - pr_debug(" <- %s:%d\n", __func__, __LINE__); + pr_devel(" <- %s:%d\n", __func__, __LINE__); return result; } @@ -1009,7 +1308,7 @@ static int dump_device_info(struct ps3_repository_device *repo, { int result = 0; - pr_debug(" -> %s:%d: bus_%u\n", __func__, __LINE__, repo->bus_index); + pr_devel(" -> %s:%d: bus_%u\n", __func__, __LINE__, repo->bus_index); for (repo->dev_index = 0; repo->dev_index < num_dev; repo->dev_index++) { @@ -1018,7 +1317,7 @@ static int dump_device_info(struct ps3_repository_device *repo, repo->dev_index, &repo->dev_type); if (result) { - pr_debug("%s:%d ps3_repository_read_dev_type" + pr_devel("%s:%d ps3_repository_read_dev_type" " (%u:%u) failed\n", __func__, __LINE__, repo->bus_index, repo->dev_index); break; @@ -1028,15 +1327,15 @@ static int dump_device_info(struct ps3_repository_device *repo, repo->dev_index, &repo->dev_id); if (result) { - pr_debug("%s:%d ps3_repository_read_dev_id" + pr_devel("%s:%d ps3_repository_read_dev_id" " (%u:%u) failed\n", __func__, __LINE__, repo->bus_index, repo->dev_index); continue; } - pr_debug("%s:%d (%u:%u): dev_type %u, dev_id %u\n", __func__, + pr_devel("%s:%d (%u:%u): dev_type %u, dev_id %lu\n", __func__, __LINE__, repo->bus_index, repo->dev_index, - repo->dev_type, repo->dev_id); + repo->dev_type, (unsigned long)repo->dev_id); ps3_repository_dump_resource_info(repo); @@ -1044,7 +1343,7 @@ static int dump_device_info(struct ps3_repository_device *repo, dump_stor_dev_info(repo); } - pr_debug(" <- %s:%d\n", __func__, __LINE__); + pr_devel(" <- %s:%d\n", __func__, __LINE__); return result; } @@ -1053,7 +1352,7 @@ int ps3_repository_dump_bus_info(void) int result = 0; struct ps3_repository_device repo; - pr_debug(" -> %s:%d\n", __func__, __LINE__); + pr_devel(" -> %s:%d\n", __func__, __LINE__); memset(&repo, 0, sizeof(repo)); @@ -1064,7 +1363,7 @@ int ps3_repository_dump_bus_info(void) &repo.bus_type); if (result) { - pr_debug("%s:%d read_bus_type(%u) failed\n", + pr_devel("%s:%d read_bus_type(%u) failed\n", __func__, __LINE__, repo.bus_index); break; } @@ -1073,32 +1372,32 @@ int ps3_repository_dump_bus_info(void) &repo.bus_id); if (result) { - pr_debug("%s:%d read_bus_id(%u) failed\n", + pr_devel("%s:%d read_bus_id(%u) failed\n", __func__, __LINE__, repo.bus_index); continue; } if (repo.bus_index != repo.bus_id) - pr_debug("%s:%d bus_index != bus_id\n", + pr_devel("%s:%d bus_index != bus_id\n", __func__, __LINE__); result = ps3_repository_read_bus_num_dev(repo.bus_index, &num_dev); if (result) { - pr_debug("%s:%d read_bus_num_dev(%u) failed\n", + pr_devel("%s:%d read_bus_num_dev(%u) failed\n", __func__, __LINE__, repo.bus_index); continue; } - pr_debug("%s:%d bus_%u: bus_type %u, bus_id %u, num_dev %u\n", + pr_devel("%s:%d bus_%u: bus_type %u, bus_id %lu, num_dev %u\n", __func__, __LINE__, repo.bus_index, repo.bus_type, - repo.bus_id, num_dev); + (unsigned long)repo.bus_id, num_dev); dump_device_info(&repo, num_dev); } - pr_debug(" <- %s:%d\n", __func__, __LINE__); + pr_devel(" <- %s:%d\n", __func__, __LINE__); return result; } diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c index 609945dbe39..3f509f86432 100644 --- a/arch/powerpc/platforms/ps3/setup.c +++ b/arch/powerpc/platforms/ps3/setup.c @@ -23,7 +23,7 @@ #include <linux/fs.h> #include <linux/root_dev.h> #include <linux/console.h> -#include <linux/kexec.h> +#include <linux/export.h> #include <linux/bootmem.h> #include <asm/machdep.h> @@ -33,6 +33,7 @@ #include <asm/udbg.h> #include <asm/prom.h> #include <asm/lv1call.h> +#include <asm/ps3gpu.h> #include "platform.h" @@ -42,9 +43,9 @@ #define DBG pr_debug #endif -#if !defined(CONFIG_SMP) -static void smp_send_stop(void) {} -#endif +/* mutex synchronizing GPU accesses and video mode changes */ +DEFINE_MUTEX(ps3_gpu_mutex); +EXPORT_SYMBOL_GPL(ps3_gpu_mutex); static union ps3_firmware_version ps3_firmware_version; @@ -95,6 +96,14 @@ static void ps3_power_off(void) ps3_sys_manager_power_off(); /* never returns */ } +static void ps3_halt(void) +{ + DBG("%s:%d\n", __func__, __LINE__); + + smp_send_stop(); + ps3_sys_manager_halt(); /* never returns */ +} + static void ps3_panic(char *str) { DBG("%s:%d %s\n", __func__, __LINE__, str); @@ -105,7 +114,8 @@ static void ps3_panic(char *str) printk(" Please press POWER button.\n"); printk("\n"); - while(1); + while(1) + lv1_pause(1); } #if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE) || \ @@ -117,7 +127,7 @@ static void __init prealloc(struct ps3_prealloc *p) p->address = __alloc_bootmem(p->size, p->align, __pa(MAX_DMA_ADDRESS)); if (!p->address) { - printk(KERN_ERR "%s: Cannot allocate %s\n", __FUNCTION__, + printk(KERN_ERR "%s: Cannot allocate %s\n", __func__, p->name); return; } @@ -174,25 +184,30 @@ early_param("ps3flash", early_parse_ps3flash); #define prealloc_ps3flash_bounce_buffer() do { } while (0) #endif -static int ps3_set_dabr(u64 dabr) +static int ps3_set_dabr(unsigned long dabr, unsigned long dabrx) { - enum {DABR_USER = 1, DABR_KERNEL = 2,}; + /* Have to set at least one bit in the DABRX */ + if (dabrx == 0 && dabr == 0) + dabrx = DABRX_USER; + /* hypervisor only allows us to set BTI, Kernel and user */ + dabrx &= DABRX_BTI | DABRX_KERNEL | DABRX_USER; - return lv1_set_dabr(dabr, DABR_KERNEL | DABR_USER) ? -1 : 0; + return lv1_set_dabr(dabr, dabrx) ? -1 : 0; } static void __init ps3_setup_arch(void) { + u64 tmp; DBG(" -> %s:%d\n", __func__, __LINE__); - lv1_get_version_info(&ps3_firmware_version.raw); + lv1_get_version_info(&ps3_firmware_version.raw, &tmp); + printk(KERN_INFO "PS3 firmware version %u.%u.%u\n", ps3_firmware_version.major, ps3_firmware_version.minor, ps3_firmware_version.rev); ps3_spu_set_platform(); - ps3_map_htab(); #ifdef CONFIG_SMP smp_init_ps3(); @@ -206,6 +221,7 @@ static void __init ps3_setup_arch(void) prealloc_ps3flash_bounce_buffer(); ppc_md.power_save = ps3_power_save; + ps3_os_area_init(); DBG(" <- %s:%d\n", __func__, __LINE__); } @@ -228,7 +244,7 @@ static int __init ps3_probe(void) powerpc_firmware_features |= FW_FEATURE_PS3_POSSIBLE; - ps3_os_area_init(); + ps3_os_area_save_params(); ps3_mm_init(); ps3_mm_vas_create(&htab_size); ps3_hpte_init(htab_size); @@ -258,17 +274,13 @@ define_machine(ps3) { .init_IRQ = ps3_init_IRQ, .panic = ps3_panic, .get_boot_time = ps3_get_boot_time, - .set_rtc_time = ps3_set_rtc_time, - .get_rtc_time = ps3_get_rtc_time, .set_dabr = ps3_set_dabr, .calibrate_decr = ps3_calibrate_decr, .progress = ps3_progress, .restart = ps3_restart, .power_off = ps3_power_off, + .halt = ps3_halt, #if defined(CONFIG_KEXEC) .kexec_cpu_down = ps3_kexec_cpu_down, - .machine_kexec = default_machine_kexec, - .machine_kexec_prepare = default_machine_kexec_prepare, - .machine_crash_shutdown = default_machine_crash_shutdown, #endif }; diff --git a/arch/powerpc/platforms/ps3/smp.c b/arch/powerpc/platforms/ps3/smp.c index f0b12f21236..b358bec6c8c 100644 --- a/arch/powerpc/platforms/ps3/smp.c +++ b/arch/powerpc/platforms/ps3/smp.c @@ -32,27 +32,14 @@ #define DBG pr_debug #endif -static irqreturn_t ipi_function_handler(int irq, void *msg) -{ - smp_message_recv((int)(long)msg); - return IRQ_HANDLED; -} - /** * ps3_ipi_virqs - a per cpu array of virqs for ipi use */ #define MSG_COUNT 4 -static DEFINE_PER_CPU(unsigned int, ps3_ipi_virqs[MSG_COUNT]); - -static const char *names[MSG_COUNT] = { - "ipi call", - "ipi reschedule", - "ipi migrate", - "ipi debug brk" -}; +static DEFINE_PER_CPU(unsigned int [MSG_COUNT], ps3_ipi_virqs); -static void do_message_pass(int target, int msg) +static void ps3_smp_message_pass(int cpu, int msg) { int result; unsigned int virq; @@ -62,72 +49,59 @@ static void do_message_pass(int target, int msg) return; } - virq = per_cpu(ps3_ipi_virqs, target)[msg]; + virq = per_cpu(ps3_ipi_virqs, cpu)[msg]; result = ps3_send_event_locally(virq); if (result) DBG("%s:%d: ps3_send_event_locally(%d, %d) failed" - " (%d)\n", __func__, __LINE__, target, msg, result); + " (%d)\n", __func__, __LINE__, cpu, msg, result); } -static void ps3_smp_message_pass(int target, int msg) +static int __init ps3_smp_probe(void) { int cpu; - if (target < NR_CPUS) - do_message_pass(target, msg); - else if (target == MSG_ALL_BUT_SELF) { - for_each_online_cpu(cpu) - if (cpu != smp_processor_id()) - do_message_pass(cpu, msg); - } else { - for_each_online_cpu(cpu) - do_message_pass(cpu, msg); - } -} + for (cpu = 0; cpu < 2; cpu++) { + int result; + unsigned int *virqs = per_cpu(ps3_ipi_virqs, cpu); + int i; -static int ps3_smp_probe(void) -{ - return 2; -} + DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu); -static void __init ps3_smp_setup_cpu(int cpu) -{ - int result; - unsigned int *virqs = per_cpu(ps3_ipi_virqs, cpu); - int i; + /* + * Check assumptions on ps3_ipi_virqs[] indexing. If this + * check fails, then a different mapping of PPC_MSG_ + * to index needs to be setup. + */ - DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu); + BUILD_BUG_ON(PPC_MSG_CALL_FUNCTION != 0); + BUILD_BUG_ON(PPC_MSG_RESCHEDULE != 1); + BUILD_BUG_ON(PPC_MSG_TICK_BROADCAST != 2); + BUILD_BUG_ON(PPC_MSG_DEBUGGER_BREAK != 3); - /* - * Check assumptions on ps3_ipi_virqs[] indexing. If this - * check fails, then a different mapping of PPC_MSG_ - * to index needs to be setup. - */ + for (i = 0; i < MSG_COUNT; i++) { + result = ps3_event_receive_port_setup(cpu, &virqs[i]); - BUILD_BUG_ON(PPC_MSG_CALL_FUNCTION != 0); - BUILD_BUG_ON(PPC_MSG_RESCHEDULE != 1); - BUILD_BUG_ON(PPC_MSG_DEBUGGER_BREAK != 3); + if (result) + continue; - for (i = 0; i < MSG_COUNT; i++) { - result = ps3_event_receive_port_setup(cpu, &virqs[i]); + DBG("%s:%d: (%d, %d) => virq %u\n", + __func__, __LINE__, cpu, i, virqs[i]); - if (result) - continue; + result = smp_request_message_ipi(virqs[i], i); - DBG("%s:%d: (%d, %d) => virq %u\n", - __func__, __LINE__, cpu, i, virqs[i]); + if (result) + virqs[i] = NO_IRQ; + else + ps3_register_ipi_irq(cpu, virqs[i]); + } - result = request_irq(virqs[i], ipi_function_handler, - IRQF_DISABLED, names[i], (void*)(long)i); + ps3_register_ipi_debug_brk(cpu, virqs[PPC_MSG_DEBUGGER_BREAK]); - if (result) - virqs[i] = NO_IRQ; + DBG(" <- %s:%d: (%d)\n", __func__, __LINE__, cpu); } - ps3_register_ipi_debug_brk(cpu, virqs[PPC_MSG_DEBUGGER_BREAK]); - - DBG(" <- %s:%d: (%d)\n", __func__, __LINE__, cpu); + return 2; } void ps3_smp_cleanup_cpu(int cpu) @@ -150,7 +124,6 @@ static struct smp_ops_t ps3_smp_ops = { .probe = ps3_smp_probe, .message_pass = ps3_smp_message_pass, .kick_cpu = smp_generic_kick_cpu, - .setup_cpu = ps3_smp_setup_cpu, }; void smp_init_ps3(void) diff --git a/arch/powerpc/platforms/ps3/spu.c b/arch/powerpc/platforms/ps3/spu.c index d1630a074ac..a0bca05e26b 100644 --- a/arch/powerpc/platforms/ps3/spu.c +++ b/arch/powerpc/platforms/ps3/spu.c @@ -20,14 +20,18 @@ #include <linux/kernel.h> #include <linux/init.h> +#include <linux/slab.h> #include <linux/mmzone.h> +#include <linux/export.h> #include <linux/io.h> #include <linux/mm.h> #include <asm/spu.h> #include <asm/spu_priv1.h> #include <asm/lv1call.h> +#include <asm/ps3.h> +#include "../cell/spufs/spufs.h" #include "platform.h" /* spu_management_ops */ @@ -139,12 +143,18 @@ static void _dump_areas(unsigned int spe_id, unsigned long priv2, pr_debug("%s:%d: shadow: %lxh\n", func, line, shadow); } +u64 ps3_get_spe_id(void *arg) +{ + return spu_pdata(arg)->spe_id; +} +EXPORT_SYMBOL_GPL(ps3_get_spe_id); + static unsigned long get_vas_id(void) { - unsigned long id; + u64 id; lv1_get_logical_ppe_id(&id); - lv1_get_virtual_address_space_id_of_ppe(id, &id); + lv1_get_virtual_address_space_id_of_ppe(&id); return id; } @@ -152,14 +162,18 @@ static unsigned long get_vas_id(void) static int __init construct_spu(struct spu *spu) { int result; - unsigned long unused; + u64 unused; + u64 problem_phys; + u64 local_store_phys; result = lv1_construct_logical_spe(PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, get_vas_id(), SPE_TYPE_LOGICAL, - &spu_pdata(spu)->priv2_addr, &spu->problem_phys, - &spu->local_store_phys, &unused, + &spu_pdata(spu)->priv2_addr, &problem_phys, + &local_store_phys, &unused, &spu_pdata(spu)->shadow_addr, &spu_pdata(spu)->spe_id); + spu->problem_phys = problem_phys; + spu->local_store_phys = local_store_phys; if (result) { pr_debug("%s:%d: lv1_construct_logical_spe failed: %s\n", @@ -178,20 +192,30 @@ static void spu_unmap(struct spu *spu) iounmap(spu_pdata(spu)->shadow); } +/** + * setup_areas - Map the spu regions into the address space. + * + * The current HV requires the spu shadow regs to be mapped with the + * PTE page protection bits set as read-only (PP=3). This implementation + * uses the low level __ioremap() to bypass the page protection settings + * inforced by ioremap_prot() to get the needed PTE bits set for the + * shadow regs. + */ + static int __init setup_areas(struct spu *spu) { struct table {char* name; unsigned long addr; unsigned long size;}; + static const unsigned long shadow_flags = _PAGE_NO_CACHE | 3; - spu_pdata(spu)->shadow = ioremap_flags(spu_pdata(spu)->shadow_addr, - sizeof(struct spe_shadow), - pgprot_val(PAGE_READONLY) | - _PAGE_NO_CACHE); + spu_pdata(spu)->shadow = __ioremap(spu_pdata(spu)->shadow_addr, + sizeof(struct spe_shadow), + shadow_flags); if (!spu_pdata(spu)->shadow) { pr_debug("%s:%d: ioremap shadow failed\n", __func__, __LINE__); goto fail_ioremap; } - spu->local_store = (__force void *)ioremap_flags(spu->local_store_phys, + spu->local_store = (__force void *)ioremap_prot(spu->local_store_phys, LS_SIZE, _PAGE_NO_CACHE); if (!spu->local_store) { @@ -419,10 +443,34 @@ static int ps3_init_affinity(void) return 0; } +/** + * ps3_enable_spu - Enable SPU run control. + * + * An outstanding enhancement for the PS3 would be to add a guard to check + * for incorrect access to the spu problem state when the spu context is + * disabled. This check could be implemented with a flag added to the spu + * context that would inhibit mapping problem state pages, and a routine + * to unmap spu problem state pages. When the spu is enabled with + * ps3_enable_spu() the flag would be set allowing pages to be mapped, + * and when the spu is disabled with ps3_disable_spu() the flag would be + * cleared and the mapped problem state pages would be unmapped. + */ + +static void ps3_enable_spu(struct spu_context *ctx) +{ +} + +static void ps3_disable_spu(struct spu_context *ctx) +{ + ctx->ops->runcntl_stop(ctx); +} + const struct spu_management_ops spu_management_ps3_ops = { .enumerate_spus = ps3_enumerate_spus, .create_spu = ps3_create_spu, .destroy_spu = ps3_destroy_spu, + .enable_spu = ps3_enable_spu, + .disable_spu = ps3_disable_spu, .init_affinity = ps3_init_affinity, }; @@ -505,8 +553,6 @@ static void mfc_sr1_set(struct spu *spu, u64 sr1) static const u64 allowed = ~(MFC_STATE1_LOCAL_STORAGE_DECODE_MASK | MFC_STATE1_PROBLEM_STATE_MASK); - sr1 |= MFC_STATE1_MASTER_RUN_CONTROL_MASK; - BUG_ON((sr1 & allowed) != (spu_pdata(spu)->cache.sr1 & allowed)); spu_pdata(spu)->cache.sr1 = sr1; diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c index 4bb634a17e4..5606fe36faf 100644 --- a/arch/powerpc/platforms/ps3/system-bus.c +++ b/arch/powerpc/platforms/ps3/system-bus.c @@ -20,18 +20,20 @@ #include <linux/kernel.h> #include <linux/init.h> -#include <linux/module.h> +#include <linux/export.h> #include <linux/dma-mapping.h> #include <linux/err.h> +#include <linux/slab.h> #include <asm/udbg.h> #include <asm/lv1call.h> #include <asm/firmware.h> +#include <asm/cell-regs.h> #include "platform.h" static struct device ps3_system_bus = { - .bus_id = "ps3_system", + .init_name = "ps3_system", }; /* FIXME: need device usage counters! */ @@ -42,8 +44,8 @@ struct { int gpu; } static usage_hack; -static int ps3_is_device(struct ps3_system_bus_device *dev, - unsigned int bus_id, unsigned int dev_id) +static int ps3_is_device(struct ps3_system_bus_device *dev, u64 bus_id, + u64 dev_id) { return dev->bus_id == bus_id && dev->dev_id == dev_id; } @@ -175,15 +177,15 @@ int ps3_open_hv_device(struct ps3_system_bus_device *dev) return ps3_open_hv_device_sb(dev); case PS3_MATCH_ID_SOUND: - case PS3_MATCH_ID_GRAPHICS: + case PS3_MATCH_ID_GPU: return ps3_open_hv_device_gpu(dev); case PS3_MATCH_ID_AV_SETTINGS: case PS3_MATCH_ID_SYSTEM_MANAGER: pr_debug("%s:%d: unsupported match_id: %u\n", __func__, __LINE__, dev->match_id); - pr_debug("%s:%d: bus_id: %u\n", __func__, - __LINE__, dev->bus_id); + pr_debug("%s:%d: bus_id: %llu\n", __func__, __LINE__, + dev->bus_id); BUG(); return -EINVAL; @@ -213,15 +215,15 @@ int ps3_close_hv_device(struct ps3_system_bus_device *dev) return ps3_close_hv_device_sb(dev); case PS3_MATCH_ID_SOUND: - case PS3_MATCH_ID_GRAPHICS: + case PS3_MATCH_ID_GPU: return ps3_close_hv_device_gpu(dev); case PS3_MATCH_ID_AV_SETTINGS: case PS3_MATCH_ID_SYSTEM_MANAGER: pr_debug("%s:%d: unsupported match_id: %u\n", __func__, __LINE__, dev->match_id); - pr_debug("%s:%d: bus_id: %u\n", __func__, - __LINE__, dev->bus_id); + pr_debug("%s:%d: bus_id: %llu\n", __func__, __LINE__, + dev->bus_id); BUG(); return -EINVAL; @@ -240,7 +242,7 @@ EXPORT_SYMBOL_GPL(ps3_close_hv_device); static void _dump_mmio_region(const struct ps3_mmio_region* r, const char* func, int line) { - pr_debug("%s:%d: dev %u:%u\n", func, line, r->dev->bus_id, + pr_debug("%s:%d: dev %llu:%llu\n", func, line, r->dev->bus_id, r->dev->dev_id); pr_debug("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr); pr_debug("%s:%d: len %lxh\n", func, line, r->len); @@ -250,9 +252,11 @@ static void _dump_mmio_region(const struct ps3_mmio_region* r, static int ps3_sb_mmio_region_create(struct ps3_mmio_region *r) { int result; + u64 lpar_addr; result = lv1_map_device_mmio_region(r->dev->bus_id, r->dev->dev_id, - r->bus_addr, r->len, r->page_size, &r->lpar_addr); + r->bus_addr, r->len, r->page_size, &lpar_addr); + r->lpar_addr = lpar_addr; if (result) { pr_debug("%s:%d: lv1_map_device_mmio_region failed: %s\n", @@ -281,7 +285,6 @@ static int ps3_sb_free_mmio_region(struct ps3_mmio_region *r) int result; dump_mmio_region(r); -; result = lv1_unmap_device_mmio_region(r->dev->bus_id, r->dev->dev_id, r->lpar_addr); @@ -347,11 +350,23 @@ static int ps3_system_bus_match(struct device *_dev, struct ps3_system_bus_driver *drv = ps3_drv_to_system_bus_drv(_drv); struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); - result = dev->match_id == drv->match_id; + if (!dev->match_sub_id) + result = dev->match_id == drv->match_id; + else + result = dev->match_sub_id == drv->match_sub_id && + dev->match_id == drv->match_id; + + if (result) + pr_info("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): match\n", + __func__, __LINE__, + dev->match_id, dev->match_sub_id, dev_name(&dev->core), + drv->match_id, drv->match_sub_id, drv->core.name); + else + pr_debug("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): miss\n", + __func__, __LINE__, + dev->match_id, dev->match_sub_id, dev_name(&dev->core), + drv->match_id, drv->match_sub_id, drv->core.name); - pr_info("%s:%d: dev=%u(%s), drv=%u(%s): %s\n", __func__, __LINE__, - dev->match_id, dev->core.bus_id, drv->match_id, drv->core.name, - (result ? "match" : "miss")); return result; } @@ -362,7 +377,7 @@ static int ps3_system_bus_probe(struct device *_dev) struct ps3_system_bus_driver *drv; BUG_ON(!dev); - pr_info(" -> %s:%d: %s\n", __func__, __LINE__, _dev->bus_id); + dev_dbg(_dev, "%s:%d\n", __func__, __LINE__); drv = ps3_system_bus_dev_to_system_bus_drv(dev); BUG_ON(!drv); @@ -370,10 +385,10 @@ static int ps3_system_bus_probe(struct device *_dev) if (drv->probe) result = drv->probe(dev); else - pr_info("%s:%d: %s no probe method\n", __func__, __LINE__, - dev->core.bus_id); + pr_debug("%s:%d: %s no probe method\n", __func__, __LINE__, + dev_name(&dev->core)); - pr_info(" <- %s:%d: %s\n", __func__, __LINE__, dev->core.bus_id); + pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev_name(&dev->core)); return result; } @@ -384,7 +399,7 @@ static int ps3_system_bus_remove(struct device *_dev) struct ps3_system_bus_driver *drv; BUG_ON(!dev); - pr_info(" -> %s:%d: %s\n", __func__, __LINE__, _dev->bus_id); + dev_dbg(_dev, "%s:%d\n", __func__, __LINE__); drv = ps3_system_bus_dev_to_system_bus_drv(dev); BUG_ON(!drv); @@ -395,7 +410,7 @@ static int ps3_system_bus_remove(struct device *_dev) dev_dbg(&dev->core, "%s:%d %s: no remove method\n", __func__, __LINE__, drv->core.name); - pr_info(" <- %s:%d: %s\n", __func__, __LINE__, dev->core.bus_id); + pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev_name(&dev->core)); return result; } @@ -420,7 +435,7 @@ static void ps3_system_bus_shutdown(struct device *_dev) BUG_ON(!drv); dev_dbg(&dev->core, "%s:%d: %s -> %s\n", __func__, __LINE__, - dev->core.bus_id, drv->core.name); + dev_name(&dev->core), drv->core.name); if (drv->shutdown) drv->shutdown(dev); @@ -437,18 +452,13 @@ static void ps3_system_bus_shutdown(struct device *_dev) dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); } -static int ps3_system_bus_uevent(struct device *_dev, char **envp, - int num_envp, char *buffer, int buffer_size) +static int ps3_system_bus_uevent(struct device *_dev, struct kobj_uevent_env *env) { struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); - int i = 0, length = 0; - if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, - &length, "MODALIAS=ps3:%d", - dev->match_id)) + if (add_uevent_var(env, "MODALIAS=ps3:%d:%d", dev->match_id, + dev->match_sub_id)) return -ENOMEM; - - envp[i] = NULL; return 0; } @@ -456,7 +466,8 @@ static ssize_t modalias_show(struct device *_dev, struct device_attribute *a, char *buf) { struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); - int len = snprintf(buf, PAGE_SIZE, "ps3:%d\n", dev->match_id); + int len = snprintf(buf, PAGE_SIZE, "ps3:%d:%d\n", dev->match_id, + dev->match_sub_id); return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; } @@ -504,7 +515,8 @@ core_initcall(ps3_system_bus_init); * to the dma address (mapping) of the first page. */ static void * ps3_alloc_coherent(struct device *_dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) { int result; struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); @@ -521,7 +533,8 @@ static void * ps3_alloc_coherent(struct device *_dev, size_t size, } result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle, - IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M); + CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | + CBE_IOPTE_SO_RW | CBE_IOPTE_M); if (result) { pr_debug("%s:%d: ps3_dma_map failed (%d)\n", @@ -540,7 +553,7 @@ clean_none: } static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr, - dma_addr_t dma_handle) + dma_addr_t dma_handle, struct dma_attrs *attrs) { struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); @@ -549,22 +562,24 @@ static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr, } /* Creates TCEs for a user provided buffer. The user buffer must be - * contiguous real kernel storage (not vmalloc). The address of the buffer - * passed here is the kernel (virtual) address of the buffer. The buffer - * need not be page aligned, the dma_addr_t returned will point to the same - * byte within the page as vaddr. + * contiguous real kernel storage (not vmalloc). The address passed here + * comprises a page address and offset into that page. The dma_addr_t + * returned will point to the same byte within the page as was passed in. */ -static dma_addr_t ps3_sb_map_single(struct device *_dev, void *ptr, size_t size, - enum dma_data_direction direction) +static dma_addr_t ps3_sb_map_page(struct device *_dev, struct page *page, + unsigned long offset, size_t size, enum dma_data_direction direction, + struct dma_attrs *attrs) { struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); int result; - unsigned long bus_addr; + dma_addr_t bus_addr; + void *ptr = page_address(page) + offset; result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, &bus_addr, - IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW | IOPTE_M); + CBE_IOPTE_PP_R | CBE_IOPTE_PP_W | + CBE_IOPTE_SO_RW | CBE_IOPTE_M); if (result) { pr_debug("%s:%d: ps3_dma_map failed (%d)\n", @@ -574,25 +589,27 @@ static dma_addr_t ps3_sb_map_single(struct device *_dev, void *ptr, size_t size, return bus_addr; } -static dma_addr_t ps3_ioc0_map_single(struct device *_dev, void *ptr, - size_t size, - enum dma_data_direction direction) +static dma_addr_t ps3_ioc0_map_page(struct device *_dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction direction, + struct dma_attrs *attrs) { struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); int result; - unsigned long bus_addr; + dma_addr_t bus_addr; u64 iopte_flag; + void *ptr = page_address(page) + offset; - iopte_flag = IOPTE_M; + iopte_flag = CBE_IOPTE_M; switch (direction) { case DMA_BIDIRECTIONAL: - iopte_flag |= IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW; + iopte_flag |= CBE_IOPTE_PP_R | CBE_IOPTE_PP_W | CBE_IOPTE_SO_RW; break; case DMA_TO_DEVICE: - iopte_flag |= IOPTE_PP_R | IOPTE_SO_R; + iopte_flag |= CBE_IOPTE_PP_R | CBE_IOPTE_SO_R; break; case DMA_FROM_DEVICE: - iopte_flag |= IOPTE_PP_W | IOPTE_SO_RW; + iopte_flag |= CBE_IOPTE_PP_W | CBE_IOPTE_SO_RW; break; default: /* not happned */ @@ -608,8 +625,8 @@ static dma_addr_t ps3_ioc0_map_single(struct device *_dev, void *ptr, return bus_addr; } -static void ps3_unmap_single(struct device *_dev, dma_addr_t dma_addr, - size_t size, enum dma_data_direction direction) +static void ps3_unmap_page(struct device *_dev, dma_addr_t dma_addr, + size_t size, enum dma_data_direction direction, struct dma_attrs *attrs) { struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); int result; @@ -622,20 +639,20 @@ static void ps3_unmap_single(struct device *_dev, dma_addr_t dma_addr, } } -static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sg, int nents, - enum dma_data_direction direction) +static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sgl, + int nents, enum dma_data_direction direction, struct dma_attrs *attrs) { #if defined(CONFIG_PS3_DYNAMIC_DMA) BUG_ON("do"); return -EPERM; #else struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); + struct scatterlist *sg; int i; - for (i = 0; i < nents; i++, sg++) { - int result = ps3_dma_map(dev->d_region, - page_to_phys(sg->page) + sg->offset, sg->length, - &sg->dma_address, 0); + for_each_sg(sgl, sg, nents, i) { + int result = ps3_dma_map(dev->d_region, sg_phys(sg), + sg->length, &sg->dma_address, 0); if (result) { pr_debug("%s:%d: ps3_dma_map failed (%d)\n", @@ -652,14 +669,15 @@ static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sg, int nents, static int ps3_ioc0_map_sg(struct device *_dev, struct scatterlist *sg, int nents, - enum dma_data_direction direction) + enum dma_data_direction direction, + struct dma_attrs *attrs) { BUG(); return 0; } static void ps3_sb_unmap_sg(struct device *_dev, struct scatterlist *sg, - int nents, enum dma_data_direction direction) + int nents, enum dma_data_direction direction, struct dma_attrs *attrs) { #if defined(CONFIG_PS3_DYNAMIC_DMA) BUG_ON("do"); @@ -667,34 +685,42 @@ static void ps3_sb_unmap_sg(struct device *_dev, struct scatterlist *sg, } static void ps3_ioc0_unmap_sg(struct device *_dev, struct scatterlist *sg, - int nents, enum dma_data_direction direction) + int nents, enum dma_data_direction direction, + struct dma_attrs *attrs) { BUG(); } static int ps3_dma_supported(struct device *_dev, u64 mask) { - return mask >= DMA_32BIT_MASK; + return mask >= DMA_BIT_MASK(32); } -static struct dma_mapping_ops ps3_sb_dma_ops = { - .alloc_coherent = ps3_alloc_coherent, - .free_coherent = ps3_free_coherent, - .map_single = ps3_sb_map_single, - .unmap_single = ps3_unmap_single, +static u64 ps3_dma_get_required_mask(struct device *_dev) +{ + return DMA_BIT_MASK(32); +} + +static struct dma_map_ops ps3_sb_dma_ops = { + .alloc = ps3_alloc_coherent, + .free = ps3_free_coherent, .map_sg = ps3_sb_map_sg, .unmap_sg = ps3_sb_unmap_sg, - .dma_supported = ps3_dma_supported + .dma_supported = ps3_dma_supported, + .get_required_mask = ps3_dma_get_required_mask, + .map_page = ps3_sb_map_page, + .unmap_page = ps3_unmap_page, }; -static struct dma_mapping_ops ps3_ioc0_dma_ops = { - .alloc_coherent = ps3_alloc_coherent, - .free_coherent = ps3_free_coherent, - .map_single = ps3_ioc0_map_single, - .unmap_single = ps3_unmap_single, +static struct dma_map_ops ps3_ioc0_dma_ops = { + .alloc = ps3_alloc_coherent, + .free = ps3_free_coherent, .map_sg = ps3_ioc0_map_sg, .unmap_sg = ps3_ioc0_unmap_sg, - .dma_supported = ps3_dma_supported + .dma_supported = ps3_dma_supported, + .get_required_mask = ps3_dma_get_required_mask, + .map_page = ps3_ioc0_map_page, + .unmap_page = ps3_unmap_page, }; /** @@ -721,6 +747,7 @@ int ps3_system_bus_device_register(struct ps3_system_bus_device *dev) static unsigned int dev_ioc0_count; static unsigned int dev_sb_count; static unsigned int dev_vuart_count; + static unsigned int dev_lpm_count; if (!dev->core.parent) dev->core.parent = &ps3_system_bus; @@ -730,27 +757,27 @@ int ps3_system_bus_device_register(struct ps3_system_bus_device *dev) switch (dev->dev_type) { case PS3_DEVICE_TYPE_IOC0: dev->core.archdata.dma_ops = &ps3_ioc0_dma_ops; - snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), - "ioc0_%02x", ++dev_ioc0_count); + dev_set_name(&dev->core, "ioc0_%02x", ++dev_ioc0_count); break; case PS3_DEVICE_TYPE_SB: dev->core.archdata.dma_ops = &ps3_sb_dma_ops; - snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), - "sb_%02x", ++dev_sb_count); + dev_set_name(&dev->core, "sb_%02x", ++dev_sb_count); break; case PS3_DEVICE_TYPE_VUART: - snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), - "vuart_%02x", ++dev_vuart_count); + dev_set_name(&dev->core, "vuart_%02x", ++dev_vuart_count); + break; + case PS3_DEVICE_TYPE_LPM: + dev_set_name(&dev->core, "lpm_%02x", ++dev_lpm_count); break; default: BUG(); }; - dev->core.archdata.of_node = NULL; - dev->core.archdata.numa_node = 0; + dev->core.of_node = NULL; + set_dev_node(&dev->core, 0); - pr_debug("%s:%d add %s\n", __func__, __LINE__, dev->core.bus_id); + pr_debug("%s:%d add %s\n", __func__, __LINE__, dev_name(&dev->core)); result = device_register(&dev->core); return result; diff --git a/arch/powerpc/platforms/ps3/time.c b/arch/powerpc/platforms/ps3/time.c index 802a9ccacb5..ce73ce86561 100644 --- a/arch/powerpc/platforms/ps3/time.c +++ b/arch/powerpc/platforms/ps3/time.c @@ -19,7 +19,9 @@ */ #include <linux/kernel.h> +#include <linux/platform_device.h> +#include <asm/firmware.h> #include <asm/rtc.h> #include <asm/lv1call.h> #include <asm/ps3.h> @@ -50,12 +52,6 @@ static void __maybe_unused _dump_time(int time, const char *func, _dump_tm(&tm, func, line); } -/** - * rtc_shift - Difference in seconds between 1970 and the ps3 rtc value. - */ - -static s64 rtc_shift; - void __init ps3_calibrate_decr(void) { int result; @@ -66,8 +62,6 @@ void __init ps3_calibrate_decr(void) ppc_tb_freq = tmp; ppc_proc_freq = ppc_tb_freq * 40; - - rtc_shift = ps3_os_area_rtc_diff(); } static u64 read_rtc(void) @@ -82,23 +76,21 @@ static u64 read_rtc(void) return rtc_val; } -int ps3_set_rtc_time(struct rtc_time *tm) +unsigned long __init ps3_get_boot_time(void) { - u64 now = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); - - rtc_shift = now - read_rtc(); - return 0; + return read_rtc() + ps3_os_area_get_rtc_diff(); } -void ps3_get_rtc_time(struct rtc_time *tm) +static int __init ps3_rtc_init(void) { - to_tm(read_rtc() + rtc_shift, tm); - tm->tm_year -= 1900; - tm->tm_mon -= 1; -} + struct platform_device *pdev; -unsigned long __init ps3_get_boot_time(void) -{ - return read_rtc() + rtc_shift; + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return -ENODEV; + + pdev = platform_device_register_simple("rtc-ps3", -1, NULL, 0); + + return PTR_ERR_OR_ZERO(pdev); } + +module_init(ps3_rtc_init); |
