summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/ps3
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-16 17:58:08 -0700
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-16 17:58:08 -0700
commit489de30259e667d7bc47da9da44a0270b050cd97 (patch)
tree6807814f443fe2c5d041c3bc3fe3ca8d22a955ca /arch/powerpc/platforms/ps3
parent1f1c2881f673671539b25686df463518d69c4649 (diff)
parentbf22f6fe2d72b4d7e9035be8ceb340414cf490e3 (diff)
downloadlinux-489de30259e667d7bc47da9da44a0270b050cd97.tar.gz
linux-489de30259e667d7bc47da9da44a0270b050cd97.tar.bz2
linux-489de30259e667d7bc47da9da44a0270b050cd97.zip
Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
* 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc: (209 commits) [POWERPC] Create add_rtc() function to enable the RTC CMOS driver [POWERPC] Add H_ILLAN_ATTRIBUTES hcall number [POWERPC] xilinxfb: Parameterize xilinxfb platform device registration [POWERPC] Oprofile support for Power 5++ [POWERPC] Enable arbitary speed tty ioctls and split input/output speed [POWERPC] Make drivers/char/hvc_console.c:khvcd() static [POWERPC] Remove dead code for preventing pread() and pwrite() calls [POWERPC] Remove unnecessary #undef printk from prom.c [POWERPC] Fix typo in Ebony default DTS [POWERPC] Check for NULL ppc_md.init_IRQ() before calling [POWERPC] Remove extra return statement [POWERPC] pasemi: Don't auto-select CONFIG_EMBEDDED [POWERPC] pasemi: Rename platform [POWERPC] arch/powerpc/kernel/sysfs.c: Move NUMA exports [POWERPC] Add __read_mostly support for powerpc [POWERPC] Modify sched_clock() to make CONFIG_PRINTK_TIME more sane [POWERPC] Create a dummy zImage if no valid platform has been selected [POWERPC] PS3: Bootwrapper support. [POWERPC] powermac i2c: Use mutex [POWERPC] Schedule removal of arch/ppc ... Fixed up conflicts manually in: Documentation/feature-removal-schedule.txt arch/powerpc/kernel/pci_32.c arch/powerpc/kernel/pci_64.c include/asm-powerpc/pci.h and asked the powerpc people to double-check the result..
Diffstat (limited to 'arch/powerpc/platforms/ps3')
-rw-r--r--arch/powerpc/platforms/ps3/Kconfig26
-rw-r--r--arch/powerpc/platforms/ps3/Makefile1
-rw-r--r--arch/powerpc/platforms/ps3/device-init.c785
-rw-r--r--arch/powerpc/platforms/ps3/htab.c31
-rw-r--r--arch/powerpc/platforms/ps3/interrupt.c272
-rw-r--r--arch/powerpc/platforms/ps3/mm.c632
-rw-r--r--arch/powerpc/platforms/ps3/os-area.c4
-rw-r--r--arch/powerpc/platforms/ps3/platform.h43
-rw-r--r--arch/powerpc/platforms/ps3/repository.c586
-rw-r--r--arch/powerpc/platforms/ps3/setup.c105
-rw-r--r--arch/powerpc/platforms/ps3/smp.c18
-rw-r--r--arch/powerpc/platforms/ps3/spu.c13
-rw-r--r--arch/powerpc/platforms/ps3/system-bus.c562
-rw-r--r--arch/powerpc/platforms/ps3/time.c2
14 files changed, 2408 insertions, 672 deletions
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
index 40f0008af4d1..a05079b07696 100644
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -7,6 +7,7 @@ config PPC_PS3
select USB_OHCI_BIG_ENDIAN_MMIO
select USB_ARCH_HAS_EHCI
select USB_EHCI_BIG_ENDIAN_MMIO
+ select MEMORY_HOTPLUG
help
This option enables support for the Sony PS3 game console
and other platforms using the PS3 hypervisor.
@@ -73,18 +74,12 @@ config PS3_USE_LPAR_ADDR
config PS3_VUART
depends on PPC_PS3
- bool "PS3 Virtual UART support" if PS3_ADVANCED
- default y
- help
- Include support for the PS3 Virtual UART.
-
- This support is required for several system services
- including the System Manager and AV Settings. In
- general, all users will say Y.
+ tristate
config PS3_PS3AV
+ depends on PPC_PS3
tristate "PS3 AV settings driver" if PS3_ADVANCED
- depends on PS3_VUART
+ select PS3_VUART
default y
help
Include support for the PS3 AV Settings driver.
@@ -93,13 +88,18 @@ config PS3_PS3AV
general, all users will say Y or M.
config PS3_SYS_MANAGER
- bool "PS3 System Manager driver" if PS3_ADVANCED
- depends on PS3_VUART
- default y
+ depends on PPC_PS3
+ tristate "PS3 System Manager driver" if PS3_ADVANCED
+ select PS3_VUART
+ default m
help
Include support for the PS3 System Manager.
This support is required for system control. In
- general, all users will say Y.
+ general, all users will say Y or M.
+
+config PS3_STORAGE
+ depends on PPC_PS3
+ tristate
endmenu
diff --git a/arch/powerpc/platforms/ps3/Makefile b/arch/powerpc/platforms/ps3/Makefile
index a0048fcf0866..ac1bdf844eca 100644
--- a/arch/powerpc/platforms/ps3/Makefile
+++ b/arch/powerpc/platforms/ps3/Makefile
@@ -4,3 +4,4 @@ obj-y += system-bus.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
new file mode 100644
index 000000000000..825ebb2cbc2a
--- /dev/null
+++ b/arch/powerpc/platforms/ps3/device-init.c
@@ -0,0 +1,785 @@
+/*
+ * PS3 device registration routines.
+ *
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * Copyright 2007 Sony Corp.
+ *
+ * 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/init.h>
+
+#include <asm/firmware.h>
+#include <asm/lv1call.h>
+#include <asm/ps3stor.h>
+
+#include "platform.h"
+
+/**
+ * ps3_setup_gelic_device - Setup and register a gelic device instance.
+ *
+ * Allocates memory for a struct ps3_system_bus_device instance, initialises the
+ * structure members, and registers the device instance with the system bus.
+ */
+
+static int __init ps3_setup_gelic_device(
+ const struct ps3_repository_device *repo)
+{
+ int result;
+ struct layout {
+ struct ps3_system_bus_device dev;
+ struct ps3_dma_region d_region;
+ } *p;
+
+ pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+ BUG_ON(repo->bus_type != PS3_BUS_TYPE_SB);
+ BUG_ON(repo->dev_type != PS3_DEV_TYPE_SB_GELIC);
+
+ p = kzalloc(sizeof(struct layout), GFP_KERNEL);
+
+ if (!p) {
+ result = -ENOMEM;
+ goto fail_malloc;
+ }
+
+ p->dev.match_id = PS3_MATCH_ID_GELIC;
+ p->dev.dev_type = PS3_DEVICE_TYPE_SB;
+ p->dev.bus_id = repo->bus_id;
+ p->dev.dev_id = repo->dev_id;
+ p->dev.d_region = &p->d_region;
+
+ result = ps3_repository_find_interrupt(repo,
+ PS3_INTERRUPT_TYPE_EVENT_PORT, &p->dev.interrupt_id);
+
+ if (result) {
+ pr_debug("%s:%d ps3_repository_find_interrupt failed\n",
+ __func__, __LINE__);
+ goto fail_find_interrupt;
+ }
+
+ BUG_ON(p->dev.interrupt_id != 0);
+
+ result = ps3_dma_region_init(&p->dev, p->dev.d_region, PS3_DMA_64K,
+ PS3_DMA_OTHER, NULL, 0);
+
+ if (result) {
+ pr_debug("%s:%d ps3_dma_region_init failed\n",
+ __func__, __LINE__);
+ goto fail_dma_init;
+ }
+
+ result = ps3_system_bus_device_register(&p->dev);
+
+ 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 result;
+
+fail_device_register:
+fail_dma_init:
+fail_find_interrupt:
+ kfree(p);
+fail_malloc:
+ pr_debug(" <- %s:%d: fail.\n", __func__, __LINE__);
+ return result;
+}
+
+static int __init_refok ps3_setup_uhc_device(
+ const struct ps3_repository_device *repo, enum ps3_match_id match_id,
+ enum ps3_interrupt_type interrupt_type, enum ps3_reg_type reg_type)
+{
+ int result;
+ struct layout {
+ struct ps3_system_bus_device dev;
+ struct ps3_dma_region d_region;
+ struct ps3_mmio_region m_region;
+ } *p;
+ u64 bus_addr;
+ u64 len;
+
+ pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+ BUG_ON(repo->bus_type != PS3_BUS_TYPE_SB);
+ BUG_ON(repo->dev_type != PS3_DEV_TYPE_SB_USB);
+
+ p = kzalloc(sizeof(struct layout), GFP_KERNEL);
+
+ if (!p) {
+ result = -ENOMEM;
+ goto fail_malloc;
+ }
+
+ p->dev.match_id = match_id;
+ p->dev.dev_type = PS3_DEVICE_TYPE_SB;
+ p->dev.bus_id = repo->bus_id;
+ p->dev.dev_id = repo->dev_id;
+ p->dev.d_region = &p->d_region;
+ p->dev.m_region = &p->m_region;
+
+ result = ps3_repository_find_interrupt(repo,
+ interrupt_type, &p->dev.interrupt_id);
+
+ if (result) {
+ pr_debug("%s:%d ps3_repository_find_interrupt failed\n",
+ __func__, __LINE__);
+ goto fail_find_interrupt;
+ }
+
+ result = ps3_repository_find_reg(repo, reg_type,
+ &bus_addr, &len);
+
+ if (result) {
+ pr_debug("%s:%d ps3_repository_find_reg failed\n",
+ __func__, __LINE__);
+ goto fail_find_reg;
+ }
+
+ result = ps3_dma_region_init(&p->dev, p->dev.d_region, PS3_DMA_64K,
+ PS3_DMA_INTERNAL, NULL, 0);
+
+ if (result) {
+ pr_debug("%s:%d ps3_dma_region_init failed\n",
+ __func__, __LINE__);
+ goto fail_dma_init;
+ }
+
+ result = ps3_mmio_region_init(&p->dev, p->dev.m_region, bus_addr, len,
+ PS3_MMIO_4K);
+
+ if (result) {
+ pr_debug("%s:%d ps3_mmio_region_init failed\n",
+ __func__, __LINE__);
+ goto fail_mmio_init;
+ }
+
+ result = ps3_system_bus_device_register(&p->dev);
+
+ 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 result;
+
+fail_device_register:
+fail_mmio_init:
+fail_dma_init:
+fail_find_reg:
+fail_find_interrupt:
+ kfree(p);
+fail_malloc:
+ pr_debug(" <- %s:%d: fail.\n", __func__, __LINE__);
+ return result;
+}
+
+static int __init ps3_setup_ehci_device(
+ const struct ps3_repository_device *repo)
+{
+ return ps3_setup_uhc_device(repo, PS3_MATCH_ID_EHCI,
+ PS3_INTERRUPT_TYPE_SB_EHCI, PS3_REG_TYPE_SB_EHCI);
+}
+
+static int __init ps3_setup_ohci_device(
+ const struct ps3_repository_device *repo)
+{
+ return ps3_setup_uhc_device(repo, PS3_MATCH_ID_OHCI,
+ PS3_INTERRUPT_TYPE_SB_OHCI, PS3_REG_TYPE_SB_OHCI);
+}
+
+static int __init ps3_setup_vuart_device(enum ps3_match_id match_id,
+ unsigned int port_number)
+{
+ int result;
+ struct layout {
+ struct ps3_system_bus_device dev;
+ } *p;
+
+ pr_debug(" -> %s:%d: match_id %u, port %u\n", __func__, __LINE__,
+ match_id, port_number);
+
+ p = kzalloc(sizeof(struct layout), GFP_KERNEL);
+
+ if (!p)
+ return -ENOMEM;
+
+ p->dev.match_id = match_id;
+ p->dev.dev_type = PS3_DEVICE_TYPE_VUART;
+ p->dev.port_number = port_number;
+
+ result = ps3_system_bus_device_register(&p->dev);
+
+ 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);
+ }
+
+ if (result)
+ pr_debug("%s:%u: check_async_status: %s, status %lx\n",
+ __func__, __LINE__, ps3_result(result), status);
+
+ return result;
+}
+
+/**
+ * ps3_storage_wait_for_device - Wait for a storage device to become ready.
+ * @repo: The repository device to wait for.
+ *
+ * Uses the hypervisor's storage device notification mechanism to wait until
+ * a storage device is ready. The device notification mechanism uses a
+ * psuedo device (id = -1) to asynchronously notify the guest when storage
+ * devices become ready. The notification device has a block size of 512
+ * bytes.
+ */
+
+static int ps3_storage_wait_for_device(const struct ps3_repository_device *repo)
+{
+ int result;
+ const u64 notification_dev_id = (u64)-1LL;
+ const unsigned int timeout = HZ;
+ u64 lpar;
+ u64 tag;
+ struct {
+ u64 operation_code; /* must be zero */
+ u64 event_mask; /* 1 = device ready */
+ } *notify_cmd;
+ struct {
+ u64 event_type; /* notify_device_ready */
+ u64 bus_id;
+ u64 dev_id;
+ u64 dev_type;
+ u64 dev_port;
+ } *notify_event;
+ enum {
+ notify_device_ready = 1
+ };
+
+ pr_debug(" -> %s:%u: bus_id %u, dev_id %u, dev_type %u\n", __func__,
+ __LINE__, repo->bus_id, repo->dev_id, repo->dev_type);
+
+ notify_cmd = kzalloc(512, GFP_KERNEL);
+ notify_event = (void *)notify_cmd;
+ if (!notify_cmd)
+ return -ENOMEM;
+
+ lpar = ps3_mm_phys_to_lpar(__pa(notify_cmd));
+
+ result = lv1_open_device(repo->bus_id, notification_dev_id, 0);
+ if (result) {
+ printk(KERN_ERR "%s:%u: lv1_open_device %s\n", __func__,
+ __LINE__, ps3_result(result));
+ result = -ENODEV;
+ goto fail_free;
+ }
+
+ /* Setup and write the request for device notification. */
+
+ notify_cmd->operation_code = 0; /* must be zero */
+ notify_cmd->event_mask = 0x01; /* device ready */
+
+ result = lv1_storage_write(notification_dev_id, 0, 0, 1, 0, lpar,
+ &tag);
+ if (result) {
+ printk(KERN_ERR "%s:%u: write failed %s\n", __func__, __LINE__,
+ ps3_result(result));
+ result = -ENODEV;
+ goto fail_close;
+ }
+
+ /* Wait for the write completion */
+
+ result = ps3stor_wait_for_completion(notification_dev_id, tag,
+ timeout);
+ if (result) {
+ printk(KERN_ERR "%s:%u: write not completed %s\n", __func__,
+ __LINE__, ps3_result(result));
+ result = -ENODEV;
+ goto fail_close;
+ }
+
+ /* Loop here processing the requested notification events. */
+
+ result = -ENODEV;
+ while (1) {
+ memset(notify_event, 0, sizeof(*notify_event));
+
+ result = lv1_storage_read(notification_dev_id, 0, 0, 1, 0,
+ lpar, &tag);
+ if (result) {
+ printk(KERN_ERR "%s:%u: write failed %s\n", __func__,
+ __LINE__, ps3_result(result));
+ break;
+ }
+
+ result = ps3stor_wait_for_completion(notification_dev_id, tag,
+ timeout);
+ if (result) {
+ printk(KERN_ERR "%s:%u: read not completed %s\n",
+ __func__, __LINE__, ps3_result(result));
+ break;
+ }
+
+ if (notify_event->event_type != notify_device_ready ||
+ notify_event->bus_id != repo->bus_id) {
+ pr_debug("%s:%u: bad notify_event: event %lu, "
+ "dev_id %lu, dev_type %lu\n",
+ __func__, __LINE__, notify_event->event_type,
+ notify_event->dev_id, notify_event->dev_type);
+ break;
+ }
+
+ if (notify_event->dev_id == repo->dev_id &&
+ notify_event->dev_type == repo->dev_type) {
+ pr_debug("%s:%u: device ready: dev_id %u\n", __func__,
+ __LINE__, repo->dev_id);
+ result = 0;
+ break;
+ }
+
+ if (notify_event->dev_id == repo->dev_id &&
+ notify_event->dev_type == PS3_DEV_TYPE_NOACCESS) {
+ pr_debug("%s:%u: no access: dev_id %u\n", __func__,
+ __LINE__, repo->dev_id);
+ break;
+ }
+ }
+
+fail_close:
+ lv1_close_device(repo->bus_id, notification_dev_id);
+fail_free:
+ kfree(notify_cmd);
+ pr_debug(" <- %s:%u\n", __func__, __LINE__);
+ return result;
+}
+
+static int ps3_setup_storage_dev(const struct ps3_repository_device *repo,
+ enum ps3_match_id match_id)
+{
+ int result;
+ struct ps3_storage_device *p;
+ u64 port, blk_size, num_blocks;
+ unsigned int num_regions, i;
+
+ pr_debug(" -> %s:%u: match_id %u\n", __func__, __LINE__, match_id);
+
+ result = ps3_repository_read_stor_dev_info(repo->bus_index,
+ repo->dev_index, &port,
+ &blk_size, &num_blocks,
+ &num_regions);
+ if (result) {
+ printk(KERN_ERR "%s:%u: _read_stor_dev_info failed %d\n",
+ __func__, __LINE__, result);
+ return -ENODEV;
+ }
+
+ pr_debug("%s:%u: index %u:%u: port %lu blk_size %lu num_blocks %lu "
+ "num_regions %u\n", __func__, __LINE__, repo->bus_index,
+ repo->dev_index, port, blk_size, num_blocks, num_regions);
+
+ p = kzalloc(sizeof(struct ps3_storage_device) +
+ num_regions * sizeof(struct ps3_storage_region),
+ GFP_KERNEL);
+ if (!p) {
+ result = -ENOMEM;
+ goto fail_malloc;
+ }
+
+ p->sbd.match_id = match_id;
+ p->sbd.dev_type = PS3_DEVICE_TYPE_SB;
+ p->sbd.bus_id = repo->bus_id;
+ p->sbd.dev_id = repo->dev_id;
+ p->sbd.d_region = &p->dma_region;
+ p->blk_size = blk_size;
+ p->num_regions = num_regions;
+
+ result = ps3_repository_find_interrupt(repo,
+ PS3_INTERRUPT_TYPE_EVENT_PORT,
+ &p->sbd.interrupt_id);
+ if (result) {
+ printk(KERN_ERR "%s:%u: find_interrupt failed %d\n", __func__,
+ __LINE__, result);
+ result = -ENODEV;
+ goto fail_find_interrupt;
+ }
+
+ /* FIXME: Arrange to only do this on a 'cold' boot */
+
+ result = ps3_storage_wait_for_device(repo);
+ if (result) {
+ printk(KERN_ERR "%s:%u: storage_notification failed %d\n",
+ __func__, __LINE__, result);
+ result = -ENODEV;
+ goto fail_probe_notification;
+ }
+
+ for (i = 0; i < num_regions; i++) {
+ unsigned int id;
+ u64 start, size;
+
+ result = ps3_repository_read_stor_dev_region(repo->bus_index,
+ repo->dev_index,
+ i, &id, &start,
+ &size);
+ if (result) {
+ printk(KERN_ERR
+ "%s:%u: read_stor_dev_region failed %d\n",
+ __func__, __LINE__, result);
+ result = -ENODEV;
+ goto fail_read_region;
+ }
+ pr_debug("%s:%u: region %u: id %u start %lu size %lu\n",
+ __func__, __LINE__, i, id, start, size);
+
+ p->regions[i].id = id;
+ p->regions[i].start = start;
+ p->regions[i].size = size;
+ }
+
+ result = ps3_system_bus_device_register(&p->sbd);
+ if (result) {
+ pr_debug("%s:%u ps3_system_bus_device_register failed\n",
+ __func__, __LINE__);
+ goto fail_device_register;
+ }
+
+ pr_debug(" <- %s:%u\n", __func__, __LINE__);
+ return 0;
+
+fail_device_register:
+fail_read_region:
+fail_probe_notification:
+fail_find_interrupt:
+ kfree(p);
+fail_malloc:
+ pr_debug(" <- %s:%u: fail.\n", __func__, __LINE__);
+ return result;
+}
+
+static int __init ps3_register_vuart_devices(void)
+{
+ int result;
+ unsigned int port_number;
+
+ pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+ result = ps3_repository_read_vuart_av_port(&port_number);
+ if (result)
+ port_number = 0; /* av default */
+
+ result = ps3_setup_vuart_device(PS3_MATCH_ID_AV_SETTINGS, port_number);
+ WARN_ON(result);
+
+ result = ps3_repository_read_vuart_sysmgr_port(&port_number);
+ if (result)
+ port_number = 2; /* sysmgr default */
+
+ result = ps3_setup_vuart_device(PS3_MATCH_ID_SYSTEM_MANAGER,
+ port_number);
+ WARN_ON(result);
+
+ pr_debug(" <- %s:%d\n", __func__, __LINE__);
+ return result;
+}
+
+static int __init ps3_register_sound_devices(void)
+{
+ int result;
+ struct layout {
+ struct ps3_system_bus_device dev;
+ struct ps3_dma_region d_region;
+ struct ps3_mmio_region m_region;
+ } *p;
+
+ pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ p->dev.match_id = PS3_MATCH_ID_SOUND;
+ p->dev.dev_type = PS3_DEVICE_TYPE_IOC0;
+ p->dev.d_region = &p->d_region;
+ p->dev.m_region = &p->m_region;
+
+ result = ps3_system_bus_device_register(&p->dev);
+
+ 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 __init ps3_register_graphics_devices(void)
+{
+ int result;
+ struct layout {
+ struct ps3_system_bus_device dev;
+ } *p;
+
+ pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+ p = kzalloc(sizeof(struct layout), GFP_KERNEL);
+
+ if (!p)
+ return -ENOMEM;
+
+ p->dev.match_id = PS3_MATCH_ID_GRAPHICS;
+ p->dev.dev_type = PS3_DEVICE_TYPE_IOC0;
+
+ result = ps3_system_bus_device_register(&p->dev);
+
+ if (result)
+ pr_debug("%s:%d ps3_system_bus_device_register failed\n",
+ __func__, __LINE__);
+
+ pr_debug(" <- %s:%d\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)
+{
+ 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;
+ case PS3_DEV_TYPE_STOR_DISK:
+ result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_DISK);
+
+ /* Some devices are not accessable from the Other OS lpar. */
+ if (result == -ENODEV) {
+ result = 0;
+ pr_debug("%s:%u: not accessable\n", __func__,
+ __LINE__);
+ }
+
+ if (result)
+ pr_debug("%s:%u ps3_setup_storage_dev failed\n",
+ __func__, __LINE__);
+ break;
+
+ case PS3_DEV_TYPE_STOR_ROM:
+ result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_ROM);
+ if (result)
+ pr_debug("%s:%u ps3_setup_storage_dev failed\n",
+ __func__, __LINE__);
+ break;
+
+ case PS3_DEV_TYPE_STOR_FLASH:
+ result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_FLASH);
+ if (result)
+ pr_debug("%s:%u ps3_setup_storage_dev failed\n",
+ __func__, __LINE__);
+ break;
+
+ default:
+ result = 0;
+ pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__,
+ repo->dev_type);
+ }
+
+ return result;
+}
+
+/**
+ * ps3_probe_thread - Background repository probing at system startup.
+ *
+ * This implementation only supports background probing on a single bus.
+ */
+
+static int ps3_probe_thread(void *data)
+{
+ struct ps3_repository_device *repo = data;
+ int result;
+ unsigned int ms = 250;
+
+ pr_debug(" -> %s:%u: kthread started\n", __func__, __LINE__);
+
+ 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)
+ break;
+
+ msleep_interruptible(ms);
+
+ /* An exponential backoff. */
+ ms <<= 1;
+
+ } while (!kthread_should_stop());
+
+ pr_debug(" <- %s:%u: kthread finished\n", __func__, __LINE__);
+
+ return 0;
+}
+
+/**
+ * ps3_start_probe_thread - Starts the background probe thread.
+ *
+ */
+
+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 */
+
+ pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+ memset(&repo, 0, sizeof(repo));
+
+ repo.bus_type = bus_type;
+
+ result = ps3_repository_find_bus(repo.bus_type, 0, &repo.bus_index);
+
+ if (result) {
+ printk(KERN_ERR "%s: Cannot find bus (%d)\n", __func__, result);
+ return -ENODEV;
+ }
+
+ result = ps3_repository_read_bus_id(repo.bus_index, &repo.bus_id);
+
+ if (result) {
+ printk(KERN_ERR "%s: read_bus_id failed %d\n", __func__,
+ result);
+ return -ENODEV;
+ }
+
+ task = kthread_run(ps3_probe_thread, &repo, "ps3-probe-%u", bus_type);
+
+ if (IS_ERR(task)) {
+ result = PTR_ERR(task);
+ printk(KERN_ERR "%s: kthread_run failed %d\n", __func__,
+ result);
+ return result;
+ }
+
+ pr_debug(" <- %s:%d\n", __func__, __LINE__);
+ return 0;
+}
+
+/**
+ * ps3_register_devices - Probe the system and register devices found.
+ *
+ * A device_initcall() routine.
+ */
+
+static int __init ps3_register_devices(void)
+{
+ int result;
+
+ if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
+ return -ENODEV;
+
+ pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+ /* ps3_repository_dump_bus_info(); */
+
+ result = ps3_start_probe_thread(PS3_BUS_TYPE_STORAGE);
+
+ ps3_register_vuart_devices();
+
+ ps3_register_graphics_devices();
+
+ ps3_repository_find_devices(PS3_BUS_TYPE_SB,
+ ps3_register_repository_device);
+
+ ps3_register_sound_devices();
+
+ pr_debug(" <- %s:%d\n", __func__, __LINE__);
+ return 0;
+}
+
+device_initcall(ps3_register_devices);
diff --git a/arch/powerpc/platforms/ps3/htab.c b/arch/powerpc/platforms/ps3/htab.c
index a1409e450c70..5d2e176a1b18 100644
--- a/arch/powerpc/platforms/ps3/htab.c
+++ b/arch/powerpc/platforms/ps3/htab.c
@@ -29,12 +29,12 @@
#include "platform.h"
#if defined(DEBUG)
-#define DBG(fmt...) udbg_printf(fmt)
+#define DBG udbg_printf
#else
-#define DBG(fmt...) do{if(0)printk(fmt);}while(0)
+#define DBG pr_debug
#endif
-static hpte_t *htab;
+static struct hash_pte *htab;
static unsigned long htab_addr;
static unsigned char *bolttab;
static unsigned char *inusetab;
@@ -44,8 +44,8 @@ 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, hpte_t lhpte, int psize,
- unsigned long slot, const char* func, int line)
+ 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,
@@ -63,7 +63,7 @@ static long ps3_hpte_insert(unsigned long hpte_group, unsigned long va,
unsigned long pa, unsigned long rflags, unsigned long vflags, int psize)
{
unsigned long slot;
- hpte_t lhpte;
+ struct hash_pte lhpte;
int secondary = 0;
unsigned long result;
unsigned long bitmap;
@@ -234,10 +234,17 @@ static void ps3_hpte_invalidate(unsigned long slot, unsigned long va,
static void ps3_hpte_clear(void)
{
- /* Make sure to clean up the frame buffer device first */
- ps3fb_cleanup();
+ int result;
- lv1_unmap_htab(htab_addr);
+ DBG(" -> %s:%d\n", __func__, __LINE__);
+
+ result = lv1_unmap_htab(htab_addr);
+ BUG_ON(result);
+
+ ps3_mm_shutdown();
+ ps3_mm_vas_destroy();
+
+ DBG(" <- %s:%d\n", __func__, __LINE__);
}
void __init ps3_hpte_init(unsigned long htab_size)
@@ -255,7 +262,7 @@ void __init ps3_hpte_init(unsigned long htab_size)
ppc64_pft_size = __ilog2(htab_size);
- bitmap_size = htab_size / sizeof(hpte_t) / 8;
+ bitmap_size = htab_size / sizeof(struct hash_pte) / 8;
bolttab = __va(lmb_alloc(bitmap_size, 1));
inusetab = __va(lmb_alloc(bitmap_size, 1));
@@ -273,8 +280,8 @@ void __init ps3_map_htab(void)
result = lv1_map_htab(0, &htab_addr);
- htab = (hpte_t *)__ioremap(htab_addr, htab_size,
- pgprot_val(PAGE_READONLY_X));
+ 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 ec9030dbb5f1..67e32ec9b37e 100644
--- a/arch/powerpc/platforms/ps3/interrupt.c
+++ b/arch/powerpc/platforms/ps3/interrupt.c
@@ -30,9 +30,9 @@
#include "platform.h"
#if defined(DEBUG)
-#define DBG(fmt...) udbg_printf(fmt)
+#define DBG udbg_printf
#else
-#define DBG(fmt...) do{if(0)printk(fmt);}while(0)
+#define DBG pr_debug
#endif
/**
@@ -78,19 +78,85 @@ struct ps3_bmp {
/**
* struct ps3_private - a per cpu data structure
* @bmp: ps3_bmp structure
- * @node: HV logical_ppe_id
- * @cpu: HV thread_id
+ * @ppe_id: HV logical_ppe_id
+ * @thread_id: HV thread_id
*/
struct ps3_private {
struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN)));
- u64 node;
- unsigned int cpu;
+ u64 ppe_id;
+ u64 thread_id;
};
static DEFINE_PER_CPU(struct ps3_private, ps3_private);
/**
+ * ps3_chip_mask - Set an interrupt mask bit in ps3_bmp.
+ * @virq: The assigned Linux virq.
+ *
+ * Sets ps3_bmp.mask and calls lv1_did_update_interrupt_mask().
+ */
+
+static void ps3_chip_mask(unsigned int virq)
+{
+ struct ps3_private *pd = get_irq_chip_data(virq);
+ unsigned long flags;
+
+ pr_debug("%s:%d: thread_id %lu, virq %d\n", __func__, __LINE__,
+ pd->thread_id, virq);
+
+ local_irq_save(flags);
+ clear_bit(63 - virq, &pd->bmp.mask);
+ lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id);
+ local_irq_restore(flags);
+}
+
+/**
+ * ps3_chip_unmask - Clear an interrupt mask bit in ps3_bmp.
+ * @virq: The assigned Linux virq.
+ *
+ * Clears ps3_bmp.mask and calls lv1_did_update_interrupt_mask().
+ */
+
+static void ps3_chip_unmask(unsigned int virq)
+{
+ struct ps3_private *pd = get_irq_chip_data(virq);
+ unsigned long flags;
+
+ pr_debug("%s:%d: thread_id %lu, virq %d\n", __func__, __LINE__,
+ pd->thread_id, virq);
+
+ local_irq_save(flags);
+ set_bit(63 - virq, &pd->bmp.mask);
+ lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id);
+ local_irq_restore(flags);
+}
+
+/**
+ * ps3_chip_eoi - HV end-of-interrupt.
+ * @virq: The assigned Linux virq.
+ *
+ * Calls lv1_end_of_interrupt_ext().
+ */
+
+static void ps3_chip_eoi(unsigned int virq)
+{
+ const struct ps3_private *pd = get_irq_chip_data(virq);
+ lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, virq);
+}
+
+/**
+ * ps3_irq_chip - Represents the ps3_bmp as a Linux struct irq_chip.
+ */
+
+static struct irq_chip ps3_irq_chip = {
+ .typename = "ps3",
+ .mask = ps3_chip_mask,
+ .unmask = ps3_chip_unmask,
+ .eoi = ps3_chip_eoi,
+};
+
+/**
* ps3_virq_setup - virq related setup.
* @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
* serviced on.
@@ -134,6 +200,8 @@ int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
goto fail_set;
}
+ ps3_chip_mask(*virq);
+
return result;
fail_set:
@@ -153,8 +221,8 @@ int ps3_virq_destroy(unsigned int virq)
{
const struct ps3_private *pd = get_irq_chip_data(virq);
- pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__,
- pd->node, pd->cpu, virq);
+ pr_debug("%s:%d: ppe_id %lu, thread_id %lu, virq %u\n", __func__,
+ __LINE__, pd->ppe_id, pd->thread_id, virq);
set_irq_chip_data(virq, NULL);
irq_dispose_mapping(virq);
@@ -190,7 +258,8 @@ int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
/* Binds outlet to cpu + virq. */
- result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0);
+ result = lv1_connect_irq_plug_ext(pd->ppe_id, pd->thread_id, *virq,
+ outlet, 0);
if (result) {
pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n",
@@ -222,10 +291,12 @@ int ps3_irq_plug_destroy(unsigned int virq)
int result;
const struct ps3_private *pd = get_irq_chip_data(virq);
- pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__,
- pd->node, pd->cpu, virq);
+ pr_debug("%s:%d: ppe_id %lu, thread_id %lu, virq %u\n", __func__,
+ __LINE__, pd->ppe_id, pd->thread_id, virq);
+
+ ps3_chip_mask(virq);
- result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, 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",
@@ -282,7 +353,9 @@ int ps3_event_receive_port_destroy(unsigned int virq)
{
int result;
- pr_debug(" -> %s:%d virq: %u\n", __func__, __LINE__, virq);
+ pr_debug(" -> %s:%d virq %u\n", __func__, __LINE__, virq);
+
+ ps3_chip_mask(virq);
result = lv1_destruct_event_receive_port(virq_to_hw(virq));
@@ -290,17 +363,14 @@ int ps3_event_receive_port_destroy(unsigned int virq)
pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n",
__func__, __LINE__, ps3_result(result));
- /* lv1_destruct_event_receive_port() destroys the IRQ plug,
- * so don't call ps3_irq_plug_destroy() here.
+ /*
+ * Don't call ps3_virq_destroy() here since ps3_smp_cleanup_cpu()
+ * calls from interrupt context (smp_call_function) when kexecing.
*/
- result = ps3_virq_destroy(virq);
- BUG_ON(result);
-
pr_debug(" <- %s:%d\n", __func__, __LINE__);
return result;
}
-EXPORT_SYMBOL_GPL(ps3_event_receive_port_destroy);
int ps3_send_event_locally(unsigned int virq)
{
@@ -311,17 +381,15 @@ int ps3_send_event_locally(unsigned int virq)
* ps3_sb_event_receive_port_setup - Setup a system bus event receive port.
* @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
* serviced on.
- * @did: The HV device identifier read from the system repository.
- * @interrupt_id: The device interrupt id read from the system repository.
+ * @dev: The system bus device instance.
* @virq: The assigned Linux virq.
*
* An event irq represents a virtual device interrupt. The interrupt_id
* coresponds to the software interrupt number.
*/
-int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu,
- const struct ps3_device_id *did, unsigned int interrupt_id,
- unsigned int *virq)
+int ps3_sb_event_receive_port_setup(struct ps3_system_bus_device *dev,
+ enum ps3_cpu_binding cpu, unsigned int *virq)
{
/* this should go in system-bus.c */
@@ -332,8 +400,8 @@ int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu,
if (result)
return result;
- result = lv1_connect_interrupt_event_receive_port(did->bus_id,
- did->dev_id, virq_to_hw(*virq), interrupt_id);
+ result = lv1_connect_interrupt_event_receive_port(dev->bus_id,
+ dev->dev_id, virq_to_hw(*virq), dev->interrupt_id);
if (result) {
pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port"
@@ -345,24 +413,24 @@ int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu,
}
pr_debug("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
- interrupt_id, *virq);
+ dev->interrupt_id, *virq);
return 0;
}
EXPORT_SYMBOL(ps3_sb_event_receive_port_setup);
-int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did,
- unsigned int interrupt_id, unsigned int virq)
+int ps3_sb_event_receive_port_destroy(struct ps3_system_bus_device *dev,
+ unsigned int virq)
{
/* this should go in system-bus.c */
int result;
pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
- interrupt_id, virq);
+ dev->interrupt_id, virq);
- result = lv1_disconnect_interrupt_event_receive_port(did->bus_id,
- did->dev_id, virq_to_hw(virq), interrupt_id);
+ 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"
@@ -372,6 +440,14 @@ int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did,
result = ps3_event_receive_port_destroy(virq);
BUG_ON(result);
+ /*
+ * ps3_event_receive_port_destroy() destroys the IRQ plug,
+ * so don't call ps3_irq_plug_destroy() here.
+ */
+
+ result = ps3_virq_destroy(virq);
+ BUG_ON(result);
+
pr_debug(" <- %s:%d\n", __func__, __LINE__);
return result;
}
@@ -412,16 +488,24 @@ EXPORT_SYMBOL_GPL(ps3_io_irq_setup);
int ps3_io_irq_destroy(unsigned int virq)
{
int result;
+ unsigned long outlet = virq_to_hw(virq);
- result = lv1_destruct_io_irq_outlet(virq_to_hw(virq));
+ ps3_chip_mask(virq);
- if (result)
- pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
- __func__, __LINE__, ps3_result(result));
+ /*
+ * lv1_destruct_io_irq_outlet() will destroy the IRQ plug,
+ * so call ps3_irq_plug_destroy() first.
+ */
result = ps3_irq_plug_destroy(virq);
BUG_ON(result);
+ result = lv1_destruct_io_irq_outlet(outlet);
+
+ if (result)
+ pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+
return result;
}
EXPORT_SYMBOL_GPL(ps3_io_irq_destroy);
@@ -461,11 +545,13 @@ int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp,
return result;
}
+EXPORT_SYMBOL_GPL(ps3_vuart_irq_setup);
int ps3_vuart_irq_destroy(unsigned int virq)
{
int result;
+ ps3_chip_mask(virq);
result = lv1_deconfigure_virtual_uart_irq();
if (result) {
@@ -479,6 +565,7 @@ int ps3_vuart_irq_destroy(unsigned int virq)
return result;
}
+EXPORT_SYMBOL_GPL(ps3_vuart_irq_destroy);
/**
* ps3_spe_irq_setup - Setup an spe virq.
@@ -514,9 +601,14 @@ int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id,
int ps3_spe_irq_destroy(unsigned int virq)
{
- int result = ps3_irq_plug_destroy(virq);
+ int result;
+
+ ps3_chip_mask(virq);
+
+ result = ps3_irq_plug_destroy(virq);
BUG_ON(result);
- return 0;
+
+ return result;
}
@@ -533,7 +625,7 @@ static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu,
*p & 0xffff);
}
-static void __attribute__ ((unused)) _dump_256_bmp(const char *header,
+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",
@@ -546,86 +638,25 @@ static void _dump_bmp(struct ps3_private* pd, const char* func, int line)
unsigned long flags;
spin_lock_irqsave(&pd->bmp.lock, flags);
- _dump_64_bmp("stat", &pd->bmp.status, pd->cpu, func, line);
- _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line);
+ _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);
}
#define dump_mask(_x) _dump_mask(_x, __func__, __LINE__)
-static void __attribute__ ((unused)) _dump_mask(struct ps3_private* pd,
+static void __maybe_unused _dump_mask(struct ps3_private *pd,
const char* func, int line)
{
unsigned long flags;
spin_lock_irqsave(&pd->bmp.lock, flags);
- _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line);
+ _dump_64_bmp("mask", &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_chip_mask(unsigned int virq)
-{
- struct ps3_private *pd = get_irq_chip_data(virq);
- u64 bit = 0x8000000000000000UL >> virq;
- u64 *p = &pd->bmp.mask;
- u64 old;
- unsigned long flags;
-
- pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
-
- local_irq_save(flags);
- asm volatile(
- "1: ldarx %0,0,%3\n"
- "andc %0,%0,%2\n"
- "stdcx. %0,0,%3\n"
- "bne- 1b"
- : "=&r" (old), "+m" (*p)
- : "r" (bit), "r" (p)
- : "cc" );
-
- lv1_did_update_interrupt_mask(pd->node, pd->cpu);
- local_irq_restore(flags);
-}
-
-static void ps3_chip_unmask(unsigned int virq)
-{
- struct ps3_private *pd = get_irq_chip_data(virq);
- u64 bit = 0x8000000000000000UL >> virq;
- u64 *p = &pd->bmp.mask;
- u64 old;
- unsigned long flags;
-
- pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
-
- local_irq_save(flags);
- asm volatile(
- "1: ldarx %0,0,%3\n"
- "or %0,%0,%2\n"
- "stdcx. %0,0,%3\n"
- "bne- 1b"
- : "=&r" (old), "+m" (*p)
- : "r" (bit), "r" (p)
- : "cc" );
-
- lv1_did_update_interrupt_mask(pd->node, pd->cpu);
- local_irq_restore(flags);
-}
-
-static void ps3_chip_eoi(unsigned int virq)
-{
- const struct ps3_private *pd = get_irq_chip_data(virq);
- lv1_end_of_interrupt_ext(pd->node, pd->cpu, virq);
-}
-
-static struct irq_chip irq_chip = {
- .typename = "ps3",
- .mask = ps3_chip_mask,
- .unmask = ps3_chip_unmask,
- .eoi = ps3_chip_eoi,
-};
-
static void ps3_host_unmap(struct irq_host *h, unsigned int virq)
{
set_irq_chip_data(virq, NULL);
@@ -637,7 +668,7 @@ static int ps3_host_map(struct irq_host *h, unsigned int virq,
pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq,
virq);
- set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq);
+ set_irq_chip_and_handler(virq, &ps3_irq_chip, handle_fasteoi_irq);
return 0;
}
@@ -657,7 +688,7 @@ void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq)
cpu, virq, pd->bmp.ipi_debug_brk_mask);
}
-unsigned int ps3_get_irq(void)
+static unsigned int ps3_get_irq(void)
{
struct ps3_private *pd = &__get_cpu_var(ps3_private);
u64 x = (pd->bmp.status & pd->bmp.mask);
@@ -672,8 +703,8 @@ unsigned int ps3_get_irq(void)
plug &= 0x3f;
if (unlikely(plug) == NO_IRQ) {
- pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__,
- pd->cpu);
+ pr_debug("%s:%d: no plug found: thread_id %lu\n", __func__,
+ __LINE__, pd->thread_id);
dump_bmp(&per_cpu(ps3_private, 0));
dump_bmp(&per_cpu(ps3_private, 1));
return NO_IRQ;
@@ -703,16 +734,16 @@ void __init ps3_init_IRQ(void)
for_each_possible_cpu(cpu) {
struct ps3_private *pd = &per_cpu(ps3_private, cpu);
- lv1_get_logical_ppe_id(&pd->node);
- pd->cpu = get_hard_smp_processor_id(cpu);
+ lv1_get_logical_ppe_id(&pd->ppe_id);
+ pd->thread_id = get_hard_smp_processor_id(cpu);
spin_lock_init(&pd->bmp.lock);
- pr_debug("%s:%d: node %lu, cpu %d, bmp %lxh\n", __func__,
- __LINE__, pd->node, pd->cpu,
+ pr_debug("%s:%d: ppe_id %lu, thread_id %lu, bmp %lxh\n",
+ __func__, __LINE__, pd->ppe_id, pd->thread_id,
ps3_mm_phys_to_lpar(__pa(&pd->bmp)));
- result = lv1_configure_irq_state_bitmap(pd->node, pd->cpu,
- ps3_mm_phys_to_lpar(__pa(&pd->bmp)));
+ result = lv1_configure_irq_state_bitmap(pd->ppe_id,
+ pd->thread_id, ps3_mm_phys_to_lpar(__pa(&pd->bmp)));
if (result)
pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:"
@@ -722,3 +753,16 @@ void __init ps3_init_IRQ(void)
ppc_md.get_irq = ps3_get_irq;
}
+
+void ps3_shutdown_IRQ(int cpu)
+{
+ int result;
+ u64 ppe_id;
+ u64 thread_id = get_hard_smp_processor_id(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__,
+ __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 f8a3e206c584..7bb3e1620974 100644
--- a/arch/powerpc/platforms/ps3/mm.c
+++ b/arch/powerpc/platforms/ps3/mm.c
@@ -30,9 +30,9 @@
#include "platform.h"
#if defined(DEBUG)
-#define DBG(fmt...) udbg_printf(fmt)
+#define DBG udbg_printf
#else
-#define DBG(fmt...) do{if(0)printk(fmt);}while(0)
+#define DBG pr_debug
#endif
enum {
@@ -115,7 +115,8 @@ struct map {
};
#define debug_dump_map(x) _debug_dump_map(x, __func__, __LINE__)
-static void _debug_dump_map(const struct map* m, const char* func, int line)
+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);
@@ -212,9 +213,15 @@ fail:
void ps3_mm_vas_destroy(void)
{
+ int result;
+
+ DBG("%s:%d: map.vas_id = %lu\n", __func__, __LINE__, map.vas_id);
+
if (map.vas_id) {
- lv1_select_virtual_address_space(0);
- lv1_destruct_virtual_address_space(map.vas_id);
+ result = lv1_select_virtual_address_space(0);
+ BUG_ON(result);
+ result = lv1_destruct_virtual_address_space(map.vas_id);
+ BUG_ON(result);
map.vas_id = 0;
}
}
@@ -232,7 +239,7 @@ void ps3_mm_vas_destroy(void)
* @size is rounded down to a multiple of the vas large page size.
*/
-int ps3_mm_region_create(struct mem_region *r, unsigned long size)
+static int ps3_mm_region_create(struct mem_region *r, unsigned long size)
{
int result;
unsigned long muid;
@@ -273,10 +280,14 @@ zero_region:
* @r: pointer to struct mem_region
*/
-void ps3_mm_region_destroy(struct mem_region *r)
+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->base) {
- lv1_release_memory(r->base);
+ result = lv1_release_memory(r->base);
+ BUG_ON(result);
r->size = r->base = r->offset = 0;
map.total = map.rm.size;
}
@@ -329,31 +340,34 @@ core_initcall(ps3_mm_add_memory);
/*============================================================================*/
/**
- * dma_lpar_to_bus - Translate an lpar address to ioc mapped bus address.
+ * dma_sb_lpar_to_bus - Translate an lpar address to ioc mapped bus address.
* @r: pointer to dma region structure
* @lpar_addr: HV lpar address
*/
-static unsigned long dma_lpar_to_bus(struct ps3_dma_region *r,
+static unsigned long dma_sb_lpar_to_bus(struct ps3_dma_region *r,
unsigned long lpar_addr)
{
- BUG_ON(lpar_addr >= map.r1.base + map.r1.size);
- return r->bus_addr + (lpar_addr <= map.rm.size ? lpar_addr
- : lpar_addr - map.r1.offset);
+ if (lpar_addr >= map.rm.size)
+ lpar_addr -= map.r1.offset;
+ BUG_ON(lpar_addr < r->offset);
+ BUG_ON(lpar_addr >= r->offset + r->len);
+ return r->bus_addr + lpar_addr - r->offset;
}
#define dma_dump_region(_a) _dma_dump_region(_a, __func__, __LINE__)
-static void _dma_dump_region(const struct ps3_dma_region *r, const char* func,
- int line)
+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->did.bus_id,
- r->did.dev_id);
+ DBG("%s:%d: dev %u:%u\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);
DBG("%s:%d: len %lxh\n", func, line, r->len);
+ DBG("%s:%d: offset %lxh\n", func, line, r->offset);
}
-/**
+ /**
* dma_chunk - A chunk of dma pages mapped by the io controller.
* @region - The dma region that owns this chunk.
* @lpar_addr: Starting lpar address of the area to map.
@@ -381,10 +395,11 @@ 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,
- c->region->did.bus_id, c->region->did.dev_id);
+ 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);
DBG("%s:%d: r.len %lxh\n", func, line, c->region->len);
+ DBG("%s:%d: r.offset %lxh\n", func, line, c->region->offset);
DBG("%s:%d: c.lpar_addr %lxh\n", func, line, c->lpar_addr);
DBG("%s:%d: c.bus_addr %lxh\n", func, line, c->bus_addr);
DBG("%s:%d: c.len %lxh\n", func, line, c->len);
@@ -395,39 +410,68 @@ static struct dma_chunk * dma_find_chunk(struct ps3_dma_region *r,
{
struct dma_chunk *c;
unsigned long aligned_bus = _ALIGN_DOWN(bus_addr, 1 << r->page_size);
- unsigned long aligned_len = _ALIGN_UP(len, 1 << r->page_size);
+ unsigned long aligned_len = _ALIGN_UP(len+bus_addr-aligned_bus,
+ 1 << r->page_size);
list_for_each_entry(c, &r->chunk_list.head, link) {
/* intersection */
- if (aligned_bus >= c->bus_addr
- && aligned_bus < c->bus_addr + c->len
- && aligned_bus + aligned_len <= c->bus_addr + c->len) {
+ if (aligned_bus >= c->bus_addr &&
+ aligned_bus + aligned_len <= c->bus_addr + c->len)
return c;
- }
+
/* below */
- if (aligned_bus + aligned_len <= c->bus_addr) {
+ if (aligned_bus + aligned_len <= c->bus_addr)
continue;
- }
+
/* above */
- if (aligned_bus >= c->bus_addr + c->len) {
+ if (aligned_bus >= c->bus_addr + c->len)
continue;
- }
/* we don't handle the multi-chunk case for now */
-
dma_dump_chunk(c);
BUG();
}
return NULL;
}
-static int dma_free_chunk(struct dma_chunk *c)
+static struct dma_chunk *dma_find_chunk_lpar(struct ps3_dma_region *r,
+ unsigned long lpar_addr, unsigned long len)
+{
+ struct dma_chunk *c;
+ unsigned long aligned_lpar = _ALIGN_DOWN(lpar_addr, 1 << r->page_size);
+ unsigned long aligned_len = _ALIGN_UP(len + lpar_addr - aligned_lpar,
+ 1 << r->page_size);
+
+ list_for_each_entry(c, &r->chunk_list.head, link) {
+ /* intersection */
+ if (c->lpar_addr <= aligned_lpar &&
+ aligned_lpar < c->lpar_addr + c->len) {
+ if (aligned_lpar + aligned_len <= c->lpar_addr + c->len)
+ return c;
+ else {
+ dma_dump_chunk(c);
+ BUG();
+ }
+ }
+ /* below */
+ if (aligned_lpar + aligned_len <= c->lpar_addr) {
+ continue;
+ }
+ /* above */
+ if (c->lpar_addr + c->len <= aligned_lpar) {
+ continue;
+ }
+ }
+ return NULL;
+}
+
+static int dma_sb_free_chunk(struct dma_chunk *c)
{
int result = 0;
if (c->bus_addr) {
- result = lv1_unmap_device_dma_region(c->region->did.bus_id,
- c->region->did.dev_id, c->bus_addr, c->len);
+ result = lv1_unmap_device_dma_region(c->region->dev->bus_id,
+ c->region->dev->dev_id, c->bus_addr, c->len);
BUG_ON(result);
}
@@ -435,8 +479,39 @@ static int dma_free_chunk(struct dma_chunk *c)
return result;
}
+static int dma_ioc0_free_chunk(struct dma_chunk *c)
+{
+ int result = 0;
+ int iopage;
+ unsigned long offset;
+ struct ps3_dma_region *r = c->region;
+
+ DBG("%s:start\n", __func__);
+ for (iopage = 0; iopage < (c->len >> r->page_size); iopage++) {
+ offset = (1 << r->page_size) * iopage;
+ /* put INVALID entry */
+ result = lv1_put_iopte(0,
+ c->bus_addr + offset,
+ c->lpar_addr + offset,
+ r->ioid,
+ 0);
+ DBG("%s: bus=%#lx, lpar=%#lx, ioid=%d\n", __func__,
+ c->bus_addr + offset,
+ c->lpar_addr + offset,
+ r->ioid);
+
+ if (result) {
+ DBG("%s:%d: lv1_put_iopte failed: %s\n", __func__,
+ __LINE__, ps3_result(result));
+ }
+ }
+ kfree(c);
+ DBG("%s:end\n", __func__);
+ return result;
+}
+
/**
- * dma_map_pages - Maps dma pages into the io controller bus address space.
+ * dma_sb_map_pages - Maps dma pages into the io controller bus address space.
* @r: Pointer to a struct ps3_dma_region.
* @phys_addr: Starting physical address of the area to map.
* @len: Length in bytes of the area to map.
@@ -446,8 +521,8 @@ static int dma_free_chunk(struct dma_chunk *c)
* make the HV call to add the pages into the io controller address space.
*/
-static int dma_map_pages(struct ps3_dma_region *r, unsigned long phys_addr,
- unsigned long len, struct dma_chunk **c_out)
+static int dma_sb_map_pages(struct ps3_dma_region *r, unsigned long phys_addr,
+ unsigned long len, struct dma_chunk **c_out, u64 iopte_flag)
{
int result;
struct dma_chunk *c;
@@ -461,13 +536,13 @@ static int dma_map_pages(struct ps3_dma_region *r, unsigned long phys_addr,
c->region = r;
c->lpar_addr = ps3_mm_phys_to_lpar(phys_addr);
- c->bus_addr = dma_lpar_to_bus(r, c->lpar_addr);
+ c->bus_addr = dma_sb_lpar_to_bus(r, c->lpar_addr);
c->len = len;
- result = lv1_map_device_dma_region(c->region->did.bus_id,
- c->region->did.dev_id, c->lpar_addr, c->bus_addr, c->len,
- 0xf800000000000000UL);
-
+ BUG_ON(iopte_flag != 0xf800000000000000UL);
+ result = lv1_map_device_dma_region(c->region->dev->bus_id,
+ c->region->dev->dev_id, c->lpar_addr,
+ c->bus_addr, c->len, iopte_flag);
if (result) {
DBG("%s:%d: lv1_map_device_dma_region failed: %s\n",
__func__, __LINE__, ps3_result(result));
@@ -487,26 +562,120 @@ fail_alloc:
return result;
}
+static int dma_ioc0_map_pages(struct ps3_dma_region *r, unsigned long phys_addr,
+ unsigned long len, struct dma_chunk **c_out,
+ u64 iopte_flag)
+{
+ int result;
+ struct dma_chunk *c, *last;
+ int iopage, pages;
+ unsigned long offset;
+
+ DBG(KERN_ERR "%s: phy=%#lx, lpar%#lx, len=%#lx\n", __func__,
+ phys_addr, ps3_mm_phys_to_lpar(phys_addr), len);
+ c = kzalloc(sizeof(struct dma_chunk), GFP_ATOMIC);
+
+ if (!c) {
+ result = -ENOMEM;
+ goto fail_alloc;
+ }
+
+ c->region = r;
+ c->len = len;
+ c->lpar_addr = ps3_mm_phys_to_lpar(phys_addr);
+ /* allocate IO address */
+ if (list_empty(&r->chunk_list.head)) {
+ /* first one */
+ c->bus_addr = r->bus_addr;
+ } else {
+ /* derive from last bus addr*/
+ last = list_entry(r->chunk_list.head.next,
+ struct dma_chunk, link);
+ c->bus_addr = last->bus_addr + last->len;
+ DBG("%s: last bus=%#lx, len=%#lx\n", __func__,
+ last->bus_addr, last->len);
+ }
+
+ /* FIXME: check whether length exceeds region size */
+
+ /* build ioptes for the area */
+ pages = len >> r->page_size;
+ DBG("%s: pgsize=%#x len=%#lx pages=%#x iopteflag=%#lx\n", __func__,
+ r->page_size, r->len, pages, iopte_flag);
+ for (iopage = 0; iopage < pages; iopage++) {
+ offset = (1 << r->page_size) * iopage;
+ result = lv1_put_iopte(0,
+ c->bus_addr + offset,
+ c->lpar_addr + offset,
+ r->ioid,
+ iopte_flag);
+ if (result) {
+ printk(KERN_WARNING "%s:%d: lv1_map_device_dma_region "
+ "failed: %s\n", __func__, __LINE__,
+ ps3_result(result));
+ goto fail_map;
+ }
+ DBG("%s: pg=%d bus=%#lx, lpar=%#lx, ioid=%#x\n", __func__,
+ iopage, c->bus_addr + offset, c->lpar_addr + offset,
+ r->ioid);
+ }
+
+ /* be sure that last allocated one is inserted at head */
+ list_add(&c->link, &r->chunk_list.head);
+
+ *c_out = c;
+ DBG("%s: end\n", __func__);
+ return 0;
+
+fail_map:
+ for (iopage--; 0 <= iopage; iopage--) {
+ lv1_put_iopte(0,
+ c->bus_addr + offset,
+ c->lpar_addr + offset,
+ r->ioid,
+ 0);
+ }
+ kfree(c);
+fail_alloc:
+ *c_out = NULL;
+ return result;
+}
+
/**
- * dma_region_create - Create a device dma region.
+ * dma_sb_region_create - Create a device dma region.
* @r: Pointer to a struct ps3_dma_region.
*
* This is the lowest level dma region create routine, and is the one that
* will make the HV call to create the region.
*/
-static int dma_region_create(struct ps3_dma_region* r)
+static int dma_sb_region_create(struct ps3_dma_region *r)
{
int result;
- r->len = _ALIGN_UP(map.total, 1 << r->page_size);
+ pr_info(" -> %s:%d:\n", __func__, __LINE__);
+
+ BUG_ON(!r);
+
+ if (!r->dev->bus_id) {
+ pr_info("%s:%d: %u:%u no dma\n", __func__, __LINE__,
+ r->dev->bus_id, r->dev->dev_id);
+ return 0;
+ }
+
+ DBG("%s:%u: len = 0x%lx, page_size = %u, offset = 0x%lx\n", __func__,
+ __LINE__, r->len, r->page_size, r->offset);
+
+ BUG_ON(!r->len);
+ BUG_ON(!r->page_size);
+ BUG_ON(!r->region_ops);
+
INIT_LIST_HEAD(&r->chunk_list.head);
spin_lock_init(&r->chunk_list.lock);
- result = lv1_allocate_device_dma_region(r->did.bus_id, r->did.dev_id,
- r->len, r->page_size, r->region_type, &r->bus_addr);
-
- dma_dump_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);
if (result) {
DBG("%s:%d: lv1_allocate_device_dma_region failed: %s\n",
@@ -517,6 +686,27 @@ static int dma_region_create(struct ps3_dma_region* r)
return result;
}
+static int dma_ioc0_region_create(struct ps3_dma_region *r)
+{
+ int result;
+
+ INIT_LIST_HEAD(&r->chunk_list.head);
+ spin_lock_init(&r->chunk_list.lock);
+
+ result = lv1_allocate_io_segment(0,
+ r->len,
+ r->page_size,
+ &r->bus_addr);
+ if (result) {
+ DBG("%s:%d: lv1_allocate_io_segment failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+ r->len = r->bus_addr = 0;
+ }
+ DBG("%s: len=%#lx, pg=%d, bus=%#lx\n", __func__,
+ r->len, r->page_size, r->bus_addr);
+ return result;
+}
+
/**
* dma_region_free - Free a device dma region.
* @r: Pointer to a struct ps3_dma_region.
@@ -525,31 +715,62 @@ static int dma_region_create(struct ps3_dma_region* r)
* will make the HV call to free the region.
*/
-static int dma_region_free(struct ps3_dma_region* r)
+static int dma_sb_region_free(struct ps3_dma_region *r)
{
int result;
struct dma_chunk *c;
struct dma_chunk *tmp;
+ BUG_ON(!r);
+
+ if (!r->dev->bus_id) {
+ pr_info("%s:%d: %u:%u no dma\n", __func__, __LINE__,
+ r->dev->bus_id, r->dev->dev_id);
+ return 0;
+ }
+
list_for_each_entry_safe(c, tmp, &r->chunk_list.head, link) {
list_del(&c->link);
- dma_free_chunk(c);
+ dma_sb_free_chunk(c);
}
- result = lv1_free_device_dma_region(r->did.bus_id, r->did.dev_id,
+ result = lv1_free_device_dma_region(r->dev->bus_id, r->dev->dev_id,
r->bus_addr);
if (result)
DBG("%s:%d: lv1_free_device_dma_region failed: %s\n",
__func__, __LINE__, ps3_result(result));
- r->len = r->bus_addr = 0;
+ r->bus_addr = 0;
+
+ return result;
+}
+
+static int dma_ioc0_region_free(struct ps3_dma_region *r)
+{
+ int result;
+ struct dma_chunk *c, *n;
+
+ DBG("%s: start\n", __func__);
+ list_for_each_entry_safe(c, n, &r->chunk_list.head, link) {
+ list_del(&c->link);
+ dma_ioc0_free_chunk(c);
+ }
+
+ result = lv1_release_io_segment(0, r->bus_addr);
+
+ if (result)
+ DBG("%s:%d: lv1_free_device_dma_region failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+
+ r->bus_addr = 0;
+ DBG("%s: end\n", __func__);
return result;
}
/**
- * dma_map_area - Map an area of memory into a device dma region.
+ * dma_sb_map_area - Map an area of memory into a device dma region.
* @r: Pointer to a struct ps3_dma_region.
* @virt_addr: Starting virtual address of the area to map.
* @len: Length in bytes of the area to map.
@@ -559,16 +780,19 @@ static int dma_region_free(struct ps3_dma_region* r)
* This is the common dma mapping routine.
*/
-static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr,
- unsigned long len, unsigned long *bus_addr)
+static int dma_sb_map_area(struct ps3_dma_region *r, unsigned long virt_addr,
+ unsigned long len, unsigned long *bus_addr,
+ u64 iopte_flag)
{
int result;
unsigned long flags;
struct dma_chunk *c;
unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr)
: virt_addr;
-
- *bus_addr = dma_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr));
+ unsigned long aligned_phys = _ALIGN_DOWN(phys_addr, 1 << r->page_size);
+ unsigned long aligned_len = _ALIGN_UP(len + phys_addr - aligned_phys,
+ 1 << r->page_size);
+ *bus_addr = dma_sb_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr));
if (!USE_DYNAMIC_DMA) {
unsigned long lpar_addr = ps3_mm_phys_to_lpar(phys_addr);
@@ -588,17 +812,18 @@ static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr,
c = dma_find_chunk(r, *bus_addr, len);
if (c) {
+ DBG("%s:%d: reusing mapped chunk", __func__, __LINE__);
+ dma_dump_chunk(c);
c->usage_count++;
spin_unlock_irqrestore(&r->chunk_list.lock, flags);
return 0;
}
- result = dma_map_pages(r, _ALIGN_DOWN(phys_addr, 1 << r->page_size),
- _ALIGN_UP(len, 1 << r->page_size), &c);
+ result = dma_sb_map_pages(r, aligned_phys, aligned_len, &c, iopte_flag);
if (result) {
*bus_addr = 0;
- DBG("%s:%d: dma_map_pages failed (%d)\n",
+ DBG("%s:%d: dma_sb_map_pages failed (%d)\n",
__func__, __LINE__, result);
spin_unlock_irqrestore(&r->chunk_list.lock, flags);
return result;
@@ -610,8 +835,57 @@ static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr,
return result;
}
+static int dma_ioc0_map_area(struct ps3_dma_region *r, unsigned long virt_addr,
+ unsigned long len, unsigned long *bus_addr,
+ u64 iopte_flag)
+{
+ int result;
+ unsigned long flags;
+ struct dma_chunk *c;
+ unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr)
+ : virt_addr;
+ unsigned long aligned_phys = _ALIGN_DOWN(phys_addr, 1 << r->page_size);
+ unsigned long aligned_len = _ALIGN_UP(len + phys_addr - aligned_phys,
+ 1 << r->page_size);
+
+ DBG(KERN_ERR "%s: vaddr=%#lx, len=%#lx\n", __func__,
+ virt_addr, len);
+ DBG(KERN_ERR "%s: ph=%#lx a_ph=%#lx a_l=%#lx\n", __func__,
+ phys_addr, aligned_phys, aligned_len);
+
+ spin_lock_irqsave(&r->chunk_list.lock, flags);
+ c = dma_find_chunk_lpar(r, ps3_mm_phys_to_lpar(phys_addr), len);
+
+ if (c) {
+ /* FIXME */
+ BUG();
+ *bus_addr = c->bus_addr + phys_addr - aligned_phys;
+ c->usage_count++;
+ spin_unlock_irqrestore(&r->chunk_list.lock, flags);
+ return 0;
+ }
+
+ result = dma_ioc0_map_pages(r, aligned_phys, aligned_len, &c,
+ iopte_flag);
+
+ if (result) {
+ *bus_addr = 0;
+ DBG("%s:%d: dma_ioc0_map_pages failed (%d)\n",
+ __func__, __LINE__, result);
+ spin_unlock_irqrestore(&r->chunk_list.lock, flags);
+ return result;
+ }
+ *bus_addr = c->bus_addr + phys_addr - aligned_phys;
+ DBG("%s: va=%#lx pa=%#lx a_pa=%#lx bus=%#lx\n", __func__,
+ virt_addr, phys_addr, aligned_phys, *bus_addr);
+ c->usage_count = 1;
+
+ spin_unlock_irqrestore(&r->chunk_list.lock, flags);
+ return result;
+}
+
/**
- * dma_unmap_area - Unmap an area of memory from a device dma region.
+ * dma_sb_unmap_area - Unmap an area of memory from a device dma region.
* @r: Pointer to a struct ps3_dma_region.
* @bus_addr: The starting ioc bus address of the area to unmap.
* @len: Length in bytes of the area to unmap.
@@ -619,7 +893,7 @@ static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr,
* This is the common dma unmap routine.
*/
-int dma_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr,
+static int dma_sb_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr,
unsigned long len)
{
unsigned long flags;
@@ -631,7 +905,8 @@ int dma_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr,
if (!c) {
unsigned long aligned_bus = _ALIGN_DOWN(bus_addr,
1 << r->page_size);
- unsigned long aligned_len = _ALIGN_UP(len, 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",
__func__, __LINE__, bus_addr);
DBG("%s:%d: not found: len %lxh\n",
@@ -647,94 +922,166 @@ int dma_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr,
if (!c->usage_count) {
list_del(&c->link);
- dma_free_chunk(c);
+ dma_sb_free_chunk(c);
}
spin_unlock_irqrestore(&r->chunk_list.lock, flags);
return 0;
}
+static int dma_ioc0_unmap_area(struct ps3_dma_region *r,
+ unsigned long bus_addr, unsigned long len)
+{
+ unsigned long flags;
+ struct dma_chunk *c;
+
+ DBG("%s: start a=%#lx l=%#lx\n", __func__, bus_addr, len);
+ spin_lock_irqsave(&r->chunk_list.lock, flags);
+ c = dma_find_chunk(r, bus_addr, len);
+
+ if (!c) {
+ unsigned long aligned_bus = _ALIGN_DOWN(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",
+ __func__, __LINE__, bus_addr);
+ DBG("%s:%d: not found: len %lxh\n",
+ __func__, __LINE__, len);
+ DBG("%s:%d: not found: aligned_bus %lxh\n",
+ __func__, __LINE__, aligned_bus);
+ DBG("%s:%d: not found: aligned_len %lxh\n",
+ __func__, __LINE__, aligned_len);
+ BUG();
+ }
+
+ c->usage_count--;
+
+ if (!c->usage_count) {
+ list_del(&c->link);
+ dma_ioc0_free_chunk(c);
+ }
+
+ spin_unlock_irqrestore(&r->chunk_list.lock, flags);
+ DBG("%s: end\n", __func__);
+ return 0;
+}
+
/**
- * dma_region_create_linear - Setup a linear dma maping for a device.
+ * dma_sb_region_create_linear - Setup a linear dma mapping for a device.
* @r: Pointer to a struct ps3_dma_region.
*
* This routine creates an HV dma region for the device and maps all available
* ram into the io controller bus address space.
*/
-static int dma_region_create_linear(struct ps3_dma_region *r)
+static int dma_sb_region_create_linear(struct ps3_dma_region *r)
{
int result;
- unsigned long tmp;
-
- /* force 16M dma pages for linear mapping */
-
- if (r->page_size != PS3_DMA_16M) {
- pr_info("%s:%d: forcing 16M pages for linear map\n",
- __func__, __LINE__);
- r->page_size = PS3_DMA_16M;
+ unsigned long virt_addr, len, tmp;
+
+ if (r->len > 16*1024*1024) { /* FIXME: need proper fix */
+ /* force 16M dma pages for linear mapping */
+ if (r->page_size != PS3_DMA_16M) {
+ pr_info("%s:%d: forcing 16M pages for linear map\n",
+ __func__, __LINE__);
+ r->page_size = PS3_DMA_16M;
+ r->len = _ALIGN_UP(r->len, 1 << r->page_size);
+ }
}
- result = dma_region_create(r);
+ result = dma_sb_region_create(r);
BUG_ON(result);
- result = dma_map_area(r, map.rm.base, map.rm.size, &tmp);
- BUG_ON(result);
-
- if (USE_LPAR_ADDR)
- result = dma_map_area(r, map.r1.base, map.r1.size,
- &tmp);
- else
- result = dma_map_area(r, map.rm.size, map.r1.size,
- &tmp);
+ if (r->offset < map.rm.size) {
+ /* Map (part of) 1st RAM chunk */
+ virt_addr = map.rm.base + r->offset;
+ len = map.rm.size - r->offset;
+ 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);
+ BUG_ON(result);
+ }
- 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;
+ 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);
+ BUG_ON(result);
+ }
return result;
}
/**
- * dma_region_free_linear - Free a linear dma mapping for a device.
+ * dma_sb_region_free_linear - Free a linear dma mapping for a device.
* @r: Pointer to a struct ps3_dma_region.
*
* This routine will unmap all mapped areas and free the HV dma region.
*/
-static int dma_region_free_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;
+
+ if (r->offset < map.rm.size) {
+ /* Unmap (part of) 1st RAM chunk */
+ lpar_addr = map.rm.base + r->offset;
+ len = map.rm.size - r->offset;
+ if (len > r->len)
+ len = r->len;
+ bus_addr = dma_sb_lpar_to_bus(r, lpar_addr);
+ result = dma_sb_unmap_area(r, bus_addr, len);
+ BUG_ON(result);
+ }
- result = dma_unmap_area(r, dma_lpar_to_bus(r, 0), map.rm.size);
- BUG_ON(result);
-
- result = dma_unmap_area(r, dma_lpar_to_bus(r, map.r1.base),
- map.r1.size);
- BUG_ON(result);
+ if (r->offset + r->len > map.rm.size) {
+ /* Unmap (part of) 2nd RAM chunk */
+ lpar_addr = map.r1.base;
+ len = r->len;
+ if (r->offset >= map.rm.size)
+ lpar_addr += r->offset - map.rm.size;
+ else
+ len -= map.rm.size - r->offset;
+ bus_addr = dma_sb_lpar_to_bus(r, lpar_addr);
+ result = dma_sb_unmap_area(r, bus_addr, len);
+ BUG_ON(result);
+ }
- result = dma_region_free(r);
+ result = dma_sb_region_free(r);
BUG_ON(result);
return result;
}
/**
- * dma_map_area_linear - Map an area of memory into a device dma region.
+ * dma_sb_map_area_linear - Map an area of memory into a device dma region.
* @r: Pointer to a struct ps3_dma_region.
* @virt_addr: Starting virtual address of the area to map.
* @len: Length in bytes of the area to map.
* @bus_addr: A pointer to return the starting ioc bus address of the area to
* map.
*
- * This routine just returns the coresponding bus address. Actual mapping
+ * This routine just returns the corresponding bus address. Actual mapping
* occurs in dma_region_create_linear().
*/
-static int dma_map_area_linear(struct ps3_dma_region *r,
- unsigned long virt_addr, unsigned long len, unsigned long *bus_addr)
+static int dma_sb_map_area_linear(struct ps3_dma_region *r,
+ unsigned long virt_addr, unsigned long len, unsigned long *bus_addr,
+ u64 iopte_flag)
{
unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr)
: virt_addr;
- *bus_addr = dma_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr));
+ *bus_addr = dma_sb_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr));
return 0;
}
@@ -744,42 +1091,98 @@ static int dma_map_area_linear(struct ps3_dma_region *r,
* @bus_addr: The starting ioc bus address of the area to unmap.
* @len: Length in bytes of the area to unmap.
*
- * This routine does nothing. Unmapping occurs in dma_region_free_linear().
+ * This routine does nothing. Unmapping occurs in dma_sb_region_free_linear().
*/
-static int dma_unmap_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)
{
return 0;
+};
+
+static const struct ps3_dma_region_ops ps3_dma_sb_region_ops = {
+ .create = dma_sb_region_create,
+ .free = dma_sb_region_free,
+ .map = dma_sb_map_area,
+ .unmap = dma_sb_unmap_area
+};
+
+static const struct ps3_dma_region_ops ps3_dma_sb_region_linear_ops = {
+ .create = dma_sb_region_create_linear,
+ .free = dma_sb_region_free_linear,
+ .map = dma_sb_map_area_linear,
+ .unmap = dma_sb_unmap_area_linear
+};
+
+static const struct ps3_dma_region_ops ps3_dma_ioc0_region_ops = {
+ .create = dma_ioc0_region_create,
+ .free = dma_ioc0_region_free,
+ .map = dma_ioc0_map_area,
+ .unmap = dma_ioc0_unmap_area
+};
+
+int ps3_dma_region_init(struct ps3_system_bus_device *dev,
+ struct ps3_dma_region *r, enum ps3_dma_page_size page_size,
+ enum ps3_dma_region_type region_type, void *addr, unsigned long len)
+{
+ unsigned long lpar_addr;
+
+ lpar_addr = addr ? ps3_mm_phys_to_lpar(__pa(addr)) : 0;
+
+ r->dev = dev;
+ r->page_size = page_size;
+ r->region_type = region_type;
+ r->offset = lpar_addr;
+ if (r->offset >= map.rm.size)
+ r->offset -= map.r1.offset;
+ r->len = len ? len : _ALIGN_UP(map.total, 1 << r->page_size);
+
+ switch (dev->dev_type) {
+ case PS3_DEVICE_TYPE_SB:
+ r->region_ops = (USE_DYNAMIC_DMA)
+ ? &ps3_dma_sb_region_ops
+ : &ps3_dma_sb_region_linear_ops;
+ break;
+ case PS3_DEVICE_TYPE_IOC0:
+ r->region_ops = &ps3_dma_ioc0_region_ops;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+ return 0;
}
+EXPORT_SYMBOL(ps3_dma_region_init);
int ps3_dma_region_create(struct ps3_dma_region *r)
{
- return (USE_DYNAMIC_DMA)
- ? dma_region_create(r)
- : dma_region_create_linear(r);
+ BUG_ON(!r);
+ BUG_ON(!r->region_ops);
+ BUG_ON(!r->region_ops->create);
+ return r->region_ops->create(r);
}
+EXPORT_SYMBOL(ps3_dma_region_create);
int ps3_dma_region_free(struct ps3_dma_region *r)
{
- return (USE_DYNAMIC_DMA)
- ? dma_region_free(r)
- : dma_region_free_linear(r);
+ BUG_ON(!r);
+ BUG_ON(!r->region_ops);
+ BUG_ON(!r->region_ops->free);
+ return r->region_ops->free(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, unsigned long *bus_addr,
+ u64 iopte_flag)
{
- return (USE_DYNAMIC_DMA)
- ? dma_map_area(r, virt_addr, len, bus_addr)
- : dma_map_area_linear(r, virt_addr, len, bus_addr);
+ 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,
unsigned long len)
{
- return (USE_DYNAMIC_DMA) ? dma_unmap_area(r, bus_addr, len)
- : dma_unmap_area_linear(r, bus_addr, len);
+ return r->region_ops->unmap(r, bus_addr, len);
}
/*============================================================================*/
@@ -810,12 +1213,13 @@ void __init ps3_mm_init(void)
BUG_ON(map.rm.base);
BUG_ON(!map.rm.size);
- lmb_add(map.rm.base, map.rm.size);
- lmb_analyze();
/* arrange to do this in ps3_mm_add_memory */
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;
+
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 5c3da08bc0c4..b70e474014f0 100644
--- a/arch/powerpc/platforms/ps3/os-area.c
+++ b/arch/powerpc/platforms/ps3/os-area.c
@@ -133,7 +133,7 @@ struct saved_params {
} static saved_params;
#define dump_header(_a) _dump_header(_a, __func__, __LINE__)
-static void _dump_header(const struct os_area_header __iomem *h, const char* func,
+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,
@@ -151,7 +151,7 @@ static void _dump_header(const struct os_area_header __iomem *h, const char* fun
}
#define dump_params(_a) _dump_params(_a, __func__, __LINE__)
-static void _dump_params(const struct os_area_params __iomem *p, const char* func,
+static void _dump_params(const struct os_area_params *p, const char *func,
int line)
{
pr_debug("%s:%d: p.boot_flag: %u\n", func, line, p->boot_flag);
diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h
index ca04f03305c7..87d52060fec0 100644
--- a/arch/powerpc/platforms/ps3/platform.h
+++ b/arch/powerpc/platforms/ps3/platform.h
@@ -41,6 +41,7 @@ void ps3_mm_shutdown(void);
/* irq */
void ps3_init_IRQ(void);
+void ps3_shutdown_IRQ(int cpu);
void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq);
/* smp */
@@ -82,6 +83,7 @@ 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_NOACCESS = 255,
};
int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str,
@@ -129,24 +131,28 @@ 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;
- struct ps3_device_id did;
+ unsigned int dev_id;
};
-int ps3_repository_find_device(enum ps3_bus_type bus_type,
- enum ps3_dev_type dev_type,
- const struct ps3_repository_device *start_dev,
- struct ps3_repository_device *dev);
-static inline int ps3_repository_find_first_device(
- enum ps3_bus_type bus_type, enum ps3_dev_type dev_type,
- struct ps3_repository_device *dev)
+static inline struct ps3_repository_device *ps3_repository_bump_device(
+ struct ps3_repository_device *repo)
{
- return ps3_repository_find_device(bus_type, dev_type, NULL, dev);
+ repo->dev_index++;
+ return repo;
}
-int ps3_repository_find_interrupt(const struct ps3_repository_device *dev,
+int ps3_repository_find_device(struct ps3_repository_device *repo);
+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,
+ unsigned int *bus_index);
+int ps3_repository_find_interrupt(const struct ps3_repository_device *repo,
enum ps3_interrupt_type intr_type, unsigned int *interrupt_id);
-int ps3_repository_find_reg(const struct ps3_repository_device *dev,
+int ps3_repository_find_reg(const struct ps3_repository_device *repo,
enum ps3_reg_type reg_type, u64 *bus_addr, u64 *len);
/* repository block device info */
@@ -216,4 +222,19 @@ 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);
+/* repository vuart info */
+
+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 ae586a0e5d3f..8cc37cfea0f2 100644
--- a/arch/powerpc/platforms/ps3/repository.c
+++ b/arch/powerpc/platforms/ps3/repository.c
@@ -138,7 +138,7 @@ static int read_node(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, u64 n4,
pr_debug("%s:%d: lv1_get_repository_node_value failed: %s\n",
__func__, __LINE__, ps3_result(result));
dump_node_name(lpar_id, n1, n2, n3, n4);
- return result;
+ return -ENOENT;
}
dump_node(lpar_id, n1, n2, n3, n4, v1, v2);
@@ -155,7 +155,7 @@ static int read_node(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, u64 n4,
pr_debug("%s:%d: warning: discarding non-zero v2: %016lx\n",
__func__, __LINE__, v2);
- return result;
+ return 0;
}
int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str,
@@ -314,324 +314,140 @@ int ps3_repository_read_dev_reg(unsigned int bus_index,
reg_index, bus_addr, len);
}
-#if defined(DEBUG)
-int ps3_repository_dump_resource_info(unsigned int bus_index,
- unsigned int dev_index)
-{
- int result = 0;
- unsigned int res_index;
- pr_debug(" -> %s:%d: (%u:%u)\n", __func__, __LINE__,
- bus_index, dev_index);
- for (res_index = 0; res_index < 10; res_index++) {
- enum ps3_interrupt_type intr_type;
- unsigned int interrupt_id;
+int ps3_repository_find_device(struct ps3_repository_device *repo)
+{
+ int result;
+ struct ps3_repository_device tmp = *repo;
+ unsigned int num_dev;
- result = ps3_repository_read_dev_intr(bus_index, dev_index,
- res_index, &intr_type, &interrupt_id);
+ BUG_ON(repo->bus_index > 10);
+ BUG_ON(repo->dev_index > 10);
- if (result) {
- if (result != LV1_NO_ENTRY)
- pr_debug("%s:%d ps3_repository_read_dev_intr"
- " (%u:%u) failed\n", __func__, __LINE__,
- bus_index, dev_index);
- break;
- }
+ result = ps3_repository_read_bus_num_dev(tmp.bus_index, &num_dev);
- pr_debug("%s:%d (%u:%u) intr_type %u, interrupt_id %u\n",
- __func__, __LINE__, bus_index, dev_index, intr_type,
- interrupt_id);
+ if (result) {
+ pr_debug("%s:%d read_bus_num_dev failed\n", __func__, __LINE__);
+ return result;
}
- for (res_index = 0; res_index < 10; res_index++) {
- enum ps3_reg_type reg_type;
- u64 bus_addr;
- u64 len;
-
- result = ps3_repository_read_dev_reg(bus_index, dev_index,
- res_index, &reg_type, &bus_addr, &len);
+ pr_debug("%s:%d: bus_type %u, bus_index %u, bus_id %u, num_dev %u\n",
+ __func__, __LINE__, tmp.bus_type, tmp.bus_index, tmp.bus_id,
+ num_dev);
- if (result) {
- if (result != LV1_NO_ENTRY)
- pr_debug("%s:%d ps3_repository_read_dev_reg"
- " (%u:%u) failed\n", __func__, __LINE__,
- bus_index, dev_index);
- break;
- }
-
- pr_debug("%s:%d (%u:%u) reg_type %u, bus_addr %lxh, len %lxh\n",
- __func__, __LINE__, bus_index, dev_index, reg_type,
- bus_addr, len);
+ if (tmp.dev_index >= num_dev) {
+ pr_debug("%s:%d: no device found\n", __func__, __LINE__);
+ return -ENODEV;
}
- pr_debug(" <- %s:%d\n", __func__, __LINE__);
- return result;
-}
-
-static int dump_stor_dev_info(unsigned int bus_index, unsigned int dev_index)
-{
- int result = 0;
- unsigned int num_regions, region_index;
- u64 port, blk_size, num_blocks;
-
- pr_debug(" -> %s:%d: (%u:%u)\n", __func__, __LINE__,
- bus_index, dev_index);
+ result = ps3_repository_read_dev_type(tmp.bus_index, tmp.dev_index,
+ &tmp.dev_type);
- result = ps3_repository_read_stor_dev_info(bus_index, dev_index, &port,
- &blk_size, &num_blocks, &num_regions);
if (result) {
- pr_debug("%s:%d ps3_repository_read_stor_dev_info"
- " (%u:%u) failed\n", __func__, __LINE__,
- bus_index, dev_index);
- goto out;
+ pr_debug("%s:%d read_dev_type failed\n", __func__, __LINE__);
+ return result;
}
- pr_debug("%s:%d (%u:%u): port %lu, blk_size %lu, num_blocks "
- "%lu, num_regions %u\n",
- __func__, __LINE__, bus_index, dev_index, port,
- blk_size, num_blocks, num_regions);
-
- for (region_index = 0; region_index < num_regions; region_index++) {
- unsigned int region_id;
- u64 region_start, region_size;
-
- result = ps3_repository_read_stor_dev_region(bus_index,
- dev_index, region_index, &region_id, &region_start,
- &region_size);
- if (result) {
- pr_debug("%s:%d ps3_repository_read_stor_dev_region"
- " (%u:%u) failed\n", __func__, __LINE__,
- bus_index, dev_index);
- break;
- }
+ result = ps3_repository_read_dev_id(tmp.bus_index, tmp.dev_index,
+ &tmp.dev_id);
- pr_debug("%s:%d (%u:%u) region_id %u, start %lxh, size %lxh\n",
- __func__, __LINE__, bus_index, dev_index, region_id,
- region_start, region_size);
+ if (result) {
+ pr_debug("%s:%d ps3_repository_read_dev_id failed\n", __func__,
+ __LINE__);
+ return result;
}
-out:
- pr_debug(" <- %s:%d\n", __func__, __LINE__);
- return result;
-}
-
-static int dump_device_info(unsigned int bus_index, enum ps3_bus_type bus_type,
- unsigned int num_dev)
-{
- int result = 0;
- unsigned int dev_index;
-
- pr_debug(" -> %s:%d: bus_%u\n", __func__, __LINE__, bus_index);
-
- for (dev_index = 0; dev_index < num_dev; dev_index++) {
- enum ps3_dev_type dev_type;
- unsigned int dev_id;
-
- result = ps3_repository_read_dev_type(bus_index, dev_index,
- &dev_type);
-
- if (result) {
- pr_debug("%s:%d ps3_repository_read_dev_type"
- " (%u:%u) failed\n", __func__, __LINE__,
- bus_index, dev_index);
- break;
- }
-
- result = ps3_repository_read_dev_id(bus_index, dev_index,
- &dev_id);
-
- if (result) {
- pr_debug("%s:%d ps3_repository_read_dev_id"
- " (%u:%u) failed\n", __func__, __LINE__,
- bus_index, dev_index);
- continue;
- }
+ pr_debug("%s:%d: found: dev_type %u, dev_index %u, dev_id %u\n",
+ __func__, __LINE__, tmp.dev_type, tmp.dev_index, tmp.dev_id);
- pr_debug("%s:%d (%u:%u): dev_type %u, dev_id %u\n", __func__,
- __LINE__, bus_index, dev_index, dev_type, dev_id);
-
- ps3_repository_dump_resource_info(bus_index, dev_index);
-
- if (bus_type == PS3_BUS_TYPE_STORAGE)
- dump_stor_dev_info(bus_index, dev_index);
- }
-
- pr_debug(" <- %s:%d\n", __func__, __LINE__);
- return result;
+ *repo = tmp;
+ return 0;
}
-int ps3_repository_dump_bus_info(void)
+int __devinit ps3_repository_find_devices(enum ps3_bus_type bus_type,
+ int (*callback)(const struct ps3_repository_device *repo))
{
int result = 0;
- unsigned int bus_index;
+ struct ps3_repository_device repo;
- pr_debug(" -> %s:%d\n", __func__, __LINE__);
+ pr_debug(" -> %s:%d: find bus_type %u\n", __func__, __LINE__, bus_type);
- for (bus_index = 0; bus_index < 10; bus_index++) {
- enum ps3_bus_type bus_type;
- unsigned int bus_id;
- unsigned int num_dev;
+ for (repo.bus_index = 0; repo.bus_index < 10; repo.bus_index++) {
- result = ps3_repository_read_bus_type(bus_index, &bus_type);
+ result = ps3_repository_read_bus_type(repo.bus_index,
+ &repo.bus_type);
if (result) {
pr_debug("%s:%d read_bus_type(%u) failed\n",
- __func__, __LINE__, bus_index);
+ __func__, __LINE__, repo.bus_index);
break;
}
- result = ps3_repository_read_bus_id(bus_index, &bus_id);
-
- if (result) {
- pr_debug("%s:%d read_bus_id(%u) failed\n",
- __func__, __LINE__, bus_index);
+ if (repo.bus_type != bus_type) {
+ pr_debug("%s:%d: skip, bus_type %u\n", __func__,
+ __LINE__, repo.bus_type);
continue;
}
- if (bus_index != bus_id)
- pr_debug("%s:%d bus_index != bus_id\n",
- __func__, __LINE__);
-
- result = ps3_repository_read_bus_num_dev(bus_index, &num_dev);
+ result = ps3_repository_read_bus_id(repo.bus_index,
+ &repo.bus_id);
if (result) {
- pr_debug("%s:%d read_bus_num_dev(%u) failed\n",
- __func__, __LINE__, bus_index);
+ pr_debug("%s:%d read_bus_id(%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",
- __func__, __LINE__, bus_index, bus_type, bus_id,
- num_dev);
+ for (repo.dev_index = 0; ; repo.dev_index++) {
+ result = ps3_repository_find_device(&repo);
- dump_device_info(bus_index, bus_type, num_dev);
- }
+ if (result == -ENODEV) {
+ result = 0;
+ break;
+ } else if (result)
+ break;
- pr_debug(" <- %s:%d\n", __func__, __LINE__);
- return result;
-}
-#endif /* defined(DEBUG) */
-
-static int find_device(unsigned int bus_index, unsigned int num_dev,
- unsigned int start_dev_index, enum ps3_dev_type dev_type,
- struct ps3_repository_device *dev)
-{
- int result = 0;
- unsigned int dev_index;
+ result = callback(&repo);
- pr_debug("%s:%d: find dev_type %u\n", __func__, __LINE__, dev_type);
-
- dev->dev_index = UINT_MAX;
-
- for (dev_index = start_dev_index; dev_index < num_dev; dev_index++) {
- enum ps3_dev_type x;
-
- result = ps3_repository_read_dev_type(bus_index, dev_index,
- &x);
-
- if (result) {
- pr_debug("%s:%d read_dev_type failed\n",
- __func__, __LINE__);
- return result;
+ if (result) {
+ pr_debug("%s:%d: abort at callback\n", __func__,
+ __LINE__);
+ break;
+ }
}
-
- if (x == dev_type)
- break;
- }
-
- if (dev_index == num_dev)
- return -1;
-
- pr_debug("%s:%d: found dev_type %u at dev_index %u\n",
- __func__, __LINE__, dev_type, dev_index);
-
- result = ps3_repository_read_dev_id(bus_index, dev_index,
- &dev->did.dev_id);
-
- if (result) {
- pr_debug("%s:%d read_dev_id failed\n",
- __func__, __LINE__);
- return result;
+ break;
}
- dev->dev_index = dev_index;
-
- pr_debug("%s:%d found: dev_id %u\n", __func__, __LINE__,
- dev->did.dev_id);
-
+ pr_debug(" <- %s:%d\n", __func__, __LINE__);
return result;
}
-int ps3_repository_find_device (enum ps3_bus_type bus_type,
- enum ps3_dev_type dev_type,
- const struct ps3_repository_device *start_dev,
- struct ps3_repository_device *dev)
+int ps3_repository_find_bus(enum ps3_bus_type bus_type, unsigned int from,
+ unsigned int *bus_index)
{
- int result = 0;
- unsigned int bus_index;
- unsigned int num_dev;
-
- pr_debug("%s:%d: find bus_type %u, dev_type %u\n", __func__, __LINE__,
- bus_type, dev_type);
-
- BUG_ON(start_dev && start_dev->bus_index > 10);
-
- for (bus_index = start_dev ? start_dev->bus_index : 0; bus_index < 10;
- bus_index++) {
- enum ps3_bus_type x;
-
- result = ps3_repository_read_bus_type(bus_index, &x);
+ unsigned int i;
+ enum ps3_bus_type type;
+ int error;
- if (result) {
+ 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",
__func__, __LINE__);
- dev->bus_index = UINT_MAX;
- return result;
+ *bus_index = UINT_MAX;
+ return error;
+ }
+ if (type == bus_type) {
+ *bus_index = i;
+ return 0;
}
- if (x == bus_type)
- break;
- }
-
- if (bus_index >= 10)
- return -ENODEV;
-
- pr_debug("%s:%d: found bus_type %u at bus_index %u\n",
- __func__, __LINE__, bus_type, bus_index);
-
- result = ps3_repository_read_bus_num_dev(bus_index, &num_dev);
-
- if (result) {
- pr_debug("%s:%d read_bus_num_dev failed\n",
- __func__, __LINE__);
- return result;
- }
-
- result = find_device(bus_index, num_dev, start_dev
- ? start_dev->dev_index + 1 : 0, dev_type, dev);
-
- if (result) {
- pr_debug("%s:%d get_did failed\n", __func__, __LINE__);
- return result;
- }
-
- result = ps3_repository_read_bus_id(bus_index, &dev->did.bus_id);
-
- if (result) {
- pr_debug("%s:%d read_bus_id failed\n",
- __func__, __LINE__);
- return result;
}
-
- dev->bus_index = bus_index;
-
- pr_debug("%s:%d found: bus_id %u, dev_id %u\n",
- __func__, __LINE__, dev->did.bus_id, dev->did.dev_id);
-
- return result;
+ *bus_index = UINT_MAX;
+ return -ENODEV;
}
-int ps3_repository_find_interrupt(const struct ps3_repository_device *dev,
+int ps3_repository_find_interrupt(const struct ps3_repository_device *repo,
enum ps3_interrupt_type intr_type, unsigned int *interrupt_id)
{
int result = 0;
@@ -645,8 +461,8 @@ int ps3_repository_find_interrupt(const struct ps3_repository_device *dev,
enum ps3_interrupt_type t;
unsigned int id;
- result = ps3_repository_read_dev_intr(dev->bus_index,
- dev->dev_index, res_index, &t, &id);
+ result = ps3_repository_read_dev_intr(repo->bus_index,
+ repo->dev_index, res_index, &t, &id);
if (result) {
pr_debug("%s:%d read_dev_intr failed\n",
@@ -669,7 +485,7 @@ int ps3_repository_find_interrupt(const struct ps3_repository_device *dev,
return result;
}
-int ps3_repository_find_reg(const struct ps3_repository_device *dev,
+int ps3_repository_find_reg(const struct ps3_repository_device *repo,
enum ps3_reg_type reg_type, u64 *bus_addr, u64 *len)
{
int result = 0;
@@ -684,8 +500,8 @@ int ps3_repository_find_reg(const struct ps3_repository_device *dev,
u64 a;
u64 l;
- result = ps3_repository_read_dev_reg(dev->bus_index,
- dev->dev_index, res_index, &t, &a, &l);
+ result = ps3_repository_read_dev_reg(repo->bus_index,
+ repo->dev_index, res_index, &t, &a, &l);
if (result) {
pr_debug("%s:%d read_dev_reg failed\n",
@@ -965,6 +781,36 @@ int ps3_repository_read_boot_dat_size(unsigned int *size)
return result;
}
+int ps3_repository_read_vuart_av_port(unsigned int *port)
+{
+ int result;
+ u64 v1;
+
+ 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);
+ *port = v1;
+ return result;
+}
+
+int ps3_repository_read_vuart_sysmgr_port(unsigned int *port)
+{
+ int result;
+ u64 v1;
+
+ 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);
+ *port = v1;
+ return result;
+}
+
/**
* ps3_repository_read_boot_dat_info - Get address and size of cell_ext_os_area.
* address: lpar address of cell_ext_os_area
@@ -1026,3 +872,205 @@ int ps3_repository_read_be_tb_freq(unsigned int be_index, u64 *tb_freq)
return result ? result
: ps3_repository_read_tb_freq(node_id, tb_freq);
}
+
+#if defined(DEBUG)
+
+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__,
+ repo->bus_index, repo->dev_index);
+
+ for (res_index = 0; res_index < 10; res_index++) {
+ enum ps3_interrupt_type intr_type;
+ unsigned int interrupt_id;
+
+ result = ps3_repository_read_dev_intr(repo->bus_index,
+ repo->dev_index, res_index, &intr_type, &interrupt_id);
+
+ if (result) {
+ if (result != LV1_NO_ENTRY)
+ pr_debug("%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",
+ __func__, __LINE__, repo->bus_index, repo->dev_index,
+ intr_type, interrupt_id);
+ }
+
+ for (res_index = 0; res_index < 10; res_index++) {
+ enum ps3_reg_type reg_type;
+ u64 bus_addr;
+ u64 len;
+
+ result = ps3_repository_read_dev_reg(repo->bus_index,
+ repo->dev_index, res_index, &reg_type, &bus_addr, &len);
+
+ if (result) {
+ if (result != LV1_NO_ENTRY)
+ pr_debug("%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",
+ __func__, __LINE__, repo->bus_index, repo->dev_index,
+ reg_type, bus_addr, len);
+ }
+
+ pr_debug(" <- %s:%d\n", __func__, __LINE__);
+ return result;
+}
+
+static int dump_stor_dev_info(struct ps3_repository_device *repo)
+{
+ int result = 0;
+ unsigned int num_regions, region_index;
+ u64 port, blk_size, num_blocks;
+
+ pr_debug(" -> %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"
+ " (%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);
+
+ for (region_index = 0; region_index < num_regions; region_index++) {
+ unsigned int region_id;
+ u64 region_start, region_size;
+
+ result = ps3_repository_read_stor_dev_region(repo->bus_index,
+ repo->dev_index, region_index, &region_id,
+ &region_start, &region_size);
+ if (result) {
+ pr_debug("%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",
+ __func__, __LINE__, repo->bus_index, repo->dev_index,
+ region_id, region_start, region_size);
+ }
+
+out:
+ pr_debug(" <- %s:%d\n", __func__, __LINE__);
+ return result;
+}
+
+static int dump_device_info(struct ps3_repository_device *repo,
+ unsigned int num_dev)
+{
+ int result = 0;
+
+ pr_debug(" -> %s:%d: bus_%u\n", __func__, __LINE__, repo->bus_index);
+
+ for (repo->dev_index = 0; repo->dev_index < num_dev;
+ repo->dev_index++) {
+
+ result = ps3_repository_read_dev_type(repo->bus_index,
+ repo->dev_index, &repo->dev_type);
+
+ if (result) {
+ pr_debug("%s:%d ps3_repository_read_dev_type"
+ " (%u:%u) failed\n", __func__, __LINE__,
+ repo->bus_index, repo->dev_index);
+ break;
+ }
+
+ result = ps3_repository_read_dev_id(repo->bus_index,
+ repo->dev_index, &repo->dev_id);
+
+ if (result) {
+ pr_debug("%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__,
+ __LINE__, repo->bus_index, repo->dev_index,
+ repo->dev_type, repo->dev_id);
+
+ ps3_repository_dump_resource_info(repo);
+
+ if (repo->bus_type == PS3_BUS_TYPE_STORAGE)
+ dump_stor_dev_info(repo);
+ }
+
+ pr_debug(" <- %s:%d\n", __func__, __LINE__);
+ return result;
+}
+
+int ps3_repository_dump_bus_info(void)
+{
+ int result = 0;
+ struct ps3_repository_device repo;
+
+ pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+ memset(&repo, 0, sizeof(repo));
+
+ for (repo.bus_index = 0; repo.bus_index < 10; repo.bus_index++) {
+ unsigned int num_dev;
+
+ result = ps3_repository_read_bus_type(repo.bus_index,
+ &repo.bus_type);
+
+ if (result) {
+ pr_debug("%s:%d read_bus_type(%u) failed\n",
+ __func__, __LINE__, repo.bus_index);
+ break;
+ }
+
+ result = ps3_repository_read_bus_id(repo.bus_index,
+ &repo.bus_id);
+
+ if (result) {
+ pr_debug("%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",
+ __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",
+ __func__, __LINE__, repo.bus_index);
+ continue;
+ }
+
+ pr_debug("%s:%d bus_%u: bus_type %u, bus_id %u, num_dev %u\n",
+ __func__, __LINE__, repo.bus_index, repo.bus_type,
+ repo.bus_id, num_dev);
+
+ dump_device_info(&repo, num_dev);
+ }
+
+ pr_debug(" <- %s:%d\n", __func__, __LINE__);
+ return result;
+}
+
+#endif /* defined(DEBUG) */
diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c
index 935396766621..aa05288de64e 100644
--- a/arch/powerpc/platforms/ps3/setup.c
+++ b/arch/powerpc/platforms/ps3/setup.c
@@ -37,27 +37,35 @@
#include "platform.h"
#if defined(DEBUG)
-#define DBG(fmt...) udbg_printf(fmt)
+#define DBG udbg_printf
#else
-#define DBG(fmt...) do{if(0)printk(fmt);}while(0)
+#define DBG pr_debug
#endif
#if !defined(CONFIG_SMP)
static void smp_send_stop(void) {}
#endif
-int ps3_get_firmware_version(union ps3_firmware_version *v)
+static union ps3_firmware_version ps3_firmware_version;
+
+void ps3_get_firmware_version(union ps3_firmware_version *v)
{
- int result = lv1_get_version_info(&v->raw);
+ *v = ps3_firmware_version;
+}
+EXPORT_SYMBOL_GPL(ps3_get_firmware_version);
- if (result) {
- v->raw = 0;
- return -1;
- }
+int ps3_compare_firmware_version(u16 major, u16 minor, u16 rev)
+{
+ union ps3_firmware_version x;
+
+ x.pad = 0;
+ x.major = major;
+ x.minor = minor;
+ x.rev = rev;
- return result;
+ return (ps3_firmware_version.raw - x.raw);
}
-EXPORT_SYMBOL_GPL(ps3_get_firmware_version);
+EXPORT_SYMBOL_GPL(ps3_compare_firmware_version);
static void ps3_power_save(void)
{
@@ -99,7 +107,8 @@ static void ps3_panic(char *str)
while(1);
}
-#ifdef CONFIG_FB_PS3
+#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE) || \
+ defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE)
static void prealloc(struct ps3_prealloc *p)
{
if (!p->size)
@@ -115,12 +124,15 @@ static void prealloc(struct ps3_prealloc *p)
printk(KERN_INFO "%s: %lu bytes at %p\n", p->name, p->size,
p->address);
}
+#endif
+#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE)
struct ps3_prealloc ps3fb_videomemory = {
- .name = "ps3fb videomemory",
- .size = CONFIG_FB_PS3_DEFAULT_SIZE_M*1024*1024,
- .align = 1024*1024 /* the GPU requires 1 MiB alignment */
+ .name = "ps3fb videomemory",
+ .size = CONFIG_FB_PS3_DEFAULT_SIZE_M*1024*1024,
+ .align = 1024*1024 /* the GPU requires 1 MiB alignment */
};
+EXPORT_SYMBOL_GPL(ps3fb_videomemory);
#define prealloc_ps3fb_videomemory() prealloc(&ps3fb_videomemory)
static int __init early_parse_ps3fb(char *p)
@@ -137,6 +149,30 @@ early_param("ps3fb", early_parse_ps3fb);
#define prealloc_ps3fb_videomemory() do { } while (0)
#endif
+#if defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE)
+struct ps3_prealloc ps3flash_bounce_buffer = {
+ .name = "ps3flash bounce buffer",
+ .size = 256*1024,
+ .align = 256*1024
+};
+EXPORT_SYMBOL_GPL(ps3flash_bounce_buffer);
+#define prealloc_ps3flash_bounce_buffer() prealloc(&ps3flash_bounce_buffer)
+
+static int __init early_parse_ps3flash(char *p)
+{
+ if (!p)
+ return 1;
+
+ if (!strcmp(p, "off"))
+ ps3flash_bounce_buffer.size = 0;
+
+ return 0;
+}
+early_param("ps3flash", early_parse_ps3flash);
+#else
+#define prealloc_ps3flash_bounce_buffer() do { } while (0)
+#endif
+
static int ps3_set_dabr(u64 dabr)
{
enum {DABR_USER = 1, DABR_KERNEL = 2,};
@@ -146,13 +182,13 @@ static int ps3_set_dabr(u64 dabr)
static void __init ps3_setup_arch(void)
{
- union ps3_firmware_version v;
DBG(" -> %s:%d\n", __func__, __LINE__);
- ps3_get_firmware_version(&v);
- printk(KERN_INFO "PS3 firmware version %u.%u.%u\n", v.major, v.minor,
- v.rev);
+ lv1_get_version_info(&ps3_firmware_version.raw);
+ 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();
@@ -166,6 +202,8 @@ static void __init ps3_setup_arch(void)
#endif
prealloc_ps3fb_videomemory();
+ prealloc_ps3flash_bounce_buffer();
+
ppc_md.power_save = ps3_power_save;
DBG(" <- %s:%d\n", __func__, __LINE__);
@@ -184,7 +222,7 @@ static int __init ps3_probe(void)
DBG(" -> %s:%d\n", __func__, __LINE__);
dt_root = of_get_flat_dt_root();
- if (!of_flat_dt_is_compatible(dt_root, "PS3"))
+ if (!of_flat_dt_is_compatible(dt_root, "sony,ps3"))
return 0;
powerpc_firmware_features |= FW_FEATURE_PS3_POSSIBLE;
@@ -201,31 +239,12 @@ static int __init ps3_probe(void)
#if defined(CONFIG_KEXEC)
static void ps3_kexec_cpu_down(int crash_shutdown, int secondary)
{
- DBG(" -> %s:%d\n", __func__, __LINE__);
-
- if (secondary) {
- int cpu;
- for_each_online_cpu(cpu)
- if (cpu)
- ps3_smp_cleanup_cpu(cpu);
- } else
- ps3_smp_cleanup_cpu(0);
-
- DBG(" <- %s:%d\n", __func__, __LINE__);
-}
-
-static void ps3_machine_kexec(struct kimage *image)
-{
- unsigned long ppe_id;
-
- DBG(" -> %s:%d\n", __func__, __LINE__);
+ int cpu = smp_processor_id();
- lv1_get_logical_ppe_id(&ppe_id);
- lv1_configure_irq_state_bitmap(ppe_id, 0, 0);
- ps3_mm_shutdown();
- ps3_mm_vas_destroy();
+ DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu);
- default_machine_kexec(image);
+ ps3_smp_cleanup_cpu(cpu);
+ ps3_shutdown_IRQ(cpu);
DBG(" <- %s:%d\n", __func__, __LINE__);
}
@@ -247,7 +266,7 @@ define_machine(ps3) {
.power_off = ps3_power_off,
#if defined(CONFIG_KEXEC)
.kexec_cpu_down = ps3_kexec_cpu_down,
- .machine_kexec = ps3_machine_kexec,
+ .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 53416ec5198b..f0b12f212363 100644
--- a/arch/powerpc/platforms/ps3/smp.c
+++ b/arch/powerpc/platforms/ps3/smp.c
@@ -27,9 +27,9 @@
#include "platform.h"
#if defined(DEBUG)
-#define DBG(fmt...) udbg_printf(fmt)
+#define DBG udbg_printf
#else
-#define DBG(fmt...) do{if(0)printk(fmt);}while(0)
+#define DBG pr_debug
#endif
static irqreturn_t ipi_function_handler(int irq, void *msg)
@@ -39,11 +39,11 @@ static irqreturn_t ipi_function_handler(int irq, void *msg)
}
/**
- * virqs - a per cpu array of virqs for ipi use
+ * ps3_ipi_virqs - a per cpu array of virqs for ipi use
*/
#define MSG_COUNT 4
-static DEFINE_PER_CPU(unsigned int, virqs[MSG_COUNT]);
+static DEFINE_PER_CPU(unsigned int, ps3_ipi_virqs[MSG_COUNT]);
static const char *names[MSG_COUNT] = {
"ipi call",
@@ -62,7 +62,7 @@ static void do_message_pass(int target, int msg)
return;
}
- virq = per_cpu(virqs, target)[msg];
+ virq = per_cpu(ps3_ipi_virqs, target)[msg];
result = ps3_send_event_locally(virq);
if (result)
@@ -94,13 +94,13 @@ static int ps3_smp_probe(void)
static void __init ps3_smp_setup_cpu(int cpu)
{
int result;
- unsigned int *virqs = per_cpu(virqs, cpu);
+ unsigned int *virqs = per_cpu(ps3_ipi_virqs, cpu);
int i;
DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu);
/*
- * Check assumptions on virqs[] indexing. If this
+ * Check assumptions on ps3_ipi_virqs[] indexing. If this
* check fails, then a different mapping of PPC_MSG_
* to index needs to be setup.
*/
@@ -132,13 +132,13 @@ static void __init ps3_smp_setup_cpu(int cpu)
void ps3_smp_cleanup_cpu(int cpu)
{
- unsigned int *virqs = per_cpu(virqs, cpu);
+ unsigned int *virqs = per_cpu(ps3_ipi_virqs, cpu);
int i;
DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu);
for (i = 0; i < MSG_COUNT; i++) {
- free_irq(virqs[i], (void*)(long)i);
+ /* Can't call free_irq from interrupt context. */
ps3_event_receive_port_destroy(virqs[i]);
virqs[i] = NO_IRQ;
}
diff --git a/arch/powerpc/platforms/ps3/spu.c b/arch/powerpc/platforms/ps3/spu.c
index 651437cb2c18..c7f734c89462 100644
--- a/arch/powerpc/platforms/ps3/spu.c
+++ b/arch/powerpc/platforms/ps3/spu.c
@@ -182,15 +182,18 @@ static int __init setup_areas(struct spu *spu)
{
struct table {char* name; unsigned long addr; unsigned long size;};
- spu_pdata(spu)->shadow = __ioremap(
- spu_pdata(spu)->shadow_addr, sizeof(struct spe_shadow),
- pgprot_val(PAGE_READONLY) | _PAGE_NO_CACHE | _PAGE_GUARDED);
+ spu_pdata(spu)->shadow = ioremap_flags(spu_pdata(spu)->shadow_addr,
+ sizeof(struct spe_shadow),
+ pgprot_val(PAGE_READONLY) |
+ _PAGE_NO_CACHE);
if (!spu_pdata(spu)->shadow) {
pr_debug("%s:%d: ioremap shadow failed\n", __func__, __LINE__);
goto fail_ioremap;
}
- spu->local_store = ioremap(spu->local_store_phys, LS_SIZE);
+ spu->local_store = (__force void *)ioremap_flags(spu->local_store_phys,
+ LS_SIZE, _PAGE_NO_CACHE);
+
if (!spu->local_store) {
pr_debug("%s:%d: ioremap local_store failed\n",
__func__, __LINE__);
@@ -199,6 +202,7 @@ static int __init setup_areas(struct spu *spu)
spu->problem = ioremap(spu->problem_phys,
sizeof(struct spu_problem));
+
if (!spu->problem) {
pr_debug("%s:%d: ioremap problem failed\n", __func__, __LINE__);
goto fail_ioremap;
@@ -206,6 +210,7 @@ static int __init setup_areas(struct spu *spu)
spu->priv2 = ioremap(spu_pdata(spu)->priv2_addr,
sizeof(struct spu_priv2));
+
if (!spu->priv2) {
pr_debug("%s:%d: ioremap priv2 failed\n", __func__, __LINE__);
goto fail_ioremap;
diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c
index 6bda51027cc6..4bb634a17e43 100644
--- a/arch/powerpc/platforms/ps3/system-bus.c
+++ b/arch/powerpc/platforms/ps3/system-bus.c
@@ -30,22 +30,228 @@
#include "platform.h"
+static struct device ps3_system_bus = {
+ .bus_id = "ps3_system",
+};
+
+/* FIXME: need device usage counters! */
+struct {
+ struct mutex mutex;
+ int sb_11; /* usb 0 */
+ int sb_12; /* usb 0 */
+ int gpu;
+} static usage_hack;
+
+static int ps3_is_device(struct ps3_system_bus_device *dev,
+ unsigned int bus_id, unsigned int dev_id)
+{
+ return dev->bus_id == bus_id && dev->dev_id == dev_id;
+}
+
+static int ps3_open_hv_device_sb(struct ps3_system_bus_device *dev)
+{
+ int result;
+
+ BUG_ON(!dev->bus_id);
+ mutex_lock(&usage_hack.mutex);
+
+ if (ps3_is_device(dev, 1, 1)) {
+ usage_hack.sb_11++;
+ if (usage_hack.sb_11 > 1) {
+ result = 0;
+ goto done;
+ }
+ }
+
+ if (ps3_is_device(dev, 1, 2)) {
+ usage_hack.sb_12++;
+ if (usage_hack.sb_12 > 1) {
+ result = 0;
+ goto done;
+ }
+ }
+
+ result = lv1_open_device(dev->bus_id, dev->dev_id, 0);
+
+ if (result) {
+ pr_debug("%s:%d: lv1_open_device failed: %s\n", __func__,
+ __LINE__, ps3_result(result));
+ result = -EPERM;
+ }
+
+done:
+ mutex_unlock(&usage_hack.mutex);
+ return result;
+}
+
+static int ps3_close_hv_device_sb(struct ps3_system_bus_device *dev)
+{
+ int result;
+
+ BUG_ON(!dev->bus_id);
+ mutex_lock(&usage_hack.mutex);
+
+ if (ps3_is_device(dev, 1, 1)) {
+ usage_hack.sb_11--;
+ if (usage_hack.sb_11) {
+ result = 0;
+ goto done;
+ }
+ }
+
+ if (ps3_is_device(dev, 1, 2)) {
+ usage_hack.sb_12--;
+ if (usage_hack.sb_12) {
+ result = 0;
+ goto done;
+ }
+ }
+
+ result = lv1_close_device(dev->bus_id, dev->dev_id);
+ BUG_ON(result);
+
+done:
+ mutex_unlock(&usage_hack.mutex);
+ return result;
+}
+
+static int ps3_open_hv_device_gpu(struct ps3_system_bus_device *dev)
+{
+ int result;
+
+ mutex_lock(&usage_hack.mutex);
+
+ usage_hack.gpu++;
+ if (usage_hack.gpu > 1) {
+ result = 0;
+ goto done;
+ }
+
+ result = lv1_gpu_open(0);
+
+ if (result) {
+ pr_debug("%s:%d: lv1_gpu_open failed: %s\n", __func__,
+ __LINE__, ps3_result(result));
+ result = -EPERM;
+ }
+
+done:
+ mutex_unlock(&usage_hack.mutex);
+ return result;
+}
+
+static int ps3_close_hv_device_gpu(struct ps3_system_bus_device *dev)
+{
+ int result;
+
+ mutex_lock(&usage_hack.mutex);
+
+ usage_hack.gpu--;
+ if (usage_hack.gpu) {
+ result = 0;
+ goto done;
+ }
+
+ result = lv1_gpu_close();
+ BUG_ON(result);
+
+done:
+ mutex_unlock(&usage_hack.mutex);
+ return result;
+}
+
+int ps3_open_hv_device(struct ps3_system_bus_device *dev)
+{
+ BUG_ON(!dev);
+ pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id);
+
+ switch (dev->match_id) {
+ case PS3_MATCH_ID_EHCI:
+ case PS3_MATCH_ID_OHCI:
+ case PS3_MATCH_ID_GELIC:
+ case PS3_MATCH_ID_STOR_DISK:
+ case PS3_MATCH_ID_STOR_ROM:
+ case PS3_MATCH_ID_STOR_FLASH:
+ return ps3_open_hv_device_sb(dev);
+
+ case PS3_MATCH_ID_SOUND:
+ case PS3_MATCH_ID_GRAPHICS:
+ 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);
+ BUG();
+ return -EINVAL;
+
+ default:
+ break;
+ }
+
+ pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__,
+ dev->match_id);
+ BUG();
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(ps3_open_hv_device);
+
+int ps3_close_hv_device(struct ps3_system_bus_device *dev)
+{
+ BUG_ON(!dev);
+ pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id);
+
+ switch (dev->match_id) {
+ case PS3_MATCH_ID_EHCI:
+ case PS3_MATCH_ID_OHCI:
+ case PS3_MATCH_ID_GELIC:
+ case PS3_MATCH_ID_STOR_DISK:
+ case PS3_MATCH_ID_STOR_ROM:
+ case PS3_MATCH_ID_STOR_FLASH:
+ return ps3_close_hv_device_sb(dev);
+
+ case PS3_MATCH_ID_SOUND:
+ case PS3_MATCH_ID_GRAPHICS:
+ 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);
+ BUG();
+ return -EINVAL;
+
+ default:
+ break;
+ }
+
+ pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__,
+ dev->match_id);
+ BUG();
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(ps3_close_hv_device);
+
#define dump_mmio_region(_a) _dump_mmio_region(_a, __func__, __LINE__)
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->did.bus_id,
- r->did.dev_id);
+ pr_debug("%s:%d: dev %u:%u\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);
pr_debug("%s:%d: lpar_addr %lxh\n", func, line, r->lpar_addr);
}
-int ps3_mmio_region_create(struct ps3_mmio_region *r)
+static int ps3_sb_mmio_region_create(struct ps3_mmio_region *r)
{
int result;
- result = lv1_map_device_mmio_region(r->did.bus_id, r->did.dev_id,
+ 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);
if (result) {
@@ -57,13 +263,26 @@ int ps3_mmio_region_create(struct ps3_mmio_region *r)
dump_mmio_region(r);
return result;
}
+
+static int ps3_ioc0_mmio_region_create(struct ps3_mmio_region *r)
+{
+ /* device specific; do nothing currently */
+ return 0;
+}
+
+int ps3_mmio_region_create(struct ps3_mmio_region *r)
+{
+ return r->mmio_ops->create(r);
+}
EXPORT_SYMBOL_GPL(ps3_mmio_region_create);
-int ps3_free_mmio_region(struct ps3_mmio_region *r)
+static int ps3_sb_free_mmio_region(struct ps3_mmio_region *r)
{
int result;
- result = lv1_unmap_device_mmio_region(r->did.bus_id, r->did.dev_id,
+ dump_mmio_region(r);
+;
+ result = lv1_unmap_device_mmio_region(r->dev->bus_id, r->dev->dev_id,
r->lpar_addr);
if (result)
@@ -73,14 +292,60 @@ int ps3_free_mmio_region(struct ps3_mmio_region *r)
r->lpar_addr = 0;
return result;
}
+
+static int ps3_ioc0_free_mmio_region(struct ps3_mmio_region *r)
+{
+ /* device specific; do nothing currently */
+ return 0;
+}
+
+
+int ps3_free_mmio_region(struct ps3_mmio_region *r)
+{
+ return r->mmio_ops->free(r);
+}
+
EXPORT_SYMBOL_GPL(ps3_free_mmio_region);
+static const struct ps3_mmio_region_ops ps3_mmio_sb_region_ops = {
+ .create = ps3_sb_mmio_region_create,
+ .free = ps3_sb_free_mmio_region
+};
+
+static const struct ps3_mmio_region_ops ps3_mmio_ioc0_region_ops = {
+ .create = ps3_ioc0_mmio_region_create,
+ .free = ps3_ioc0_free_mmio_region
+};
+
+int ps3_mmio_region_init(struct ps3_system_bus_device *dev,
+ struct ps3_mmio_region *r, unsigned long bus_addr, unsigned long len,
+ enum ps3_mmio_page_size page_size)
+{
+ r->dev = dev;
+ r->bus_addr = bus_addr;
+ r->len = len;
+ r->page_size = page_size;
+ switch (dev->dev_type) {
+ case PS3_DEVICE_TYPE_SB:
+ r->mmio_ops = &ps3_mmio_sb_region_ops;
+ break;
+ case PS3_DEVICE_TYPE_IOC0:
+ r->mmio_ops = &ps3_mmio_ioc0_region_ops;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ps3_mmio_region_init);
+
static int ps3_system_bus_match(struct device *_dev,
struct device_driver *_drv)
{
int result;
- struct ps3_system_bus_driver *drv = to_ps3_system_bus_driver(_drv);
- struct ps3_system_bus_device *dev = to_ps3_system_bus_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;
@@ -92,32 +357,14 @@ static int ps3_system_bus_match(struct device *_dev,
static int ps3_system_bus_probe(struct device *_dev)
{
- int result;
- struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
- struct ps3_system_bus_driver *drv =
- to_ps3_system_bus_driver(_dev->driver);
-
- result = lv1_open_device(dev->did.bus_id, dev->did.dev_id, 0);
-
- if (result) {
- pr_debug("%s:%d: lv1_open_device failed (%d)\n",
- __func__, __LINE__, result);
- result = -EACCES;
- goto clean_none;
- }
-
- if (dev->d_region->did.bus_id) {
- result = ps3_dma_region_create(dev->d_region);
+ int result = 0;
+ struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
+ struct ps3_system_bus_driver *drv;
- if (result) {
- pr_debug("%s:%d: ps3_dma_region_create failed (%d)\n",
- __func__, __LINE__, result);
- BUG_ON("check region type");
- result = -EINVAL;
- goto clean_device;
- }
- }
+ BUG_ON(!dev);
+ pr_info(" -> %s:%d: %s\n", __func__, __LINE__, _dev->bus_id);
+ drv = ps3_system_bus_dev_to_system_bus_drv(dev);
BUG_ON(!drv);
if (drv->probe)
@@ -126,56 +373,127 @@ static int ps3_system_bus_probe(struct device *_dev)
pr_info("%s:%d: %s no probe method\n", __func__, __LINE__,
dev->core.bus_id);
- if (result) {
- pr_debug("%s:%d: drv->probe failed\n", __func__, __LINE__);
- goto clean_dma;
- }
-
- return result;
-
-clean_dma:
- ps3_dma_region_free(dev->d_region);
-clean_device:
- lv1_close_device(dev->did.bus_id, dev->did.dev_id);
-clean_none:
+ pr_info(" <- %s:%d: %s\n", __func__, __LINE__, dev->core.bus_id);
return result;
}
static int ps3_system_bus_remove(struct device *_dev)
{
- struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
- struct ps3_system_bus_driver *drv =
- to_ps3_system_bus_driver(_dev->driver);
+ int result = 0;
+ struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
+ struct ps3_system_bus_driver *drv;
+
+ BUG_ON(!dev);
+ pr_info(" -> %s:%d: %s\n", __func__, __LINE__, _dev->bus_id);
+
+ drv = ps3_system_bus_dev_to_system_bus_drv(dev);
+ BUG_ON(!drv);
if (drv->remove)
- drv->remove(dev);
+ result = drv->remove(dev);
else
- pr_info("%s:%d: %s no remove method\n", __func__, __LINE__,
- dev->core.bus_id);
+ 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);
+ return result;
+}
+
+static void ps3_system_bus_shutdown(struct device *_dev)
+{
+ struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
+ struct ps3_system_bus_driver *drv;
+
+ BUG_ON(!dev);
+
+ dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__,
+ dev->match_id);
+
+ if (!dev->core.driver) {
+ dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__,
+ __LINE__);
+ return;
+ }
+
+ drv = ps3_system_bus_dev_to_system_bus_drv(dev);
+
+ BUG_ON(!drv);
+
+ dev_dbg(&dev->core, "%s:%d: %s -> %s\n", __func__, __LINE__,
+ dev->core.bus_id, drv->core.name);
+
+ if (drv->shutdown)
+ drv->shutdown(dev);
+ else if (drv->remove) {
+ dev_dbg(&dev->core, "%s:%d %s: no shutdown, calling remove\n",
+ __func__, __LINE__, drv->core.name);
+ drv->remove(dev);
+ } else {
+ dev_dbg(&dev->core, "%s:%d %s: no shutdown method\n",
+ __func__, __LINE__, drv->core.name);
+ BUG();
+ }
+
+ 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)
+{
+ struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
+ int i = 0, length = 0;
- ps3_dma_region_free(dev->d_region);
- ps3_free_mmio_region(dev->m_region);
- lv1_close_device(dev->did.bus_id, dev->did.dev_id);
+ if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
+ &length, "MODALIAS=ps3:%d",
+ dev->match_id))
+ return -ENOMEM;
+ envp[i] = NULL;
return 0;
}
+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);
+
+ return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static struct device_attribute ps3_system_bus_dev_attrs[] = {
+ __ATTR_RO(modalias),
+ __ATTR_NULL,
+};
+
struct bus_type ps3_system_bus_type = {
.name = "ps3_system_bus",
.match = ps3_system_bus_match,
+ .uevent = ps3_system_bus_uevent,
.probe = ps3_system_bus_probe,
.remove = ps3_system_bus_remove,
+ .shutdown = ps3_system_bus_shutdown,
+ .dev_attrs = ps3_system_bus_dev_attrs,
};
-int __init ps3_system_bus_init(void)
+static int __init ps3_system_bus_init(void)
{
int result;
if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
return -ENODEV;
+ pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+ mutex_init(&usage_hack.mutex);
+
+ result = device_register(&ps3_system_bus);
+ BUG_ON(result);
+
result = bus_register(&ps3_system_bus_type);
BUG_ON(result);
+
+ pr_debug(" <- %s:%d\n", __func__, __LINE__);
return result;
}
@@ -185,16 +503,13 @@ core_initcall(ps3_system_bus_init);
* Returns the virtual address of the buffer and sets dma_handle
* 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)
{
int result;
- struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
+ struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
unsigned long virt_addr;
- BUG_ON(!dev->d_region->bus_addr);
-
flag &= ~(__GFP_DMA | __GFP_HIGHMEM);
flag |= __GFP_ZERO;
@@ -205,7 +520,8 @@ static void * ps3_alloc_coherent(struct device *_dev, size_t size,
goto clean_none;
}
- result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle);
+ result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle,
+ IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M);
if (result) {
pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
@@ -226,7 +542,7 @@ clean_none:
static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr,
dma_addr_t dma_handle)
{
- struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
+ struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
ps3_dma_unmap(dev->d_region, dma_handle, size);
free_pages((unsigned long)vaddr, get_order(size));
@@ -239,15 +555,16 @@ static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr,
* byte within the page as vaddr.
*/
-static dma_addr_t ps3_map_single(struct device *_dev, void *ptr, size_t size,
+static dma_addr_t ps3_sb_map_single(struct device *_dev, void *ptr, size_t size,
enum dma_data_direction direction)
{
- struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
+ struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
int result;
unsigned long bus_addr;
result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size,
- &bus_addr);
+ &bus_addr,
+ IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW | IOPTE_M);
if (result) {
pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
@@ -257,10 +574,44 @@ static dma_addr_t ps3_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)
+{
+ struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
+ int result;
+ unsigned long bus_addr;
+ u64 iopte_flag;
+
+ iopte_flag = IOPTE_M;
+ switch (direction) {
+ case DMA_BIDIRECTIONAL:
+ iopte_flag |= IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW;
+ break;
+ case DMA_TO_DEVICE:
+ iopte_flag |= IOPTE_PP_R | IOPTE_SO_R;
+ break;
+ case DMA_FROM_DEVICE:
+ iopte_flag |= IOPTE_PP_W | IOPTE_SO_RW;
+ break;
+ default:
+ /* not happned */
+ BUG();
+ };
+ result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size,
+ &bus_addr, iopte_flag);
+
+ if (result) {
+ pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
+ __func__, __LINE__, result);
+ }
+ return bus_addr;
+}
+
static void ps3_unmap_single(struct device *_dev, dma_addr_t dma_addr,
size_t size, enum dma_data_direction direction)
{
- struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
+ struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
int result;
result = ps3_dma_unmap(dev->d_region, dma_addr, size);
@@ -271,20 +622,20 @@ static void ps3_unmap_single(struct device *_dev, dma_addr_t dma_addr,
}
}
-static int ps3_map_sg(struct device *_dev, struct scatterlist *sg, int nents,
+static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sg, int nents,
enum dma_data_direction direction)
{
#if defined(CONFIG_PS3_DYNAMIC_DMA)
BUG_ON("do");
return -EPERM;
#else
- struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
+ struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
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);
+ &sg->dma_address, 0);
if (result) {
pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
@@ -299,7 +650,15 @@ static int ps3_map_sg(struct device *_dev, struct scatterlist *sg, int nents,
#endif
}
-static void ps3_unmap_sg(struct device *_dev, struct scatterlist *sg,
+static int ps3_ioc0_map_sg(struct device *_dev, struct scatterlist *sg,
+ int nents,
+ enum dma_data_direction direction)
+{
+ BUG();
+ return 0;
+}
+
+static void ps3_sb_unmap_sg(struct device *_dev, struct scatterlist *sg,
int nents, enum dma_data_direction direction)
{
#if defined(CONFIG_PS3_DYNAMIC_DMA)
@@ -307,18 +666,34 @@ static void ps3_unmap_sg(struct device *_dev, struct scatterlist *sg,
#endif
}
+static void ps3_ioc0_unmap_sg(struct device *_dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction)
+{
+ BUG();
+}
+
static int ps3_dma_supported(struct device *_dev, u64 mask)
{
return mask >= DMA_32BIT_MASK;
}
-static struct dma_mapping_ops ps3_dma_ops = {
+static struct dma_mapping_ops ps3_sb_dma_ops = {
.alloc_coherent = ps3_alloc_coherent,
.free_coherent = ps3_free_coherent,
- .map_single = ps3_map_single,
+ .map_single = ps3_sb_map_single,
.unmap_single = ps3_unmap_single,
- .map_sg = ps3_map_sg,
- .unmap_sg = ps3_unmap_sg,
+ .map_sg = ps3_sb_map_sg,
+ .unmap_sg = ps3_sb_unmap_sg,
+ .dma_supported = ps3_dma_supported
+};
+
+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,
+ .map_sg = ps3_ioc0_map_sg,
+ .unmap_sg = ps3_ioc0_unmap_sg,
.dma_supported = ps3_dma_supported
};
@@ -328,7 +703,7 @@ static struct dma_mapping_ops ps3_dma_ops = {
static void ps3_system_bus_release_device(struct device *_dev)
{
- struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
+ struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
kfree(dev);
}
@@ -343,19 +718,38 @@ static void ps3_system_bus_release_device(struct device *_dev)
int ps3_system_bus_device_register(struct ps3_system_bus_device *dev)
{
int result;
- static unsigned int dev_count = 1;
+ static unsigned int dev_ioc0_count;
+ static unsigned int dev_sb_count;
+ static unsigned int dev_vuart_count;
- dev->core.parent = NULL;
+ if (!dev->core.parent)
+ dev->core.parent = &ps3_system_bus;
dev->core.bus = &ps3_system_bus_type;
dev->core.release = ps3_system_bus_release_device;
+ 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);
+ 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);
+
+ break;
+ case PS3_DEVICE_TYPE_VUART:
+ snprintf(dev->core.bus_id, sizeof(dev->core.bus_id),
+ "vuart_%02x", ++dev_vuart_count);
+ break;
+ default:
+ BUG();
+ };
+
dev->core.archdata.of_node = NULL;
- dev->core.archdata.dma_ops = &ps3_dma_ops;
dev->core.archdata.numa_node = 0;
- snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "sb_%02x",
- dev_count++);
-
pr_debug("%s:%d add %s\n", __func__, __LINE__, dev->core.bus_id);
result = device_register(&dev->core);
@@ -368,9 +762,15 @@ int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv)
{
int result;
+ pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name);
+
+ if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
+ return -ENODEV;
+
drv->core.bus = &ps3_system_bus_type;
result = driver_register(&drv->core);
+ pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name);
return result;
}
@@ -378,7 +778,9 @@ EXPORT_SYMBOL_GPL(ps3_system_bus_driver_register);
void ps3_system_bus_driver_unregister(struct ps3_system_bus_driver *drv)
{
+ pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name);
driver_unregister(&drv->core);
+ pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name);
}
EXPORT_SYMBOL_GPL(ps3_system_bus_driver_unregister);
diff --git a/arch/powerpc/platforms/ps3/time.c b/arch/powerpc/platforms/ps3/time.c
index 1bae8b19b363..802a9ccacb5e 100644
--- a/arch/powerpc/platforms/ps3/time.c
+++ b/arch/powerpc/platforms/ps3/time.c
@@ -39,7 +39,7 @@ static void _dump_tm(const struct rtc_time *tm, const char* func, int line)
}
#define dump_time(_a) _dump_time(_a, __func__, __LINE__)
-static void __attribute__ ((unused)) _dump_time(int time, const char* func,
+static void __maybe_unused _dump_time(int time, const char *func,
int line)
{
struct rtc_time tm;