summaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/ps3
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/ps3')
-rw-r--r--arch/powerpc/platforms/ps3/Kconfig55
-rw-r--r--arch/powerpc/platforms/ps3/Makefile1
-rw-r--r--arch/powerpc/platforms/ps3/htab.c9
-rw-r--r--arch/powerpc/platforms/ps3/interrupt.c444
-rw-r--r--arch/powerpc/platforms/ps3/mm.c3
-rw-r--r--arch/powerpc/platforms/ps3/os-area.c22
-rw-r--r--arch/powerpc/platforms/ps3/platform.h151
-rw-r--r--arch/powerpc/platforms/ps3/repository.c220
-rw-r--r--arch/powerpc/platforms/ps3/setup.c86
-rw-r--r--arch/powerpc/platforms/ps3/smp.c3
-rw-r--r--arch/powerpc/platforms/ps3/spu.c62
-rw-r--r--arch/powerpc/platforms/ps3/system-bus.c384
12 files changed, 1152 insertions, 288 deletions
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
index de52ec4e9e58..40f0008af4d1 100644
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -1,9 +1,40 @@
+config PPC_PS3
+ bool "Sony PS3 (incomplete)"
+ depends on PPC_MULTIPLATFORM && PPC64
+ select PPC_CELL
+ select USB_ARCH_HAS_OHCI
+ select USB_OHCI_LITTLE_ENDIAN
+ select USB_OHCI_BIG_ENDIAN_MMIO
+ select USB_ARCH_HAS_EHCI
+ select USB_EHCI_BIG_ENDIAN_MMIO
+ help
+ This option enables support for the Sony PS3 game console
+ and other platforms using the PS3 hypervisor.
+ Support for this platform is not yet complete, so
+ enabling this will not result in a bootable kernel on a
+ PS3 system.
+
menu "PS3 Platform Options"
depends on PPC_PS3
+config PS3_ADVANCED
+ depends on PPC_PS3
+ bool "PS3 Advanced configuration options"
+ help
+ This gives you access to some advanced options for the PS3. The
+ defaults should be fine for most users, but these options may make
+ it possible to better control the kernel configuration if you know
+ what you are doing.
+
+ Note that the answer to this question won't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about these options.
+
+ Most users should say N to this question.
+
config PS3_HTAB_SIZE
depends on PPC_PS3
- int "PS3 Platform pagetable size"
+ int "PS3 Platform pagetable size" if PS3_ADVANCED
range 18 20
default 20
help
@@ -42,7 +73,7 @@ config PS3_USE_LPAR_ADDR
config PS3_VUART
depends on PPC_PS3
- bool "PS3 Virtual UART support"
+ bool "PS3 Virtual UART support" if PS3_ADVANCED
default y
help
Include support for the PS3 Virtual UART.
@@ -51,4 +82,24 @@ config PS3_VUART
including the System Manager and AV Settings. In
general, all users will say Y.
+config PS3_PS3AV
+ tristate "PS3 AV settings driver" if PS3_ADVANCED
+ depends on PS3_VUART
+ default y
+ help
+ Include support for the PS3 AV Settings driver.
+
+ This support is required for graphics and sound. In
+ 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
+ help
+ Include support for the PS3 System Manager.
+
+ This support is required for system control. In
+ general, all users will say Y.
+
endmenu
diff --git a/arch/powerpc/platforms/ps3/Makefile b/arch/powerpc/platforms/ps3/Makefile
index 1994904f580f..a0048fcf0866 100644
--- a/arch/powerpc/platforms/ps3/Makefile
+++ b/arch/powerpc/platforms/ps3/Makefile
@@ -1,5 +1,6 @@
obj-y += setup.o mm.o time.o hvcall.o htab.o repository.o
obj-y += interrupt.o exports.o os-area.o
+obj-y += system-bus.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_SPU_BASE) += spu.o
diff --git a/arch/powerpc/platforms/ps3/htab.c b/arch/powerpc/platforms/ps3/htab.c
index 8fe1769655a3..ea60c451cf87 100644
--- a/arch/powerpc/platforms/ps3/htab.c
+++ b/arch/powerpc/platforms/ps3/htab.c
@@ -2,7 +2,7 @@
* PS3 pagetable management routines.
*
* Copyright (C) 2006 Sony Computer Entertainment Inc.
- * Copyright 2006 Sony Corp.
+ * Copyright 2006, 2007 Sony Corporation
*
* 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
@@ -23,8 +23,8 @@
#include <asm/machdep.h>
#include <asm/lmb.h>
#include <asm/udbg.h>
-#include <asm/ps3.h>
#include <asm/lv1call.h>
+#include <asm/ps3fb.h>
#include "platform.h"
@@ -39,7 +39,7 @@ static unsigned long htab_addr;
static unsigned char *bolttab;
static unsigned char *inusetab;
-static spinlock_t ps3_bolttab_lock = SPIN_LOCK_UNLOCKED;
+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__)
@@ -234,6 +234,9 @@ 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();
+
lv1_unmap_htab(htab_addr);
}
diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c
index 6f5de438b980..631c30095617 100644
--- a/arch/powerpc/platforms/ps3/interrupt.c
+++ b/arch/powerpc/platforms/ps3/interrupt.c
@@ -24,7 +24,6 @@
#include <asm/machdep.h>
#include <asm/udbg.h>
-#include <asm/ps3.h>
#include <asm/lv1call.h>
#include "platform.h"
@@ -36,15 +35,148 @@
#endif
/**
+ * struct ps3_bmp - a per cpu irq status and mask bitmap structure
+ * @status: 256 bit status bitmap indexed by plug
+ * @unused_1:
+ * @mask: 256 bit mask bitmap indexed by plug
+ * @unused_2:
+ * @lock:
+ * @ipi_debug_brk_mask:
+ *
+ * The HV mantains per SMT thread mappings of HV outlet to HV plug on
+ * behalf of the guest. These mappings are implemented as 256 bit guest
+ * supplied bitmaps indexed by plug number. The addresses of the bitmaps
+ * are registered with the HV through lv1_configure_irq_state_bitmap().
+ * The HV requires that the 512 bits of status + mask not cross a page
+ * boundary. PS3_BMP_MINALIGN is used to define this minimal 64 byte
+ * alignment.
+ *
+ * The HV supports 256 plugs per thread, assigned as {0..255}, for a total
+ * of 512 plugs supported on a processor. To simplify the logic this
+ * implementation equates HV plug value to Linux virq value, constrains each
+ * interrupt to have a system wide unique plug number, and limits the range
+ * of the plug values to map into the first dword of the bitmaps. This
+ * gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note
+ * that there is no constraint on how many in this set an individual thread
+ * can acquire.
+ */
+
+#define PS3_BMP_MINALIGN 64
+
+struct ps3_bmp {
+ struct {
+ u64 status;
+ u64 unused_1[3];
+ u64 mask;
+ u64 unused_2[3];
+ };
+ u64 ipi_debug_brk_mask;
+ spinlock_t lock;
+};
+
+/**
+ * struct ps3_private - a per cpu data structure
+ * @bmp: ps3_bmp structure
+ * @node: HV logical_ppe_id
+ * @cpu: HV thread_id
+ */
+
+struct ps3_private {
+ struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN)));
+ u64 node;
+ unsigned int cpu;
+};
+
+static DEFINE_PER_CPU(struct ps3_private, ps3_private);
+
+int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet,
+ unsigned int *virq)
+{
+ int result;
+ struct ps3_private *pd;
+
+ /* This defines the default interrupt distribution policy. */
+
+ if (cpu == PS3_BINDING_CPU_ANY)
+ cpu = 0;
+
+ pd = &per_cpu(ps3_private, cpu);
+
+ *virq = irq_create_mapping(NULL, outlet);
+
+ if (*virq == NO_IRQ) {
+ pr_debug("%s:%d: irq_create_mapping failed: outlet %lu\n",
+ __func__, __LINE__, outlet);
+ result = -ENOMEM;
+ goto fail_create;
+ }
+
+ /* Binds outlet to cpu + virq. */
+
+ result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0);
+
+ if (result) {
+ pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+ result = -EPERM;
+ goto fail_connect;
+ }
+
+ pr_debug("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__,
+ outlet, cpu, *virq);
+
+ result = set_irq_chip_data(*virq, pd);
+
+ if (result) {
+ pr_debug("%s:%d: set_irq_chip_data failed\n",
+ __func__, __LINE__);
+ goto fail_set;
+ }
+
+ return result;
+
+fail_set:
+ lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, *virq);
+fail_connect:
+ irq_dispose_mapping(*virq);
+fail_create:
+ return result;
+}
+EXPORT_SYMBOL_GPL(ps3_alloc_irq);
+
+int ps3_free_irq(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);
+
+ result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq);
+
+ if (result)
+ pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+
+ set_irq_chip_data(virq, NULL);
+ irq_dispose_mapping(virq);
+ return result;
+}
+EXPORT_SYMBOL_GPL(ps3_free_irq);
+
+/**
* ps3_alloc_io_irq - Assign a virq to a system bus device.
- * interrupt_id: The device interrupt id read from the system repository.
+ * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
+ * serviced on.
+ * @interrupt_id: The device interrupt id read from the system repository.
* @virq: The assigned Linux virq.
*
* An io irq represents a non-virtualized device interrupt. interrupt_id
* coresponds to the interrupt number of the interrupt controller.
*/
-int ps3_alloc_io_irq(unsigned int interrupt_id, unsigned int *virq)
+int ps3_alloc_io_irq(enum ps3_cpu_binding cpu, unsigned int interrupt_id,
+ unsigned int *virq)
{
int result;
unsigned long outlet;
@@ -57,13 +189,12 @@ int ps3_alloc_io_irq(unsigned int interrupt_id, unsigned int *virq)
return result;
}
- *virq = irq_create_mapping(NULL, outlet);
-
- pr_debug("%s:%d: interrupt_id %u => outlet %lu, virq %u\n",
- __func__, __LINE__, interrupt_id, outlet, *virq);
+ result = ps3_alloc_irq(cpu, outlet, virq);
+ BUG_ON(result);
- return 0;
+ return result;
}
+EXPORT_SYMBOL_GPL(ps3_alloc_io_irq);
int ps3_free_io_irq(unsigned int virq)
{
@@ -75,13 +206,16 @@ int ps3_free_io_irq(unsigned int virq)
pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
__func__, __LINE__, ps3_result(result));
- irq_dispose_mapping(virq);
+ ps3_free_irq(virq);
return result;
}
+EXPORT_SYMBOL_GPL(ps3_free_io_irq);
/**
* ps3_alloc_event_irq - Allocate a virq for use with a system event.
+ * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
+ * serviced on.
* @virq: The assigned Linux virq.
*
* The virq can be used with lv1_connect_interrupt_event_receive_port() to
@@ -89,7 +223,7 @@ int ps3_free_io_irq(unsigned int virq)
* events.
*/
-int ps3_alloc_event_irq(unsigned int *virq)
+int ps3_alloc_event_irq(enum ps3_cpu_binding cpu, unsigned int *virq)
{
int result;
unsigned long outlet;
@@ -103,12 +237,10 @@ int ps3_alloc_event_irq(unsigned int *virq)
return result;
}
- *virq = irq_create_mapping(NULL, outlet);
-
- pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__, outlet,
- *virq);
+ result = ps3_alloc_irq(cpu, outlet, virq);
+ BUG_ON(result);
- return 0;
+ return result;
}
int ps3_free_event_irq(unsigned int virq)
@@ -123,7 +255,7 @@ int ps3_free_event_irq(unsigned int virq)
pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n",
__func__, __LINE__, ps3_result(result));
- irq_dispose_mapping(virq);
+ ps3_free_irq(virq);
pr_debug(" <- %s:%d\n", __func__, __LINE__);
return result;
@@ -136,6 +268,8 @@ int ps3_send_event_locally(unsigned int virq)
/**
* ps3_connect_event_irq - Assign a virq to a system bus device.
+ * @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.
* @virq: The assigned Linux virq.
@@ -144,12 +278,13 @@ int ps3_send_event_locally(unsigned int virq)
* coresponds to the software interrupt number.
*/
-int ps3_connect_event_irq(const struct ps3_device_id *did,
- unsigned int interrupt_id, unsigned int *virq)
+int ps3_connect_event_irq(enum ps3_cpu_binding cpu,
+ const struct ps3_device_id *did, unsigned int interrupt_id,
+ unsigned int *virq)
{
int result;
- result = ps3_alloc_event_irq(virq);
+ result = ps3_alloc_event_irq(cpu, virq);
if (result)
return result;
@@ -196,6 +331,8 @@ int ps3_disconnect_event_irq(const struct ps3_device_id *did,
/**
* ps3_alloc_vuart_irq - Configure the system virtual uart virq.
+ * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
+ * serviced on.
* @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap.
* @virq: The assigned Linux virq.
*
@@ -203,13 +340,14 @@ int ps3_disconnect_event_irq(const struct ps3_device_id *did,
* freeing the interrupt will return a wrong state error.
*/
-int ps3_alloc_vuart_irq(void* virt_addr_bmp, unsigned int *virq)
+int ps3_alloc_vuart_irq(enum ps3_cpu_binding cpu, void* virt_addr_bmp,
+ unsigned int *virq)
{
int result;
unsigned long outlet;
- unsigned long lpar_addr;
+ u64 lpar_addr;
- BUG_ON(!is_kernel_addr((unsigned long)virt_addr_bmp));
+ BUG_ON(!is_kernel_addr((u64)virt_addr_bmp));
lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp));
@@ -221,12 +359,10 @@ int ps3_alloc_vuart_irq(void* virt_addr_bmp, unsigned int *virq)
return result;
}
- *virq = irq_create_mapping(NULL, outlet);
-
- pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__,
- outlet, *virq);
+ result = ps3_alloc_irq(cpu, outlet, virq);
+ BUG_ON(result);
- return 0;
+ return result;
}
int ps3_free_vuart_irq(unsigned int virq)
@@ -241,21 +377,23 @@ int ps3_free_vuart_irq(unsigned int virq)
return result;
}
- irq_dispose_mapping(virq);
+ ps3_free_irq(virq);
return result;
}
/**
* ps3_alloc_spe_irq - Configure an spe virq.
+ * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
+ * serviced on.
* @spe_id: The spe_id returned from lv1_construct_logical_spe().
* @class: The spe interrupt class {0,1,2}.
* @virq: The assigned Linux virq.
*
*/
-int ps3_alloc_spe_irq(unsigned long spe_id, unsigned int class,
- unsigned int *virq)
+int ps3_alloc_spe_irq(enum ps3_cpu_binding cpu, unsigned long spe_id,
+ unsigned int class, unsigned int *virq)
{
int result;
unsigned long outlet;
@@ -270,73 +408,24 @@ int ps3_alloc_spe_irq(unsigned long spe_id, unsigned int class,
return result;
}
- *virq = irq_create_mapping(NULL, outlet);
-
- pr_debug("%s:%d: spe_id %lu, class %u, outlet %lu, virq %u\n",
- __func__, __LINE__, spe_id, class, outlet, *virq);
+ result = ps3_alloc_irq(cpu, outlet, virq);
+ BUG_ON(result);
- return 0;
+ return result;
}
int ps3_free_spe_irq(unsigned int virq)
{
- irq_dispose_mapping(virq);
+ ps3_free_irq(virq);
return 0;
}
+
#define PS3_INVALID_OUTLET ((irq_hw_number_t)-1)
#define PS3_PLUG_MAX 63
-/**
- * struct bmp - a per cpu irq status and mask bitmap structure
- * @status: 256 bit status bitmap indexed by plug
- * @unused_1:
- * @mask: 256 bit mask bitmap indexed by plug
- * @unused_2:
- * @lock:
- * @ipi_debug_brk_mask:
- *
- * The HV mantains per SMT thread mappings of HV outlet to HV plug on
- * behalf of the guest. These mappings are implemented as 256 bit guest
- * supplied bitmaps indexed by plug number. The address of the bitmaps are
- * registered with the HV through lv1_configure_irq_state_bitmap().
- *
- * The HV supports 256 plugs per thread, assigned as {0..255}, for a total
- * of 512 plugs supported on a processor. To simplify the logic this
- * implementation equates HV plug value to linux virq value, constrains each
- * interrupt to have a system wide unique plug number, and limits the range
- * of the plug values to map into the first dword of the bitmaps. This
- * gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note
- * that there is no constraint on how many in this set an individual thread
- * can aquire.
- */
-
-struct bmp {
- struct {
- unsigned long status;
- unsigned long unused_1[3];
- unsigned long mask;
- unsigned long unused_2[3];
- } __attribute__ ((packed));
- spinlock_t lock;
- unsigned long ipi_debug_brk_mask;
-};
-
-/**
- * struct private - a per cpu data structure
- * @node: HV node id
- * @cpu: HV thread id
- * @bmp: an HV bmp structure
- */
-
-struct private {
- unsigned long node;
- unsigned int cpu;
- struct bmp bmp;
-};
-
#if defined(DEBUG)
-static void _dump_64_bmp(const char *header, const unsigned long *p, unsigned cpu,
+static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu,
const char* func, int line)
{
pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n",
@@ -346,14 +435,14 @@ static void _dump_64_bmp(const char *header, const unsigned long *p, unsigned cp
}
static void __attribute__ ((unused)) _dump_256_bmp(const char *header,
- const unsigned long *p, unsigned cpu, const char* func, int line)
+ const u64 *p, unsigned cpu, const char* func, int line)
{
pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n",
func, line, header, cpu, p[0], p[1], p[2], p[3]);
}
#define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__)
-static void _dump_bmp(struct private* pd, const char* func, int line)
+static void _dump_bmp(struct ps3_private* pd, const char* func, int line)
{
unsigned long flags;
@@ -364,7 +453,7 @@ static void _dump_bmp(struct private* pd, const char* func, int line)
}
#define dump_mask(_x) _dump_mask(_x, __func__, __LINE__)
-static void __attribute__ ((unused)) _dump_mask(struct private* pd,
+static void __attribute__ ((unused)) _dump_mask(struct ps3_private* pd,
const char* func, int line)
{
unsigned long flags;
@@ -374,109 +463,94 @@ static void __attribute__ ((unused)) _dump_mask(struct private* pd,
spin_unlock_irqrestore(&pd->bmp.lock, flags);
}
#else
-static void dump_bmp(struct private* pd) {};
+static void dump_bmp(struct ps3_private* pd) {};
#endif /* defined(DEBUG) */
-static void chip_mask(unsigned int virq)
+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;
- struct private *pd = get_irq_chip_data(virq);
pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
- BUG_ON(virq < NUM_ISA_INTERRUPTS);
- BUG_ON(virq > PS3_PLUG_MAX);
-
- spin_lock_irqsave(&pd->bmp.lock, flags);
- pd->bmp.mask &= ~(0x8000000000000000UL >> virq);
- spin_unlock_irqrestore(&pd->bmp.lock, flags);
+ 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 chip_unmask(unsigned int virq)
+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;
- struct private *pd = get_irq_chip_data(virq);
pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
- BUG_ON(virq < NUM_ISA_INTERRUPTS);
- BUG_ON(virq > PS3_PLUG_MAX);
-
- spin_lock_irqsave(&pd->bmp.lock, flags);
- pd->bmp.mask |= (0x8000000000000000UL >> virq);
- spin_unlock_irqrestore(&pd->bmp.lock, flags);
+ 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 chip_eoi(unsigned int virq)
+static void ps3_chip_eoi(unsigned int virq)
{
- lv1_end_of_interrupt(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 = chip_mask,
- .unmask = chip_unmask,
- .eoi = chip_eoi,
+ .mask = ps3_chip_mask,
+ .unmask = ps3_chip_unmask,
+ .eoi = ps3_chip_eoi,
};
-static void host_unmap(struct irq_host *h, unsigned int virq)
+static void ps3_host_unmap(struct irq_host *h, unsigned int virq)
{
- int result;
-
- pr_debug("%s:%d: virq %d\n", __func__, __LINE__, virq);
-
- lv1_disconnect_irq_plug(virq);
-
- result = set_irq_chip_data(virq, NULL);
- BUG_ON(result);
+ set_irq_chip_data(virq, NULL);
}
-static DEFINE_PER_CPU(struct private, private);
-
-static int host_map(struct irq_host *h, unsigned int virq,
+static int ps3_host_map(struct irq_host *h, unsigned int virq,
irq_hw_number_t hwirq)
{
- int result;
- unsigned int cpu;
-
- pr_debug(" -> %s:%d\n", __func__, __LINE__);
- pr_debug("%s:%d: hwirq %lu => virq %u\n", __func__, __LINE__, hwirq,
+ pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq,
virq);
- /* bind this virq to a cpu */
-
- preempt_disable();
- cpu = smp_processor_id();
- result = lv1_connect_irq_plug(virq, hwirq);
- preempt_enable();
-
- if (result) {
- pr_info("%s:%d: lv1_connect_irq_plug failed:"
- " %s\n", __func__, __LINE__, ps3_result(result));
- return -EPERM;
- }
-
- result = set_irq_chip_data(virq, &per_cpu(private, cpu));
- BUG_ON(result);
-
set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq);
- pr_debug(" <- %s:%d\n", __func__, __LINE__);
- return result;
+ return 0;
}
-static struct irq_host_ops host_ops = {
- .map = host_map,
- .unmap = host_unmap,
+static struct irq_host_ops ps3_host_ops = {
+ .map = ps3_host_map,
+ .unmap = ps3_host_unmap,
};
void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq)
{
- struct private *pd = &per_cpu(private, cpu);
+ struct ps3_private *pd = &per_cpu(ps3_private, cpu);
pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq;
@@ -484,57 +558,32 @@ void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq)
cpu, virq, pd->bmp.ipi_debug_brk_mask);
}
-static int bmp_get_and_clear_status_bit(struct bmp *m)
+unsigned int ps3_get_irq(void)
{
- unsigned long flags;
- unsigned int bit;
- unsigned long x;
-
- spin_lock_irqsave(&m->lock, flags);
+ struct ps3_private *pd = &__get_cpu_var(ps3_private);
+ u64 x = (pd->bmp.status & pd->bmp.mask);
+ unsigned int plug;
/* check for ipi break first to stop this cpu ASAP */
- if (m->status & m->ipi_debug_brk_mask) {
- m->status &= ~m->ipi_debug_brk_mask;
- spin_unlock_irqrestore(&m->lock, flags);
- return __ilog2(m->ipi_debug_brk_mask);
- }
-
- x = (m->status & m->mask);
+ if (x & pd->bmp.ipi_debug_brk_mask)
+ x &= pd->bmp.ipi_debug_brk_mask;
- for (bit = NUM_ISA_INTERRUPTS, x <<= bit; x; bit++, x <<= 1)
- if (x & 0x8000000000000000UL) {
- m->status &= ~(0x8000000000000000UL >> bit);
- spin_unlock_irqrestore(&m->lock, flags);
- return bit;
- }
+ asm volatile("cntlzd %0,%1" : "=r" (plug) : "r" (x));
+ plug &= 0x3f;
- spin_unlock_irqrestore(&m->lock, flags);
-
- pr_debug("%s:%d: not found\n", __func__, __LINE__);
- return -1;
-}
-
-unsigned int ps3_get_irq(void)
-{
- int plug;
-
- struct private *pd = &__get_cpu_var(private);
-
- plug = bmp_get_and_clear_status_bit(&pd->bmp);
-
- if (plug < 1) {
+ if (unlikely(plug) == NO_IRQ) {
pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__,
pd->cpu);
- dump_bmp(&per_cpu(private, 0));
- dump_bmp(&per_cpu(private, 1));
+ dump_bmp(&per_cpu(ps3_private, 0));
+ dump_bmp(&per_cpu(ps3_private, 1));
return NO_IRQ;
}
#if defined(DEBUG)
- if (plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX) {
- dump_bmp(&per_cpu(private, 0));
- dump_bmp(&per_cpu(private, 1));
+ if (unlikely(plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX)) {
+ dump_bmp(&per_cpu(ps3_private, 0));
+ dump_bmp(&per_cpu(ps3_private, 1));
BUG();
}
#endif
@@ -544,26 +593,27 @@ unsigned int ps3_get_irq(void)
void __init ps3_init_IRQ(void)
{
int result;
- unsigned long node;
unsigned cpu;
struct irq_host *host;
- lv1_get_logical_ppe_id(&node);
-
- host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &host_ops,
+ host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &ps3_host_ops,
PS3_INVALID_OUTLET);
irq_set_default_host(host);
irq_set_virq_count(PS3_PLUG_MAX + 1);
for_each_possible_cpu(cpu) {
- struct private *pd = &per_cpu(private, cpu);
+ struct ps3_private *pd = &per_cpu(ps3_private, cpu);
- pd->node = node;
- pd->cpu = cpu;
+ lv1_get_logical_ppe_id(&pd->node);
+ pd->cpu = get_hard_smp_processor_id(cpu);
spin_lock_init(&pd->bmp.lock);
- result = lv1_configure_irq_state_bitmap(node, cpu,
- ps3_mm_phys_to_lpar(__pa(&pd->bmp.status)));
+ pr_debug("%s:%d: node %lu, cpu %d, bmp %lxh\n", __func__,
+ __LINE__, pd->node, pd->cpu,
+ 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)));
if (result)
pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:"
diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c
index 49c0d010d491..2014d2b44449 100644
--- a/arch/powerpc/platforms/ps3/mm.c
+++ b/arch/powerpc/platforms/ps3/mm.c
@@ -25,7 +25,6 @@
#include <asm/firmware.h>
#include <asm/lmb.h>
#include <asm/udbg.h>
-#include <asm/ps3.h>
#include <asm/lv1call.h>
#include "platform.h"
@@ -295,7 +294,7 @@ static int __init ps3_mm_add_memory(void)
unsigned long nr_pages;
if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
- return 0;
+ return -ENODEV;
BUG_ON(!mem_init_done);
diff --git a/arch/powerpc/platforms/ps3/os-area.c b/arch/powerpc/platforms/ps3/os-area.c
index 58358305dc10..5c3da08bc0c4 100644
--- a/arch/powerpc/platforms/ps3/os-area.c
+++ b/arch/powerpc/platforms/ps3/os-area.c
@@ -22,7 +22,6 @@
#include <linux/io.h>
#include <asm/lmb.h>
-#include <asm/ps3.h>
#include "platform.h"
@@ -59,7 +58,7 @@ struct os_area_header {
u32 ldr_format;
u32 ldr_size;
u32 _reserved_2[6];
-} __attribute__ ((packed));
+};
enum {
PARAM_BOOT_FLAG_GAME_OS = 0,
@@ -67,13 +66,6 @@ enum {
};
enum {
- PARAM_AV_MULTI_OUT_NTSC = 0,
- PARAM_AV_MULTI_OUT_PAL_RGB = 1,
- PARAM_AV_MULTI_OUT_PAL_YCBCR = 2,
- PARAM_AV_MULTI_OUT_SECAM = 3,
-};
-
-enum {
PARAM_CTRL_BUTTON_O_IS_YES = 0,
PARAM_CTRL_BUTTON_X_IS_YES = 1,
};
@@ -114,7 +106,7 @@ struct os_area_params {
u8 dns_primary[4];
u8 dns_secondary[4];
u8 _reserved_5[8];
-} __attribute__ ((packed));
+};
/**
* struct saved_params - Static working copies of data from the 'Other OS' area.
@@ -257,3 +249,13 @@ u64 ps3_os_area_rtc_diff(void)
{
return saved_params.rtc_diff ? saved_params.rtc_diff : 946684800UL;
}
+
+/**
+ * ps3_os_area_get_av_multi_out - Returns the default video mode.
+ */
+
+enum ps3_param_av_multi_out ps3_os_area_get_av_multi_out(void)
+{
+ return saved_params.av_multi_out;
+}
+EXPORT_SYMBOL_GPL(ps3_os_area_get_av_multi_out);
diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h
index 23b111bea9d0..ca04f03305c7 100644
--- a/arch/powerpc/platforms/ps3/platform.h
+++ b/arch/powerpc/platforms/ps3/platform.h
@@ -22,6 +22,9 @@
#define _PS3_PLATFORM_H
#include <linux/rtc.h>
+#include <scsi/scsi.h>
+
+#include <asm/ps3.h>
/* htab */
@@ -65,4 +68,152 @@ void ps3_spu_set_platform (void);
static inline void ps3_spu_set_platform (void) {}
#endif
+/* repository bus info */
+
+enum ps3_bus_type {
+ PS3_BUS_TYPE_SB = 4,
+ PS3_BUS_TYPE_STORAGE = 5,
+};
+
+enum ps3_dev_type {
+ PS3_DEV_TYPE_STOR_DISK = TYPE_DISK, /* 0 */
+ PS3_DEV_TYPE_SB_GELIC = 3,
+ PS3_DEV_TYPE_SB_USB = 4,
+ PS3_DEV_TYPE_STOR_ROM = TYPE_ROM, /* 5 */
+ PS3_DEV_TYPE_SB_GPIO = 6,
+ PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC, /* 14 */
+};
+
+int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str,
+ u64 *value);
+int ps3_repository_read_bus_id(unsigned int bus_index, unsigned int *bus_id);
+int ps3_repository_read_bus_type(unsigned int bus_index,
+ enum ps3_bus_type *bus_type);
+int ps3_repository_read_bus_num_dev(unsigned int bus_index,
+ unsigned int *num_dev);
+
+/* repository bus device info */
+
+enum ps3_interrupt_type {
+ PS3_INTERRUPT_TYPE_EVENT_PORT = 2,
+ PS3_INTERRUPT_TYPE_SB_OHCI = 3,
+ PS3_INTERRUPT_TYPE_SB_EHCI = 4,
+ PS3_INTERRUPT_TYPE_OTHER = 5,
+};
+
+enum ps3_reg_type {
+ PS3_REG_TYPE_SB_OHCI = 3,
+ PS3_REG_TYPE_SB_EHCI = 4,
+ PS3_REG_TYPE_SB_GPIO = 5,
+};
+
+int ps3_repository_read_dev_str(unsigned int bus_index,
+ unsigned int dev_index, const char *dev_str, u64 *value);
+int ps3_repository_read_dev_id(unsigned int bus_index, unsigned int dev_index,
+ unsigned int *dev_id);
+int ps3_repository_read_dev_type(unsigned int bus_index,
+ unsigned int dev_index, enum ps3_dev_type *dev_type);
+int ps3_repository_read_dev_intr(unsigned int bus_index,
+ unsigned int dev_index, unsigned int intr_index,
+ enum ps3_interrupt_type *intr_type, unsigned int *interrupt_id);
+int ps3_repository_read_dev_reg_type(unsigned int bus_index,
+ unsigned int dev_index, unsigned int reg_index,
+ enum ps3_reg_type *reg_type);
+int ps3_repository_read_dev_reg_addr(unsigned int bus_index,
+ unsigned int dev_index, unsigned int reg_index, u64 *bus_addr,
+ u64 *len);
+int ps3_repository_read_dev_reg(unsigned int bus_index,
+ unsigned int dev_index, unsigned int reg_index,
+ enum ps3_reg_type *reg_type, u64 *bus_addr, u64 *len);
+
+/* repository bus enumerators */
+
+struct ps3_repository_device {
+ unsigned int bus_index;
+ unsigned int dev_index;
+ struct ps3_device_id did;
+};
+
+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)
+{
+ return ps3_repository_find_device(bus_type, dev_type, NULL, dev);
+}
+int ps3_repository_find_interrupt(const struct ps3_repository_device *dev,
+ enum ps3_interrupt_type intr_type, unsigned int *interrupt_id);
+int ps3_repository_find_reg(const struct ps3_repository_device *dev,
+ enum ps3_reg_type reg_type, u64 *bus_addr, u64 *len);
+
+/* repository block device info */
+
+int ps3_repository_read_stor_dev_port(unsigned int bus_index,
+ unsigned int dev_index, u64 *port);
+int ps3_repository_read_stor_dev_blk_size(unsigned int bus_index,
+ unsigned int dev_index, u64 *blk_size);
+int ps3_repository_read_stor_dev_num_blocks(unsigned int bus_index,
+ unsigned int dev_index, u64 *num_blocks);
+int ps3_repository_read_stor_dev_num_regions(unsigned int bus_index,
+ unsigned int dev_index, unsigned int *num_regions);
+int ps3_repository_read_stor_dev_region_id(unsigned int bus_index,
+ unsigned int dev_index, unsigned int region_index,
+ unsigned int *region_id);
+int ps3_repository_read_stor_dev_region_size(unsigned int bus_index,
+ unsigned int dev_index, unsigned int region_index, u64 *region_size);
+int ps3_repository_read_stor_dev_region_start(unsigned int bus_index,
+ unsigned int dev_index, unsigned int region_index, u64 *region_start);
+int ps3_repository_read_stor_dev_info(unsigned int bus_index,
+ unsigned int dev_index, u64 *port, u64 *blk_size,
+ u64 *num_blocks, unsigned int *num_regions);
+int ps3_repository_read_stor_dev_region(unsigned int bus_index,
+ unsigned int dev_index, unsigned int region_index,
+ unsigned int *region_id, u64 *region_start, u64 *region_size);
+
+/* repository pu and memory info */
+
+int ps3_repository_read_num_pu(unsigned int *num_pu);
+int ps3_repository_read_ppe_id(unsigned int *pu_index, unsigned int *ppe_id);
+int ps3_repository_read_rm_base(unsigned int ppe_id, u64 *rm_base);
+int ps3_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size);
+int ps3_repository_read_region_total(u64 *region_total);
+int ps3_repository_read_mm_info(u64 *rm_base, u64 *rm_size,
+ u64 *region_total);
+
+/* repository pme info */
+
+int ps3_repository_read_num_be(unsigned int *num_be);
+int ps3_repository_read_be_node_id(unsigned int be_index, u64 *node_id);
+int ps3_repository_read_tb_freq(u64 node_id, u64 *tb_freq);
+int ps3_repository_read_be_tb_freq(unsigned int be_index, u64 *tb_freq);
+
+/* repository 'Other OS' area */
+
+int ps3_repository_read_boot_dat_addr(u64 *lpar_addr);
+int ps3_repository_read_boot_dat_size(unsigned int *size);
+int ps3_repository_read_boot_dat_info(u64 *lpar_addr, unsigned int *size);
+
+/* repository spu info */
+
+/**
+ * enum spu_resource_type - Type of spu resource.
+ * @spu_resource_type_shared: Logical spu is shared with other partions.
+ * @spu_resource_type_exclusive: Logical spu is not shared with other partions.
+ *
+ * Returned by ps3_repository_read_spu_resource_id().
+ */
+
+enum ps3_spu_resource_type {
+ PS3_SPU_RESOURCE_TYPE_SHARED = 0,
+ PS3_SPU_RESOURCE_TYPE_EXCLUSIVE = 0x8000000000000000UL,
+};
+
+int ps3_repository_read_num_spu_reserved(unsigned int *num_spu_reserved);
+int ps3_repository_read_num_spu_resource_id(unsigned int *num_resource_id);
+int ps3_repository_read_spu_resource_id(unsigned int res_index,
+ enum ps3_spu_resource_type* resource_type, unsigned int *resource_id);
+
#endif
diff --git a/arch/powerpc/platforms/ps3/repository.c b/arch/powerpc/platforms/ps3/repository.c
index 273a0d621bdd..ae586a0e5d3f 100644
--- a/arch/powerpc/platforms/ps3/repository.c
+++ b/arch/powerpc/platforms/ps3/repository.c
@@ -18,9 +18,10 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <asm/ps3.h>
#include <asm/lv1call.h>
+#include "platform.h"
+
enum ps3_vendor_id {
PS3_VENDOR_ID_NONE = 0,
PS3_VENDOR_ID_SONY = 0x8000000000000000UL,
@@ -257,7 +258,7 @@ int ps3_repository_read_dev_type(unsigned int bus_index,
int ps3_repository_read_dev_intr(unsigned int bus_index,
unsigned int dev_index, unsigned int intr_index,
- unsigned int *intr_type, unsigned int* interrupt_id)
+ enum ps3_interrupt_type *intr_type, unsigned int* interrupt_id)
{
int result;
u64 v1;
@@ -275,7 +276,8 @@ int ps3_repository_read_dev_intr(unsigned int bus_index,
}
int ps3_repository_read_dev_reg_type(unsigned int bus_index,
- unsigned int dev_index, unsigned int reg_index, unsigned int *reg_type)
+ unsigned int dev_index, unsigned int reg_index,
+ enum ps3_reg_type *reg_type)
{
int result;
u64 v1;
@@ -302,8 +304,8 @@ int ps3_repository_read_dev_reg_addr(unsigned int bus_index,
}
int ps3_repository_read_dev_reg(unsigned int bus_index,
- unsigned int dev_index, unsigned int reg_index, unsigned int *reg_type,
- u64 *bus_addr, u64 *len)
+ unsigned int dev_index, unsigned int reg_index,
+ enum ps3_reg_type *reg_type, u64 *bus_addr, u64 *len)
{
int result = ps3_repository_read_dev_reg_type(bus_index, dev_index,
reg_index, reg_type);
@@ -343,7 +345,7 @@ int ps3_repository_dump_resource_info(unsigned int bus_index,
}
for (res_index = 0; res_index < 10; res_index++) {
- enum ps3_region_type reg_type;
+ enum ps3_reg_type reg_type;
u64 bus_addr;
u64 len;
@@ -367,7 +369,55 @@ int ps3_repository_dump_resource_info(unsigned int bus_index,
return result;
}
-static int dump_device_info(unsigned int bus_index, unsigned int num_dev)
+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_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 (%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;
+ }
+
+ 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);
+ }
+
+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;
@@ -402,6 +452,9 @@ static int dump_device_info(unsigned int bus_index, unsigned int num_dev)
__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__);
@@ -452,7 +505,7 @@ int ps3_repository_dump_bus_info(void)
__func__, __LINE__, bus_index, bus_type, bus_id,
num_dev);
- dump_device_info(bus_index, num_dev);
+ dump_device_info(bus_index, bus_type, num_dev);
}
pr_debug(" <- %s:%d\n", __func__, __LINE__);
@@ -487,7 +540,8 @@ static int find_device(unsigned int bus_index, unsigned int num_dev,
break;
}
- BUG_ON(dev_index == num_dev);
+ 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);
@@ -521,7 +575,7 @@ int ps3_repository_find_device (enum ps3_bus_type bus_type,
pr_debug("%s:%d: find bus_type %u, dev_type %u\n", __func__, __LINE__,
bus_type, dev_type);
- dev->bus_index = UINT_MAX;
+ BUG_ON(start_dev && start_dev->bus_index > 10);
for (bus_index = start_dev ? start_dev->bus_index : 0; bus_index < 10;
bus_index++) {
@@ -532,13 +586,15 @@ int ps3_repository_find_device (enum ps3_bus_type bus_type,
if (result) {
pr_debug("%s:%d read_bus_type failed\n",
__func__, __LINE__);
+ dev->bus_index = UINT_MAX;
return result;
}
if (x == bus_type)
break;
}
- BUG_ON(bus_index == 10);
+ 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);
@@ -604,7 +660,8 @@ int ps3_repository_find_interrupt(const struct ps3_repository_device *dev,
}
}
- BUG_ON(res_index == 10);
+ if (res_index == 10)
+ return -ENODEV;
pr_debug("%s:%d: found intr_type %u at res_index %u\n",
__func__, __LINE__, intr_type, res_index);
@@ -612,8 +669,8 @@ int ps3_repository_find_interrupt(const struct ps3_repository_device *dev,
return result;
}
-int ps3_repository_find_region(const struct ps3_repository_device *dev,
- enum ps3_region_type reg_type, u64 *bus_addr, u64 *len)
+int ps3_repository_find_reg(const struct ps3_repository_device *dev,
+ enum ps3_reg_type reg_type, u64 *bus_addr, u64 *len)
{
int result = 0;
unsigned int res_index;
@@ -623,7 +680,7 @@ int ps3_repository_find_region(const struct ps3_repository_device *dev,
*bus_addr = *len = 0;
for (res_index = 0; res_index < 10; res_index++) {
- enum ps3_region_type t;
+ enum ps3_reg_type t;
u64 a;
u64 l;
@@ -643,7 +700,8 @@ int ps3_repository_find_region(const struct ps3_repository_device *dev,
}
}
- BUG_ON(res_index == 10);
+ if (res_index == 10)
+ return -ENODEV;
pr_debug("%s:%d: found reg_type %u at res_index %u\n",
__func__, __LINE__, reg_type, res_index);
@@ -651,6 +709,136 @@ int ps3_repository_find_region(const struct ps3_repository_device *dev,
return result;
}
+int ps3_repository_read_stor_dev_port(unsigned int bus_index,
+ unsigned int dev_index, u64 *port)
+{
+ return read_node(PS3_LPAR_ID_PME,
+ make_first_field("bus", bus_index),
+ make_field("dev", dev_index),
+ make_field("port", 0),
+ 0, port, 0);
+}
+
+int ps3_repository_read_stor_dev_blk_size(unsigned int bus_index,
+ unsigned int dev_index, u64 *blk_size)
+{
+ return read_node(PS3_LPAR_ID_PME,
+ make_first_field("bus", bus_index),
+ make_field("dev", dev_index),
+ make_field("blk_size", 0),
+ 0, blk_size, 0);
+}
+
+int ps3_repository_read_stor_dev_num_blocks(unsigned int bus_index,
+ unsigned int dev_index, u64 *num_blocks)
+{
+ return read_node(PS3_LPAR_ID_PME,
+ make_first_field("bus", bus_index),
+ make_field("dev", dev_index),
+ make_field("n_blocks", 0),
+ 0, num_blocks, 0);
+}
+
+int ps3_repository_read_stor_dev_num_regions(unsigned int bus_index,
+ unsigned int dev_index, unsigned int *num_regions)
+{
+ int result;
+ u64 v1;
+
+ result = read_node(PS3_LPAR_ID_PME,
+ make_first_field("bus", bus_index),
+ make_field("dev", dev_index),
+ make_field("n_regs", 0),
+ 0, &v1, 0);
+ *num_regions = v1;
+ return result;
+}
+
+int ps3_repository_read_stor_dev_region_id(unsigned int bus_index,
+ unsigned int dev_index, unsigned int region_index,
+ unsigned int *region_id)
+{
+ int result;
+ u64 v1;
+
+ result = read_node(PS3_LPAR_ID_PME,
+ make_first_field("bus", bus_index),
+ make_field("dev", dev_index),
+ make_field("region", region_index),
+ make_field("id", 0),
+ &v1, 0);
+ *region_id = v1;
+ return result;
+}
+
+int ps3_repository_read_stor_dev_region_size(unsigned int bus_index,
+ unsigned int dev_index, unsigned int region_index, u64 *region_size)
+{
+ return read_node(PS3_LPAR_ID_PME,
+ make_first_field("bus", bus_index),
+ make_field("dev", dev_index),
+ make_field("region", region_index),
+ make_field("size", 0),
+ region_size, 0);
+}
+
+int ps3_repository_read_stor_dev_region_start(unsigned int bus_index,
+ unsigned int dev_index, unsigned int region_index, u64 *region_start)
+{
+ return read_node(PS3_LPAR_ID_PME,
+ make_first_field("bus", bus_index),
+ make_field("dev", dev_index),
+ make_field("region", region_index),
+ make_field("start", 0),
+ region_start, 0);
+}
+
+int ps3_repository_read_stor_dev_info(unsigned int bus_index,
+ unsigned int dev_index, u64 *port, u64 *blk_size,
+ u64 *num_blocks, unsigned int *num_regions)
+{
+ int result;
+
+ result = ps3_repository_read_stor_dev_port(bus_index, dev_index, port);
+ if (result)
+ return result;
+
+ result = ps3_repository_read_stor_dev_blk_size(bus_index, dev_index,
+ blk_size);
+ if (result)
+ return result;
+
+ result = ps3_repository_read_stor_dev_num_blocks(bus_index, dev_index,
+ num_blocks);
+ if (result)
+ return result;
+
+ result = ps3_repository_read_stor_dev_num_regions(bus_index, dev_index,
+ num_regions);
+ return result;
+}
+
+int ps3_repository_read_stor_dev_region(unsigned int bus_index,
+ unsigned int dev_index, unsigned int region_index,
+ unsigned int *region_id, u64 *region_start, u64 *region_size)
+{
+ int result;
+
+ result = ps3_repository_read_stor_dev_region_id(bus_index, dev_index,
+ region_index, region_id);
+ if (result)
+ return result;
+
+ result = ps3_repository_read_stor_dev_region_start(bus_index, dev_index,
+ region_index, region_start);
+ if (result)
+ return result;
+
+ result = ps3_repository_read_stor_dev_region_size(bus_index, dev_index,
+ region_index, region_size);
+ return result;
+}
+
int ps3_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size)
{
return read_node(PS3_LPAR_ID_CURRENT,
diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c
index d8b5cadbe80e..ac5df9688dcb 100644
--- a/arch/powerpc/platforms/ps3/setup.c
+++ b/arch/powerpc/platforms/ps3/setup.c
@@ -24,6 +24,7 @@
#include <linux/root_dev.h>
#include <linux/console.h>
#include <linux/kexec.h>
+#include <linux/bootmem.h>
#include <asm/machdep.h>
#include <asm/firmware.h>
@@ -41,10 +42,22 @@
#define DBG(fmt...) do{if(0)printk(fmt);}while(0)
#endif
-static void ps3_show_cpuinfo(struct seq_file *m)
+#if !defined(CONFIG_SMP)
+static void smp_send_stop(void) {}
+#endif
+
+int ps3_get_firmware_version(union ps3_firmware_version *v)
{
- seq_printf(m, "machine\t\t: %s\n", ppc_md.name);
+ int result = lv1_get_version_info(&v->raw);
+
+ if (result) {
+ v->raw = 0;
+ return -1;
+ }
+
+ return result;
}
+EXPORT_SYMBOL_GPL(ps3_get_firmware_version);
static void ps3_power_save(void)
{
@@ -57,25 +70,84 @@ static void ps3_power_save(void)
lv1_pause(0);
}
+static void ps3_restart(char *cmd)
+{
+ DBG("%s:%d cmd '%s'\n", __func__, __LINE__, cmd);
+
+ smp_send_stop();
+ ps3_sys_manager_restart(); /* never returns */
+}
+
+static void ps3_power_off(void)
+{
+ DBG("%s:%d\n", __func__, __LINE__);
+
+ smp_send_stop();
+ ps3_sys_manager_power_off(); /* never returns */
+}
+
static void ps3_panic(char *str)
{
DBG("%s:%d %s\n", __func__, __LINE__, str);
-#ifdef CONFIG_SMP
smp_send_stop();
-#endif
printk("\n");
printk(" System does not reboot automatically.\n");
printk(" Please press POWER button.\n");
printk("\n");
- for (;;) ;
+ while(1);
+}
+
+static void prealloc(struct ps3_prealloc *p)
+{
+ if (!p->size)
+ return;
+
+ p->address = __alloc_bootmem(p->size, p->align, __pa(MAX_DMA_ADDRESS));
+ if (!p->address) {
+ printk(KERN_ERR "%s: Cannot allocate %s\n", __FUNCTION__,
+ p->name);
+ return;
+ }
+
+ printk(KERN_INFO "%s: %lu bytes at %p\n", p->name, p->size,
+ p->address);
}
+#ifdef CONFIG_FB_PS3
+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 */
+};
+#define prealloc_ps3fb_videomemory() prealloc(&ps3fb_videomemory)
+
+static int __init early_parse_ps3fb(char *p)
+{
+ if (!p)
+ return 1;
+
+ ps3fb_videomemory.size = _ALIGN_UP(memparse(p, &p),
+ ps3fb_videomemory.align);
+ return 0;
+}
+early_param("ps3fb", early_parse_ps3fb);
+#else
+#define prealloc_ps3fb_videomemory() do { } while (0)
+#endif
+
+
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);
+
ps3_spu_set_platform();
ps3_map_htab();
@@ -87,6 +159,7 @@ static void __init ps3_setup_arch(void)
conswitchp = &dummy_con;
#endif
+ prealloc_ps3fb_videomemory();
ppc_md.power_save = ps3_power_save;
DBG(" <- %s:%d\n", __func__, __LINE__);
@@ -156,7 +229,6 @@ define_machine(ps3) {
.name = "PS3",
.probe = ps3_probe,
.setup_arch = ps3_setup_arch,
- .show_cpuinfo = ps3_show_cpuinfo,
.init_IRQ = ps3_init_IRQ,
.panic = ps3_panic,
.get_boot_time = ps3_get_boot_time,
@@ -164,6 +236,8 @@ define_machine(ps3) {
.get_rtc_time = ps3_get_rtc_time,
.calibrate_decr = ps3_calibrate_decr,
.progress = ps3_progress,
+ .restart = ps3_restart,
+ .power_off = ps3_power_off,
#if defined(CONFIG_KEXEC)
.kexec_cpu_down = ps3_kexec_cpu_down,
.machine_kexec = ps3_machine_kexec,
diff --git a/arch/powerpc/platforms/ps3/smp.c b/arch/powerpc/platforms/ps3/smp.c
index 11d2080607ed..6fb887961a6d 100644
--- a/arch/powerpc/platforms/ps3/smp.c
+++ b/arch/powerpc/platforms/ps3/smp.c
@@ -23,7 +23,6 @@
#include <asm/machdep.h>
#include <asm/udbg.h>
-#include <asm/ps3.h>
#include "platform.h"
@@ -111,7 +110,7 @@ static void __init ps3_smp_setup_cpu(int cpu)
BUILD_BUG_ON(PPC_MSG_DEBUGGER_BREAK != 3);
for (i = 0; i < MSG_COUNT; i++) {
- result = ps3_alloc_event_irq(&virqs[i]);
+ result = ps3_alloc_event_irq(cpu, &virqs[i]);
if (result)
continue;
diff --git a/arch/powerpc/platforms/ps3/spu.c b/arch/powerpc/platforms/ps3/spu.c
index 644532c3b7c4..a397e4e17c13 100644
--- a/arch/powerpc/platforms/ps3/spu.c
+++ b/arch/powerpc/platforms/ps3/spu.c
@@ -26,9 +26,10 @@
#include <asm/spu.h>
#include <asm/spu_priv1.h>
-#include <asm/ps3.h>
#include <asm/lv1call.h>
+#include "platform.h"
+
/* spu_management_ops */
/**
@@ -50,7 +51,7 @@ enum spe_type {
*/
struct spe_shadow {
- u8 padding_0000[0x0140];
+ u8 padding_0140[0x0140];
u64 int_status_class0_RW; /* 0x0140 */
u64 int_status_class1_RW; /* 0x0148 */
u64 int_status_class2_RW; /* 0x0150 */
@@ -67,8 +68,7 @@ struct spe_shadow {
u8 padding_0c08[0x0f00-0x0c08];
u64 spe_execution_status; /* 0x0f00 */
u8 padding_0f08[0x1000-0x0f08];
-} __attribute__ ((packed));
-
+};
/**
* enum spe_ex_state - Logical spe execution state.
@@ -170,31 +170,6 @@ static int __init construct_spu(struct spu *spu)
return result;
}
-static int __init add_spu_pages(unsigned long start_addr, unsigned long size)
-{
- int result;
- unsigned long start_pfn;
- unsigned long nr_pages;
- struct pglist_data *pgdata;
- struct zone *zone;
-
- BUG_ON(!mem_init_done);
-
- start_pfn = start_addr >> PAGE_SHIFT;
- nr_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
-
- pgdata = NODE_DATA(0);
- zone = pgdata->node_zones;
-
- result = __add_pages(zone, start_pfn, nr_pages);
-
- if (result)
- pr_debug("%s:%d: __add_pages failed: (%d)\n",
- __func__, __LINE__, result);
-
- return result;
-}
-
static void spu_unmap(struct spu *spu)
{
iounmap(spu->priv2);
@@ -206,19 +181,6 @@ static void spu_unmap(struct spu *spu)
static int __init setup_areas(struct spu *spu)
{
struct table {char* name; unsigned long addr; unsigned long size;};
- int result;
-
- /* setup pages */
-
- result = add_spu_pages(spu->local_store_phys, LS_SIZE);
- if (result)
- goto fail_add;
-
- result = add_spu_pages(spu->problem_phys, sizeof(struct spu_problem));
- if (result)
- goto fail_add;
-
- /* ioremap */
spu_pdata(spu)->shadow = __ioremap(
spu_pdata(spu)->shadow_addr, sizeof(struct spe_shadow),
@@ -260,28 +222,28 @@ static int __init setup_areas(struct spu *spu)
fail_ioremap:
spu_unmap(spu);
-fail_add:
- return result;
+
+ return -ENOMEM;
}
static int __init setup_interrupts(struct spu *spu)
{
int result;
- result = ps3_alloc_spe_irq(spu_pdata(spu)->spe_id, 0,
- &spu->irqs[0]);
+ result = ps3_alloc_spe_irq(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id,
+ 0, &spu->irqs[0]);
if (result)
goto fail_alloc_0;
- result = ps3_alloc_spe_irq(spu_pdata(spu)->spe_id, 1,
- &spu->irqs[1]);
+ result = ps3_alloc_spe_irq(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id,
+ 1, &spu->irqs[1]);
if (result)
goto fail_alloc_1;
- result = ps3_alloc_spe_irq(spu_pdata(spu)->spe_id, 2,
- &spu->irqs[2]);
+ result = ps3_alloc_spe_irq(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id,
+ 2, &spu->irqs[2]);
if (result)
goto fail_alloc_2;
diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c
new file mode 100644
index 000000000000..3c48cce98a5c
--- /dev/null
+++ b/arch/powerpc/platforms/ps3/system-bus.c
@@ -0,0 +1,384 @@
+/*
+ * PS3 system bus driver.
+ *
+ * Copyright (C) 2006 Sony Computer Entertainment Inc.
+ * Copyright 2006 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/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+
+#include <asm/udbg.h>
+#include <asm/lv1call.h>
+#include <asm/firmware.h>
+
+#include "platform.h"
+
+#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: 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)
+{
+ int result;
+
+ result = lv1_map_device_mmio_region(r->did.bus_id, r->did.dev_id,
+ r->bus_addr, r->len, r->page_size, &r->lpar_addr);
+
+ if (result) {
+ pr_debug("%s:%d: lv1_map_device_mmio_region failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+ r->lpar_addr = 0;
+ }
+
+ dump_mmio_region(r);
+ return result;
+}
+EXPORT_SYMBOL_GPL(ps3_mmio_region_create);
+
+int ps3_free_mmio_region(struct ps3_mmio_region *r)
+{
+ int result;
+
+ result = lv1_unmap_device_mmio_region(r->did.bus_id, r->did.dev_id,
+ r->lpar_addr);
+
+ if (result)
+ pr_debug("%s:%d: lv1_unmap_device_mmio_region failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+
+ r->lpar_addr = 0;
+ return result;
+}
+EXPORT_SYMBOL_GPL(ps3_free_mmio_region);
+
+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);
+
+ result = dev->match_id == drv->match_id;
+
+ pr_info("%s:%d: dev=%u(%s), drv=%u(%s): %s\n", __func__, __LINE__,
+ dev->match_id, dev->core.bus_id, drv->match_id, drv->core.name,
+ (result ? "match" : "miss"));
+ return result;
+}
+
+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);
+
+ 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(!drv);
+
+ if (drv->probe)
+ result = drv->probe(dev);
+ else
+ 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:
+ 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);
+
+ if (drv->remove)
+ drv->remove(dev);
+ else
+ pr_info("%s:%d: %s no remove method\n", __func__, __LINE__,
+ dev->core.bus_id);
+
+ 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);
+
+ return 0;
+}
+
+struct bus_type ps3_system_bus_type = {
+ .name = "ps3_system_bus",
+ .match = ps3_system_bus_match,
+ .probe = ps3_system_bus_probe,
+ .remove = ps3_system_bus_remove,
+};
+
+int __init ps3_system_bus_init(void)
+{
+ int result;
+
+ if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
+ return -ENODEV;
+
+ result = bus_register(&ps3_system_bus_type);
+ BUG_ON(result);
+ return result;
+}
+
+core_initcall(ps3_system_bus_init);
+
+/* Allocates a contiguous real buffer and creates mappings over it.
+ * 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)
+{
+ int result;
+ struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
+ unsigned long virt_addr;
+
+ BUG_ON(!dev->d_region->bus_addr);
+
+ flag &= ~(__GFP_DMA | __GFP_HIGHMEM);
+ flag |= __GFP_ZERO;
+
+ virt_addr = __get_free_pages(flag, get_order(size));
+
+ if (!virt_addr) {
+ pr_debug("%s:%d: get_free_pages failed\n", __func__, __LINE__);
+ goto clean_none;
+ }
+
+ result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle);
+
+ if (result) {
+ pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
+ __func__, __LINE__, result);
+ BUG_ON("check region type");
+ goto clean_alloc;
+ }
+
+ return (void*)virt_addr;
+
+clean_alloc:
+ free_pages(virt_addr, get_order(size));
+clean_none:
+ dma_handle = NULL;
+ return NULL;
+}
+
+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);
+
+ ps3_dma_unmap(dev->d_region, dma_handle, size);
+ free_pages((unsigned long)vaddr, get_order(size));
+}
+
+/* Creates TCEs for a user provided buffer. The user buffer must be
+ * contiguous real kernel storage (not vmalloc). The address of the buffer
+ * passed here is the kernel (virtual) address of the buffer. The buffer
+ * need not be page aligned, the dma_addr_t returned will point to the same
+ * byte within the page as vaddr.
+ */
+
+static dma_addr_t ps3_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);
+ int result;
+ unsigned long bus_addr;
+
+ result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size,
+ &bus_addr);
+
+ 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);
+ int result;
+
+ result = ps3_dma_unmap(dev->d_region, dma_addr, size);
+
+ if (result) {
+ pr_debug("%s:%d: ps3_dma_unmap failed (%d)\n",
+ __func__, __LINE__, result);
+ }
+}
+
+static int ps3_map_sg(struct device *_dev, struct scatterlist *sg, int nents,
+ enum dma_data_direction direction)
+{
+ struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
+ int i;
+
+#if defined(CONFIG_PS3_DYNAMIC_DMA)
+ BUG_ON("do");
+ return -EPERM;
+#else
+ 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);
+
+ if (result) {
+ pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
+ __func__, __LINE__, result);
+ return -EINVAL;
+ }
+
+ sg->dma_length = sg->length;
+ }
+
+ return nents;
+#endif
+}
+
+static void ps3_unmap_sg(struct device *_dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction)
+{
+#if defined(CONFIG_PS3_DYNAMIC_DMA)
+ BUG_ON("do");
+#endif
+}
+
+static int ps3_dma_supported(struct device *_dev, u64 mask)
+{
+ return mask >= DMA_32BIT_MASK;
+}
+
+static struct dma_mapping_ops ps3_dma_ops = {
+ .alloc_coherent = ps3_alloc_coherent,
+ .free_coherent = ps3_free_coherent,
+ .map_single = ps3_map_single,
+ .unmap_single = ps3_unmap_single,
+ .map_sg = ps3_map_sg,
+ .unmap_sg = ps3_unmap_sg,
+ .dma_supported = ps3_dma_supported
+};
+
+/**
+ * ps3_system_bus_release_device - remove a device from the system bus
+ */
+
+static void ps3_system_bus_release_device(struct device *_dev)
+{
+ struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
+ kfree(dev);
+}
+
+/**
+ * ps3_system_bus_device_register - add a device to the system bus
+ *
+ * ps3_system_bus_device_register() expects the dev object to be allocated
+ * dynamically by the caller. The system bus takes ownership of the dev
+ * object and frees the object in ps3_system_bus_release_device().
+ */
+
+int ps3_system_bus_device_register(struct ps3_system_bus_device *dev)
+{
+ int result;
+ static unsigned int dev_count = 1;
+
+ dev->core.parent = NULL;
+ dev->core.bus = &ps3_system_bus_type;
+ dev->core.release = ps3_system_bus_release_device;
+
+ 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);
+ return result;
+}
+
+EXPORT_SYMBOL_GPL(ps3_system_bus_device_register);
+
+int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv)
+{
+ int result;
+
+ drv->core.bus = &ps3_system_bus_type;
+
+ result = driver_register(&drv->core);
+ return result;
+}
+
+EXPORT_SYMBOL_GPL(ps3_system_bus_driver_register);
+
+void ps3_system_bus_driver_unregister(struct ps3_system_bus_driver *drv)
+{
+ driver_unregister(&drv->core);
+}
+
+EXPORT_SYMBOL_GPL(ps3_system_bus_driver_unregister);