diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-08 10:04:20 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-08 10:04:20 -0800 |
commit | 21eb4fa1700112d1420d72e1de708af671a251c8 (patch) | |
tree | 3afd9f526da50108c27e05ac69826be5e7c2ad6e /arch/powerpc/platforms/ps3 | |
parent | 0c0e8caf9fd6c9a49fb9fbdba14a8b7b4239adde (diff) | |
parent | d003e7a1a569501cbe9a5ca14748177498c4893a (diff) |
Merge master.kernel.org:/pub/scm/linux/kernel/git/paulus/powerpc
* master.kernel.org:/pub/scm/linux/kernel/git/paulus/powerpc: (116 commits)
[POWERPC] Add export of vgacon_remap_base
[POWERPC] Remove bogus comment about page_is_ram
[POWERPC] windfarm: don't die on suspend thread signal
[POWERPC] Fix comment in kernel/irq.c
[POWERPC] ppc: Fix booke watchdog initialization
[POWERPC] PPC: Use ARRAY_SIZE macro when appropriate
[POWERPC] Use ARRAY_SIZE macro when appropriate
[POWERPC] Fix ppc64's writing to struct file_operations
[POWERPC] ppc: use syslog macro for the printk log level
[POWERPC] ppc: cs4218_tdm remove extra brace
[POWERPC] Add mpc52xx/lite5200 PCI support
[POWERPC] Only use H_BULK_REMOVE if the firmware supports it
[POWERPC] Fixup error handling when emulating a floating point instruction
[POWERPC] Enable interrupts if we are doing fp math emulation
[POWERPC] Added kprobes support to ppc32
[POWERPC] Make pSeries use the H_BULK_REMOVE hypervisor call
[POWERPC] Clear RI bit in MSR before restoring r13 when returning to userspace
[POWERPC] Fix performance monitor exception
[POWERPC] Compile fixes for arch/powerpc dcr code
[POWERPC] Maple: use mmio nvram
...
Diffstat (limited to 'arch/powerpc/platforms/ps3')
-rw-r--r-- | arch/powerpc/platforms/ps3/Makefile | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/htab.c | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/interrupt.c | 442 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/mm.c | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/os-area.c | 22 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/platform.h | 151 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/repository.c | 220 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/setup.c | 19 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/smp.c | 3 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/spu.c | 20 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/system-bus.c | 382 |
11 files changed, 1022 insertions, 240 deletions
diff --git a/arch/powerpc/platforms/ps3/Makefile b/arch/powerpc/platforms/ps3/Makefile index 1994904f580..a0048fcf086 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 8fe1769655a..a4b5a1bc60f 100644 --- a/arch/powerpc/platforms/ps3/htab.c +++ b/arch/powerpc/platforms/ps3/htab.c @@ -23,7 +23,6 @@ #include <asm/machdep.h> #include <asm/lmb.h> #include <asm/udbg.h> -#include <asm/ps3.h> #include <asm/lv1call.h> #include "platform.h" diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c index 6f5de438b98..bb17283275a 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,12 +189,10 @@ 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; } int ps3_free_io_irq(unsigned int virq) @@ -75,13 +205,15 @@ 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; } /** * 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 +221,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 +235,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 +253,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 +266,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 +276,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 +329,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 +338,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 +357,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 +375,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 +406,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 +433,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 +451,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 +461,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 +556,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 +591,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 49c0d010d49..42354de3f55 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" diff --git a/arch/powerpc/platforms/ps3/os-area.c b/arch/powerpc/platforms/ps3/os-area.c index 58358305dc1..5c3da08bc0c 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 23b111bea9d..ca04f03305c 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 273a0d621bd..ae586a0e5d3 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, ®ion_id, ®ion_start, + ®ion_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 d8b5cadbe80..e62505e1881 100644 --- a/arch/powerpc/platforms/ps3/setup.c +++ b/arch/powerpc/platforms/ps3/setup.c @@ -41,10 +41,18 @@ #define DBG(fmt...) do{if(0)printk(fmt);}while(0) #endif -static void ps3_show_cpuinfo(struct seq_file *m) +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) { @@ -74,8 +82,14 @@ static void ps3_panic(char *str) 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(); @@ -156,7 +170,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, diff --git a/arch/powerpc/platforms/ps3/smp.c b/arch/powerpc/platforms/ps3/smp.c index 11d2080607e..6fb887961a6 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 644532c3b7c..d1929721b0e 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. @@ -268,20 +268,20 @@ 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 00000000000..bce6136cbce --- /dev/null +++ b/arch/powerpc/platforms/ps3/system-bus.c @@ -0,0 +1,382 @@ +/* + * 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; +} + +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; +} + +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 0; + + 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); |