summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/include/asm/dbell.h8
-rw-r--r--arch/powerpc/include/asm/machdep.h1
-rw-r--r--arch/powerpc/include/asm/mmu-book3e.h4
-rw-r--r--arch/powerpc/include/asm/reg.h2
-rw-r--r--arch/powerpc/include/asm/reg_booke.h5
-rw-r--r--arch/powerpc/kernel/Makefile3
-rw-r--r--arch/powerpc/kernel/dbell.c78
-rw-r--r--arch/powerpc/kernel/exceptions-64e.S50
-rw-r--r--arch/powerpc/kernel/idle_book3e.S86
-rw-r--r--arch/powerpc/kernel/irq.c16
-rw-r--r--arch/powerpc/kernel/process.c22
-rw-r--r--arch/powerpc/kernel/traps.c21
-rw-r--r--arch/powerpc/mm/tlb_nohash.c129
-rw-r--r--arch/powerpc/oprofile/Makefile2
-rw-r--r--arch/powerpc/oprofile/common.c2
-rw-r--r--arch/powerpc/platforms/85xx/smp.c4
-rw-r--r--arch/powerpc/xmon/xmon.c152
17 files changed, 512 insertions, 73 deletions
diff --git a/arch/powerpc/include/asm/dbell.h b/arch/powerpc/include/asm/dbell.h
index 501189a543d..0893ab9343a 100644
--- a/arch/powerpc/include/asm/dbell.h
+++ b/arch/powerpc/include/asm/dbell.h
@@ -27,10 +27,10 @@ enum ppc_dbell {
PPC_G_DBELL_MC = 4, /* guest mcheck doorbell */
};
-#ifdef CONFIG_SMP
-extern unsigned long dbell_smp_message[NR_CPUS];
-extern void smp_dbell_message_pass(int target, int msg);
-#endif
+extern void doorbell_message_pass(int target, int msg);
+extern void doorbell_exception(struct pt_regs *regs);
+extern void doorbell_check_self(void);
+extern void doorbell_setup_this_cpu(void);
static inline void ppc_msgsnd(enum ppc_dbell type, u32 flags, u32 tag)
{
diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h
index 2bad6e5855a..adc8e6cdf33 100644
--- a/arch/powerpc/include/asm/machdep.h
+++ b/arch/powerpc/include/asm/machdep.h
@@ -278,6 +278,7 @@ extern void e500_idle(void);
extern void power4_idle(void);
extern void power4_cpu_offline_powersave(void);
extern void ppc6xx_idle(void);
+extern void book3e_idle(void);
/*
* ppc_md contains a copy of the machine description structure for the
diff --git a/arch/powerpc/include/asm/mmu-book3e.h b/arch/powerpc/include/asm/mmu-book3e.h
index 74695816205..87a1d787c5b 100644
--- a/arch/powerpc/include/asm/mmu-book3e.h
+++ b/arch/powerpc/include/asm/mmu-book3e.h
@@ -193,6 +193,10 @@ struct mmu_psize_def
{
unsigned int shift; /* number of bits */
unsigned int enc; /* PTE encoding */
+ unsigned int ind; /* Corresponding indirect page size shift */
+ unsigned int flags;
+#define MMU_PAGE_SIZE_DIRECT 0x1 /* Supported as a direct size */
+#define MMU_PAGE_SIZE_INDIRECT 0x2 /* Supported as an indirect size */
};
extern struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT];
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index d62fdf4e504..d8be016d2ed 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -890,7 +890,7 @@
#ifndef __ASSEMBLY__
#define mfmsr() ({unsigned long rval; \
asm volatile("mfmsr %0" : "=r" (rval)); rval;})
-#ifdef CONFIG_PPC64
+#ifdef CONFIG_PPC_BOOK3S_64
#define __mtmsrd(v, l) asm volatile("mtmsrd %0," __stringify(l) \
: : "r" (v) : "memory")
#define mtmsrd(v) __mtmsrd((v), 0)
diff --git a/arch/powerpc/include/asm/reg_booke.h b/arch/powerpc/include/asm/reg_booke.h
index 2360317179a..667a498eaee 100644
--- a/arch/powerpc/include/asm/reg_booke.h
+++ b/arch/powerpc/include/asm/reg_booke.h
@@ -29,8 +29,8 @@
#if defined(CONFIG_PPC_BOOK3E_64)
#define MSR_ MSR_ME | MSR_CE
#define MSR_KERNEL MSR_ | MSR_CM
-#define MSR_USER32 MSR_ | MSR_PR | MSR_EE
-#define MSR_USER64 MSR_USER32 | MSR_CM
+#define MSR_USER32 MSR_ | MSR_PR | MSR_EE | MSR_DE
+#define MSR_USER64 MSR_USER32 | MSR_CM | MSR_DE
#elif defined (CONFIG_40x)
#define MSR_KERNEL (MSR_ME|MSR_RI|MSR_IR|MSR_DR|MSR_CE)
#define MSR_USER (MSR_KERNEL|MSR_PR|MSR_EE)
@@ -62,6 +62,7 @@
#define SPRN_TLB0PS 0x158 /* TLB 0 Page Size Register */
#define SPRN_MAS5_MAS6 0x15c /* MMU Assist Register 5 || 6 */
#define SPRN_MAS8_MAS1 0x15d /* MMU Assist Register 8 || 1 */
+#define SPRN_EPTCFG 0x15e /* Embedded Page Table Config */
#define SPRN_MAS7_MAS3 0x174 /* MMU Assist Register 7 || 3 */
#define SPRN_MAS0_MAS1 0x175 /* MMU Assist Register 0 || 1 */
#define SPRN_IVOR0 0x190 /* Interrupt Vector Offset Register 0 */
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 01006040f59..77d831a1cc3 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -37,7 +37,7 @@ obj-$(CONFIG_PPC64) += setup_64.o sys_ppc32.o \
obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
obj-$(CONFIG_PPC_BOOK3S_64) += cpu_setup_ppc970.o cpu_setup_pa6t.o
obj64-$(CONFIG_RELOCATABLE) += reloc_64.o
-obj-$(CONFIG_PPC_BOOK3E_64) += exceptions-64e.o
+obj-$(CONFIG_PPC_BOOK3E_64) += exceptions-64e.o idle_book3e.o
obj-$(CONFIG_PPC64) += vdso64/
obj-$(CONFIG_ALTIVEC) += vecemu.o
obj-$(CONFIG_PPC_970_NAP) += idle_power4.o
@@ -68,6 +68,7 @@ obj64-$(CONFIG_HIBERNATION) += swsusp_asm64.o
obj-$(CONFIG_MODULES) += module.o module_$(CONFIG_WORD_SIZE).o
obj-$(CONFIG_44x) += cpu_setup_44x.o
obj-$(CONFIG_FSL_BOOKE) += cpu_setup_fsl_booke.o dbell.o
+obj-$(CONFIG_PPC_BOOK3E_64) += dbell.o
extra-y := head_$(CONFIG_WORD_SIZE).o
extra-$(CONFIG_PPC_BOOK3E_32) := head_new_booke.o
diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/dbell.c
index 1493734cd87..3307a52d797 100644
--- a/arch/powerpc/kernel/dbell.c
+++ b/arch/powerpc/kernel/dbell.c
@@ -13,32 +13,88 @@
#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/threads.h>
+#include <linux/percpu.h>
#include <asm/dbell.h>
+#include <asm/irq_regs.h>
#ifdef CONFIG_SMP
-unsigned long dbell_smp_message[NR_CPUS];
+struct doorbell_cpu_info {
+ unsigned long messages; /* current messages bits */
+ unsigned int tag; /* tag value */
+};
-void smp_dbell_message_pass(int target, int msg)
+static DEFINE_PER_CPU(struct doorbell_cpu_info, doorbell_cpu_info);
+
+void doorbell_setup_this_cpu(void)
+{
+ struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info);
+
+ info->messages = 0;
+ info->tag = mfspr(SPRN_PIR) & 0x3fff;
+}
+
+void doorbell_message_pass(int target, int msg)
{
+ struct doorbell_cpu_info *info;
int i;
- if(target < NR_CPUS) {
- set_bit(msg, &dbell_smp_message[target]);
- ppc_msgsnd(PPC_DBELL, 0, target);
+ if (target < NR_CPUS) {
+ info = &per_cpu(doorbell_cpu_info, target);
+ set_bit(msg, &info->messages);
+ ppc_msgsnd(PPC_DBELL, 0, info->tag);
}
- else if(target == MSG_ALL_BUT_SELF) {
+ else if (target == MSG_ALL_BUT_SELF) {
for_each_online_cpu(i) {
if (i == smp_processor_id())
continue;
- set_bit(msg, &dbell_smp_message[i]);
- ppc_msgsnd(PPC_DBELL, 0, i);
+ info = &per_cpu(doorbell_cpu_info, i);
+ set_bit(msg, &info->messages);
+ ppc_msgsnd(PPC_DBELL, 0, info->tag);
}
}
else { /* target == MSG_ALL */
- for_each_online_cpu(i)
- set_bit(msg, &dbell_smp_message[i]);
+ for_each_online_cpu(i) {
+ info = &per_cpu(doorbell_cpu_info, i);
+ set_bit(msg, &info->messages);
+ }
ppc_msgsnd(PPC_DBELL, PPC_DBELL_MSG_BRDCAST, 0);
}
}
-#endif
+
+void doorbell_exception(struct pt_regs *regs)
+{
+ struct pt_regs *old_regs = set_irq_regs(regs);
+ struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info);
+ int msg;
+
+ /* Warning: regs can be NULL when called from irq enable */
+
+ if (!info->messages || (num_online_cpus() < 2))
+ goto out;
+
+ for (msg = 0; msg < 4; msg++)
+ if (test_and_clear_bit(msg, &info->messages))
+ smp_message_recv(msg);
+
+out:
+ set_irq_regs(old_regs);
+}
+
+void doorbell_check_self(void)
+{
+ struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info);
+
+ if (!info->messages)
+ return;
+
+ ppc_msgsnd(PPC_DBELL, 0, info->tag);
+}
+
+#else /* CONFIG_SMP */
+void doorbell_exception(struct pt_regs *regs)
+{
+ printk(KERN_WARNING "Received doorbell on non-smp system\n");
+}
+#endif /* CONFIG_SMP */
+
diff --git a/arch/powerpc/kernel/exceptions-64e.S b/arch/powerpc/kernel/exceptions-64e.S
index 24dcc0ecf24..5c43063d250 100644
--- a/arch/powerpc/kernel/exceptions-64e.S
+++ b/arch/powerpc/kernel/exceptions-64e.S
@@ -191,6 +191,12 @@ exc_##n##_bad_stack: \
sth r1,PACA_TRAP_SAVE(r13); /* store trap */ \
b bad_stack_book3e; /* bad stack error */
+/* WARNING: If you change the layout of this stub, make sure you chcek
+ * the debug exception handler which handles single stepping
+ * into exceptions from userspace, and the MM code in
+ * arch/powerpc/mm/tlb_nohash.c which patches the branch here
+ * and would need to be updated if that branch is moved
+ */
#define EXCEPTION_STUB(loc, label) \
. = interrupt_base_book3e + loc; \
nop; /* To make debug interrupts happy */ \
@@ -204,11 +210,30 @@ exc_##n##_bad_stack: \
lis r,TSR_FIS@h; \
mtspr SPRN_TSR,r
+/* Used by asynchronous interrupt that may happen in the idle loop.
+ *
+ * This check if the thread was in the idle loop, and if yes, returns
+ * to the caller rather than the PC. This is to avoid a race if
+ * interrupts happen before the wait instruction.
+ */
+#define CHECK_NAPPING() \
+ clrrdi r11,r1,THREAD_SHIFT; \
+ ld r10,TI_LOCAL_FLAGS(r11); \
+ andi. r9,r10,_TLF_NAPPING; \
+ beq+ 1f; \
+ ld r8,_LINK(r1); \
+ rlwinm r7,r10,0,~_TLF_NAPPING; \
+ std r8,_NIP(r1); \
+ std r7,TI_LOCAL_FLAGS(r11); \
+1:
+
+
#define MASKABLE_EXCEPTION(trapnum, label, hdlr, ack) \
START_EXCEPTION(label); \
NORMAL_EXCEPTION_PROLOG(trapnum, PROLOG_ADDITION_MASKABLE) \
EXCEPTION_COMMON(trapnum, PACA_EXGEN, INTS_DISABLE_ALL) \
ack(r8); \
+ CHECK_NAPPING(); \
addi r3,r1,STACK_FRAME_OVERHEAD; \
bl hdlr; \
b .ret_from_except_lite;
@@ -246,11 +271,9 @@ interrupt_base_book3e: /* fake trap */
EXCEPTION_STUB(0x1a0, watchdog) /* 0x09f0 */
EXCEPTION_STUB(0x1c0, data_tlb_miss)
EXCEPTION_STUB(0x1e0, instruction_tlb_miss)
+ EXCEPTION_STUB(0x280, doorbell)
+ EXCEPTION_STUB(0x2a0, doorbell_crit)
-#if 0
- EXCEPTION_STUB(0x280, processor_doorbell)
- EXCEPTION_STUB(0x220, processor_doorbell_crit)
-#endif
.globl interrupt_end_book3e
interrupt_end_book3e:
@@ -259,6 +282,7 @@ interrupt_end_book3e:
CRIT_EXCEPTION_PROLOG(0x100, PROLOG_ADDITION_NONE)
// EXCEPTION_COMMON(0x100, PACA_EXCRIT, INTS_DISABLE_ALL)
// bl special_reg_save_crit
+// CHECK_NAPPING();
// addi r3,r1,STACK_FRAME_OVERHEAD
// bl .critical_exception
// b ret_from_crit_except
@@ -270,6 +294,7 @@ interrupt_end_book3e:
// EXCEPTION_COMMON(0x200, PACA_EXMC, INTS_DISABLE_ALL)
// bl special_reg_save_mc
// addi r3,r1,STACK_FRAME_OVERHEAD
+// CHECK_NAPPING();
// bl .machine_check_exception
// b ret_from_mc_except
b .
@@ -340,6 +365,7 @@ interrupt_end_book3e:
CRIT_EXCEPTION_PROLOG(0x9f0, PROLOG_ADDITION_NONE)
// EXCEPTION_COMMON(0x9f0, PACA_EXCRIT, INTS_DISABLE_ALL)
// bl special_reg_save_crit
+// CHECK_NAPPING();
// addi r3,r1,STACK_FRAME_OVERHEAD
// bl .unknown_exception
// b ret_from_crit_except
@@ -428,6 +454,20 @@ interrupt_end_book3e:
kernel_dbg_exc:
b . /* NYI */
+/* Doorbell interrupt */
+ MASKABLE_EXCEPTION(0x2070, doorbell, .doorbell_exception, ACK_NONE)
+
+/* Doorbell critical Interrupt */
+ START_EXCEPTION(doorbell_crit);
+ CRIT_EXCEPTION_PROLOG(0x2080, PROLOG_ADDITION_NONE)
+// EXCEPTION_COMMON(0x2080, PACA_EXCRIT, INTS_DISABLE_ALL)
+// bl special_reg_save_crit
+// CHECK_NAPPING();
+// addi r3,r1,STACK_FRAME_OVERHEAD
+// bl .doorbell_critical_exception
+// b ret_from_crit_except
+ b .
+
/*
* An interrupt came in while soft-disabled; clear EE in SRR1,
@@ -563,6 +603,8 @@ BAD_STACK_TRAMPOLINE(0xd00)
BAD_STACK_TRAMPOLINE(0xe00)
BAD_STACK_TRAMPOLINE(0xf00)
BAD_STACK_TRAMPOLINE(0xf20)
+BAD_STACK_TRAMPOLINE(0x2070)
+BAD_STACK_TRAMPOLINE(0x2080)
.globl bad_stack_book3e
bad_stack_book3e:
diff --git a/arch/powerpc/kernel/idle_book3e.S b/arch/powerpc/kernel/idle_book3e.S
new file mode 100644
index 00000000000..16c002d6bdf
--- /dev/null
+++ b/arch/powerpc/kernel/idle_book3e.S
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2010 IBM Corp, Benjamin Herrenschmidt <benh@kernel.crashing.org>
+ *
+ * Generic idle routine for Book3E processors
+ *
+ * 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.
+ */
+
+#include <linux/threads.h>
+#include <asm/reg.h>
+#include <asm/ppc_asm.h>
+#include <asm/asm-offsets.h>
+#include <asm/ppc-opcode.h>
+#include <asm/processor.h>
+#include <asm/thread_info.h>
+
+/* 64-bit version only for now */
+#ifdef CONFIG_PPC64
+
+_GLOBAL(book3e_idle)
+ /* Save LR for later */
+ mflr r0
+ std r0,16(r1)
+
+ /* Hard disable interrupts */
+ wrteei 0
+
+ /* Now check if an interrupt came in while we were soft disabled
+ * since we may otherwise lose it (doorbells etc...). We know
+ * that since PACAHARDIRQEN will have been cleared in that case.
+ */
+ lbz r3,PACAHARDIRQEN(r13)
+ cmpwi cr0,r3,0
+ beqlr
+
+ /* Now we are going to mark ourselves as soft and hard enables in
+ * order to be able to take interrupts while asleep. We inform lockdep
+ * of that. We don't actually turn interrupts on just yet tho.
+ */
+#ifdef CONFIG_TRACE_IRQFLAGS
+ stdu r1,-128(r1)
+ bl .trace_hardirqs_on
+#endif
+ li r0,1
+ stb r0,PACASOFTIRQEN(r13)
+ stb r0,PACAHARDIRQEN(r13)
+
+ /* Interrupts will make use return to LR, so get something we want
+ * in there
+ */
+ bl 1f
+
+ /* Hard disable interrupts again */
+ wrteei 0
+
+ /* Mark them off again in the PACA as well */
+ li r0,0
+ stb r0,PACASOFTIRQEN(r13)
+ stb r0,PACAHARDIRQEN(r13)
+
+ /* Tell lockdep about it */
+#ifdef CONFIG_TRACE_IRQFLAGS
+ bl .trace_hardirqs_off
+ addi r1,r1,128
+#endif
+ ld r0,16(r1)
+ mtlr r0
+ blr
+
+1: /* Let's set the _TLF_NAPPING flag so interrupts make us return
+ * to the right spot
+ */
+ clrrdi r11,r1,THREAD_SHIFT
+ ld r10,TI_LOCAL_FLAGS(r11)
+ ori r10,r10,_TLF_NAPPING
+ std r10,TI_LOCAL_FLAGS(r11)
+
+ /* We can now re-enable hard interrupts and go to sleep */
+ wrteei 1
+1: PPC_WAIT(0)
+ b 1b
+
+#endif /* CONFIG_PPC64 */
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index 77be3d058a6..8f96d319890 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -64,6 +64,8 @@
#include <asm/ptrace.h>
#include <asm/machdep.h>
#include <asm/udbg.h>
+#include <asm/dbell.h>
+
#ifdef CONFIG_PPC64
#include <asm/paca.h>
#include <asm/firmware.h>
@@ -153,14 +155,28 @@ notrace void raw_local_irq_restore(unsigned long en)
if (get_hard_enabled())
return;
+#if defined(CONFIG_BOOKE) && defined(CONFIG_SMP)
+ /* Check for pending doorbell interrupts and resend to ourself */
+ doorbell_check_self();
+#endif
+
/*
* Need to hard-enable interrupts here. Since currently disabled,
* no need to take further asm precautions against preemption; but
* use local_paca instead of get_paca() to avoid preemption checking.
*/
local_paca->hard_enabled = en;
+
+#ifndef CONFIG_BOOKE
+ /* On server, re-trigger the decrementer if it went negative since
+ * some processors only trigger on edge transitions of the sign bit.
+ *
+ * BookE has a level sensitive decrementer (latches in TSR) so we
+ * don't need that
+ */
if ((int)mfspr(SPRN_DEC) < 0)
mtspr(SPRN_DEC, 1);
+#endif /* CONFIG_BOOKE */
/*
* Force the delivery of pending soft-disabled interrupts on PS3.
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 1e78453645b..551f6713ff4 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -477,6 +477,28 @@ struct task_struct *__switch_to(struct task_struct *prev,
new_thread = &new->thread;
old_thread = &current->thread;
+#if defined(CONFIG_PPC_BOOK3E_64)
+ /* XXX Current Book3E code doesn't deal with kernel side DBCR0,
+ * we always hold the user values, so we set it now.
+ *
+ * However, we ensure the kernel MSR:DE is appropriately cleared too
+ * to avoid spurrious single step exceptions in the kernel.
+ *
+ * This will have to change to merge with the ppc32 code at some point,
+ * but I don't like much what ppc32 is doing today so there's some
+ * thinking needed there
+ */
+ if ((new_thread->dbcr0 | old_thread->dbcr0) & DBCR0_IDM) {
+ u32 dbcr0;
+
+ mtmsr(mfmsr() & ~MSR_DE);
+ isync();
+ dbcr0 = mfspr(SPRN_DBCR0);
+ dbcr0 = (dbcr0 & DBCR0_EDM) | new_thread->dbcr0;
+ mtspr(SPRN_DBCR0, dbcr0);
+ }
+#endif /* CONFIG_PPC64_BOOK3E */
+
#ifdef CONFIG_PPC64
/*
* Collect processor utilization data per process
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index e5fe5a8522a..a45a63c3a0c 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -55,9 +55,6 @@
#endif
#include <asm/kexec.h>
#include <asm/ppc-opcode.h>
-#ifdef CONFIG_FSL_BOOKE
-#include <asm/dbell.h>
-#endif
#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
int (*__debugger)(struct pt_regs *regs) __read_mostly;
@@ -1342,24 +1339,6 @@ void vsx_assist_exception(struct pt_regs *regs)
#endif /* CONFIG_VSX */
#ifdef CONFIG_FSL_BOOKE
-
-void doorbell_exception(struct pt_regs *regs)
-{
-#ifdef CONFIG_SMP
- int cpu = smp_processor_id();
- int msg;
-
- if (num_online_cpus() < 2)
- return;
-
- for (msg = 0; msg < 4; msg++)
- if (test_and_clear_bit(msg, &dbell_smp_message[cpu]))
- smp_message_recv(msg);
-#else
- printk(KERN_WARNING "Received doorbell on non-smp system\n");
-#endif
-}
-
void CacheLockingException(struct pt_regs *regs, unsigned long address,
unsigned long error_code)
{
diff --git a/arch/powerpc/mm/tlb_nohash.c b/arch/powerpc/mm/tlb_nohash.c
index e81d5d67f83..3b10f804b73 100644
--- a/arch/powerpc/mm/tlb_nohash.c
+++ b/arch/powerpc/mm/tlb_nohash.c
@@ -46,6 +46,7 @@
struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT] = {
[MMU_PAGE_4K] = {
.shift = 12,
+ .ind = 20,
.enc = BOOK3E_PAGESZ_4K,
},
[MMU_PAGE_16K] = {
@@ -54,6 +55,7 @@ struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT] = {
},
[MMU_PAGE_64K] = {
.shift = 16,
+ .ind = 28,
.enc = BOOK3E_PAGESZ_64K,
},
[MMU_PAGE_1M] = {
@@ -62,6 +64,7 @@ struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT] = {
},
[MMU_PAGE_16M] = {
.shift = 24,
+ .ind = 36,
.enc = BOOK3E_PAGESZ_16M,
},
[MMU_PAGE_256M] = {
@@ -344,16 +347,108 @@ void tlb_flush_pgtable(struct mmu_gather *tlb, unsigned long address)
}
}
-/*
- * Early initialization of the MMU TLB code
- */
-static void __early_init_mmu(int boot_cpu)
+static void setup_page_sizes(void)
+{
+ unsigned int tlb0cfg = mfspr(SPRN_TLB0CFG);
+ unsigned int tlb0ps = mfspr(SPRN_TLB0PS);
+ unsigned int eptcfg = mfspr(SPRN_EPTCFG);
+ int i, psize;
+
+ /* Look for supported direct sizes */
+ for (psize = 0; psize < MMU_PAGE_COUNT; ++psize) {
+ struct mmu_psize_def *def = &mmu_psize_defs[psize];
+
+ if (tlb0ps & (1U << (def->shift - 10)))
+ def->flags |= MMU_PAGE_SIZE_DIRECT;
+ }
+
+ /* Indirect page sizes supported ? */
+ if ((tlb0cfg & TLBnCFG_IND) == 0)
+ goto no_indirect;
+
+ /* Now, we only deal with one IND page size for each
+ * direct size. Hopefully all implementations today are
+ * unambiguous, but we might want to be careful in the
+ * future.
+ */
+ for (i = 0; i < 3; i++) {
+ unsigned int ps, sps;
+
+ sps = eptcfg & 0x1f;
+ eptcfg >>= 5;
+ ps = eptcfg & 0x1f;
+ eptcfg >>= 5;
+ if (!ps || !sps)
+ continue;
+ for (psize = 0; psize < MMU_PAGE_COUNT; psize++) {
+ struct mmu_psize_def *def = &mmu_psize_defs[psize];
+
+ if (ps == (def->shift - 10))
+ def->flags |= MMU_PAGE_SIZE_INDIRECT;
+ if (sps == (def->shift - 10))
+ def->ind = ps + 10;
+ }
+ }
+ no_indirect:
+
+ /* Cleanup array and print summary */
+ pr_info("MMU: Supported page sizes\n");
+ for (psize = 0; psize < MMU_PAGE_COUNT; ++psize) {
+ struct mmu_psize_def *def = &mmu_psize_defs[psize];
+ const char *__page_type_names[] = {
+ "unsupported",
+ "direct",
+ "indirect",
+ "direct & indirect"
+ };
+ if (def->flags == 0) {
+ def->shift = 0;
+ continue;
+ }
+ pr_info(" %8ld KB as %s\n", 1ul << (def->shift - 10),
+ __page_type_names[def->flags & 0x3]);
+ }
+}
+
+static void setup_mmu_htw(void)
{
extern unsigned int interrupt_base_book3e;
extern unsigned int exc_data_tlb_miss_htw_book3e;
extern unsigned int exc_instruction_tlb_miss_htw_book3e;
unsigned int *ibase = &interrupt_base_book3e;
+
+ /* Check if HW tablewalk is present, and if yes, enable it by:
+ *
+ * - patching the TLB miss handlers to branch to the
+ * one dedicates to it
+ *
+ * - setting the global book3e_htw_enabled
+ */
+ unsigned int tlb0cfg = mfspr(SPRN_TLB0CFG);
+
+ if ((tlb0cfg & TLBnCFG_IND) &&
+ (tlb0cfg & TLBnCFG_PT)) {
+ /* Our exceptions vectors start with a NOP and -then- a branch
+ * to deal with single stepping from userspace which stops on
+ * the second instruction. Thus we need to patch the second
+ * instruction of the exception, not the first one
+ */
+ patch_branch(ibase + (0x1c0 / 4) + 1,
+ (unsigned long)&exc_data_tlb_miss_htw_book3e, 0);
+ patch_branch(ibase + (0x1e0 / 4) + 1,
+ (unsigned long)&exc_instruction_tlb_miss_htw_book3e, 0);
+ book3e_htw_enabled = 1;
+ }
+ pr_info("MMU: Book3E Page Tables %s\n",
+ book3e_htw_enabled ? "Enabled" : "Disabled");
+}
+
+/*
+ * Early initialization of the MMU TLB code
+ */
+static void __early_init_mmu(int boot_cpu)
+{
unsigned int mas4;
/* XXX This will have to be decided at runtime, but right
@@ -370,35 +465,17 @@ static void __early_init_mmu(int boot_cpu)
*/
mmu_vmemmap_psize = MMU_PAGE_16M;
- /* Check if HW tablewalk is present, and if yes, enable it by:
- *
- * - patching the TLB miss handlers to branch to the
- * one dedicates to it
- *
- * - setting the global book3e_htw_enabled
- *
- * - Set MAS4:INDD and default page size
- */
-
/* XXX This code only checks for TLB 0 capabilities and doesn't
* check what page size combos are supported by the HW. It
* also doesn't handle the case where a separate array holds
* the IND entries from the array loaded by the PT.
*/
if (boot_cpu) {
- unsigned int tlb0cfg = mfspr(SPRN_TLB0CFG);
+ /* Look for supported page sizes */
+ setup_page_sizes();
- /* Check if HW loader is supported */
- if ((tlb0cfg & TLBnCFG_IND) &&
- (tlb0cfg & TLBnCFG_PT)) {
- patch_branch(ibase + (0x1c0 / 4),
- (unsigned long)&exc_data_tlb_miss_htw_book3e, 0);
- patch_branch(ibase + (0x1e0 / 4),
- (unsigned long)&exc_instruction_tlb_miss_htw_book3e, 0);
- book3e_htw_enabled = 1;
- }
- pr_info("MMU: Book3E Page Tables %s\n",
- book3e_htw_enabled ? "Enabled" : "Disabled");
+ /* Look for HW tablewalk support */
+ setup_mmu_htw();
}
/* Set MAS4 based on page table setting */
diff --git a/arch/powerpc/oprofile/Makefile b/arch/powerpc/oprofile/Makefile
index 73e1c2ca055..e219ca43962 100644
--- a/arch/powerpc/oprofile/Makefile
+++ b/arch/powerpc/oprofile/Makefile
@@ -16,6 +16,6 @@ oprofile-y := $(DRIVER_OBJS) common.o backtrace.o
oprofile-$(CONFIG_OPROFILE_CELL) += op_model_cell.o \
cell/spu_profiler.o cell/vma_map.o \
cell/spu_task_sync.o
-oprofile-$(CONFIG_PPC64) += op_model_rs64.o op_model_power4.o op_model_pa6t.o
+oprofile-$(CONFIG_PPC_BOOK3S_64) += op_model_rs64.o op_model_power4.o op_model_pa6t.o
oprofile-$(CONFIG_FSL_EMB_PERFMON) += op_model_fsl_emb.o
oprofile-$(CONFIG_6xx) += op_model_7450.o
diff --git a/arch/powerpc/oprofile/common.c b/arch/powerpc/oprofile/common.c
index 21f16edf6c8..d65e68f3cb2 100644
--- a/arch/powerpc/oprofile/common.c
+++ b/arch/powerpc/oprofile/common.c
@@ -199,7 +199,7 @@ int __init oprofile_arch_init(struct oprofile_operations *ops)
return -ENODEV;
switch (cur_cpu_spec->oprofile_type) {
-#ifdef CONFIG_PPC64
+#ifdef CONFIG_PPC_BOOK3S_64
#ifdef CONFIG_OPROFILE_CELL
case PPC_OPROFILE_CELL:
if (firmware_has_feature(FW_FEATURE_LPAR))
diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c
index a15f582300d..4c3cde911c7 100644
--- a/arch/powerpc/platforms/85xx/smp.c
+++ b/arch/powerpc/platforms/85xx/smp.c
@@ -99,6 +99,8 @@ static void __init
smp_85xx_setup_cpu(int cpu_nr)
{
mpic_setup_this_cpu();
+ if (cpu_has_feature(CPU_FTR_DBELL))
+ doorbell_setup_this_cpu();
}
struct smp_ops_t smp_85xx_ops = {
@@ -117,7 +119,7 @@ void __init mpc85xx_smp_init(void)
}
if (cpu_has_feature(CPU_FTR_DBELL))
- smp_85xx_ops.message_pass = smp_dbell_message_pass;
+ smp_85xx_ops.message_pass = doorbell_message_pass;
BUG_ON(!smp_85xx_ops.message_pass);
diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c
index 8bad7d5f32a..0554445200b 100644
--- a/arch/powerpc/xmon/xmon.c
+++ b/arch/powerpc/xmon/xmon.c
@@ -155,6 +155,9 @@ static int do_spu_cmd(void);
#ifdef CONFIG_44x
static void dump_tlb_44x(void);
#endif
+#ifdef CONFIG_PPC_BOOK3E
+static void dump_tlb_book3e(void);
+#endif
static int xmon_no_auto_backtrace;
@@ -888,6 +891,11 @@ cmds(struct pt_regs *excp)
dump_tlb_44x();
break;
#endif
+#ifdef CONFIG_PPC_BOOK3E
+ case 'u':
+ dump_tlb_book3e();
+ break;
+#endif
default:
printf("Unrecognized command: ");
do {
@@ -2701,6 +2709,150 @@ static void dump_tlb_44x(void)
}
#endif /* CONFIG_44x */
+#ifdef CONFIG_PPC_BOOK3E
+static void dump_tlb_book3e(void)
+{
+ u32 mmucfg, pidmask, lpidmask;
+ u64 ramask;
+ int i, tlb, ntlbs, pidsz, lpidsz, rasz, lrat = 0;
+ int mmu_version;
+ static const char *pgsz_names[] = {
+ " 1K",
+ " 2K",
+ " 4K",
+ " 8K",
+ " 16K",
+ " 32K",
+ " 64K",
+ "128K",
+ "256K",
+ "512K",
+ " 1M",
+ " 2M",
+ " 4M",
+ " 8M",
+ " 16M",
+ " 32M",
+ " 64M",
+ "128M",
+ "256M",
+ "512M",
+ " 1G",
+ " 2G",
+ " 4G",
+ " 8G",
+ " 16G",
+ " 32G",
+ " 64G",
+ "128G",
+ "256G",
+ "512G",
+ " 1T",
+ " 2T",
+ };
+
+ /* Gather some infos about the MMU */
+ mmucfg = mfspr(SPRN_MMUCFG);
+ mmu_version = (mmucfg & 3) + 1;
+ ntlbs = ((mmucfg >> 2) & 3) + 1;
+ pidsz = ((mmucfg >> 6) & 0x1f) + 1;
+ lpidsz = (mmucfg >> 24) & 0xf;
+ rasz = (mmucfg >> 16) & 0x7f;
+ if ((mmu_version > 1) && (mmucfg & 0x10000))
+ lrat = 1;
+ printf("Book3E MMU MAV=%d.0,%d TLBs,%d-bit PID,%d-bit LPID,%d-bit RA\n",
+ mmu_version, ntlbs, pidsz, lpidsz, rasz);
+ pidmask = (1ul << pidsz) - 1;
+ lpidmask = (1ul << lpidsz) - 1;
+ ramask = (1ull << rasz) - 1;
+
+ for (tlb = 0; tlb < ntlbs; tlb++) {
+ u32 tlbcfg;
+ int nent, assoc, new_cc = 1;
+ printf("TLB %d:\n------\n", tlb);
+ switch(tlb) {
+ case 0:
+ tlbcfg = mfspr(SPRN_TLB0CFG);
+ break;
+ case 1:
+ tlbcfg = mfspr(SPRN_TLB1CFG);
+ break;
+ case 2:
+ tlbcfg = mfspr(SPRN_TLB2CFG);
+ break;
+ case 3:
+ tlbcfg = mfspr(SPRN_TLB3CFG);
+ break;
+ default:
+ printf("Unsupported TLB number !\n");
+ continue;
+ }
+ nent = tlbcfg & 0xfff;
+ assoc = (tlbcfg >> 24) & 0xff;
+ for (i = 0; i < nent; i++) {
+ u32 mas0 = MAS0_TLBSEL(tlb);
+ u32 mas1 = MAS1_TSIZE(BOOK3E_PAGESZ_4K);
+ u64 mas2 = 0;
+ u64 mas7_mas3;
+ int esel = i, cc = i;
+
+ if (assoc != 0) {
+ cc = i / assoc;
+ esel = i % assoc;
+ mas2 = cc * 0x1000;
+ }
+
+ mas0 |= MAS0_ESEL(esel);
+ mtspr(SPRN_MAS0, mas0);
+ mtspr(SPRN_MAS1, mas1);
+ mtspr(SPRN_MAS2, mas2);
+ asm volatile("tlbre 0,0,0" : : : "memory");
+ mas1 = mfspr(SPRN_MAS1);
+ mas2 = mfspr(SPRN_MAS2);
+ mas7_mas3 = mfspr(SPRN_MAS7_MAS3);
+ if (assoc && (i % assoc) == 0)
+ new_cc = 1;
+ if (!(mas1 & MAS1_VALID))
+ continue;
+ if (assoc == 0)
+ printf("%04x- ", i);
+ else if (new_cc)
+ printf("%04x-%c", cc, 'A' + esel);
+ else
+ printf(" |%c", 'A' + esel);
+ new_cc = 0;
+ printf(" %016llx %04x %s %c%c AS%c",
+ mas2 & ~0x3ffull,
+ (mas1 >> 16) & 0x3fff,
+ pgsz_names[(mas1 >> 7) & 0x1f],
+ mas1 & MAS1_IND ? 'I' : ' ',
+ mas1 & MAS1_IPROT ? 'P' : ' ',
+ mas1 & MAS1_TS ? '1' : '0');
+ printf(" %c%c%c%c%c%c%c",
+ mas2 & MAS2_X0 ? 'a' : ' ',
+ mas2 & MAS2_X1 ? 'v' : ' ',
+ mas2 & MAS2_W ? 'w' : ' ',
+ mas2 & MAS2_I ? 'i' : ' ',
+ mas2 & MAS2_M ? 'm' : ' ',
+ mas2 & MAS2_G ? 'g' : ' ',
+ mas2 & MAS2_E ? 'e' : ' ');
+ printf(" %016llx", mas7_mas3 & ramask & ~0x7ffull);
+ if (mas1 & MAS1_IND)
+ printf(" %s\n",
+ pgsz_names[(mas7_mas3 >> 1) & 0x1f]);
+ else
+ printf(" U%c%c%c S%c%c%c\n",
+ mas7_mas3 & MAS3_UX ? 'x' : ' ',
+ mas7_mas3 & MAS3_UW ? 'w' : ' ',
+ mas7_mas3 & MAS3_UR ? 'r' : ' ',
+ mas7_mas3 & MAS3_SX ? 'x' : ' ',
+ mas7_mas3 & MAS3_SW ? 'w' : ' ',
+ mas7_mas3 & MAS3_SR ? 'r' : ' ');
+ }
+ }
+}
+#endif /* CONFIG_PPC_BOOK3E */
+
static void xmon_init(int enable)
{
#ifdef CONFIG_PPC_ISERIES