diff options
34 files changed, 2299 insertions, 537 deletions
diff --git a/arch/powerpc/configs/ps3_defconfig b/arch/powerpc/configs/ps3_defconfig index 0b5469f..7994955c 100644 --- a/arch/powerpc/configs/ps3_defconfig +++ b/arch/powerpc/configs/ps3_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.24-rc4 -# Tue Dec 4 22:49:57 2007 +# Linux kernel version: 2.6.24-rc8 +# Wed Jan 16 14:31:21 2008 # CONFIG_PPC64=y @@ -103,6 +103,7 @@ CONFIG_VM_EVENT_COUNTERS=y CONFIG_SLAB=y # CONFIG_SLUB is not set # CONFIG_SLOB is not set +CONFIG_SLABINFO=y CONFIG_RT_MUTEXES=y # CONFIG_TINY_SHMEM is not set CONFIG_BASE_SMALL=0 @@ -154,7 +155,6 @@ CONFIG_PPC_PS3=y # CONFIG_PS3_ADVANCED is not set CONFIG_PS3_HTAB_SIZE=20 # CONFIG_PS3_DYNAMIC_DMA is not set -CONFIG_PS3_USE_LPAR_ADDR=y CONFIG_PS3_VUART=y CONFIG_PS3_PS3AV=y CONFIG_PS3_SYS_MANAGER=y @@ -162,6 +162,7 @@ CONFIG_PS3_STORAGE=y CONFIG_PS3_DISK=y CONFIG_PS3_ROM=y CONFIG_PS3_FLASH=y +CONFIG_PS3_LPM=m CONFIG_PPC_CELL=y # CONFIG_PPC_CELL_NATIVE is not set # CONFIG_PPC_IBM_CELL_BLADE is not set @@ -225,7 +226,7 @@ CONFIG_HAVE_MEMORY_PRESENT=y # CONFIG_SPARSEMEM_STATIC is not set CONFIG_SPARSEMEM_EXTREME=y CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y -CONFIG_SPARSEMEM_VMEMMAP=y +# CONFIG_SPARSEMEM_VMEMMAP is not set CONFIG_MEMORY_HOTPLUG=y CONFIG_MEMORY_HOTPLUG_SPARSE=y CONFIG_SPLIT_PTLOCK_CPUS=4 @@ -338,7 +339,26 @@ CONFIG_IPV6_SIT=y # CONFIG_NET_PKTGEN is not set # CONFIG_HAMRADIO is not set # CONFIG_IRDA is not set -# CONFIG_BT is not set +CONFIG_BT=m +CONFIG_BT_L2CAP=m +CONFIG_BT_SCO=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=m + +# +# Bluetooth device drivers +# +CONFIG_BT_HCIUSB=m +CONFIG_BT_HCIUSB_SCO=y +# CONFIG_BT_HCIUART is not set +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBPA10X is not set +# CONFIG_BT_HCIBFUSB is not set +# CONFIG_BT_HCIVHCI is not set # CONFIG_AF_RXRPC is not set # @@ -666,14 +686,14 @@ CONFIG_LOGO_LINUX_CLUT224=y # # Sound # -CONFIG_SOUND=y +CONFIG_SOUND=m # # Advanced Linux Sound Architecture # -CONFIG_SND=y -CONFIG_SND_TIMER=y -CONFIG_SND_PCM=y +CONFIG_SND=m +CONFIG_SND_TIMER=m +CONFIG_SND_PCM=m # CONFIG_SND_SEQUENCER is not set # CONFIG_SND_MIXER_OSS is not set # CONFIG_SND_PCM_OSS is not set @@ -702,7 +722,7 @@ CONFIG_SND_VERBOSE_PROCFS=y # # ALSA PowerPC devices # -CONFIG_SND_PS3=y +CONFIG_SND_PS3=m CONFIG_SND_PS3_DEFAULT_START_DELAY=2000 # @@ -747,7 +767,7 @@ CONFIG_USB_SUPPORT=y CONFIG_USB_ARCH_HAS_HCD=y CONFIG_USB_ARCH_HAS_OHCI=y CONFIG_USB_ARCH_HAS_EHCI=y -CONFIG_USB=y +CONFIG_USB=m # CONFIG_USB_DEBUG is not set # @@ -761,13 +781,13 @@ CONFIG_USB_DEVICEFS=y # # USB Host Controller Drivers # -CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD=m # CONFIG_USB_EHCI_SPLIT_ISO is not set # CONFIG_USB_EHCI_ROOT_HUB_TT is not set # CONFIG_USB_EHCI_TT_NEWSCHED is not set CONFIG_USB_EHCI_BIG_ENDIAN_MMIO=y # CONFIG_USB_ISP116X_HCD is not set -CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD=m # CONFIG_USB_OHCI_HCD_PPC_OF is not set # CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set CONFIG_USB_OHCI_BIG_ENDIAN_MMIO=y @@ -1033,7 +1053,8 @@ CONFIG_HAS_IOMEM=y CONFIG_HAS_IOPORT=y CONFIG_HAS_DMA=y CONFIG_INSTRUMENTATION=y -# CONFIG_PROFILING is not set +CONFIG_PROFILING=y +CONFIG_OPROFILE=m # CONFIG_KPROBES is not set # CONFIG_MARKERS is not set diff --git a/arch/powerpc/kernel/crash.c b/arch/powerpc/kernel/crash.c index 77c749a..571132e 100644 --- a/arch/powerpc/kernel/crash.c +++ b/arch/powerpc/kernel/crash.c @@ -32,6 +32,8 @@ #include <asm/lmb.h> #include <asm/firmware.h> #include <asm/smp.h> +#include <asm/system.h> +#include <asm/setjmp.h> #ifdef DEBUG #include <asm/udbg.h> @@ -45,6 +47,11 @@ int crashing_cpu = -1; static cpumask_t cpus_in_crash = CPU_MASK_NONE; cpumask_t cpus_in_sr = CPU_MASK_NONE; +#define CRASH_HANDLER_MAX 1 +/* NULL terminated list of shutdown handles */ +static crash_shutdown_t crash_shutdown_handles[CRASH_HANDLER_MAX+1]; +static DEFINE_SPINLOCK(crash_handlers_lock); + #ifdef CONFIG_SMP static atomic_t enter_on_soft_reset = ATOMIC_INIT(0); @@ -285,9 +292,72 @@ static inline void crash_kexec_stop_spus(void) } #endif /* CONFIG_SPU_BASE */ +/* + * Register a function to be called on shutdown. Only use this if you + * can't reset your device in the second kernel. + */ +int crash_shutdown_register(crash_shutdown_t handler) +{ + unsigned int i, rc; + + spin_lock(&crash_handlers_lock); + for (i = 0 ; i < CRASH_HANDLER_MAX; i++) + if (!crash_shutdown_handles[i]) { + /* Insert handle at first empty entry */ + crash_shutdown_handles[i] = handler; + rc = 0; + break; + } + + if (i == CRASH_HANDLER_MAX) { + printk(KERN_ERR "Crash shutdown handles full, " + "not registered.\n"); + rc = 1; + } + + spin_unlock(&crash_handlers_lock); + return rc; +} +EXPORT_SYMBOL(crash_shutdown_register); + +int crash_shutdown_unregister(crash_shutdown_t handler) +{ + unsigned int i, rc; + + spin_lock(&crash_handlers_lock); + for (i = 0 ; i < CRASH_HANDLER_MAX; i++) + if (crash_shutdown_handles[i] == handler) + break; + + if (i == CRASH_HANDLER_MAX) { + printk(KERN_ERR "Crash shutdown handle not found\n"); + rc = 1; + } else { + /* Shift handles down */ + for (; crash_shutdown_handles[i]; i++) + crash_shutdown_handles[i] = + crash_shutdown_handles[i+1]; + rc = 0; + } + + spin_unlock(&crash_handlers_lock); + return rc; +} +EXPORT_SYMBOL(crash_shutdown_unregister); + +static unsigned long crash_shutdown_buf[JMP_BUF_LEN]; + +static int handle_fault(struct pt_regs *regs) +{ + longjmp(crash_shutdown_buf, 1); + return 0; +} + void default_machine_crash_shutdown(struct pt_regs *regs) { - unsigned int irq; + unsigned int i; + int (*old_handler)(struct pt_regs *regs); + /* * This function is only called after the system @@ -301,15 +371,36 @@ void default_machine_crash_shutdown(struct pt_regs *regs) */ hard_irq_disable(); - for_each_irq(irq) { - struct irq_desc *desc = irq_desc + irq; + for_each_irq(i) { + struct irq_desc *desc = irq_desc + i; if (desc->status & IRQ_INPROGRESS) - desc->chip->eoi(irq); + desc->chip->eoi(i); if (!(desc->status & IRQ_DISABLED)) - desc->chip->disable(irq); + desc->chip->disable(i); + } + + /* + * Call registered shutdown routines savely. Swap out + * __debugger_fault_handler, and replace on exit. + */ + old_handler = __debugger_fault_handler; + __debugger_fault_handler = handle_fault; + for (i = 0; crash_shutdown_handles[i]; i++) { + if (setjmp(crash_shutdown_buf) == 0) { + /* + * Insert syncs and delay to ensure + * instructions in the dangerous region don't + * leak away from this protected region. + */ + asm volatile("sync; isync"); + /* dangerous region */ + crash_shutdown_handles[i](); + asm volatile("sync; isync"); + } } + __debugger_fault_handler = old_handler; /* * Make a note of crashing cpu. Will be used in machine_kexec diff --git a/arch/powerpc/kernel/dma_64.c b/arch/powerpc/kernel/dma_64.c index 14206e3..8423907 100644 --- a/arch/powerpc/kernel/dma_64.c +++ b/arch/powerpc/kernel/dma_64.c @@ -112,10 +112,16 @@ EXPORT_SYMBOL(dma_iommu_ops); /* * Generic direct DMA implementation * - * This implementation supports a global offset that can be applied if - * the address at which memory is visible to devices is not 0. + * This implementation supports a per-device offset that can be applied if + * the address at which memory is visible to devices is not 0. Platform code + * can set archdata.dma_data to an unsigned long holding the offset. By + * default the offset is zero. */ -unsigned long dma_direct_offset; + +static unsigned long get_dma_direct_offset(struct device *dev) +{ + return (unsigned long)dev->archdata.dma_data; +} static void *dma_direct_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag) @@ -124,13 +130,12 @@ static void *dma_direct_alloc_coherent(struct device *dev, size_t size, void *ret; int node = dev->archdata.numa_node; - /* TODO: Maybe use the numa node here too ? */ page = alloc_pages_node(node, flag, get_order(size)); if (page == NULL) return NULL; ret = page_address(page); memset(ret, 0, size); - *dma_handle = virt_to_abs(ret) | dma_direct_offset; + *dma_handle = virt_to_abs(ret) + get_dma_direct_offset(dev); return ret; } @@ -145,7 +150,7 @@ static dma_addr_t dma_direct_map_single(struct device *dev, void *ptr, size_t size, enum dma_data_direction direction) { - return virt_to_abs(ptr) | dma_direct_offset; + return virt_to_abs(ptr) + get_dma_direct_offset(dev); } static void dma_direct_unmap_single(struct device *dev, dma_addr_t dma_addr, @@ -161,7 +166,7 @@ static int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int i; for_each_sg(sgl, sg, nents, i) { - sg->dma_address = sg_phys(sg) | dma_direct_offset; + sg->dma_address = sg_phys(sg) + get_dma_direct_offset(dev); sg->dma_length = sg->length; } diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c index fd5ae8d..2f50bb5 100644 --- a/arch/powerpc/kernel/ibmebus.c +++ b/arch/powerpc/kernel/ibmebus.c @@ -198,16 +198,13 @@ int ibmebus_register_driver(struct of_platform_driver *drv) /* If the driver uses devices that ibmebus doesn't know, add them */ ibmebus_create_devices(drv->match_table); - drv->driver.name = drv->name; - drv->driver.bus = &ibmebus_bus_type; - - return driver_register(&drv->driver); + return of_register_driver(drv, &ibmebus_bus_type); } EXPORT_SYMBOL(ibmebus_register_driver); void ibmebus_unregister_driver(struct of_platform_driver *drv) { - driver_unregister(&drv->driver); + of_unregister_driver(drv); } EXPORT_SYMBOL(ibmebus_unregister_driver); diff --git a/arch/powerpc/kernel/misc.S b/arch/powerpc/kernel/misc.S index 74ce0c7..7b91602 100644 --- a/arch/powerpc/kernel/misc.S +++ b/arch/powerpc/kernel/misc.S @@ -8,6 +8,8 @@ * Adapted for iSeries by Mike Corrigan (mikejc@us.ibm.com) * PPC64 updates by Dave Engebretsen (engebret@us.ibm.com) * + * setjmp/longjmp code by Paul Mackerras. + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version @@ -15,6 +17,8 @@ */ #include <asm/ppc_asm.h> #include <asm/unistd.h> +#include <asm/asm-compat.h> +#include <asm/asm-offsets.h> .text @@ -51,3 +55,64 @@ _GLOBAL(kernel_execve) bnslr neg r3,r3 blr + +_GLOBAL(setjmp) + mflr r0 + PPC_STL r0,0(r3) + PPC_STL r1,SZL(r3) + PPC_STL r2,2*SZL(r3) + mfcr r0 + PPC_STL r0,3*SZL(r3) + PPC_STL r13,4*SZL(r3) + PPC_STL r14,5*SZL(r3) + PPC_STL r15,6*SZL(r3) + PPC_STL r16,7*SZL(r3) + PPC_STL r17,8*SZL(r3) + PPC_STL r18,9*SZL(r3) + PPC_STL r19,10*SZL(r3) + PPC_STL r20,11*SZL(r3) + PPC_STL r21,12*SZL(r3) + PPC_STL r22,13*SZL(r3) + PPC_STL r23,14*SZL(r3) + PPC_STL r24,15*SZL(r3) + PPC_STL r25,16*SZL(r3) + PPC_STL r26,17*SZL(r3) + PPC_STL r27,18*SZL(r3) + PPC_STL r28,19*SZL(r3) + PPC_STL r29,20*SZL(r3) + PPC_STL r30,21*SZL(r3) + PPC_STL r31,22*SZL(r3) + li r3,0 + blr + +_GLOBAL(longjmp) + PPC_LCMPI r4,0 + bne 1f + li r4,1 +1: PPC_LL r13,4*SZL(r3) + PPC_LL r14,5*SZL(r3) + PPC_LL r15,6*SZL(r3) + PPC_LL r16,7*SZL(r3) + PPC_LL r17,8*SZL(r3) + PPC_LL r18,9*SZL(r3) + PPC_LL r19,10*SZL(r3) + PPC_LL r20,11*SZL(r3) + PPC_LL r21,12*SZL(r3) + PPC_LL r22,13*SZL(r3) + PPC_LL r23,14*SZL(r3) + PPC_LL r24,15*SZL(r3) + PPC_LL r25,16*SZL(r3) + PPC_LL r26,17*SZL(r3) + PPC_LL r27,18*SZL(r3) + PPC_LL r28,19*SZL(r3) + PPC_LL r29,20*SZL(r3) + PPC_LL r30,21*SZL(r3) + PPC_LL r31,22*SZL(r3) + PPC_LL r0,3*SZL(r3) + mtcrf 0x38,r0 + PPC_LL r0,0(r3) + PPC_LL r1,SZL(r3) + PPC_LL r2,2*SZL(r3) + mtlr r0 + mr r3,r4 + blr diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 1930f39..5275074 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -357,7 +357,6 @@ void __devinit scan_phb(struct pci_controller *hose) struct pci_bus *bus; struct device_node *node = hose->dn; int i, mode; - struct resource *res; DBG("PCI: Scanning PHB %s\n", node ? node->full_name : "<NO NAME>"); @@ -375,12 +374,10 @@ void __devinit scan_phb(struct pci_controller *hose) pcibios_map_io_space(bus); /* Wire up PHB bus resources */ - if (hose->io_resource.flags) { - DBG("PCI: PHB IO resource = %016lx-%016lx [%lx]\n", - hose->io_resource.start, hose->io_resource.end, - hose->io_resource.flags); - bus->resource[0] = res = &hose->io_resource; - } + DBG("PCI: PHB IO resource = %016lx-%016lx [%lx]\n", + hose->io_resource.start, hose->io_resource.end, + hose->io_resource.flags); + bus->resource[0] = &hose->io_resource; for (i = 0; i < 3; ++i) { DBG("PCI: PHB MEM resource %d = %016lx-%016lx [%lx]\n", i, hose->mem_resources[i].start, diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c index 13ebeb2..aa9ff35 100644 --- a/arch/powerpc/kernel/ppc_ksyms.c +++ b/arch/powerpc/kernel/ppc_ksyms.c @@ -59,6 +59,7 @@ extern void single_step_exception(struct pt_regs *regs); extern int sys_sigreturn(struct pt_regs *regs); EXPORT_SYMBOL(clear_pages); +EXPORT_SYMBOL(copy_page); EXPORT_SYMBOL(ISA_DMA_THRESHOLD); EXPORT_SYMBOL(DMA_MODE_READ); EXPORT_SYMBOL(DMA_MODE_WRITE); diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 338950a..be35ffae 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -76,6 +76,8 @@ void smp_call_function_interrupt(void); int smt_enabled_at_boot = 1; +static int ipi_fail_ok; + static void (*crash_ipi_function_ptr)(struct pt_regs *) = NULL; #ifdef CONFIG_PPC64 @@ -181,12 +183,13 @@ static struct call_data_struct { * <wait> If true, wait (atomically) until function has completed on other CPUs. * [RETURNS] 0 on success, else a negative status code. Does not return until * remote CPUs are nearly ready to execute <<func>> or are or have executed. + * <map> is a cpu map of the cpus to send IPI to. * * You must not call this function with disabled interrupts or from a * hardware interrupt handler or from a bottom half handler. */ -int smp_call_function_map(void (*func) (void *info), void *info, int nonatomic, - int wait, cpumask_t map) +static int __smp_call_function_map(void (*func) (void *info), void *info, + int nonatomic, int wait, cpumask_t map) { struct call_data_struct data; int ret = -1, num_cpus; @@ -203,8 +206,6 @@ int smp_call_function_map(void (*func) (void *info), void *info, int nonatomic, if (wait) atomic_set(&data.finished, 0); - spin_lock(&call_lock); - /* remove 'self' from the map */ if (cpu_isset(smp_processor_id(), map)) cpu_clear(smp_processor_id(), map); @@ -231,7 +232,8 @@ int smp_call_function_map(void (*func) (void *info), void *info, int nonatomic, printk("smp_call_function on cpu %d: other cpus not " "responding (%d)\n", smp_processor_id(), atomic_read(&data.started)); - debugger(NULL); + if (!ipi_fail_ok) + debugger(NULL); goto out; } } @@ -258,14 +260,18 @@ int smp_call_function_map(void (*func) (void *info), void *info, int nonatomic, out: call_data = NULL; HMT_medium(); - spin_unlock(&call_lock); return ret; } static int __smp_call_function(void (*func)(void *info), void *info, int nonatomic, int wait) { - return smp_call_function_map(func,info,nonatomic,wait,cpu_online_map); + int ret; + spin_lock(&call_lock); + ret =__smp_call_function_map(func, info, nonatomic, wait, + cpu_online_map); + spin_unlock(&call_lock); + return ret; } int smp_call_function(void (*func) (void *info), void *info, int nonatomic, @@ -278,8 +284,8 @@ int smp_call_function(void (*func) (void *info), void *info, int nonatomic, } EXPORT_SYMBOL(smp_call_function); -int smp_call_function_single(int cpu, void (*func) (void *info), void *info, int nonatomic, - int wait) +int smp_call_function_single(int cpu, void (*func) (void *info), void *info, + int nonatomic, int wait) { cpumask_t map = CPU_MASK_NONE; int ret = 0; @@ -291,9 +297,11 @@ int smp_call_function_single(int cpu, void (*func) (void *info), void *info, int return -EINVAL; cpu_set(cpu, map); - if (cpu != get_cpu()) - ret = smp_call_function_map(func,info,nonatomic,wait,map); - else { + if (cpu != get_cpu()) { + spin_lock(&call_lock); + ret = __smp_call_function_map(func, info, nonatomic, wait, map); + spin_unlock(&call_lock); + } else { local_irq_disable(); func(info); local_irq_enable(); @@ -305,7 +313,22 @@ EXPORT_SYMBOL(smp_call_function_single); void smp_send_stop(void) { - __smp_call_function(stop_this_cpu, NULL, 1, 0); + int nolock; + + /* It's OK to fail sending the IPI, since the alternative is to + * be stuck forever waiting on the other CPU to take the interrupt. + * + * It's better to at least continue and go through reboot, since this + * function is usually called at panic or reboot time in the first + * place. + */ + ipi_fail_ok = 1; + + /* Don't deadlock in case we got called through panic */ + nolock = !spin_trylock(&call_lock); + __smp_call_function_map(stop_this_cpu, NULL, 1, 0, cpu_online_map); + if (!nolock) + spin_unlock(&call_lock); } void smp_call_function_interrupt(void) diff --git a/arch/powerpc/kernel/systbl_chk.c b/arch/powerpc/kernel/systbl_chk.c index 77824d1..238aa63 100644 --- a/arch/powerpc/kernel/systbl_chk.c +++ b/arch/powerpc/kernel/systbl_chk.c @@ -6,7 +6,7 @@ * Unfortunately, we cannot check for the correct ordering of entries * using SYSX(). * - * Copyright © IBM Corporation + * Copyright © IBM Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/arch/powerpc/kernel/systbl_chk.sh b/arch/powerpc/kernel/systbl_chk.sh index 367d208..19415e7 100644 --- a/arch/powerpc/kernel/systbl_chk.sh +++ b/arch/powerpc/kernel/systbl_chk.sh @@ -3,7 +3,7 @@ # Just process the CPP output from systbl_chk.c and complain # if anything is out of order. # -# Copyright © 2008 IBM Corporation +# Copyright © 2008 IBM Corporation # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 10dda22..7b25107 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -167,10 +167,8 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, if (notify_page_fault(regs)) return 0; - if (trap == 0x300) { - if (debugger_fault_handler(regs)) - return 0; - } + if (unlikely(debugger_fault_handler(regs))) + return 0; /* On a kernel SLB miss we can only check for a valid exception entry */ if (!user_mode(regs) && (address >= TASK_SIZE)) diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index bceb5e1..eb2a94b 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -310,8 +310,8 @@ static void cell_iommu_setup_hardware(struct cbe_iommu *iommu, unsigned long siz { struct page *page; int ret, i; - unsigned long reg, segments, pages_per_segment, ptab_size, n_pte_pages; - unsigned long xlate_base; + unsigned long reg, segments, pages_per_segment, ptab_size, stab_size, + n_pte_pages, xlate_base; unsigned int virq; if (cell_iommu_find_ioc(iommu->nid, &xlate_base)) @@ -328,7 +328,8 @@ static void cell_iommu_setup_hardware(struct cbe_iommu *iommu, unsigned long siz __FUNCTION__, iommu->nid, segments, pages_per_segment); /* set up the segment table */ - page = alloc_pages_node(iommu->nid, GFP_KERNEL, 0); + stab_size = segments * sizeof(unsigned long); + page = alloc_pages_node(iommu->nid, GFP_KERNEL, get_order(stab_size)); BUG_ON(!page); iommu->stab = page_address(page); clear_page(iommu->stab); @@ -490,15 +491,18 @@ static struct cbe_iommu *cell_iommu_for_node(int nid) return NULL; } +static unsigned long cell_dma_direct_offset; + static void cell_dma_dev_setup(struct device *dev) { struct iommu_window *window; struct cbe_iommu *iommu; struct dev_archdata *archdata = &dev->archdata; - /* If we run without iommu, no need to do anything */ - if (get_pci_dma_ops() == &dma_direct_ops) + if (get_pci_dma_ops() == &dma_direct_ops) { + archdata->dma_data = (void *)cell_dma_direct_offset; return; + } /* Current implementation uses the first window available in that * node's iommu. We -might- do something smarter later though it may @@ -654,7 +658,7 @@ static int __init cell_iommu_init_disabled(void) /* If we have no Axon, we set up the spider DMA magic offset */ if (of_find_node_by_name(NULL, "axon") == NULL) - dma_direct_offset = SPIDER_DMA_OFFSET; + cell_dma_direct_offset = SPIDER_DMA_OFFSET; /* Now we need to check to see where the memory is mapped * in PCI space. We assume that all busses use the same dma @@ -688,10 +692,13 @@ static int __init cell_iommu_init_disabled(void) return -ENODEV; } - dma_direct_offset += base; + cell_dma_direct_offset += base; + + if (cell_dma_direct_offset != 0) + ppc_md.pci_dma_dev_setup = cell_pci_dma_dev_setup; printk("iommu: disabled, direct DMA offset is 0x%lx\n", - dma_direct_offset); + cell_dma_direct_offset); return 0; } diff --git a/arch/powerpc/platforms/celleb/iommu.c b/arch/powerpc/platforms/celleb/iommu.c index 41e1e6f..93b0efd 100644 --- a/arch/powerpc/platforms/celleb/iommu.c +++ b/arch/powerpc/platforms/celleb/iommu.c @@ -52,6 +52,8 @@ static int __init find_dma_window(u64 *io_space_id, u64 *ioid, return 0; } +static unsigned long celleb_dma_direct_offset; + static void __init celleb_init_direct_mapping(void) { u64 lpar_addr, io_addr; @@ -69,7 +71,18 @@ static void __init celleb_init_direct_mapping(void) ioid, DMA_FLAGS); } - dma_direct_offset = dma_base; + celleb_dma_direct_offset = dma_base; +} + +static void celleb_dma_dev_setup(struct device *dev) +{ + dev->archdata.dma_ops = get_pci_dma_ops(); + dev->archdata.dma_data = (void *)celleb_dma_direct_offset; +} + +static void celleb_pci_dma_dev_setup(struct pci_dev *pdev) +{ + celleb_dma_dev_setup(&pdev->dev); } static int celleb_of_bus_notify(struct notifier_block *nb, @@ -81,7 +94,7 @@ static int celleb_of_bus_notify(struct notifier_block *nb, if (action != BUS_NOTIFY_ADD_DEVICE) return 0; - dev->archdata.dma_ops = get_pci_dma_ops(); + celleb_dma_dev_setup(dev); return 0; } @@ -94,6 +107,7 @@ static int __init celleb_init_iommu(void) { celleb_init_direct_mapping(); set_pci_dma_ops(&dma_direct_ops); + ppc_md.pci_dma_dev_setup = celleb_pci_dma_dev_setup; bus_register_notifier(&of_platform_bus_type, &celleb_of_bus_notifier); return 0; diff --git a/arch/powerpc/platforms/chrp/pci.c b/arch/powerpc/platforms/chrp/pci.c index d51f83a..609c46d 100644 --- a/arch/powerpc/platforms/chrp/pci.c +++ b/arch/powerpc/platforms/chrp/pci.c @@ -354,7 +354,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105, * mode as well. The same fixup must be done to the class-code property in * the IDE node /pci@80000000/ide@C,1 */ -static void __devinit chrp_pci_fixup_vt8231_ata(struct pci_dev *viaide) +static void chrp_pci_fixup_vt8231_ata(struct pci_dev *viaide) { u8 progif; struct pci_dev *viaisa; @@ -375,4 +375,4 @@ static void __devinit chrp_pci_fixup_vt8231_ata(struct pci_dev *viaide) pci_dev_put(viaisa); } -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, chrp_pci_fixup_vt8231_ata); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, chrp_pci_fixup_vt8231_ata); diff --git a/arch/powerpc/platforms/chrp/setup.c b/arch/powerpc/platforms/chrp/setup.c index 42a21ba..116babb 100644 --- a/arch/powerpc/platforms/chrp/setup.c +++ b/arch/powerpc/platforms/chrp/setup.c @@ -256,6 +256,57 @@ static void briq_restart(char *cmd) for(;;); } +/* + * Per default, input/output-device points to the keyboard/screen + * If no card is installed, the built-in serial port is used as a fallback. + * But unfortunately, the firmware does not connect /chosen/{stdin,stdout} + * the the built-in serial node. Instead, a /failsafe node is created. + */ +static void chrp_init_early(void) +{ + struct device_node *node; + const char *property; + + if (strstr(cmd_line, "console=")) + return; + /* find the boot console from /chosen/stdout */ + if (!of_chosen) + return; + node = of_find_node_by_path("/"); + if (!node) + return; + property = of_get_property(node, "model", NULL); + if (!property) + goto out_put; + if (strcmp(property, "Pegasos2")) + goto out_put; + /* this is a Pegasos2 */ + property = of_get_property(of_chosen, "linux,stdout-path", NULL); + if (!property) + goto out_put; + of_node_put(node); + node = of_find_node_by_path(property); + if (!node) + return; + property = of_get_property(node, "device_type", NULL); + if (!property) + goto out_put; + if (strcmp(property, "serial")) + goto out_put; + /* + * The 9pin connector is either /failsafe + * or /pci@80000000/isa@C/serial@i2F8 + * The optional graphics card has also type 'serial' in VGA mode. + */ + property = of_get_property(node, "name", NULL); + if (!property) + goto out_put; + if (!strcmp(property, "failsafe") || !strcmp(property, "serial")) + add_preferred_console("ttyS", 0, NULL); +out_put: + of_node_put(node); +} + void __init chrp_setup_arch(void) { struct device_node *root = of_find_node_by_path("/"); @@ -599,6 +650,7 @@ define_machine(chrp) { .probe = chrp_probe, .setup_arch = chrp_setup_arch, .init = chrp_init2, + .init_early = chrp_init_early, .show_cpuinfo = chrp_show_cpuinfo, .init_IRQ = chrp_init_IRQ, .restart = rtas_restart, diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig index 298f1c9..a5f4e95 100644 --- a/arch/powerpc/platforms/ps3/Kconfig +++ b/arch/powerpc/platforms/ps3/Kconfig @@ -61,17 +61,6 @@ config PS3_DYNAMIC_DMA This support is mainly for Linux kernel development. If unsure, say N. -config PS3_USE_LPAR_ADDR - depends on PPC_PS3 && EXPERIMENTAL - bool "PS3 use lpar address space" - default y - help - This option is solely for experimentation by experts. Disables - translation of lpar addresses. SPE support currently won't work - without this set to y. - - If you have any doubt, choose the default y. - config PS3_VUART depends on PPC_PS3 tristate @@ -138,4 +127,17 @@ config PS3_FLASH be disabled on the kernel command line using "ps3flash=off", to not allocate this fixed buffer. +config PS3_LPM + tristate "PS3 Logical Performance Monitor support" + depends on PPC_PS3 + help + Include support for the PS3 Logical Performance Monitor. + + This support is required to use the logical performance monitor + of the PS3's LV1 hypervisor. + + If you intend to use the advanced performance monitoring and + profiling support of the Cell processor with programs like + oprofile and perfmon2, then say Y or M, otherwise say N. + endmenu diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c index fd063fe..9d251d0 100644 --- a/arch/powerpc/platforms/ps3/device-init.c +++ b/arch/powerpc/platforms/ps3/device-init.c @@ -23,6 +23,7 @@ #include <linux/kernel.h> #include <linux/kthread.h> #include <linux/init.h> +#include <linux/reboot.h> #include <asm/firmware.h> #include <asm/lv1call.h> @@ -30,6 +31,89 @@ #include "platform.h" +static int __init ps3_register_lpm_devices(void) +{ + int result; + u64 tmp1; + u64 tmp2; + struct ps3_system_bus_device *dev; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->match_id = PS3_MATCH_ID_LPM; + dev->dev_type = PS3_DEVICE_TYPE_LPM; + + /* The current lpm driver only supports a single BE processor. */ + + result = ps3_repository_read_be_node_id(0, &dev->lpm.node_id); + + if (result) { + pr_debug("%s:%d: ps3_repository_read_be_node_id failed \n", + __func__, __LINE__); + goto fail_read_repo; + } + + result = ps3_repository_read_lpm_privileges(dev->lpm.node_id, &tmp1, + &dev->lpm.rights); + + if (result) { + pr_debug("%s:%d: ps3_repository_read_lpm_privleges failed \n", + __func__, __LINE__); + goto fail_read_repo; + } + + lv1_get_logical_partition_id(&tmp2); + + if (tmp1 != tmp2) { + pr_debug("%s:%d: wrong lpar\n", + __func__, __LINE__); + result = -ENODEV; + goto fail_rights; + } + + if (!(dev->lpm.rights & PS3_LPM_RIGHTS_USE_LPM)) { + pr_debug("%s:%d: don't have rights to use lpm\n", + __func__, __LINE__); + result = -EPERM; + goto fail_rights; + } + + pr_debug("%s:%d: pu_id %lu, rights %lu(%lxh)\n", + __func__, __LINE__, dev->lpm.pu_id, dev->lpm.rights, + dev->lpm.rights); + + result = ps3_repository_read_pu_id(0, &dev->lpm.pu_id); + + if (result) { + pr_debug("%s:%d: ps3_repository_read_pu_id failed \n", + __func__, __LINE__); + goto fail_read_repo; + } + + result = ps3_system_bus_device_register(dev); + + if (result) { + pr_debug("%s:%d ps3_system_bus_device_register failed\n", + __func__, __LINE__); + goto fail_register; + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; + + +fail_register: +fail_rights: +fail_read_repo: + kfree(dev); + pr_debug(" <- %s:%d: failed\n", __func__, __LINE__); + return result; +} + /** * ps3_setup_gelic_device - Setup and register a gelic device instance. * @@ -238,166 +322,6 @@ static int __init ps3_setup_vuart_device(enum ps3_match_id match_id, return result; } -static int ps3stor_wait_for_completion(u64 dev_id, u64 tag, - unsigned int timeout) -{ - int result = -1; - unsigned int retries = 0; - u64 status; - - for (retries = 0; retries < timeout; retries++) { - result = lv1_storage_check_async_status(dev_id, tag, &status); - if (!result) - break; - - msleep(1); - } - - if (result) - pr_debug("%s:%u: check_async_status: %s, status %lx\n", - __func__, __LINE__, ps3_result(result), status); - - return result; -} - -/** - * ps3_storage_wait_for_device - Wait for a storage device to become ready. - * @repo: The repository device to wait for. - * - * Uses the hypervisor's storage device notification mechanism to wait until - * a storage device is ready. The device notification mechanism uses a - * psuedo device (id = -1) to asynchronously notify the guest when storage - * devices become ready. The notification device has a block size of 512 - * bytes. - */ - -static int ps3_storage_wait_for_device(const struct ps3_repository_device *repo) -{ - int error = -ENODEV; - int result; - const u64 notification_dev_id = (u64)-1LL; - const unsigned int timeout = HZ; - u64 lpar; - u64 tag; - void *buf; - enum ps3_notify_type { - notify_device_ready = 0, - notify_region_probe = 1, - notify_region_update = 2, - }; - struct { - u64 operation_code; /* must be zero */ - u64 event_mask; /* OR of 1UL << enum ps3_notify_type */ - } *notify_cmd; - struct { - u64 event_type; /* enum ps3_notify_type */ - u64 bus_id; - u64 dev_id; - u64 dev_type; - u64 dev_port; - } *notify_event; - - pr_debug(" -> %s:%u: (%u:%u:%u)\n", __func__, __LINE__, repo->bus_id, - repo->dev_id, repo->dev_type); - - buf = kzalloc(512, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - lpar = ps3_mm_phys_to_lpar(__pa(buf)); - notify_cmd = buf; - notify_event = buf; - - result = lv1_open_device(repo->bus_id, notification_dev_id, 0); - if (result) { - printk(KERN_ERR "%s:%u: lv1_open_device %s\n", __func__, - __LINE__, ps3_result(result)); - goto fail_free; - } - - /* Setup and write the request for device notification. */ - - notify_cmd->operation_code = 0; /* must be zero */ - notify_cmd->event_mask = 1UL << notify_region_probe; - - result = lv1_storage_write(notification_dev_id, 0, 0, 1, 0, lpar, - &tag); - if (result) { - printk(KERN_ERR "%s:%u: write failed %s\n", __func__, __LINE__, - ps3_result(result)); - goto fail_close; - } - - /* Wait for the write completion */ - - result = ps3stor_wait_for_completion(notification_dev_id, tag, - timeout); - if (result) { - printk(KERN_ERR "%s:%u: write not completed %s\n", __func__, - __LINE__, ps3_result(result)); - goto fail_close; - } - - /* Loop here processing the requested notification events. */ - - while (1) { - memset(notify_event, 0, sizeof(*notify_event)); - - result = lv1_storage_read(notification_dev_id, 0, 0, 1, 0, - lpar, &tag); - if (result) { - printk(KERN_ERR "%s:%u: write failed %s\n", __func__, - __LINE__, ps3_result(result)); - break; - } - - result = ps3stor_wait_for_completion(notification_dev_id, tag, - timeout); - if (result) { - printk(KERN_ERR "%s:%u: read not completed %s\n", - __func__, __LINE__, ps3_result(result)); - break; - } - - pr_debug("%s:%d: notify event (%u:%u:%u): event_type 0x%lx, " - "port %lu\n", __func__, __LINE__, repo->bus_index, - repo->dev_index, repo->dev_type, - notify_event->event_type, notify_event->dev_port); - - if (notify_event->event_type != notify_region_probe || - notify_event->bus_id != repo->bus_id) { - pr_debug("%s:%u: bad notify_event: event %lu, " - "dev_id %lu, dev_type %lu\n", - __func__, __LINE__, notify_event->event_type, - notify_event->dev_id, notify_event->dev_type); - break; - } - - if (notify_event->dev_id == repo->dev_id && - notify_event->dev_type == repo->dev_type) { - pr_debug("%s:%u: device ready (%u:%u:%u)\n", __func__, - __LINE__, repo->bus_index, repo->dev_index, - repo->dev_type); - error = 0; - break; - } - - if (notify_event->dev_id == repo->dev_id && - notify_event->dev_type == PS3_DEV_TYPE_NOACCESS) { - pr_debug("%s:%u: no access: dev_id %u\n", __func__, - __LINE__, repo->dev_id); - break; - } - } - -fail_close: - lv1_close_device(repo->bus_id, notification_dev_id); -fail_free: - kfree(buf); - pr_debug(" <- %s:%u\n", __func__, __LINE__); - return error; -} - static int ps3_setup_storage_dev(const struct ps3_repository_device *repo, enum ps3_match_id match_id) { @@ -449,16 +373,6 @@ static int ps3_setup_storage_dev(const struct ps3_repository_device *repo, goto fail_find_interrupt; } - /* FIXME: Arrange to only do this on a 'cold' boot */ - - result = ps3_storage_wait_for_device(repo); - if (result) { - printk(KERN_ERR "%s:%u: storage_notification failed %d\n", - __func__, __LINE__, result); - result = -ENODEV; - goto fail_probe_notification; - } - for (i = 0; i < num_regions; i++) { unsigned int id; u64 start, size; @@ -494,7 +408,6 @@ static int ps3_setup_storage_dev(const struct ps3_repository_device *repo, fail_device_register: fail_read_region: -fail_probe_notification: fail_find_interrupt: kfree(p); fail_malloc: @@ -659,62 +572,268 @@ static int ps3_register_repository_device( return result; } +static void ps3_find_and_add_device(u64 bus_id, u64 dev_id) +{ + struct ps3_repository_device repo; + int res; + unsigned int retries; + unsigned long rem; + + /* + * On some firmware versions (e.g. 1.90), the device may not show up + * in the repository immediately + */ + for (retries = 0; retries < 10; retries++) { + res = ps3_repository_find_device_by_id(&repo, bus_id, dev_id); + if (!res) + goto found; + + rem = msleep_interruptible(100); + if (rem) + break; + } + pr_warning("%s:%u: device %lu:%lu not found\n", __func__, __LINE__, + bus_id, dev_id); + return; + +found: + if (retries) + pr_debug("%s:%u: device %lu:%lu found after %u retries\n", + __func__, __LINE__, bus_id, dev_id, retries); + + ps3_register_repository_device(&repo); + return; +} + +#define PS3_NOTIFICATION_DEV_ID ULONG_MAX +#define PS3_NOTIFICATION_INTERRUPT_ID 0 + +struct ps3_notification_device { + struct ps3_system_bus_device sbd; + spinlock_t lock; + u64 tag; + u64 lv1_status; + struct completion done; +}; + +enum ps3_notify_type { + notify_device_ready = 0, + notify_region_probe = 1, + notify_region_update = 2, +}; + +struct ps3_notify_cmd { + u64 operation_code; /* must be zero */ + u64 event_mask; /* OR of 1UL << enum ps3_notify_type */ +}; + +struct ps3_notify_event { + u64 event_type; /* enum ps3_notify_type */ + u64 bus_id; + u64 dev_id; + u64 dev_type; + u64 dev_port; +}; + +static irqreturn_t ps3_notification_interrupt(int irq, void *data) +{ + struct ps3_notification_device *dev = data; + int res; + u64 tag, status; + + spin_lock(&dev->lock); + res = lv1_storage_get_async_status(PS3_NOTIFICATION_DEV_ID, &tag, + &status); + if (tag != dev->tag) + pr_err("%s:%u: tag mismatch, got %lx, expected %lx\n", + __func__, __LINE__, tag, dev->tag); + + if (res) { + pr_err("%s:%u: res %d status 0x%lx\n", __func__, __LINE__, res, + status); + } else { + pr_debug("%s:%u: completed, status 0x%lx\n", __func__, + __LINE__, status); + dev->lv1_status = status; + complete(&dev->done); + } + spin_unlock(&dev->lock); + return IRQ_HANDLED; +} + +static int ps3_notification_read_write(struct ps3_notification_device *dev, + u64 lpar, int write) +{ + const char *op = write ? "write" : "read"; + unsigned long flags; + int res; + + init_completion(&dev->done); + spin_lock_irqsave(&dev->lock, flags); + res = write ? lv1_storage_write(dev->sbd.dev_id, 0, 0, 1, 0, lpar, + &dev->tag) + : lv1_storage_read(dev->sbd.dev_id, 0, 0, 1, 0, lpar, + &dev->tag); + spin_unlock_irqrestore(&dev->lock, flags); + if (res) { + pr_err("%s:%u: %s failed %d\n", __func__, __LINE__, op, res); + return -EPERM; + } + pr_debug("%s:%u: notification %s issued\n", __func__, __LINE__, op); + + res = wait_event_interruptible(dev->done.wait, + dev->done.done || kthread_should_stop()); + if (kthread_should_stop()) + res = -EINTR; + if (res) { + pr_debug("%s:%u: interrupted %s\n", __func__, __LINE__, op); + return res; + } + + if (dev->lv1_status) { + pr_err("%s:%u: %s not completed, status 0x%lx\n", __func__, + __LINE__, op, dev->lv1_status); + return -EIO; + } + pr_debug("%s:%u: notification %s completed\n", __func__, __LINE__, op); + + return 0; +} + +static struct task_struct *probe_task; + /** * ps3_probe_thread - Background repository probing at system startup. * * This implementation only supports background probing on a single bus. + * It uses the hypervisor's storage device notification mechanism to wait until + * a storage device is ready. The device notification mechanism uses a + * pseudo device to asynchronously notify the guest when storage devices become + * ready. The notification device has a block size of 512 bytes. */ static int ps3_probe_thread(void *data) { - struct ps3_repository_device *repo = data; - int result; - unsigned int ms = 250; + struct ps3_notification_device dev; + int res; + unsigned int irq; + u64 lpar; + void *buf; + struct ps3_notify_cmd *notify_cmd; + struct ps3_notify_event *notify_event; pr_debug(" -> %s:%u: kthread started\n", __func__, __LINE__); + buf = kzalloc(512, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + lpar = ps3_mm_phys_to_lpar(__pa(buf)); + notify_cmd = buf; + notify_event = buf; + + /* dummy system bus device */ + dev.sbd.bus_id = (u64)data; + dev.sbd.dev_id = PS3_NOTIFICATION_DEV_ID; + dev.sbd.interrupt_id = PS3_NOTIFICATION_INTERRUPT_ID; + + res = lv1_open_device(dev.sbd.bus_id, dev.sbd.dev_id, 0); + if (res) { + pr_err("%s:%u: lv1_open_device failed %s\n", __func__, + __LINE__, ps3_result(res)); + goto fail_free; + } + + res = ps3_sb_event_receive_port_setup(&dev.sbd, PS3_BINDING_CPU_ANY, + &irq); + if (res) { + pr_err("%s:%u: ps3_sb_event_receive_port_setup failed %d\n", + __func__, __LINE__, res); + goto fail_close_device; + } + + spin_lock_init(&dev.lock); + + res = request_irq(irq, ps3_notification_interrupt, IRQF_DISABLED, + "ps3_notification", &dev); + if (res) { + pr_err("%s:%u: request_irq failed %d\n", __func__, __LINE__, + res); + goto fail_sb_event_receive_port_destroy; + } + + /* Setup and write the request for device notification. */ + notify_cmd->operation_code = 0; /* must be zero */ + notify_cmd->event_mask = 1UL << notify_region_probe; + + res = ps3_notification_read_write(&dev, lpar, 1); + if (res) + goto fail_free_irq; + + /* Loop here processing the requested notification events. */ do { try_to_freeze(); - pr_debug("%s:%u: probing...\n", __func__, __LINE__); - - do { - result = ps3_repository_find_device(repo); - - if (result == -ENODEV) - pr_debug("%s:%u: nothing new\n", __func__, - __LINE__); - else if (result) - pr_debug("%s:%u: find device error.\n", - __func__, __LINE__); - else { - pr_debug("%s:%u: found device (%u:%u:%u)\n", - __func__, __LINE__, repo->bus_index, - repo->dev_index, repo->dev_type); - ps3_register_repository_device(repo); - ps3_repository_bump_device(repo); - ms = 250; - } - } while (!result); - - pr_debug("%s:%u: ms %u\n", __func__, __LINE__, ms); - - if ( ms > 60000) + memset(notify_event, 0, sizeof(*notify_event)); + + res = ps3_notification_read_write(&dev, lpar, 0); + if (res) break; - msleep_interruptible(ms); + pr_debug("%s:%u: notify event type 0x%lx bus id %lu dev id %lu" + " type %lu port %lu\n", __func__, __LINE__, + notify_event->event_type, notify_event->bus_id, + notify_event->dev_id, notify_event->dev_type, + notify_event->dev_port); - /* An exponential backoff. */ - ms <<= 1; + if (notify_event->event_type != notify_region_probe || + notify_event->bus_id != dev.sbd.bus_id) { + pr_warning("%s:%u: bad notify_event: event %lu, " + "dev_id %lu, dev_type %lu\n", + __func__, __LINE__, notify_event->event_type, + notify_event->dev_id, + notify_event->dev_type); + continue; + } + + ps3_find_and_add_device(dev.sbd.bus_id, notify_event->dev_id); } while (!kthread_should_stop()); +fail_free_irq: + free_irq(irq, &dev); +fail_sb_event_receive_port_destroy: + ps3_sb_event_receive_port_destroy(&dev.sbd, irq); +fail_close_device: + lv1_close_device(dev.sbd.bus_id, dev.sbd.dev_id); +fail_free: + kfree(buf); + + probe_task = NULL; + pr_debug(" <- %s:%u: kthread finished\n", __func__, __LINE__); return 0; } /** + * ps3_stop_probe_thread - Stops the background probe thread. + * + */ + +static int ps3_stop_probe_thread(struct notifier_block *nb, unsigned long code, + void *data) +{ + if (probe_task) + kthread_stop(probe_task); + return 0; +} + +static struct notifier_block nb = { + .notifier_call = ps3_stop_probe_thread +}; + +/** * ps3_start_probe_thread - Starts the background probe thread. * */ @@ -723,7 +842,7 @@ static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type) { int result; struct task_struct *task; - static struct ps3_repository_device repo; /* must be static */ + struct ps3_repository_device repo; pr_debug(" -> %s:%d\n", __func__, __LINE__); @@ -746,7 +865,8 @@ static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type) return -ENODEV; } - task = kthread_run(ps3_probe_thread, &repo, "ps3-probe-%u", bus_type); + task = kthread_run(ps3_probe_thread, (void *)repo.bus_id, + "ps3-probe-%u", bus_type); if (IS_ERR(task)) { result = PTR_ERR(task); @@ -755,6 +875,9 @@ static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type) return result; } + probe_task = task; + register_reboot_notifier(&nb); + pr_debug(" <- %s:%d\n", __func__, __LINE__); return 0; } @@ -787,6 +910,8 @@ static int __init ps3_register_devices(void) ps3_register_sound_devices(); + ps3_register_lpm_devices(); + pr_debug(" <- %s:%d\n", __func__, __LINE__); return 0; } diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c index 7bb3e16..6890047 100644 --- a/arch/powerpc/platforms/ps3/mm.c +++ b/arch/powerpc/platforms/ps3/mm.c @@ -36,11 +36,6 @@ #endif enum { -#if defined(CONFIG_PS3_USE_LPAR_ADDR) - USE_LPAR_ADDR = 1, -#else - USE_LPAR_ADDR = 0, -#endif #if defined(CONFIG_PS3_DYNAMIC_DMA) USE_DYNAMIC_DMA = 1, #else @@ -137,11 +132,8 @@ static struct map map; unsigned long ps3_mm_phys_to_lpar(unsigned long phys_addr) { BUG_ON(is_kernel_addr(phys_addr)); - if (USE_LPAR_ADDR) - return phys_addr; - else - return (phys_addr < map.rm.size || phys_addr >= map.total) - ? phys_addr : phys_addr + map.r1.offset; + return (phys_addr < map.rm.size || phys_addr >= map.total) + ? phys_addr : phys_addr + map.r1.offset; } EXPORT_SYMBOL(ps3_mm_phys_to_lpar); @@ -309,7 +301,7 @@ static int __init ps3_mm_add_memory(void) BUG_ON(!mem_init_done); - start_addr = USE_LPAR_ADDR ? map.r1.base : map.rm.size; + start_addr = map.rm.size; start_pfn = start_addr >> PAGE_SHIFT; nr_pages = (map.r1.size + PAGE_SIZE - 1) >> PAGE_SHIFT; @@ -359,7 +351,7 @@ static unsigned long dma_sb_lpar_to_bus(struct ps3_dma_region *r, static void __maybe_unused _dma_dump_region(const struct ps3_dma_region *r, const char *func, int line) { - DBG("%s:%d: dev %u:%u\n", func, line, r->dev->bus_id, + DBG("%s:%d: dev %lu:%lu\n", func, line, r->dev->bus_id, r->dev->dev_id); DBG("%s:%d: page_size %u\n", func, line, r->page_size); DBG("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr); @@ -394,7 +386,7 @@ struct dma_chunk { static void _dma_dump_chunk (const struct dma_chunk* c, const char* func, int line) { - DBG("%s:%d: r.dev %u:%u\n", func, line, + DBG("%s:%d: r.dev %lu:%lu\n", func, line, c->region->dev->bus_id, c->region->dev->dev_id); DBG("%s:%d: r.bus_addr %lxh\n", func, line, c->region->bus_addr); DBG("%s:%d: r.page_size %u\n", func, line, c->region->page_size); @@ -658,7 +650,7 @@ static int dma_sb_region_create(struct ps3_dma_region *r) BUG_ON(!r); if (!r->dev->bus_id) { - pr_info("%s:%d: %u:%u no dma\n", __func__, __LINE__, + pr_info("%s:%d: %lu:%lu no dma\n", __func__, __LINE__, r->dev->bus_id, r->dev->dev_id); return 0; } @@ -724,7 +716,7 @@ static int dma_sb_region_free(struct ps3_dma_region *r) BUG_ON(!r); if (!r->dev->bus_id) { - pr_info("%s:%d: %u:%u no dma\n", __func__, __LINE__, + pr_info("%s:%d: %lu:%lu no dma\n", __func__, __LINE__, r->dev->bus_id, r->dev->dev_id); return 0; } @@ -1007,7 +999,7 @@ static int dma_sb_region_create_linear(struct ps3_dma_region *r) if (r->offset + r->len > map.rm.size) { /* Map (part of) 2nd RAM chunk */ - virt_addr = USE_LPAR_ADDR ? map.r1.base : map.rm.size; + virt_addr = map.rm.size; len = r->len; if (r->offset >= map.rm.size) virt_addr += r->offset - map.rm.size; diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h index 01f0c95..235c13e 100644 --- a/arch/powerpc/platforms/ps3/platform.h +++ b/arch/powerpc/platforms/ps3/platform.h @@ -89,13 +89,11 @@ enum ps3_dev_type { PS3_DEV_TYPE_STOR_ROM = TYPE_ROM, /* 5 */ PS3_DEV_TYPE_SB_GPIO = 6, PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC, /* 14 */ - PS3_DEV_TYPE_STOR_DUMMY = 32, - PS3_DEV_TYPE_NOACCESS = 255, }; int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str, u64 *value); -int ps3_repository_read_bus_id(unsigned int bus_index, unsigned int *bus_id); +int ps3_repository_read_bus_id(unsigned int bus_index, u64 *bus_id); int ps3_repository_read_bus_type(unsigned int bus_index, enum ps3_bus_type *bus_type); int ps3_repository_read_bus_num_dev(unsigned int bus_index, @@ -119,7 +117,7 @@ enum ps3_reg_type { int ps3_repository_read_dev_str(unsigned int bus_index, unsigned int dev_index, const char *dev_str, u64 *value); int ps3_repository_read_dev_id(unsigned int bus_index, unsigned int dev_index, - unsigned int *dev_id); + u64 *dev_id); int ps3_repository_read_dev_type(unsigned int bus_index, unsigned int dev_index, enum ps3_dev_type *dev_type); int ps3_repository_read_dev_intr(unsigned int bus_index, @@ -138,21 +136,17 @@ int ps3_repository_read_dev_reg(unsigned int bus_index, /* repository bus enumerators */ struct ps3_repository_device { - enum ps3_bus_type bus_type; unsigned int bus_index; - unsigned int bus_id; - enum ps3_dev_type dev_type; unsigned int dev_index; - unsigned int dev_id; + enum ps3_bus_type bus_type; + enum ps3_dev_type dev_type; + u64 bus_id; + u64 dev_id; }; -static inline struct ps3_repository_device *ps3_repository_bump_device( - struct ps3_repository_device *repo) -{ - repo->dev_index++; - return repo; -} int ps3_repository_find_device(struct ps3_repository_device *repo); +int ps3_repository_find_device_by_id(struct ps3_repository_device *repo, + u64 bus_id, u64 dev_id); int ps3_repository_find_devices(enum ps3_bus_type bus_type, int (*callback)(const struct ps3_repository_device *repo)); int ps3_repository_find_bus(enum ps3_bus_type bus_type, unsigned int from, @@ -186,10 +180,10 @@ int ps3_repository_read_stor_dev_region(unsigned int bus_index, unsigned int dev_index, unsigned int region_index, unsigned int *region_id, u64 *region_start, u64 *region_size); -/* repository pu and memory info */ +/* repository logical pu and memory info */ -int ps3_repository_read_num_pu(unsigned int *num_pu); -int ps3_repository_read_ppe_id(unsigned int *pu_index, unsigned int *ppe_id); +int ps3_repository_read_num_pu(u64 *num_pu); +int ps3_repository_read_pu_id(unsigned int pu_index, u64 *pu_id); int ps3_repository_read_rm_base(unsigned int ppe_id, u64 *rm_base); int ps3_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size); int ps3_repository_read_region_total(u64 *region_total); @@ -200,9 +194,15 @@ int ps3_repository_read_mm_info(u64 *rm_base, u64 *rm_size, int ps3_repository_read_num_be(unsigned int *num_be); int ps3_repository_read_be_node_id(unsigned int be_index, u64 *node_id); +int ps3_repository_read_be_id(u64 node_id, u64 *be_id); int ps3_repository_read_tb_freq(u64 node_id, u64 *tb_freq); int ps3_repository_read_be_tb_freq(unsigned int be_index, u64 *tb_freq); +/* repository performance monitor info */ + +int ps3_repository_read_lpm_privileges(unsigned int be_index, u64 *lpar, + u64 *rights); + /* repository 'Other OS' area */ int ps3_repository_read_boot_dat_addr(u64 *lpar_addr); diff --git a/arch/powerpc/platforms/ps3/repository.c b/arch/powerpc/platforms/ps3/repository.c index 1c94824..22063ad 100644 --- a/arch/powerpc/platforms/ps3/repository.c +++ b/arch/powerpc/platforms/ps3/repository.c @@ -33,7 +33,7 @@ enum ps3_lpar_id { }; #define dump_field(_a, _b) _dump_field(_a, _b, __func__, __LINE__) -static void _dump_field(const char *hdr, u64 n, const char* func, int line) +static void _dump_field(const char *hdr, u64 n, const char *func, int line) { #if defined(DEBUG) char s[16]; @@ -50,8 +50,8 @@ static void _dump_field(const char *hdr, u64 n, const char* func, int line) #define dump_node_name(_a, _b, _c, _d, _e) \ _dump_node_name(_a, _b, _c, _d, _e, __func__, __LINE__) -static void _dump_node_name (unsigned int lpar_id, u64 n1, u64 n2, u64 n3, - u64 n4, const char* func, int line) +static void _dump_node_name(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, + u64 n4, const char *func, int line) { pr_debug("%s:%d: lpar: %u\n", func, line, lpar_id); _dump_field("n1: ", n1, func, line); @@ -63,7 +63,7 @@ static void _dump_node_name (unsigned int lpar_id, u64 n1, u64 n2, u64 n3, #define dump_node(_a, _b, _c, _d, _e, _f, _g) \ _dump_node(_a, _b, _c, _d, _e, _f, _g, __func__, __LINE__) static void _dump_node(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, u64 n4, - u64 v1, u64 v2, const char* func, int line) + u64 v1, u64 v2, const char *func, int line) { pr_debug("%s:%d: lpar: %u\n", func, line, lpar_id); _dump_field("n1: ", n1, func, line); @@ -165,21 +165,18 @@ int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str, make_first_field("bus", bus_index), make_field(bus_str, 0), 0, 0, - value, 0); + value, NULL); } -int ps3_repository_read_bus_id(unsigned int bus_index, unsigned int *bus_id) +int ps3_repository_read_bus_id(unsigned int bus_index, u64 *bus_id) { int result; - u64 v1; - u64 v2; /* unused */ result = read_node(PS3_LPAR_ID_PME, make_first_field("bus", bus_index), make_field("id", 0), 0, 0, - &v1, &v2); - *bus_id = v1; + bus_id, NULL); return result; } @@ -193,7 +190,7 @@ int ps3_repository_read_bus_type(unsigned int bus_index, make_first_field("bus", bus_index), make_field("type", 0), 0, 0, - &v1, 0); + &v1, NULL); *bus_type = v1; return result; } @@ -208,7 +205,7 @@ int ps3_repository_read_bus_num_dev(unsigned int bus_index, make_first_field("bus", bus_index), make_field("num_dev", 0), 0, 0, - &v1, 0); + &v1, NULL); *num_dev = v1; return result; } @@ -221,22 +218,20 @@ int ps3_repository_read_dev_str(unsigned int bus_index, make_field("dev", dev_index), make_field(dev_str, 0), 0, - value, 0); + value, NULL); } int ps3_repository_read_dev_id(unsigned int bus_index, unsigned int dev_index, - unsigned int *dev_id) + u64 *dev_id) { int result; - u64 v1; result = read_node(PS3_LPAR_ID_PME, make_first_field("bus", bus_index), make_field("dev", dev_index), make_field("id", 0), 0, - &v1, 0); - *dev_id = v1; + dev_id, NULL); return result; } @@ -251,14 +246,14 @@ int ps3_repository_read_dev_type(unsigned int bus_index, make_field("dev", dev_index), make_field("type", 0), 0, - &v1, 0); + &v1, NULL); *dev_type = v1; return result; } int ps3_repository_read_dev_intr(unsigned int bus_index, unsigned int dev_index, unsigned int intr_index, - enum ps3_interrupt_type *intr_type, unsigned int* interrupt_id) + enum ps3_interrupt_type *intr_type, unsigned int *interrupt_id) { int result; u64 v1; @@ -287,7 +282,7 @@ int ps3_repository_read_dev_reg_type(unsigned int bus_index, make_field("dev", dev_index), make_field("reg", reg_index), make_field("type", 0), - &v1, 0); + &v1, NULL); *reg_type = v1; return result; } @@ -332,7 +327,7 @@ int ps3_repository_find_device(struct ps3_repository_device *repo) return result; } - pr_debug("%s:%d: bus_type %u, bus_index %u, bus_id %u, num_dev %u\n", + pr_debug("%s:%d: bus_type %u, bus_index %u, bus_id %lu, num_dev %u\n", __func__, __LINE__, tmp.bus_type, tmp.bus_index, tmp.bus_id, num_dev); @@ -349,47 +344,95 @@ int ps3_repository_find_device(struct ps3_repository_device *repo) return result; } - if (tmp.bus_type == PS3_BUS_TYPE_STORAGE) { - /* - * A storage device may show up in the repository before the - * hypervisor has finished probing its type and regions - */ - unsigned int num_regions; - - if (tmp.dev_type == PS3_DEV_TYPE_STOR_DUMMY) { - pr_debug("%s:%u storage device not ready\n", __func__, - __LINE__); - return -ENODEV; - } + result = ps3_repository_read_dev_id(tmp.bus_index, tmp.dev_index, + &tmp.dev_id); - result = ps3_repository_read_stor_dev_num_regions(tmp.bus_index, - tmp.dev_index, - &num_regions); + if (result) { + pr_debug("%s:%d ps3_repository_read_dev_id failed\n", __func__, + __LINE__); + return result; + } + + pr_debug("%s:%d: found: dev_type %u, dev_index %u, dev_id %lu\n", + __func__, __LINE__, tmp.dev_type, tmp.dev_index, tmp.dev_id); + + *repo = tmp; + return 0; +} + +int ps3_repository_find_device_by_id(struct ps3_repository_device *repo, + u64 bus_id, u64 dev_id) +{ + int result = -ENODEV; + struct ps3_repository_device tmp; + unsigned int num_dev; + + pr_debug(" -> %s:%u: find device by id %lu:%lu\n", __func__, __LINE__, + bus_id, dev_id); + + for (tmp.bus_index = 0; tmp.bus_index < 10; tmp.bus_index++) { + result = ps3_repository_read_bus_id(tmp.bus_index, + &tmp.bus_id); if (result) { - pr_debug("%s:%d read_stor_dev_num_regions failed\n", - __func__, __LINE__); + pr_debug("%s:%u read_bus_id(%u) failed\n", __func__, + __LINE__, tmp.bus_index); return result; } - if (!num_regions) { - pr_debug("%s:%u storage device has no regions yet\n", - __func__, __LINE__); - return -ENODEV; - } + if (tmp.bus_id == bus_id) + goto found_bus; + + pr_debug("%s:%u: skip, bus_id %lu\n", __func__, __LINE__, + tmp.bus_id); } + pr_debug(" <- %s:%u: bus not found\n", __func__, __LINE__); + return result; - result = ps3_repository_read_dev_id(tmp.bus_index, tmp.dev_index, - &tmp.dev_id); +found_bus: + result = ps3_repository_read_bus_type(tmp.bus_index, &tmp.bus_type); + if (result) { + pr_debug("%s:%u read_bus_type(%u) failed\n", __func__, + __LINE__, tmp.bus_index); + return result; + } + result = ps3_repository_read_bus_num_dev(tmp.bus_index, &num_dev); if (result) { - pr_debug("%s:%d ps3_repository_read_dev_id failed\n", __func__, - __LINE__); + pr_debug("%s:%u read_bus_num_dev failed\n", __func__, + __LINE__); return result; } - pr_debug("%s:%d: found: dev_type %u, dev_index %u, dev_id %u\n", - __func__, __LINE__, tmp.dev_type, tmp.dev_index, tmp.dev_id); + for (tmp.dev_index = 0; tmp.dev_index < num_dev; tmp.dev_index++) { + result = ps3_repository_read_dev_id(tmp.bus_index, + tmp.dev_index, + &tmp.dev_id); + if (result) { + pr_debug("%s:%u read_dev_id(%u:%u) failed\n", __func__, + __LINE__, tmp.bus_index, tmp.dev_index); + return result; + } + if (tmp.dev_id == dev_id) + goto found_dev; + + pr_debug("%s:%u: skip, dev_id %lu\n", __func__, __LINE__, + tmp.dev_id); + } + pr_debug(" <- %s:%u: dev not found\n", __func__, __LINE__); + return result; + +found_dev: + result = ps3_repository_read_dev_type(tmp.bus_index, tmp.dev_index, + &tmp.dev_type); + if (result) { + pr_debug("%s:%u read_dev_type failed\n", __func__, __LINE__); + return result; + } + + pr_debug(" <- %s:%u: found: type (%u:%u) index (%u:%u) id (%lu:%lu)\n", + __func__, __LINE__, tmp.bus_type, tmp.dev_type, tmp.bus_index, + tmp.dev_index, tmp.bus_id, tmp.dev_id); *repo = tmp; return 0; } @@ -402,50 +445,34 @@ int __devinit ps3_repository_find_devices(enum ps3_bus_type bus_type, pr_debug(" -> %s:%d: find bus_type %u\n", __func__, __LINE__, bus_type); - for (repo.bus_index = 0; repo.bus_index < 10; repo.bus_index++) { + repo.bus_type = bus_type; + result = ps3_repository_find_bus(repo.bus_type, 0, &repo.bus_index); + if (result) { + pr_debug(" <- %s:%u: bus not found\n", __func__, __LINE__); + return result; + } - result = ps3_repository_read_bus_type(repo.bus_index, - &repo.bus_type); + result = ps3_repository_read_bus_id(repo.bus_index, &repo.bus_id); + if (result) { + pr_debug("%s:%d read_bus_id(%u) failed\n", __func__, __LINE__, + repo.bus_index); + return result; + } - if (result) { - pr_debug("%s:%d read_bus_type(%u) failed\n", - __func__, __LINE__, repo.bus_index); + for (repo.dev_index = 0; ; repo.dev_index++) { + result = ps3_repository_find_device(&repo); + if (result == -ENODEV) { + result = 0; + break; + } else if (result) break; - } - - if (repo.bus_type != bus_type) { - pr_debug("%s:%d: skip, bus_type %u\n", __func__, - __LINE__, repo.bus_type); - continue; - } - - result = ps3_repository_read_bus_id(repo.bus_index, - &repo.bus_id); + result = callback(&repo); if (result) { - pr_debug("%s:%d read_bus_id(%u) failed\n", - __func__, __LINE__, repo.bus_index); - continue; - } - - for (repo.dev_index = 0; ; repo.dev_index++) { - result = ps3_repository_find_device(&repo); - - if (result == -ENODEV) { - result = 0; - break; - } else if (result) - break; - - result = callback(&repo); - - if (result) { - pr_debug("%s:%d: abort at callback\n", __func__, - __LINE__); - break; - } + pr_debug("%s:%d: abort at callback\n", __func__, + __LINE__); + break; } - break; } pr_debug(" <- %s:%d\n", __func__, __LINE__); @@ -561,7 +588,7 @@ int ps3_repository_read_stor_dev_port(unsigned int bus_index, make_first_field("bus", bus_index), make_field("dev", dev_index), make_field("port", 0), - 0, port, 0); + 0, port, NULL); } int ps3_repository_read_stor_dev_blk_size(unsigned int bus_index, @@ -571,7 +598,7 @@ int ps3_repository_read_stor_dev_blk_size(unsigned int bus_index, make_first_field("bus", bus_index), make_field("dev", dev_index), make_field("blk_size", 0), - 0, blk_size, 0); + 0, blk_size, NULL); } int ps3_repository_read_stor_dev_num_blocks(unsigned int bus_index, @@ -581,7 +608,7 @@ int ps3_repository_read_stor_dev_num_blocks(unsigned int bus_index, make_first_field("bus", bus_index), make_field("dev", dev_index), make_field("n_blocks", 0), - 0, num_blocks, 0); + 0, num_blocks, NULL); } int ps3_repository_read_stor_dev_num_regions(unsigned int bus_index, @@ -594,7 +621,7 @@ int ps3_repository_read_stor_dev_num_regions(unsigned int bus_index, make_first_field("bus", bus_index), make_field("dev", dev_index), make_field("n_regs", 0), - 0, &v1, 0); + 0, &v1, NULL); *num_regions = v1; return result; } @@ -611,7 +638,7 @@ int ps3_repository_read_stor_dev_region_id(unsigned int bus_index, make_field("dev", dev_index), make_field("region", region_index), make_field("id", 0), - &v1, 0); + &v1, NULL); *region_id = v1; return result; } @@ -624,7 +651,7 @@ int ps3_repository_read_stor_dev_region_size(unsigned int bus_index, make_field("dev", dev_index), make_field("region", region_index), make_field("size", 0), - region_size, 0); + region_size, NULL); } int ps3_repository_read_stor_dev_region_start(unsigned int bus_index, @@ -635,7 +662,7 @@ int ps3_repository_read_stor_dev_region_start(unsigned int bus_index, make_field("dev", dev_index), make_field("region", region_index), make_field("start", 0), - region_start, 0); + region_start, NULL); } int ps3_repository_read_stor_dev_info(unsigned int bus_index, @@ -684,6 +711,35 @@ int ps3_repository_read_stor_dev_region(unsigned int bus_index, return result; } +/** + * ps3_repository_read_num_pu - Number of logical PU processors for this lpar. + */ + +int ps3_repository_read_num_pu(u64 *num_pu) +{ + *num_pu = 0; + return read_node(PS3_LPAR_ID_CURRENT, + make_first_field("bi", 0), + make_field("pun", 0), + 0, 0, + num_pu, NULL); +} + +/** + * ps3_repository_read_pu_id - Read the logical PU id. + * @pu_index: Zero based index. + * @pu_id: The logical PU id. + */ + +int ps3_repository_read_pu_id(unsigned int pu_index, u64 *pu_id) +{ + return read_node(PS3_LPAR_ID_CURRENT, + make_first_field("bi", 0), + make_field("pu", pu_index), + 0, 0, + pu_id, NULL); +} + int ps3_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size) { return read_node(PS3_LPAR_ID_CURRENT, @@ -691,7 +747,7 @@ int ps3_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size) make_field("pu", 0), ppe_id, make_field("rm_size", 0), - rm_size, 0); + rm_size, NULL); } int ps3_repository_read_region_total(u64 *region_total) @@ -700,7 +756,7 @@ int ps3_repository_read_region_total(u64 *region_total) make_first_field("bi", 0), make_field("rgntotal", 0), 0, 0, - region_total, 0); + region_total, NULL); } /** @@ -736,7 +792,7 @@ int ps3_repository_read_num_spu_reserved(unsigned int *num_spu_reserved) make_first_field("bi", 0), make_field("spun", 0), 0, 0, - &v1, 0); + &v1, NULL); *num_spu_reserved = v1; return result; } @@ -755,7 +811,7 @@ int ps3_repository_read_num_spu_resource_id(unsigned int *num_resource_id) make_first_field("bi", 0), make_field("spursvn", 0), 0, 0, - &v1, 0); + &v1, NULL); *num_resource_id = v1; return result; } @@ -768,7 +824,7 @@ int ps3_repository_read_num_spu_resource_id(unsigned int *num_resource_id) */ int ps3_repository_read_spu_resource_id(unsigned int res_index, - enum ps3_spu_resource_type* resource_type, unsigned int *resource_id) + enum ps3_spu_resource_type *resource_type, unsigned int *resource_id) { int result; u64 v1; @@ -785,14 +841,14 @@ int ps3_repository_read_spu_resource_id(unsigned int res_index, return result; } -int ps3_repository_read_boot_dat_address(u64 *address) +static int ps3_repository_read_boot_dat_address(u64 *address) { return read_node(PS3_LPAR_ID_CURRENT, make_first_field("bi", 0), make_field("boot_dat", 0), make_field("address", 0), 0, - address, 0); + address, NULL); } int ps3_repository_read_boot_dat_size(unsigned int *size) @@ -805,7 +861,7 @@ int ps3_repository_read_boot_dat_size(unsigned int *size) make_field("boot_dat", 0), make_field("size", 0), 0, - &v1, 0); + &v1, NULL); *size = v1; return result; } @@ -820,7 +876,7 @@ int ps3_repository_read_vuart_av_port(unsigned int *port) make_field("vir_uart", 0), make_field("port", 0), make_field("avset", 0), - &v1, 0); + &v1, NULL); *port = v1; return result; } @@ -835,7 +891,7 @@ int ps3_repository_read_vuart_sysmgr_port(unsigned int *port) make_field("vir_uart", 0), make_field("port", 0), make_field("sysmgr", 0), - &v1, 0); + &v1, NULL); *port = v1; return result; } @@ -856,6 +912,10 @@ int ps3_repository_read_boot_dat_info(u64 *lpar_addr, unsigned int *size) : ps3_repository_read_boot_dat_size(size); } +/** + * ps3_repository_read_num_be - Number of physical BE processors in the system. + */ + int ps3_repository_read_num_be(unsigned int *num_be) { int result; @@ -866,11 +926,17 @@ int ps3_repository_read_num_be(unsigned int *num_be) 0, 0, 0, - &v1, 0); + &v1, NULL); *num_be = v1; return result; } +/** + * ps3_repository_read_be_node_id - Read the physical BE processor node id. + * @be_index: Zero based index. + * @node_id: The BE processor node id. + */ + int ps3_repository_read_be_node_id(unsigned int be_index, u64 *node_id) { return read_node(PS3_LPAR_ID_PME, @@ -878,7 +944,23 @@ int ps3_repository_read_be_node_id(unsigned int be_index, u64 *node_id) 0, 0, 0, - node_id, 0); + node_id, NULL); +} + +/** + * ps3_repository_read_be_id - Read the physical BE processor id. + * @node_id: The BE processor node id. + * @be_id: The BE processor id. + */ + +int ps3_repository_read_be_id(u64 node_id, u64 *be_id) +{ + return read_node(PS3_LPAR_ID_PME, + make_first_field("be", 0), + node_id, + 0, + 0, + be_id, NULL); } int ps3_repository_read_tb_freq(u64 node_id, u64 *tb_freq) @@ -888,7 +970,7 @@ int ps3_repository_read_tb_freq(u64 node_id, u64 *tb_freq) node_id, make_field("clock", 0), 0, - tb_freq, 0); + tb_freq, NULL); } int ps3_repository_read_be_tb_freq(unsigned int be_index, u64 *tb_freq) @@ -897,11 +979,29 @@ int ps3_repository_read_be_tb_freq(unsigned int be_index, u64 *tb_freq) u64 node_id; *tb_freq = 0; - result = ps3_repository_read_be_node_id(0, &node_id); + result = ps3_repository_read_be_node_id(be_index, &node_id); return result ? result : ps3_repository_read_tb_freq(node_id, tb_freq); } +int ps3_repository_read_lpm_privileges(unsigned int be_index, u64 *lpar, + u64 *rights) +{ + int result; + u64 node_id; + + *lpar = 0; + *rights = 0; + result = ps3_repository_read_be_node_id(be_index, &node_id); + return result ? result + : read_node(PS3_LPAR_ID_PME, + make_first_field("be", 0), + node_id, + make_field("lpm", 0), + make_field("priv", 0), + lpar, rights); +} + #if defined(DEBUG) int ps3_repository_dump_resource_info(const struct ps3_repository_device *repo) @@ -1034,7 +1134,7 @@ static int dump_device_info(struct ps3_repository_device *repo, continue; } - pr_debug("%s:%d (%u:%u): dev_type %u, dev_id %u\n", __func__, + pr_debug("%s:%d (%u:%u): dev_type %u, dev_id %lu\n", __func__, __LINE__, repo->bus_index, repo->dev_index, repo->dev_type, repo->dev_id); @@ -1091,7 +1191,7 @@ int ps3_repository_dump_bus_info(void) continue; } - pr_debug("%s:%d bus_%u: bus_type %u, bus_id %u, num_dev %u\n", + pr_debug("%s:%d bus_%u: bus_type %u, bus_id %lu, num_dev %u\n", __func__, __LINE__, repo.bus_index, repo.bus_type, repo.bus_id, num_dev); diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c index 6405f4a..43c493f 100644 --- a/arch/powerpc/platforms/ps3/system-bus.c +++ b/arch/powerpc/platforms/ps3/system-bus.c @@ -42,8 +42,8 @@ struct { int gpu; } static usage_hack; -static int ps3_is_device(struct ps3_system_bus_device *dev, - unsigned int bus_id, unsigned int dev_id) +static int ps3_is_device(struct ps3_system_bus_device *dev, u64 bus_id, + u64 dev_id) { return dev->bus_id == bus_id && dev->dev_id == dev_id; } @@ -182,8 +182,8 @@ int ps3_open_hv_device(struct ps3_system_bus_device *dev) case PS3_MATCH_ID_SYSTEM_MANAGER: pr_debug("%s:%d: unsupported match_id: %u\n", __func__, __LINE__, dev->match_id); - pr_debug("%s:%d: bus_id: %u\n", __func__, - __LINE__, dev->bus_id); + pr_debug("%s:%d: bus_id: %lu\n", __func__, __LINE__, + dev->bus_id); BUG(); return -EINVAL; @@ -220,8 +220,8 @@ int ps3_close_hv_device(struct ps3_system_bus_device *dev) case PS3_MATCH_ID_SYSTEM_MANAGER: pr_debug("%s:%d: unsupported match_id: %u\n", __func__, __LINE__, dev->match_id); - pr_debug("%s:%d: bus_id: %u\n", __func__, - __LINE__, dev->bus_id); + pr_debug("%s:%d: bus_id: %lu\n", __func__, __LINE__, + dev->bus_id); BUG(); return -EINVAL; @@ -240,7 +240,7 @@ EXPORT_SYMBOL_GPL(ps3_close_hv_device); static void _dump_mmio_region(const struct ps3_mmio_region* r, const char* func, int line) { - pr_debug("%s:%d: dev %u:%u\n", func, line, r->dev->bus_id, + pr_debug("%s:%d: dev %lu:%lu\n", func, line, r->dev->bus_id, r->dev->dev_id); pr_debug("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr); pr_debug("%s:%d: len %lxh\n", func, line, r->len); @@ -715,6 +715,7 @@ int ps3_system_bus_device_register(struct ps3_system_bus_device *dev) static unsigned int dev_ioc0_count; static unsigned int dev_sb_count; static unsigned int dev_vuart_count; + static unsigned int dev_lpm_count; if (!dev->core.parent) dev->core.parent = &ps3_system_bus; @@ -737,6 +738,10 @@ int ps3_system_bus_device_register(struct ps3_system_bus_device *dev) snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "vuart_%02x", ++dev_vuart_count); break; + case PS3_DEVICE_TYPE_LPM: + snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), + "lpm_%02x", ++dev_lpm_count); + break; default: BUG(); }; diff --git a/arch/powerpc/xmon/setjmp.S b/arch/powerpc/xmon/setjmp.S index 96a91f1..04c0b30 100644 --- a/arch/powerpc/xmon/setjmp.S +++ b/arch/powerpc/xmon/setjmp.S @@ -12,67 +12,6 @@ #include <asm/ppc_asm.h> #include <asm/asm-offsets.h> -_GLOBAL(xmon_setjmp) - mflr r0 - PPC_STL r0,0(r3) - PPC_STL r1,SZL(r3) - PPC_STL r2,2*SZL(r3) - mfcr r0 - PPC_STL r0,3*SZL(r3) - PPC_STL r13,4*SZL(r3) - PPC_STL r14,5*SZL(r3) - PPC_STL r15,6*SZL(r3) - PPC_STL r16,7*SZL(r3) - PPC_STL r17,8*SZL(r3) - PPC_STL r18,9*SZL(r3) - PPC_STL r19,10*SZL(r3) - PPC_STL r20,11*SZL(r3) - PPC_STL r21,12*SZL(r3) - PPC_STL r22,13*SZL(r3) - PPC_STL r23,14*SZL(r3) - PPC_STL r24,15*SZL(r3) - PPC_STL r25,16*SZL(r3) - PPC_STL r26,17*SZL(r3) - PPC_STL r27,18*SZL(r3) - PPC_STL r28,19*SZL(r3) - PPC_STL r29,20*SZL(r3) - PPC_STL r30,21*SZL(r3) - PPC_STL r31,22*SZL(r3) - li r3,0 - blr - -_GLOBAL(xmon_longjmp) - PPC_LCMPI r4,0 - bne 1f - li r4,1 -1: PPC_LL r13,4*SZL(r3) - PPC_LL r14,5*SZL(r3) - PPC_LL r15,6*SZL(r3) - PPC_LL r16,7*SZL(r3) - PPC_LL r17,8*SZL(r3) - PPC_LL r18,9*SZL(r3) - PPC_LL r19,10*SZL(r3) - PPC_LL r20,11*SZL(r3) - PPC_LL r21,12*SZL(r3) - PPC_LL r22,13*SZL(r3) - PPC_LL r23,14*SZL(r3) - PPC_LL r24,15*SZL(r3) - PPC_LL r25,16*SZL(r3) - PPC_LL r26,17*SZL(r3) - PPC_LL r27,18*SZL(r3) - PPC_LL r28,19*SZL(r3) - PPC_LL r29,20*SZL(r3) - PPC_LL r30,21*SZL(r3) - PPC_LL r31,22*SZL(r3) - PPC_LL r0,3*SZL(r3) - mtcrf 0x38,r0 - PPC_LL r0,0(r3) - PPC_LL r1,SZL(r3) - PPC_LL r2,2*SZL(r3) - mtlr r0 - mr r3,r4 - blr - /* * Grab the register values as they are now. * This won't do a particularily good job because we really diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 865e367..a34172d 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -40,6 +40,7 @@ #include <asm/spu.h> #include <asm/spu_priv1.h> #include <asm/firmware.h> +#include <asm/setjmp.h> #ifdef CONFIG_PPC64 #include <asm/hvcall.h> @@ -71,12 +72,9 @@ static unsigned long ncsum = 4096; static int termch; static char tmpstr[128]; -#define JMP_BUF_LEN 23 static long bus_error_jmp[JMP_BUF_LEN]; static int catch_memory_errors; static long *xmon_fault_jmp[NR_CPUS]; -#define setjmp xmon_setjmp -#define longjmp xmon_longjmp /* Breakpoint stuff */ struct bpt { @@ -162,8 +160,6 @@ int xmon_no_auto_backtrace; extern void xmon_enter(void); extern void xmon_leave(void); -extern long setjmp(long *); -extern void longjmp(long *, long); extern void xmon_save_regs(struct pt_regs *); #ifdef CONFIG_PPC64 diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c index 0a42bf5..055af08 100644 --- a/drivers/net/ps3_gelic_net.c +++ b/drivers/net/ps3_gelic_net.c @@ -58,11 +58,11 @@ static inline struct device *ctodev(struct gelic_net_card *card) { return &card->dev->core; } -static inline unsigned int bus_id(struct gelic_net_card *card) +static inline u64 bus_id(struct gelic_net_card *card) { return card->dev->bus_id; } -static inline unsigned int dev_id(struct gelic_net_card *card) +static inline u64 dev_id(struct gelic_net_card *card) { return card->dev->dev_id; } diff --git a/drivers/ps3/Makefile b/drivers/ps3/Makefile index 1f5a2d3..ccea15c 100644 --- a/drivers/ps3/Makefile +++ b/drivers/ps3/Makefile @@ -4,3 +4,4 @@ ps3av_mod-objs += ps3av.o ps3av_cmd.o obj-$(CONFIG_PPC_PS3) += sys-manager-core.o obj-$(CONFIG_PS3_SYS_MANAGER) += ps3-sys-manager.o obj-$(CONFIG_PS3_STORAGE) += ps3stor_lib.o +obj-$(CONFIG_PS3_LPM) += ps3-lpm.o diff --git a/drivers/ps3/ps3-lpm.c b/drivers/ps3/ps3-lpm.c new file mode 100644 index 0000000..4c066545 --- /dev/null +++ b/drivers/ps3/ps3-lpm.c @@ -0,0 +1,1248 @@ +/* + * PS3 Logical Performance Monitor. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/uaccess.h> +#include <asm/ps3.h> +#include <asm/lv1call.h> +#include <asm/cell-pmu.h> + + +/* BOOKMARK tag macros */ +#define PS3_PM_BOOKMARK_START 0x8000000000000000ULL +#define PS3_PM_BOOKMARK_STOP 0x4000000000000000ULL +#define PS3_PM_BOOKMARK_TAG_KERNEL 0x1000000000000000ULL +#define PS3_PM_BOOKMARK_TAG_USER 0x3000000000000000ULL +#define PS3_PM_BOOKMARK_TAG_MASK_HI 0xF000000000000000ULL +#define PS3_PM_BOOKMARK_TAG_MASK_LO 0x0F00000000000000ULL + +/* CBE PM CONTROL register macros */ +#define PS3_PM_CONTROL_PPU_TH0_BOOKMARK 0x00001000 +#define PS3_PM_CONTROL_PPU_TH1_BOOKMARK 0x00000800 +#define PS3_PM_CONTROL_PPU_COUNT_MODE_MASK 0x000C0000 +#define PS3_PM_CONTROL_PPU_COUNT_MODE_PROBLEM 0x00080000 +#define PS3_WRITE_PM_MASK 0xFFFFFFFFFFFFFFFFULL + +/* CBE PM START STOP register macros */ +#define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_START 0x02000000 +#define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_START 0x01000000 +#define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_STOP 0x00020000 +#define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_STOP 0x00010000 +#define PS3_PM_START_STOP_START_MASK 0xFF000000 +#define PS3_PM_START_STOP_STOP_MASK 0x00FF0000 + +/* CBE PM COUNTER register macres */ +#define PS3_PM_COUNTER_MASK_HI 0xFFFFFFFF00000000ULL +#define PS3_PM_COUNTER_MASK_LO 0x00000000FFFFFFFFULL + +/* BASE SIGNAL GROUP NUMBER macros */ +#define PM_ISLAND2_BASE_SIGNAL_GROUP_NUMBER 0 +#define PM_ISLAND2_SIGNAL_GROUP_NUMBER1 6 +#define PM_ISLAND2_SIGNAL_GROUP_NUMBER2 7 +#define PM_ISLAND3_BASE_SIGNAL_GROUP_NUMBER 7 +#define PM_ISLAND4_BASE_SIGNAL_GROUP_NUMBER 15 +#define PM_SPU_TRIGGER_SIGNAL_GROUP_NUMBER 17 +#define PM_SPU_EVENT_SIGNAL_GROUP_NUMBER 18 +#define PM_ISLAND5_BASE_SIGNAL_GROUP_NUMBER 18 +#define PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER 24 +#define PM_ISLAND7_BASE_SIGNAL_GROUP_NUMBER 49 +#define PM_ISLAND8_BASE_SIGNAL_GROUP_NUMBER 52 +#define PM_SIG_GROUP_SPU 41 +#define PM_SIG_GROUP_SPU_TRIGGER 42 +#define PM_SIG_GROUP_SPU_EVENT 43 +#define PM_SIG_GROUP_MFC_MAX 60 + +/** + * struct ps3_lpm_shadow_regs - Performance monitor shadow registers. + * + * @pm_control: Shadow of the processor's pm_control register. + * @pm_start_stop: Shadow of the processor's pm_start_stop register. + * @pm_interval: Shadow of the processor's pm_interval register. + * @group_control: Shadow of the processor's group_control register. + * @debug_bus_control: Shadow of the processor's debug_bus_control register. + * + * The logical performance monitor provides a write-only interface to + * these processor registers. These shadow variables cache the processor + * register values for reading. + * + * The initial value of the shadow registers at lpm creation is + * PS3_LPM_SHADOW_REG_INIT. + */ + +struct ps3_lpm_shadow_regs { + u64 pm_control; + u64 pm_start_stop; + u64 pm_interval; + u64 group_control; + u64 debug_bus_control; +}; + +#define PS3_LPM_SHADOW_REG_INIT 0xFFFFFFFF00000000ULL + +/** + * struct ps3_lpm_priv - Private lpm device data. + * + * @open: An atomic variable indicating the lpm driver has been opened. + * @rights: The lpm rigths granted by the system policy module. A logical + * OR of enum ps3_lpm_rights. + * @node_id: The node id of a BE prosessor whose performance monitor this + * lpar has the right to use. + * @pu_id: The lv1 id of the logical PU. + * @lpm_id: The lv1 id of this lpm instance. + * @outlet_id: The outlet created by lv1 for this lpm instance. + * @tb_count: The number of bytes of data held in the lv1 trace buffer. + * @tb_cache: Kernel buffer to receive the data from the lv1 trace buffer. + * Must be 128 byte aligned. + * @tb_cache_size: Size of the kernel @tb_cache buffer. Must be 128 byte + * aligned. + * @tb_cache_internal: An unaligned buffer allocated by this driver to be + * used for the trace buffer cache when ps3_lpm_open() is called with a + * NULL tb_cache argument. Otherwise unused. + * @shadow: Processor register shadow of type struct ps3_lpm_shadow_regs. + * @sbd: The struct ps3_system_bus_device attached to this driver. + * + * The trace buffer is a buffer allocated and used internally to the lv1 + * hypervisor to collect trace data. The trace buffer cache is a guest + * buffer that accepts the trace data from the trace buffer. + */ + +struct ps3_lpm_priv { + atomic_t open; + u64 rights; + u64 node_id; + u64 pu_id; + u64 lpm_id; + u64 outlet_id; + u64 tb_count; + void *tb_cache; + u64 tb_cache_size; + void *tb_cache_internal; + struct ps3_lpm_shadow_regs shadow; + struct ps3_system_bus_device *sbd; +}; + +enum { + PS3_LPM_DEFAULT_TB_CACHE_SIZE = 0x4000, +}; + +/** + * lpm_priv - Static instance of the lpm data. + * + * Since the exported routines don't support the notion of a device + * instance we need to hold the instance in this static variable + * and then only allow at most one instance at a time to be created. + */ + +static struct ps3_lpm_priv *lpm_priv; + +static struct device *sbd_core(void) +{ + BUG_ON(!lpm_priv || !lpm_priv->sbd); + return &lpm_priv->sbd->core; +} + +/** + * use_start_stop_bookmark - Enable the PPU bookmark trace. + * + * And it enables PPU bookmark triggers ONLY if the other triggers are not set. + * The start/stop bookmarks are inserted at ps3_enable_pm() and ps3_disable_pm() + * to start/stop LPM. + * + * Used to get good quality of the performance counter. + */ + +enum {use_start_stop_bookmark = 1,}; + +void ps3_set_bookmark(u64 bookmark) +{ + /* + * As per the PPE book IV, to avoid bookmark loss there must + * not be a traced branch within 10 cycles of setting the + * SPRN_BKMK register. The actual text is unclear if 'within' + * includes cycles before the call. + */ + + asm volatile("or 29, 29, 29;"); /* db10cyc */ + mtspr(SPRN_BKMK, bookmark); + asm volatile("or 29, 29, 29;"); /* db10cyc */ +} +EXPORT_SYMBOL_GPL(ps3_set_bookmark); + +void ps3_set_pm_bookmark(u64 tag, u64 incident, u64 th_id) +{ + u64 bookmark; + + bookmark = (get_tb() & 0x00000000FFFFFFFFULL) | + PS3_PM_BOOKMARK_TAG_KERNEL; + bookmark = ((tag << 56) & PS3_PM_BOOKMARK_TAG_MASK_LO) | + (incident << 48) | (th_id << 32) | bookmark; + ps3_set_bookmark(bookmark); +} +EXPORT_SYMBOL_GPL(ps3_set_pm_bookmark); + +/** + * ps3_read_phys_ctr - Read physical counter registers. + * + * Each physical counter can act as one 32 bit counter or as two 16 bit + * counters. + */ + +u32 ps3_read_phys_ctr(u32 cpu, u32 phys_ctr) +{ + int result; + u64 counter0415; + u64 counter2637; + + if (phys_ctr >= NR_PHYS_CTRS) { + dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__, + __LINE__, phys_ctr); + return 0; + } + + result = lv1_set_lpm_counter(lpm_priv->lpm_id, 0, 0, 0, 0, &counter0415, + &counter2637); + if (result) { + dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter failed: " + "phys_ctr %u, %s\n", __func__, __LINE__, phys_ctr, + ps3_result(result)); + return 0; + } + + switch (phys_ctr) { + case 0: + return counter0415 >> 32; + case 1: + return counter0415 & PS3_PM_COUNTER_MASK_LO; + case 2: + return counter2637 >> 32; + case 3: + return counter2637 & PS3_PM_COUNTER_MASK_LO; + default: + BUG(); + } + return 0; +} +EXPORT_SYMBOL_GPL(ps3_read_phys_ctr); + +/** + * ps3_write_phys_ctr - Write physical counter registers. + * + * Each physical counter can act as one 32 bit counter or as two 16 bit + * counters. + */ + +void ps3_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val) +{ + u64 counter0415; + u64 counter0415_mask; + u64 counter2637; + u64 counter2637_mask; + int result; + + if (phys_ctr >= NR_PHYS_CTRS) { + dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__, + __LINE__, phys_ctr); + return; + } + + switch (phys_ctr) { + case 0: + counter0415 = (u64)val << 32; + counter0415_mask = PS3_PM_COUNTER_MASK_HI; + counter2637 = 0x0; + counter2637_mask = 0x0; + break; + case 1: + counter0415 = (u64)val; + counter0415_mask = PS3_PM_COUNTER_MASK_LO; + counter2637 = 0x0; + counter2637_mask = 0x0; + break; + case 2: + counter0415 = 0x0; + counter0415_mask = 0x0; + counter2637 = (u64)val << 32; + counter2637_mask = PS3_PM_COUNTER_MASK_HI; + break; + case 3: + counter0415 = 0x0; + counter0415_mask = 0x0; + counter2637 = (u64)val; + counter2637_mask = PS3_PM_COUNTER_MASK_LO; + break; + default: + BUG(); + } + + result = lv1_set_lpm_counter(lpm_priv->lpm_id, + counter0415, counter0415_mask, + counter2637, counter2637_mask, + &counter0415, &counter2637); + if (result) + dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter failed: " + "phys_ctr %u, val %u, %s\n", __func__, __LINE__, + phys_ctr, val, ps3_result(result)); +} +EXPORT_SYMBOL_GPL(ps3_write_phys_ctr); + +/** + * ps3_read_ctr - Read counter. + * + * Read 16 or 32 bits depending on the current size of the counter. + * Counters 4, 5, 6 & 7 are always 16 bit. + */ + +u32 ps3_read_ctr(u32 cpu, u32 ctr) +{ + u32 val; + u32 phys_ctr = ctr & (NR_PHYS_CTRS - 1); + + val = ps3_read_phys_ctr(cpu, phys_ctr); + + if (ps3_get_ctr_size(cpu, phys_ctr) == 16) + val = (ctr < NR_PHYS_CTRS) ? (val >> 16) : (val & 0xffff); + + return val; +} +EXPORT_SYMBOL_GPL(ps3_read_ctr); + +/** + * ps3_write_ctr - Write counter. + * + * Write 16 or 32 bits depending on the current size of the counter. + * Counters 4, 5, 6 & 7 are always 16 bit. + */ + +void ps3_write_ctr(u32 cpu, u32 ctr, u32 val) +{ + u32 phys_ctr; + u32 phys_val; + + phys_ctr = ctr & (NR_PHYS_CTRS - 1); + + if (ps3_get_ctr_size(cpu, phys_ctr) == 16) { + phys_val = ps3_read_phys_ctr(cpu, phys_ctr); + + if (ctr < NR_PHYS_CTRS) + val = (val << 16) | (phys_val & 0xffff); + else + val = (val & 0xffff) | (phys_val & 0xffff0000); + } + + ps3_write_phys_ctr(cpu, phys_ctr, val); +} +EXPORT_SYMBOL_GPL(ps3_write_ctr); + +/** + * ps3_read_pm07_control - Read counter control registers. + * + * Each logical counter has a corresponding control register. + */ + +u32 ps3_read_pm07_control(u32 cpu, u32 ctr) +{ + return 0; +} +EXPORT_SYMBOL_GPL(ps3_read_pm07_control); + +/** + * ps3_write_pm07_control - Write counter control registers. + * + * Each logical counter has a corresponding control register. + */ + +void ps3_write_pm07_control(u32 cpu, u32 ctr, u32 val) +{ + int result; + static const u64 mask = 0xFFFFFFFFFFFFFFFFULL; + u64 old_value; + + if (ctr >= NR_CTRS) { + dev_dbg(sbd_core(), "%s:%u: ctr too big: %u\n", __func__, + __LINE__, ctr); + return; + } + + result = lv1_set_lpm_counter_control(lpm_priv->lpm_id, ctr, val, mask, + &old_value); + if (result) + dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter_control " + "failed: ctr %u, %s\n", __func__, __LINE__, ctr, + ps3_result(result)); +} +EXPORT_SYMBOL_GPL(ps3_write_pm07_control); + +/** + * ps3_read_pm - Read Other LPM control registers. + */ + +u32 ps3_read_pm(u32 cpu, enum pm_reg_name reg) +{ + int result = 0; + u64 val = 0; + + switch (reg) { + case pm_control: + return lpm_priv->shadow.pm_control; + case trace_address: + return CBE_PM_TRACE_BUF_EMPTY; + case pm_start_stop: + return lpm_priv->shadow.pm_start_stop; + case pm_interval: + return lpm_priv->shadow.pm_interval; + case group_control: + return lpm_priv->shadow.group_control; + case debug_bus_control: + return lpm_priv->shadow.debug_bus_control; + case pm_status: + result = lv1_get_lpm_interrupt_status(lpm_priv->lpm_id, + &val); + if (result) { + val = 0; + dev_dbg(sbd_core(), "%s:%u: lv1 get_lpm_status failed: " + "reg %u, %s\n", __func__, __LINE__, reg, + ps3_result(result)); + } + return (u32)val; + case ext_tr_timer: + return 0; + default: + dev_dbg(sbd_core(), "%s:%u: unknown reg: %d\n", __func__, + __LINE__, reg); + BUG(); + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ps3_read_pm); + +/** + * ps3_write_pm - Write Other LPM control registers. + */ + +void ps3_write_pm(u32 cpu, enum pm_reg_name reg, u32 val) +{ + int result = 0; + u64 dummy; + + switch (reg) { + case group_control: + if (val != lpm_priv->shadow.group_control) + result = lv1_set_lpm_group_control(lpm_priv->lpm_id, + val, + PS3_WRITE_PM_MASK, + &dummy); + lpm_priv->shadow.group_control = val; + break; + case debug_bus_control: + if (val != lpm_priv->shadow.debug_bus_control) + result = lv1_set_lpm_debug_bus_control(lpm_priv->lpm_id, + val, + PS3_WRITE_PM_MASK, + &dummy); + lpm_priv->shadow.debug_bus_control = val; + break; + case pm_control: + if (use_start_stop_bookmark) + val |= (PS3_PM_CONTROL_PPU_TH0_BOOKMARK | + PS3_PM_CONTROL_PPU_TH1_BOOKMARK); + if (val != lpm_priv->shadow.pm_control) + result = lv1_set_lpm_general_control(lpm_priv->lpm_id, + val, + PS3_WRITE_PM_MASK, + 0, 0, &dummy, + &dummy); + lpm_priv->shadow.pm_control = val; + break; + case pm_interval: + if (val != lpm_priv->shadow.pm_interval) + result = lv1_set_lpm_interval(lpm_priv->lpm_id, val, + PS3_WRITE_PM_MASK, &dummy); + lpm_priv->shadow.pm_interval = val; + break; + case pm_start_stop: + if (val != lpm_priv->shadow.pm_start_stop) + result = lv1_set_lpm_trigger_control(lpm_priv->lpm_id, + val, + PS3_WRITE_PM_MASK, + &dummy); + lpm_priv->shadow.pm_start_stop = val; + break; + case trace_address: + case ext_tr_timer: + case pm_status: + break; + default: + dev_dbg(sbd_core(), "%s:%u: unknown reg: %d\n", __func__, + __LINE__, reg); + BUG(); + break; + } + + if (result) + dev_err(sbd_core(), "%s:%u: lv1 set_control failed: " + "reg %u, %s\n", __func__, __LINE__, reg, + ps3_result(result)); +} +EXPORT_SYMBOL_GPL(ps3_write_pm); + +/** + * ps3_get_ctr_size - Get the size of a physical counter. + * + * Returns either 16 or 32. + */ + +u32 ps3_get_ctr_size(u32 cpu, u32 phys_ctr) +{ + u32 pm_ctrl; + + if (phys_ctr >= NR_PHYS_CTRS) { + dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__, + __LINE__, phys_ctr); + return 0; + } + + pm_ctrl = ps3_read_pm(cpu, pm_control); + return (pm_ctrl & CBE_PM_16BIT_CTR(phys_ctr)) ? 16 : 32; +} +EXPORT_SYMBOL_GPL(ps3_get_ctr_size); + +/** + * ps3_set_ctr_size - Set the size of a physical counter to 16 or 32 bits. + */ + +void ps3_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size) +{ + u32 pm_ctrl; + + if (phys_ctr >= NR_PHYS_CTRS) { + dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__, + __LINE__, phys_ctr); + return; + } + + pm_ctrl = ps3_read_pm(cpu, pm_control); + + switch (ctr_size) { + case 16: + pm_ctrl |= CBE_PM_16BIT_CTR(phys_ctr); + ps3_write_pm(cpu, pm_control, pm_ctrl); + break; + + case 32: + pm_ctrl &= ~CBE_PM_16BIT_CTR(phys_ctr); + ps3_write_pm(cpu, pm_control, pm_ctrl); + break; + default: + BUG(); + } +} +EXPORT_SYMBOL_GPL(ps3_set_ctr_size); + +static u64 pm_translate_signal_group_number_on_island2(u64 subgroup) +{ + + if (subgroup == 2) + subgroup = 3; + + if (subgroup <= 6) + return PM_ISLAND2_BASE_SIGNAL_GROUP_NUMBER + subgroup; + else if (subgroup == 7) + return PM_ISLAND2_SIGNAL_GROUP_NUMBER1; + else + return PM_ISLAND2_SIGNAL_GROUP_NUMBER2; +} + +static u64 pm_translate_signal_group_number_on_island3(u64 subgroup) +{ + + switch (subgroup) { + case 2: + case 3: + case 4: + subgroup += 2; + break; + case 5: + subgroup = 8; + break; + default: + break; + } + return PM_ISLAND3_BASE_SIGNAL_GROUP_NUMBER + subgroup; +} + +static u64 pm_translate_signal_group_number_on_island4(u64 subgroup) +{ + return PM_ISLAND4_BASE_SIGNAL_GROUP_NUMBER + subgroup; +} + +static u64 pm_translate_signal_group_number_on_island5(u64 subgroup) +{ + + switch (subgroup) { + case 3: + subgroup = 4; + break; + case 4: + subgroup = 6; + break; + default: + break; + } + return PM_ISLAND5_BASE_SIGNAL_GROUP_NUMBER + subgroup; +} + +static u64 pm_translate_signal_group_number_on_island6(u64 subgroup, + u64 subsubgroup) +{ + switch (subgroup) { + case 3: + case 4: + case 5: + subgroup += 1; + break; + default: + break; + } + + switch (subsubgroup) { + case 4: + case 5: + case 6: + subsubgroup += 2; + break; + case 7: + case 8: + case 9: + case 10: + subsubgroup += 4; + break; + case 11: + case 12: + case 13: + subsubgroup += 5; + break; + default: + break; + } + + if (subgroup <= 5) + return (PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER + subgroup); + else + return (PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER + subgroup + + subsubgroup - 1); +} + +static u64 pm_translate_signal_group_number_on_island7(u64 subgroup) +{ + return PM_ISLAND7_BASE_SIGNAL_GROUP_NUMBER + subgroup; +} + +static u64 pm_translate_signal_group_number_on_island8(u64 subgroup) +{ + return PM_ISLAND8_BASE_SIGNAL_GROUP_NUMBER + subgroup; +} + +static u64 pm_signal_group_to_ps3_lv1_signal_group(u64 group) +{ + u64 island; + u64 subgroup; + u64 subsubgroup; + + subgroup = 0; + subsubgroup = 0; + island = 0; + if (group < 1000) { + if (group < 100) { + if (20 <= group && group < 30) { + island = 2; + subgroup = group - 20; + } else if (30 <= group && group < 40) { + island = 3; + subgroup = group - 30; + } else if (40 <= group && group < 50) { + island = 4; + subgroup = group - 40; + } else if (50 <= group && group < 60) { + island = 5; + subgroup = group - 50; + } else if (60 <= group && group < 70) { + island = 6; + subgroup = group - 60; + } else if (70 <= group && group < 80) { + island = 7; + subgroup = group - 70; + } else if (80 <= group && group < 90) { + island = 8; + subgroup = group - 80; + } + } else if (200 <= group && group < 300) { + island = 2; + subgroup = group - 200; + } else if (600 <= group && group < 700) { + island = 6; + subgroup = 5; + subsubgroup = group - 650; + } + } else if (6000 <= group && group < 7000) { + island = 6; + subgroup = 5; + subsubgroup = group - 6500; + } + + switch (island) { + case 2: + return pm_translate_signal_group_number_on_island2(subgroup); + case 3: + return pm_translate_signal_group_number_on_island3(subgroup); + case 4: + return pm_translate_signal_group_number_on_island4(subgroup); + case 5: + return pm_translate_signal_group_number_on_island5(subgroup); + case 6: + return pm_translate_signal_group_number_on_island6(subgroup, + subsubgroup); + case 7: + return pm_translate_signal_group_number_on_island7(subgroup); + case 8: + return pm_translate_signal_group_number_on_island8(subgroup); + default: + dev_dbg(sbd_core(), "%s:%u: island not found: %lu\n", __func__, + __LINE__, group); + BUG(); + break; + } + return 0; +} + +static u64 pm_bus_word_to_ps3_lv1_bus_word(u8 word) +{ + + switch (word) { + case 1: + return 0xF000; + case 2: + return 0x0F00; + case 4: + return 0x00F0; + case 8: + default: + return 0x000F; + } +} + +static int __ps3_set_signal(u64 lv1_signal_group, u64 bus_select, + u64 signal_select, u64 attr1, u64 attr2, u64 attr3) +{ + int ret; + + ret = lv1_set_lpm_signal(lpm_priv->lpm_id, lv1_signal_group, bus_select, + signal_select, attr1, attr2, attr3); + if (ret) + dev_err(sbd_core(), + "%s:%u: error:%d 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", + __func__, __LINE__, ret, lv1_signal_group, bus_select, + signal_select, attr1, attr2, attr3); + + return ret; +} + +int ps3_set_signal(u64 signal_group, u8 signal_bit, u16 sub_unit, + u8 bus_word) +{ + int ret; + u64 lv1_signal_group; + u64 bus_select; + u64 signal_select; + u64 attr1, attr2, attr3; + + if (signal_group == 0) + return __ps3_set_signal(0, 0, 0, 0, 0, 0); + + lv1_signal_group = + pm_signal_group_to_ps3_lv1_signal_group(signal_group); + bus_select = pm_bus_word_to_ps3_lv1_bus_word(bus_word); + + switch (signal_group) { + case PM_SIG_GROUP_SPU_TRIGGER: + signal_select = 1; + signal_select = signal_select << (63 - signal_bit); + break; + case PM_SIG_GROUP_SPU_EVENT: + signal_select = 1; + signal_select = (signal_select << (63 - signal_bit)) | 0x3; + break; + default: + signal_select = 0; + break; + } + + /* + * 0: physical object. + * 1: logical object. + * This parameter is only used for the PPE and SPE signals. + */ + attr1 = 1; + + /* + * This parameter is used to specify the target physical/logical + * PPE/SPE object. + */ + if (PM_SIG_GROUP_SPU <= signal_group && + signal_group < PM_SIG_GROUP_MFC_MAX) + attr2 = sub_unit; + else + attr2 = lpm_priv->pu_id; + + /* + * This parameter is only used for setting the SPE signal. + */ + attr3 = 0; + + ret = __ps3_set_signal(lv1_signal_group, bus_select, signal_select, + attr1, attr2, attr3); + if (ret) + dev_err(sbd_core(), "%s:%u: __ps3_set_signal failed: %d\n", + __func__, __LINE__, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(ps3_set_signal); + +u32 ps3_get_hw_thread_id(int cpu) +{ + return get_hard_smp_processor_id(cpu); +} +EXPORT_SYMBOL_GPL(ps3_get_hw_thread_id); + +/** + * ps3_enable_pm - Enable the entire performance monitoring unit. + * + * When we enable the LPM, all pending writes to counters get committed. + */ + +void ps3_enable_pm(u32 cpu) +{ + int result; + u64 tmp; + int insert_bookmark = 0; + + lpm_priv->tb_count = 0; + + if (use_start_stop_bookmark) { + if (!(lpm_priv->shadow.pm_start_stop & + (PS3_PM_START_STOP_START_MASK + | PS3_PM_START_STOP_STOP_MASK))) { + result = lv1_set_lpm_trigger_control(lpm_priv->lpm_id, + (PS3_PM_START_STOP_PPU_TH0_BOOKMARK_START | + PS3_PM_START_STOP_PPU_TH1_BOOKMARK_START | + PS3_PM_START_STOP_PPU_TH0_BOOKMARK_STOP | + PS3_PM_START_STOP_PPU_TH1_BOOKMARK_STOP), + 0xFFFFFFFFFFFFFFFFULL, &tmp); + + if (result) + dev_err(sbd_core(), "%s:%u: " + "lv1_set_lpm_trigger_control failed: " + "%s\n", __func__, __LINE__, + ps3_result(result)); + + insert_bookmark = !result; + } + } + + result = lv1_start_lpm(lpm_priv->lpm_id); + + if (result) + dev_err(sbd_core(), "%s:%u: lv1_start_lpm failed: %s\n", + __func__, __LINE__, ps3_result(result)); + + if (use_start_stop_bookmark && !result && insert_bookmark) + ps3_set_bookmark(get_tb() | PS3_PM_BOOKMARK_START); +} +EXPORT_SYMBOL_GPL(ps3_enable_pm); + +/** + * ps3_disable_pm - Disable the entire performance monitoring unit. + */ + +void ps3_disable_pm(u32 cpu) +{ + int result; + u64 tmp; + + ps3_set_bookmark(get_tb() | PS3_PM_BOOKMARK_STOP); + + result = lv1_stop_lpm(lpm_priv->lpm_id, &tmp); + + if (result) { + if(result != LV1_WRONG_STATE) + dev_err(sbd_core(), "%s:%u: lv1_stop_lpm failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return; + } + + lpm_priv->tb_count = tmp; + + dev_dbg(sbd_core(), "%s:%u: tb_count %lu (%lxh)\n", __func__, __LINE__, + lpm_priv->tb_count, lpm_priv->tb_count); +} +EXPORT_SYMBOL_GPL(ps3_disable_pm); + +/** + * ps3_lpm_copy_tb - Copy data from the trace buffer to a kernel buffer. + * @offset: Offset in bytes from the start of the trace buffer. + * @buf: Copy destination. + * @count: Maximum count of bytes to copy. + * @bytes_copied: Pointer to a variable that will recieve the number of + * bytes copied to @buf. + * + * On error @buf will contain any successfully copied trace buffer data + * and bytes_copied will be set to the number of bytes successfully copied. + */ + +int ps3_lpm_copy_tb(unsigned long offset, void *buf, unsigned long count, + unsigned long *bytes_copied) +{ + int result; + + *bytes_copied = 0; + + if (!lpm_priv->tb_cache) + return -EPERM; + + if (offset >= lpm_priv->tb_count) + return 0; + + count = min(count, lpm_priv->tb_count - offset); + + while (*bytes_copied < count) { + const unsigned long request = count - *bytes_copied; + u64 tmp; + + result = lv1_copy_lpm_trace_buffer(lpm_priv->lpm_id, offset, + request, &tmp); + if (result) { + dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%lx\n", + __func__, __LINE__, request, offset); + + dev_err(sbd_core(), "%s:%u: lv1_copy_lpm_trace_buffer " + "failed: %s\n", __func__, __LINE__, + ps3_result(result)); + return result == LV1_WRONG_STATE ? -EBUSY : -EINVAL; + } + + memcpy(buf, lpm_priv->tb_cache, tmp); + buf += tmp; + *bytes_copied += tmp; + offset += tmp; + } + dev_dbg(sbd_core(), "%s:%u: copied %lxh bytes\n", __func__, __LINE__, + *bytes_copied); + + return 0; +} +EXPORT_SYMBOL_GPL(ps3_lpm_copy_tb); + +/** + * ps3_lpm_copy_tb_to_user - Copy data from the trace buffer to a user buffer. + * @offset: Offset in bytes from the start of the trace buffer. + * @buf: A __user copy destination. + * @count: Maximum count of bytes to copy. + * @bytes_copied: Pointer to a variable that will recieve the number of + * bytes copied to @buf. + * + * On error @buf will contain any successfully copied trace buffer data + * and bytes_copied will be set to the number of bytes successfully copied. + */ + +int ps3_lpm_copy_tb_to_user(unsigned long offset, void __user *buf, + unsigned long count, unsigned long *bytes_copied) +{ + int result; + + *bytes_copied = 0; + + if (!lpm_priv->tb_cache) + return -EPERM; + + if (offset >= lpm_priv->tb_count) + return 0; + + count = min(count, lpm_priv->tb_count - offset); + + while (*bytes_copied < count) { + const unsigned long request = count - *bytes_copied; + u64 tmp; + + result = lv1_copy_lpm_trace_buffer(lpm_priv->lpm_id, offset, + request, &tmp); + if (result) { + dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%lx\n", + __func__, __LINE__, request, offset); + dev_err(sbd_core(), "%s:%u: lv1_copy_lpm_trace_buffer " + "failed: %s\n", __func__, __LINE__, + ps3_result(result)); + return result == LV1_WRONG_STATE ? -EBUSY : -EINVAL; + } + + result = copy_to_user(buf, lpm_priv->tb_cache, tmp); + + if (result) { + dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%p\n", + __func__, __LINE__, tmp, buf); + dev_err(sbd_core(), "%s:%u: copy_to_user failed: %d\n", + __func__, __LINE__, result); + return -EFAULT; + } + + buf += tmp; + *bytes_copied += tmp; + offset += tmp; + } + dev_dbg(sbd_core(), "%s:%u: copied %lxh bytes\n", __func__, __LINE__, + *bytes_copied); + + return 0; +} +EXPORT_SYMBOL_GPL(ps3_lpm_copy_tb_to_user); + +/** + * ps3_get_and_clear_pm_interrupts - + * + * Clearing interrupts for the entire performance monitoring unit. + * Reading pm_status clears the interrupt bits. + */ + +u32 ps3_get_and_clear_pm_interrupts(u32 cpu) +{ + return ps3_read_pm(cpu, pm_status); +} +EXPORT_SYMBOL_GPL(ps3_get_and_clear_pm_interrupts); + +/** + * ps3_enable_pm_interrupts - + * + * Enabling interrupts for the entire performance monitoring unit. + * Enables the interrupt bits in the pm_status register. + */ + +void ps3_enable_pm_interrupts(u32 cpu, u32 thread, u32 mask) +{ + if (mask) + ps3_write_pm(cpu, pm_status, mask); +} +EXPORT_SYMBOL_GPL(ps3_enable_pm_interrupts); + +/** + * ps3_enable_pm_interrupts - + * + * Disabling interrupts for the entire performance monitoring unit. + */ + +void ps3_disable_pm_interrupts(u32 cpu) +{ + ps3_get_and_clear_pm_interrupts(cpu); + ps3_write_pm(cpu, pm_status, 0); +} +EXPORT_SYMBOL_GPL(ps3_disable_pm_interrupts); + +/** + * ps3_lpm_open - Open the logical performance monitor device. + * @tb_type: Specifies the type of trace buffer lv1 sould use for this lpm + * instance, specified by one of enum ps3_lpm_tb_type. + * @tb_cache: Optional user supplied buffer to use as the trace buffer cache. + * If NULL, the driver will allocate and manage an internal buffer. + * Unused when when @tb_type is PS3_LPM_TB_TYPE_NONE. + * @tb_cache_size: The size in bytes of the user supplied @tb_cache buffer. + * Unused when @tb_cache is NULL or @tb_type is PS3_LPM_TB_TYPE_NONE. + */ + +int ps3_lpm_open(enum ps3_lpm_tb_type tb_type, void *tb_cache, + u64 tb_cache_size) +{ + int result; + u64 tb_size; + + BUG_ON(!lpm_priv); + BUG_ON(tb_type != PS3_LPM_TB_TYPE_NONE + && tb_type != PS3_LPM_TB_TYPE_INTERNAL); + + if (tb_type == PS3_LPM_TB_TYPE_NONE && tb_cache) + dev_dbg(sbd_core(), "%s:%u: bad in vals\n", __func__, __LINE__); + + if (!atomic_add_unless(&lpm_priv->open, 1, 1)) { + dev_dbg(sbd_core(), "%s:%u: busy\n", __func__, __LINE__); + return -EBUSY; + } + + /* Note tb_cache needs 128 byte alignment. */ + + if (tb_type == PS3_LPM_TB_TYPE_NONE) { + lpm_priv->tb_cache_size = 0; + lpm_priv->tb_cache_internal = NULL; + lpm_priv->tb_cache = NULL; + } else if (tb_cache) { + if (tb_cache != (void *)_ALIGN_UP((unsigned long)tb_cache, 128) + || tb_cache_size != _ALIGN_UP(tb_cache_size, 128)) { + dev_err(sbd_core(), "%s:%u: unaligned tb_cache\n", + __func__, __LINE__); + result = -EINVAL; + goto fail_align; + } + lpm_priv->tb_cache_size = tb_cache_size; + lpm_priv->tb_cache_internal = NULL; + lpm_priv->tb_cache = tb_cache; + } else { + lpm_priv->tb_cache_size = PS3_LPM_DEFAULT_TB_CACHE_SIZE; + lpm_priv->tb_cache_internal = kzalloc( + lpm_priv->tb_cache_size + 127, GFP_KERNEL); + if (!lpm_priv->tb_cache_internal) { + dev_err(sbd_core(), "%s:%u: alloc internal tb_cache " + "failed\n", __func__, __LINE__); + result = -ENOMEM; + goto fail_malloc; + } + lpm_priv->tb_cache = (void *)_ALIGN_UP( + (unsigned long)lpm_priv->tb_cache_internal, 128); + } + + result = lv1_construct_lpm(lpm_priv->node_id, tb_type, 0, 0, + ps3_mm_phys_to_lpar(__pa(lpm_priv->tb_cache)), + lpm_priv->tb_cache_size, &lpm_priv->lpm_id, + &lpm_priv->outlet_id, &tb_size); + + if (result) { + dev_err(sbd_core(), "%s:%u: lv1_construct_lpm failed: %s\n", + __func__, __LINE__, ps3_result(result)); + result = -EINVAL; + goto fail_construct; + } + + lpm_priv->shadow.pm_control = PS3_LPM_SHADOW_REG_INIT; + lpm_priv->shadow.pm_start_stop = PS3_LPM_SHADOW_REG_INIT; + lpm_priv->shadow.pm_interval = PS3_LPM_SHADOW_REG_INIT; + lpm_priv->shadow.group_control = PS3_LPM_SHADOW_REG_INIT; + lpm_priv->shadow.debug_bus_control = PS3_LPM_SHADOW_REG_INIT; + + dev_dbg(sbd_core(), "%s:%u: lpm_id 0x%lx, outlet_id 0x%lx, " + "tb_size 0x%lx\n", __func__, __LINE__, lpm_priv->lpm_id, + lpm_priv->outlet_id, tb_size); + + return 0; + +fail_construct: + kfree(lpm_priv->tb_cache_internal); + lpm_priv->tb_cache_internal = NULL; +fail_malloc: +fail_align: + atomic_dec(&lpm_priv->open); + return result; +} +EXPORT_SYMBOL_GPL(ps3_lpm_open); + +/** + * ps3_lpm_close - Close the lpm device. + * + */ + +int ps3_lpm_close(void) +{ + dev_dbg(sbd_core(), "%s:%u\n", __func__, __LINE__); + + lv1_destruct_lpm(lpm_priv->lpm_id); + lpm_priv->lpm_id = 0; + + kfree(lpm_priv->tb_cache_internal); + lpm_priv->tb_cache_internal = NULL; + + atomic_dec(&lpm_priv->open); + return 0; +} +EXPORT_SYMBOL_GPL(ps3_lpm_close); + +static int __devinit ps3_lpm_probe(struct ps3_system_bus_device *dev) +{ + dev_dbg(&dev->core, " -> %s:%u\n", __func__, __LINE__); + + if (lpm_priv) { + dev_info(&dev->core, "%s:%u: called twice\n", + __func__, __LINE__); + return -EBUSY; + } + + lpm_priv = kzalloc(sizeof(*lpm_priv), GFP_KERNEL); + + if (!lpm_priv) + return -ENOMEM; + + lpm_priv->sbd = dev; + lpm_priv->node_id = dev->lpm.node_id; + lpm_priv->pu_id = dev->lpm.pu_id; + lpm_priv->rights = dev->lpm.rights; + + dev_info(&dev->core, " <- %s:%u:\n", __func__, __LINE__); + + return 0; +} + +static int ps3_lpm_remove(struct ps3_system_bus_device *dev) +{ + dev_dbg(&dev->core, " -> %s:%u:\n", __func__, __LINE__); + + ps3_lpm_close(); + + kfree(lpm_priv); + lpm_priv = NULL; + + dev_info(&dev->core, " <- %s:%u:\n", __func__, __LINE__); + return 0; +} + +static struct ps3_system_bus_driver ps3_lpm_driver = { + .match_id = PS3_MATCH_ID_LPM, + .core.name = "ps3-lpm", + .core.owner = THIS_MODULE, + .probe = ps3_lpm_probe, + .remove = ps3_lpm_remove, + .shutdown = ps3_lpm_remove, +}; + +static int __init ps3_lpm_init(void) +{ + pr_debug("%s:%d:\n", __func__, __LINE__); + return ps3_system_bus_driver_register(&ps3_lpm_driver); +} + +static void __exit ps3_lpm_exit(void) +{ + pr_debug("%s:%d:\n", __func__, __LINE__); + ps3_system_bus_driver_unregister(&ps3_lpm_driver); +} + +module_init(ps3_lpm_init); +module_exit(ps3_lpm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PS3 Logical Performance Monitor Driver"); +MODULE_AUTHOR("Sony Corporation"); +MODULE_ALIAS(PS3_MODULE_ALIAS_LPM); diff --git a/drivers/ps3/ps3-sys-manager.c b/drivers/ps3/ps3-sys-manager.c index 8461b08..c3c3aba 100644 --- a/drivers/ps3/ps3-sys-manager.c +++ b/drivers/ps3/ps3-sys-manager.c @@ -452,7 +452,7 @@ static int ps3_sys_manager_handle_event(struct ps3_system_bus_device *dev) case PS3_SM_EVENT_THERMAL_ALERT: dev_dbg(&dev->core, "%s:%d: THERMAL_ALERT (zone %u)\n", __func__, __LINE__, event.value); - printk(KERN_INFO "PS3 Thermal Alert Zone %u\n", event.value); + pr_info("PS3 Thermal Alert Zone %u\n", event.value); break; case PS3_SM_EVENT_THERMAL_CLEARED: dev_dbg(&dev->core, "%s:%d: THERMAL_CLEARED (zone %u)\n", @@ -488,7 +488,7 @@ static int ps3_sys_manager_handle_cmd(struct ps3_system_bus_device *dev) result = ps3_vuart_read(dev, &cmd, sizeof(cmd)); BUG_ON(result && "need to retry here"); - if(result) + if (result) return result; if (cmd.version != 1) { @@ -521,7 +521,7 @@ static int ps3_sys_manager_handle_msg(struct ps3_system_bus_device *dev) result = ps3_vuart_read(dev, &header, sizeof(struct ps3_sys_manager_header)); - if(result) + if (result) return result; if (header.version != 1) { @@ -589,9 +589,9 @@ static void ps3_sys_manager_final_power_off(struct ps3_system_bus_device *dev) PS3_SM_WAKE_DEFAULT); ps3_sys_manager_send_request_shutdown(dev); - printk(KERN_EMERG "System Halted, OK to turn off power\n"); + pr_emerg("System Halted, OK to turn off power\n"); - while(1) + while (1) ps3_sys_manager_handle_msg(dev); } @@ -626,9 +626,9 @@ static void ps3_sys_manager_final_restart(struct ps3_system_bus_device *dev) PS3_SM_WAKE_DEFAULT); ps3_sys_manager_send_request_shutdown(dev); - printk(KERN_EMERG "System Halted, OK to turn off power\n"); + pr_emerg("System Halted, OK to turn off power\n"); - while(1) + while (1) ps3_sys_manager_handle_msg(dev); } diff --git a/drivers/ps3/ps3-vuart.c b/drivers/ps3/ps3-vuart.c index bb8d5b1..90c097a 100644 --- a/drivers/ps3/ps3-vuart.c +++ b/drivers/ps3/ps3-vuart.c @@ -108,18 +108,18 @@ static struct ps3_vuart_port_priv *to_port_priv( struct ports_bmp { u64 status; u64 unused[3]; -} __attribute__ ((aligned (32))); +} __attribute__((aligned(32))); #define dump_ports_bmp(_b) _dump_ports_bmp(_b, __func__, __LINE__) static void __maybe_unused _dump_ports_bmp( - const struct ports_bmp* bmp, const char* func, int line) + const struct ports_bmp *bmp, const char *func, int line) { pr_debug("%s:%d: ports_bmp: %016lxh\n", func, line, bmp->status); } #define dump_port_params(_b) _dump_port_params(_b, __func__, __LINE__) static void __maybe_unused _dump_port_params(unsigned int port_number, - const char* func, int line) + const char *func, int line) { #if defined(DEBUG) static const char *strings[] = { @@ -363,7 +363,7 @@ int ps3_vuart_disable_interrupt_disconnect(struct ps3_system_bus_device *dev) */ static int ps3_vuart_raw_write(struct ps3_system_bus_device *dev, - const void* buf, unsigned int bytes, unsigned long *bytes_written) + const void *buf, unsigned int bytes, unsigned long *bytes_written) { int result; struct ps3_vuart_port_priv *priv = to_port_priv(dev); @@ -431,7 +431,7 @@ void ps3_vuart_clear_rx_bytes(struct ps3_system_bus_device *dev, int result; struct ps3_vuart_port_priv *priv = to_port_priv(dev); u64 bytes_waiting; - void* tmp; + void *tmp; result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes_waiting); @@ -526,9 +526,8 @@ int ps3_vuart_write(struct ps3_system_bus_device *dev, const void *buf, lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_KERNEL); - if (!lb) { + if (!lb) return -ENOMEM; - } memcpy(lb->data, buf, bytes); lb->head = lb->data; @@ -878,7 +877,7 @@ static int ps3_vuart_handle_port_interrupt(struct ps3_system_bus_device *dev) struct vuart_bus_priv { struct ports_bmp *bmp; unsigned int virq; - struct semaphore probe_mutex; + struct mutex probe_mutex; int use_count; struct ps3_system_bus_device *devices[PORT_COUNT]; } static vuart_bus_priv; @@ -926,9 +925,8 @@ static int ps3_vuart_bus_interrupt_get(void) BUG_ON(vuart_bus_priv.use_count > 2); - if (vuart_bus_priv.use_count != 1) { + if (vuart_bus_priv.use_count != 1) return 0; - } BUG_ON(vuart_bus_priv.bmp); @@ -1017,7 +1015,7 @@ static int ps3_vuart_probe(struct ps3_system_bus_device *dev) return -EINVAL; } - down(&vuart_bus_priv.probe_mutex); + mutex_lock(&vuart_bus_priv.probe_mutex); result = ps3_vuart_bus_interrupt_get(); @@ -1077,7 +1075,7 @@ static int ps3_vuart_probe(struct ps3_system_bus_device *dev) goto fail_probe; } - up(&vuart_bus_priv.probe_mutex); + mutex_unlock(&vuart_bus_priv.probe_mutex); return result; @@ -1090,7 +1088,7 @@ fail_dev_malloc: fail_busy: ps3_vuart_bus_interrupt_put(); fail_setup_interrupt: - up(&vuart_bus_priv.probe_mutex); + mutex_unlock(&vuart_bus_priv.probe_mutex); dev_dbg(&dev->core, "%s:%d: failed\n", __func__, __LINE__); return result; } @@ -1129,7 +1127,7 @@ static int ps3_vuart_remove(struct ps3_system_bus_device *dev) BUG_ON(!dev); - down(&vuart_bus_priv.probe_mutex); + mutex_lock(&vuart_bus_priv.probe_mutex); dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__, dev->match_id); @@ -1137,7 +1135,7 @@ static int ps3_vuart_remove(struct ps3_system_bus_device *dev) if (!dev->core.driver) { dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__, __LINE__); - up(&vuart_bus_priv.probe_mutex); + mutex_unlock(&vuart_bus_priv.probe_mutex); return 0; } @@ -1160,7 +1158,7 @@ static int ps3_vuart_remove(struct ps3_system_bus_device *dev) priv = NULL; dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); - up(&vuart_bus_priv.probe_mutex); + mutex_unlock(&vuart_bus_priv.probe_mutex); return 0; } @@ -1180,7 +1178,7 @@ static int ps3_vuart_shutdown(struct ps3_system_bus_device *dev) BUG_ON(!dev); - down(&vuart_bus_priv.probe_mutex); + mutex_lock(&vuart_bus_priv.probe_mutex); dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__, dev->match_id); @@ -1188,7 +1186,7 @@ static int ps3_vuart_shutdown(struct ps3_system_bus_device *dev) if (!dev->core.driver) { dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__, __LINE__); - up(&vuart_bus_priv.probe_mutex); + mutex_unlock(&vuart_bus_priv.probe_mutex); return 0; } @@ -1212,7 +1210,7 @@ static int ps3_vuart_shutdown(struct ps3_system_bus_device *dev) dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); - up(&vuart_bus_priv.probe_mutex); + mutex_unlock(&vuart_bus_priv.probe_mutex); return 0; } @@ -1223,7 +1221,7 @@ static int __init ps3_vuart_bus_init(void) if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) return -ENODEV; - init_MUTEX(&vuart_bus_priv.probe_mutex); + mutex_init(&vuart_bus_priv.probe_mutex); return 0; } diff --git a/include/asm-powerpc/dma-mapping.h b/include/asm-powerpc/dma-mapping.h index e974876..5eea6db 100644 --- a/include/asm-powerpc/dma-mapping.h +++ b/include/asm-powerpc/dma-mapping.h @@ -189,8 +189,6 @@ static inline void dma_unmap_sg(struct device *dev, struct scatterlist *sg, extern struct dma_mapping_ops dma_iommu_ops; extern struct dma_mapping_ops dma_direct_ops; -extern unsigned long dma_direct_offset; - #else /* CONFIG_PPC64 */ #define dma_supported(dev, mask) (1) diff --git a/include/asm-powerpc/kexec.h b/include/asm-powerpc/kexec.h index b6f817b..701857b 100644 --- a/include/asm-powerpc/kexec.h +++ b/include/asm-powerpc/kexec.h @@ -123,6 +123,9 @@ struct pt_regs; extern void default_machine_kexec(struct kimage *image); extern int default_machine_kexec_prepare(struct kimage *image); extern void default_machine_crash_shutdown(struct pt_regs *regs); +typedef void (*crash_shutdown_t)(void); +extern int crash_shutdown_register(crash_shutdown_t handler); +extern int crash_shutdown_unregister(crash_shutdown_t handler); extern void machine_kexec_simple(struct kimage *image); extern void crash_kexec_secondary(struct pt_regs *regs); diff --git a/include/asm-powerpc/ps3.h b/include/asm-powerpc/ps3.h index f577a16..2b69367 100644 --- a/include/asm-powerpc/ps3.h +++ b/include/asm-powerpc/ps3.h @@ -24,6 +24,7 @@ #include <linux/init.h> #include <linux/types.h> #include <linux/device.h> +#include "cell-pmu.h" union ps3_firmware_version { u64 raw; @@ -317,6 +318,7 @@ enum ps3_match_id { PS3_MATCH_ID_STOR_FLASH = 8, PS3_MATCH_ID_SOUND = 9, PS3_MATCH_ID_GRAPHICS = 10, + PS3_MATCH_ID_LPM = 11, }; #define PS3_MODULE_ALIAS_EHCI "ps3:1" @@ -329,11 +331,13 @@ enum ps3_match_id { #define PS3_MODULE_ALIAS_STOR_FLASH "ps3:8" #define PS3_MODULE_ALIAS_SOUND "ps3:9" #define PS3_MODULE_ALIAS_GRAPHICS "ps3:10" +#define PS3_MODULE_ALIAS_LPM "ps3:11" enum ps3_system_bus_device_type { PS3_DEVICE_TYPE_IOC0 = 1, PS3_DEVICE_TYPE_SB, PS3_DEVICE_TYPE_VUART, + PS3_DEVICE_TYPE_LPM, }; /** @@ -344,12 +348,17 @@ struct ps3_system_bus_device { enum ps3_match_id match_id; enum ps3_system_bus_device_type dev_type; - unsigned int bus_id; /* SB */ - unsigned int dev_id; /* SB */ + u64 bus_id; /* SB */ + u64 dev_id; /* SB */ unsigned int interrupt_id; /* SB */ struct ps3_dma_region *d_region; /* SB, IOC0 */ struct ps3_mmio_region *m_region; /* SB, IOC0*/ unsigned int port_number; /* VUART */ + struct { /* LPM */ + u64 node_id; + u64 pu_id; + u64 rights; + } lpm; /* struct iommu_table *iommu_table; -- waiting for BenH's cleanups */ struct device core; @@ -438,5 +447,66 @@ struct ps3_prealloc { extern struct ps3_prealloc ps3fb_videomemory; extern struct ps3_prealloc ps3flash_bounce_buffer; +/* logical performance monitor */ + +/** + * enum ps3_lpm_rights - Rigths granted by the system policy module. + * + * @PS3_LPM_RIGHTS_USE_LPM: The right to use the lpm. + * @PS3_LPM_RIGHTS_USE_TB: The right to use the internal trace buffer. + */ + +enum ps3_lpm_rights { + PS3_LPM_RIGHTS_USE_LPM = 0x001, + PS3_LPM_RIGHTS_USE_TB = 0x100, +}; + +/** + * enum ps3_lpm_tb_type - Type of trace buffer lv1 should use. + * + * @PS3_LPM_TB_TYPE_NONE: Do not use a trace buffer. + * @PS3_LPM_RIGHTS_USE_TB: Use the lv1 internal trace buffer. Must have + * rights @PS3_LPM_RIGHTS_USE_TB. + */ + +enum ps3_lpm_tb_type { + PS3_LPM_TB_TYPE_NONE = 0, + PS3_LPM_TB_TYPE_INTERNAL = 1, +}; + +int ps3_lpm_open(enum ps3_lpm_tb_type tb_type, void *tb_cache, + u64 tb_cache_size); +int ps3_lpm_close(void); +int ps3_lpm_copy_tb(unsigned long offset, void *buf, unsigned long count, + unsigned long *bytes_copied); +int ps3_lpm_copy_tb_to_user(unsigned long offset, void __user *buf, + unsigned long count, unsigned long *bytes_copied); +void ps3_set_bookmark(u64 bookmark); +void ps3_set_pm_bookmark(u64 tag, u64 incident, u64 th_id); +int ps3_set_signal(u64 rtas_signal_group, u8 signal_bit, u16 sub_unit, + u8 bus_word); + +u32 ps3_read_phys_ctr(u32 cpu, u32 phys_ctr); +void ps3_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val); +u32 ps3_read_ctr(u32 cpu, u32 ctr); +void ps3_write_ctr(u32 cpu, u32 ctr, u32 val); + +u32 ps3_read_pm07_control(u32 cpu, u32 ctr); +void ps3_write_pm07_control(u32 cpu, u32 ctr, u32 val); +u32 ps3_read_pm(u32 cpu, enum pm_reg_name reg); +void ps3_write_pm(u32 cpu, enum pm_reg_name reg, u32 val); + +u32 ps3_get_ctr_size(u32 cpu, u32 phys_ctr); +void ps3_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size); + +void ps3_enable_pm(u32 cpu); +void ps3_disable_pm(u32 cpu); +void ps3_enable_pm_interrupts(u32 cpu, u32 thread, u32 mask); +void ps3_disable_pm_interrupts(u32 cpu); + +u32 ps3_get_and_clear_pm_interrupts(u32 cpu); +void ps3_sync_irq(int node); +u32 ps3_get_hw_thread_id(int cpu); +u64 ps3_get_spe_id(void *arg); #endif diff --git a/include/asm-powerpc/reg.h b/include/asm-powerpc/reg.h index 1f68504..2408a29 100644 --- a/include/asm-powerpc/reg.h +++ b/include/asm-powerpc/reg.h @@ -553,6 +553,7 @@ #define SPRN_PA6T_BTCR 978 /* Breakpoint and Tagging Control Register */ #define SPRN_PA6T_IMAAT 979 /* Instruction Match Array Action Table */ #define SPRN_PA6T_PCCR 1019 /* Power Counter Control Register */ +#define SPRN_BKMK 1020 /* Cell Bookmark Register */ #define SPRN_PA6T_RPCCR 1021 /* Retire PC Trace Control Register */ diff --git a/include/asm-powerpc/setjmp.h b/include/asm-powerpc/setjmp.h new file mode 100644 index 0000000..279d03a --- /dev/null +++ b/include/asm-powerpc/setjmp.h @@ -0,0 +1,18 @@ +/* + * Copyright © 2008 Michael Neuling IBM 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 the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ +#ifndef _ASM_POWERPC_SETJMP_H +#define _ASM_POWERPC_SETJMP_H + +#define JMP_BUF_LEN 23 + +extern long setjmp(long *); +extern void longjmp(long *, long); + +#endif /* _ASM_POWERPC_SETJMP_H */ diff --git a/include/asm-powerpc/sparsemem.h b/include/asm-powerpc/sparsemem.h index 48ad807..e8b493d 100644 --- a/include/asm-powerpc/sparsemem.h +++ b/include/asm-powerpc/sparsemem.h @@ -10,13 +10,8 @@ */ #define SECTION_SIZE_BITS 24 -#if defined(CONFIG_PS3_USE_LPAR_ADDR) -#define MAX_PHYSADDR_BITS 47 -#define MAX_PHYSMEM_BITS 47 -#else #define MAX_PHYSADDR_BITS 44 #define MAX_PHYSMEM_BITS 44 -#endif #ifdef CONFIG_MEMORY_HOTPLUG extern void create_section_mapping(unsigned long start, unsigned long end); |