summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/configs/ps3_defconfig49
-rw-r--r--arch/powerpc/kernel/crash.c101
-rw-r--r--arch/powerpc/kernel/dma_64.c19
-rw-r--r--arch/powerpc/kernel/ibmebus.c7
-rw-r--r--arch/powerpc/kernel/misc.S65
-rw-r--r--arch/powerpc/kernel/pci_64.c11
-rw-r--r--arch/powerpc/kernel/ppc_ksyms.c1
-rw-r--r--arch/powerpc/kernel/smp.c49
-rw-r--r--arch/powerpc/kernel/systbl_chk.c2
-rw-r--r--arch/powerpc/kernel/systbl_chk.sh2
-rw-r--r--arch/powerpc/mm/fault.c6
-rw-r--r--arch/powerpc/platforms/cell/iommu.c23
-rw-r--r--arch/powerpc/platforms/celleb/iommu.c18
-rw-r--r--arch/powerpc/platforms/chrp/pci.c4
-rw-r--r--arch/powerpc/platforms/chrp/setup.c52
-rw-r--r--arch/powerpc/platforms/ps3/Kconfig24
-rw-r--r--arch/powerpc/platforms/ps3/device-init.c531
-rw-r--r--arch/powerpc/platforms/ps3/mm.c24
-rw-r--r--arch/powerpc/platforms/ps3/platform.h34
-rw-r--r--arch/powerpc/platforms/ps3/repository.c320
-rw-r--r--arch/powerpc/platforms/ps3/system-bus.c19
-rw-r--r--arch/powerpc/xmon/setjmp.S61
-rw-r--r--arch/powerpc/xmon/xmon.c6
23 files changed, 929 insertions, 499 deletions
diff --git a/arch/powerpc/configs/ps3_defconfig b/arch/powerpc/configs/ps3_defconfig
index 0b5469fb6e0..7994955c29d 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 77c749a1337..571132ed12c 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 14206e3f081..84239076a5b 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 fd5ae8d17c9..2f50bb5d00f 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 74ce0c7a7b1..7b916022069 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 1930f393d01..52750745edf 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 13ebeb2d71e..aa9ff35b0e6 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 338950aeb6f..be35ffae10f 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 77824d1cb15..238aa63ced8 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 367d208301d..19415e7674a 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 10dda224a36..7b251079926 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 bceb5e13da5..eb2a94b0dc4 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 41e1e6f8e05..93b0efddd65 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 d51f83aeef7..609c46db4a1 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 42a21bab76c..116babbaaf8 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 298f1c9679f..a5f4e95dfc3 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 fd063fe0c9b..9d251d0ca8c 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 7bb3e162097..68900476c84 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 01f0c9506e1..235c13ebacd 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 1c94824f7b6..22063adeb38 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 6405f4a3676..43c493fca2d 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 96a91f10e2e..04c0b305ad4 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 865e36751f2..a34172ddc46 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