From d939be3add4f1410079dad2755d4936cdb70903b Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Fri, 27 Feb 2015 23:52:31 +0900 Subject: treewide: Fix typo in printk messages This patch fix spelling typo in printk messages. Signed-off-by: Masanari Iida Acked-by: Randy Dunlap Signed-off-by: Jiri Kosina --- arch/powerpc/kernel/prom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 099f27e6d1b0..20ce051312fc 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -721,7 +721,7 @@ void __init early_init_devtree(void *params) */ of_scan_flat_dt(early_init_dt_scan_cpus, NULL); if (boot_cpuid < 0) { - printk("Failed to indentify boot CPU !\n"); + printk("Failed to identify boot CPU !\n"); BUG(); } -- cgit v1.2.3 From c2ce6f9f3dc00daca5714ef070a9a2d4e78eb336 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 10 Feb 2015 09:51:22 +1100 Subject: powerpc: Change vrX register defines to vX to match gcc and glibc As our various loops (copy, string, crypto etc) get more complicated, we want to share implementations between userspace (eg glibc) and the kernel. We also want to write userspace test harnesses to put in tools/testing/selftest. One gratuitous difference between userspace and the kernel is the VMX register definitions - the kernel uses vrX whereas both gcc and glibc use vX. Change the kernel to match userspace. Signed-off-by: Anton Blanchard Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/ppc_asm.h | 64 +++--- arch/powerpc/include/uapi/asm/ptrace.h | 2 +- arch/powerpc/kernel/tm.S | 8 +- arch/powerpc/kernel/vector.S | 24 +-- arch/powerpc/lib/copypage_power7.S | 32 +-- arch/powerpc/lib/copyuser_power7.S | 226 ++++++++++----------- arch/powerpc/lib/crtsavres.S | 96 ++++----- arch/powerpc/lib/ldstfp.S | 26 +-- arch/powerpc/lib/memcpy_power7.S | 226 ++++++++++----------- .../selftests/powerpc/copyloops/asm/ppc_asm.h | 33 --- 10 files changed, 352 insertions(+), 385 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index 7e4612528546..c7461032b469 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h @@ -637,38 +637,38 @@ END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,945) /* AltiVec Registers (VPRs) */ -#define vr0 0 -#define vr1 1 -#define vr2 2 -#define vr3 3 -#define vr4 4 -#define vr5 5 -#define vr6 6 -#define vr7 7 -#define vr8 8 -#define vr9 9 -#define vr10 10 -#define vr11 11 -#define vr12 12 -#define vr13 13 -#define vr14 14 -#define vr15 15 -#define vr16 16 -#define vr17 17 -#define vr18 18 -#define vr19 19 -#define vr20 20 -#define vr21 21 -#define vr22 22 -#define vr23 23 -#define vr24 24 -#define vr25 25 -#define vr26 26 -#define vr27 27 -#define vr28 28 -#define vr29 29 -#define vr30 30 -#define vr31 31 +#define v0 0 +#define v1 1 +#define v2 2 +#define v3 3 +#define v4 4 +#define v5 5 +#define v6 6 +#define v7 7 +#define v8 8 +#define v9 9 +#define v10 10 +#define v11 11 +#define v12 12 +#define v13 13 +#define v14 14 +#define v15 15 +#define v16 16 +#define v17 17 +#define v18 18 +#define v19 19 +#define v20 20 +#define v21 21 +#define v22 22 +#define v23 23 +#define v24 24 +#define v25 25 +#define v26 26 +#define v27 27 +#define v28 28 +#define v29 29 +#define v30 30 +#define v31 31 /* VSX Registers (VSRs) */ diff --git a/arch/powerpc/include/uapi/asm/ptrace.h b/arch/powerpc/include/uapi/asm/ptrace.h index 77d2ed35b111..8036b385417d 100644 --- a/arch/powerpc/include/uapi/asm/ptrace.h +++ b/arch/powerpc/include/uapi/asm/ptrace.h @@ -136,7 +136,7 @@ struct pt_regs { #endif /* __powerpc64__ */ /* - * Get/set all the altivec registers vr0..vr31, vscr, vrsave, in one go. + * Get/set all the altivec registers v0..v31, vscr, vrsave, in one go. * The transfer totals 34 quadword. Quadwords 0-31 contain the * corresponding vector registers. Quadword 32 contains the vscr as the * last word (offset 12) within that quadword. Quadword 33 contains the diff --git a/arch/powerpc/kernel/tm.S b/arch/powerpc/kernel/tm.S index 2a324f4cb1b9..5754b226da7e 100644 --- a/arch/powerpc/kernel/tm.S +++ b/arch/powerpc/kernel/tm.S @@ -152,9 +152,9 @@ _GLOBAL(tm_reclaim) addi r7, r3, THREAD_TRANSACT_VRSTATE SAVE_32VRS(0, r6, r7) /* r6 scratch, r7 transact vr state */ - mfvscr vr0 + mfvscr v0 li r6, VRSTATE_VSCR - stvx vr0, r7, r6 + stvx v0, r7, r6 dont_backup_vec: mfspr r0, SPRN_VRSAVE std r0, THREAD_TRANSACT_VRSAVE(r3) @@ -359,8 +359,8 @@ _GLOBAL(__tm_recheckpoint) addi r8, r3, THREAD_VRSTATE li r5, VRSTATE_VSCR - lvx vr0, r8, r5 - mtvscr vr0 + lvx v0, r8, r5 + mtvscr v0 REST_32VRS(0, r5, r8) /* r5 scratch, r8 ptr */ dont_restore_vec: ld r5, THREAD_VRSAVE(r3) diff --git a/arch/powerpc/kernel/vector.S b/arch/powerpc/kernel/vector.S index 74f8050518d6..f5c80d567d8d 100644 --- a/arch/powerpc/kernel/vector.S +++ b/arch/powerpc/kernel/vector.S @@ -24,8 +24,8 @@ _GLOBAL(do_load_up_transact_altivec) stw r4,THREAD_USED_VR(r3) li r10,THREAD_TRANSACT_VRSTATE+VRSTATE_VSCR - lvx vr0,r10,r3 - mtvscr vr0 + lvx v0,r10,r3 + mtvscr v0 addi r10,r3,THREAD_TRANSACT_VRSTATE REST_32VRS(0,r4,r10) @@ -52,8 +52,8 @@ _GLOBAL(vec_enable) */ _GLOBAL(load_vr_state) li r4,VRSTATE_VSCR - lvx vr0,r4,r3 - mtvscr vr0 + lvx v0,r4,r3 + mtvscr v0 REST_32VRS(0,r4,r3) blr @@ -63,9 +63,9 @@ _GLOBAL(load_vr_state) */ _GLOBAL(store_vr_state) SAVE_32VRS(0, r4, r3) - mfvscr vr0 + mfvscr v0 li r4, VRSTATE_VSCR - stvx vr0, r4, r3 + stvx v0, r4, r3 blr /* @@ -104,9 +104,9 @@ _GLOBAL(load_up_altivec) addi r4,r4,THREAD addi r6,r4,THREAD_VRSTATE SAVE_32VRS(0,r5,r6) - mfvscr vr0 + mfvscr v0 li r10,VRSTATE_VSCR - stvx vr0,r10,r6 + stvx v0,r10,r6 /* Disable VMX for last_task_used_altivec */ PPC_LL r5,PT_REGS(r4) toreal(r5) @@ -142,8 +142,8 @@ _GLOBAL(load_up_altivec) li r4,1 li r10,VRSTATE_VSCR stw r4,THREAD_USED_VR(r5) - lvx vr0,r10,r6 - mtvscr vr0 + lvx v0,r10,r6 + mtvscr v0 REST_32VRS(0,r4,r6) #ifndef CONFIG_SMP /* Update last_task_used_altivec to 'current' */ @@ -186,9 +186,9 @@ _GLOBAL(giveup_altivec) addi r7,r3,THREAD_VRSTATE 2: PPC_LCMPI 0,r5,0 SAVE_32VRS(0,r4,r7) - mfvscr vr0 + mfvscr v0 li r4,VRSTATE_VSCR - stvx vr0,r4,r7 + stvx v0,r4,r7 beq 1f PPC_LL r4,_MSR-STACK_FRAME_OVERHEAD(r5) #ifdef CONFIG_VSX diff --git a/arch/powerpc/lib/copypage_power7.S b/arch/powerpc/lib/copypage_power7.S index d7dafb3777ac..a84d333ecb09 100644 --- a/arch/powerpc/lib/copypage_power7.S +++ b/arch/powerpc/lib/copypage_power7.S @@ -83,23 +83,23 @@ _GLOBAL(copypage_power7) li r12,112 .align 5 -1: lvx vr7,r0,r4 - lvx vr6,r4,r6 - lvx vr5,r4,r7 - lvx vr4,r4,r8 - lvx vr3,r4,r9 - lvx vr2,r4,r10 - lvx vr1,r4,r11 - lvx vr0,r4,r12 +1: lvx v7,r0,r4 + lvx v6,r4,r6 + lvx v5,r4,r7 + lvx v4,r4,r8 + lvx v3,r4,r9 + lvx v2,r4,r10 + lvx v1,r4,r11 + lvx v0,r4,r12 addi r4,r4,128 - stvx vr7,r0,r3 - stvx vr6,r3,r6 - stvx vr5,r3,r7 - stvx vr4,r3,r8 - stvx vr3,r3,r9 - stvx vr2,r3,r10 - stvx vr1,r3,r11 - stvx vr0,r3,r12 + stvx v7,r0,r3 + stvx v6,r3,r6 + stvx v5,r3,r7 + stvx v4,r3,r8 + stvx v3,r3,r9 + stvx v2,r3,r10 + stvx v1,r3,r11 + stvx v0,r3,r12 addi r3,r3,128 bdnz 1b diff --git a/arch/powerpc/lib/copyuser_power7.S b/arch/powerpc/lib/copyuser_power7.S index 92ee840529bc..da0c568d18c4 100644 --- a/arch/powerpc/lib/copyuser_power7.S +++ b/arch/powerpc/lib/copyuser_power7.S @@ -388,29 +388,29 @@ err3; std r0,0(r3) li r11,48 bf cr7*4+3,5f -err3; lvx vr1,r0,r4 +err3; lvx v1,r0,r4 addi r4,r4,16 -err3; stvx vr1,r0,r3 +err3; stvx v1,r0,r3 addi r3,r3,16 5: bf cr7*4+2,6f -err3; lvx vr1,r0,r4 -err3; lvx vr0,r4,r9 +err3; lvx v1,r0,r4 +err3; lvx v0,r4,r9 addi r4,r4,32 -err3; stvx vr1,r0,r3 -err3; stvx vr0,r3,r9 +err3; stvx v1,r0,r3 +err3; stvx v0,r3,r9 addi r3,r3,32 6: bf cr7*4+1,7f -err3; lvx vr3,r0,r4 -err3; lvx vr2,r4,r9 -err3; lvx vr1,r4,r10 -err3; lvx vr0,r4,r11 +err3; lvx v3,r0,r4 +err3; lvx v2,r4,r9 +err3; lvx v1,r4,r10 +err3; lvx v0,r4,r11 addi r4,r4,64 -err3; stvx vr3,r0,r3 -err3; stvx vr2,r3,r9 -err3; stvx vr1,r3,r10 -err3; stvx vr0,r3,r11 +err3; stvx v3,r0,r3 +err3; stvx v2,r3,r9 +err3; stvx v1,r3,r10 +err3; stvx v0,r3,r11 addi r3,r3,64 7: sub r5,r5,r6 @@ -433,23 +433,23 @@ err3; stvx vr0,r3,r11 */ .align 5 8: -err4; lvx vr7,r0,r4 -err4; lvx vr6,r4,r9 -err4; lvx vr5,r4,r10 -err4; lvx vr4,r4,r11 -err4; lvx vr3,r4,r12 -err4; lvx vr2,r4,r14 -err4; lvx vr1,r4,r15 -err4; lvx vr0,r4,r16 +err4; lvx v7,r0,r4 +err4; lvx v6,r4,r9 +err4; lvx v5,r4,r10 +err4; lvx v4,r4,r11 +err4; lvx v3,r4,r12 +err4; lvx v2,r4,r14 +err4; lvx v1,r4,r15 +err4; lvx v0,r4,r16 addi r4,r4,128 -err4; stvx vr7,r0,r3 -err4; stvx vr6,r3,r9 -err4; stvx vr5,r3,r10 -err4; stvx vr4,r3,r11 -err4; stvx vr3,r3,r12 -err4; stvx vr2,r3,r14 -err4; stvx vr1,r3,r15 -err4; stvx vr0,r3,r16 +err4; stvx v7,r0,r3 +err4; stvx v6,r3,r9 +err4; stvx v5,r3,r10 +err4; stvx v4,r3,r11 +err4; stvx v3,r3,r12 +err4; stvx v2,r3,r14 +err4; stvx v1,r3,r15 +err4; stvx v0,r3,r16 addi r3,r3,128 bdnz 8b @@ -463,29 +463,29 @@ err4; stvx vr0,r3,r16 mtocrf 0x01,r6 bf cr7*4+1,9f -err3; lvx vr3,r0,r4 -err3; lvx vr2,r4,r9 -err3; lvx vr1,r4,r10 -err3; lvx vr0,r4,r11 +err3; lvx v3,r0,r4 +err3; lvx v2,r4,r9 +err3; lvx v1,r4,r10 +err3; lvx v0,r4,r11 addi r4,r4,64 -err3; stvx vr3,r0,r3 -err3; stvx vr2,r3,r9 -err3; stvx vr1,r3,r10 -err3; stvx vr0,r3,r11 +err3; stvx v3,r0,r3 +err3; stvx v2,r3,r9 +err3; stvx v1,r3,r10 +err3; stvx v0,r3,r11 addi r3,r3,64 9: bf cr7*4+2,10f -err3; lvx vr1,r0,r4 -err3; lvx vr0,r4,r9 +err3; lvx v1,r0,r4 +err3; lvx v0,r4,r9 addi r4,r4,32 -err3; stvx vr1,r0,r3 -err3; stvx vr0,r3,r9 +err3; stvx v1,r0,r3 +err3; stvx v0,r3,r9 addi r3,r3,32 10: bf cr7*4+3,11f -err3; lvx vr1,r0,r4 +err3; lvx v1,r0,r4 addi r4,r4,16 -err3; stvx vr1,r0,r3 +err3; stvx v1,r0,r3 addi r3,r3,16 /* Up to 15B to go */ @@ -560,42 +560,42 @@ err3; stw r7,4(r3) li r10,32 li r11,48 - LVS(vr16,0,r4) /* Setup permute control vector */ -err3; lvx vr0,0,r4 + LVS(v16,0,r4) /* Setup permute control vector */ +err3; lvx v0,0,r4 addi r4,r4,16 bf cr7*4+3,5f -err3; lvx vr1,r0,r4 - VPERM(vr8,vr0,vr1,vr16) +err3; lvx v1,r0,r4 + VPERM(v8,v0,v1,v16) addi r4,r4,16 -err3; stvx vr8,r0,r3 +err3; stvx v8,r0,r3 addi r3,r3,16 - vor vr0,vr1,vr1 + vor v0,v1,v1 5: bf cr7*4+2,6f -err3; lvx vr1,r0,r4 - VPERM(vr8,vr0,vr1,vr16) -err3; lvx vr0,r4,r9 - VPERM(vr9,vr1,vr0,vr16) +err3; lvx v1,r0,r4 + VPERM(v8,v0,v1,v16) +err3; lvx v0,r4,r9 + VPERM(v9,v1,v0,v16) addi r4,r4,32 -err3; stvx vr8,r0,r3 -err3; stvx vr9,r3,r9 +err3; stvx v8,r0,r3 +err3; stvx v9,r3,r9 addi r3,r3,32 6: bf cr7*4+1,7f -err3; lvx vr3,r0,r4 - VPERM(vr8,vr0,vr3,vr16) -err3; lvx vr2,r4,r9 - VPERM(vr9,vr3,vr2,vr16) -err3; lvx vr1,r4,r10 - VPERM(vr10,vr2,vr1,vr16) -err3; lvx vr0,r4,r11 - VPERM(vr11,vr1,vr0,vr16) +err3; lvx v3,r0,r4 + VPERM(v8,v0,v3,v16) +err3; lvx v2,r4,r9 + VPERM(v9,v3,v2,v16) +err3; lvx v1,r4,r10 + VPERM(v10,v2,v1,v16) +err3; lvx v0,r4,r11 + VPERM(v11,v1,v0,v16) addi r4,r4,64 -err3; stvx vr8,r0,r3 -err3; stvx vr9,r3,r9 -err3; stvx vr10,r3,r10 -err3; stvx vr11,r3,r11 +err3; stvx v8,r0,r3 +err3; stvx v9,r3,r9 +err3; stvx v10,r3,r10 +err3; stvx v11,r3,r11 addi r3,r3,64 7: sub r5,r5,r6 @@ -618,31 +618,31 @@ err3; stvx vr11,r3,r11 */ .align 5 8: -err4; lvx vr7,r0,r4 - VPERM(vr8,vr0,vr7,vr16) -err4; lvx vr6,r4,r9 - VPERM(vr9,vr7,vr6,vr16) -err4; lvx vr5,r4,r10 - VPERM(vr10,vr6,vr5,vr16) -err4; lvx vr4,r4,r11 - VPERM(vr11,vr5,vr4,vr16) -err4; lvx vr3,r4,r12 - VPERM(vr12,vr4,vr3,vr16) -err4; lvx vr2,r4,r14 - VPERM(vr13,vr3,vr2,vr16) -err4; lvx vr1,r4,r15 - VPERM(vr14,vr2,vr1,vr16) -err4; lvx vr0,r4,r16 - VPERM(vr15,vr1,vr0,vr16) +err4; lvx v7,r0,r4 + VPERM(v8,v0,v7,v16) +err4; lvx v6,r4,r9 + VPERM(v9,v7,v6,v16) +err4; lvx v5,r4,r10 + VPERM(v10,v6,v5,v16) +err4; lvx v4,r4,r11 + VPERM(v11,v5,v4,v16) +err4; lvx v3,r4,r12 + VPERM(v12,v4,v3,v16) +err4; lvx v2,r4,r14 + VPERM(v13,v3,v2,v16) +err4; lvx v1,r4,r15 + VPERM(v14,v2,v1,v16) +err4; lvx v0,r4,r16 + VPERM(v15,v1,v0,v16) addi r4,r4,128 -err4; stvx vr8,r0,r3 -err4; stvx vr9,r3,r9 -err4; stvx vr10,r3,r10 -err4; stvx vr11,r3,r11 -err4; stvx vr12,r3,r12 -err4; stvx vr13,r3,r14 -err4; stvx vr14,r3,r15 -err4; stvx vr15,r3,r16 +err4; stvx v8,r0,r3 +err4; stvx v9,r3,r9 +err4; stvx v10,r3,r10 +err4; stvx v11,r3,r11 +err4; stvx v12,r3,r12 +err4; stvx v13,r3,r14 +err4; stvx v14,r3,r15 +err4; stvx v15,r3,r16 addi r3,r3,128 bdnz 8b @@ -656,36 +656,36 @@ err4; stvx vr15,r3,r16 mtocrf 0x01,r6 bf cr7*4+1,9f -err3; lvx vr3,r0,r4 - VPERM(vr8,vr0,vr3,vr16) -err3; lvx vr2,r4,r9 - VPERM(vr9,vr3,vr2,vr16) -err3; lvx vr1,r4,r10 - VPERM(vr10,vr2,vr1,vr16) -err3; lvx vr0,r4,r11 - VPERM(vr11,vr1,vr0,vr16) +err3; lvx v3,r0,r4 + VPERM(v8,v0,v3,v16) +err3; lvx v2,r4,r9 + VPERM(v9,v3,v2,v16) +err3; lvx v1,r4,r10 + VPERM(v10,v2,v1,v16) +err3; lvx v0,r4,r11 + VPERM(v11,v1,v0,v16) addi r4,r4,64 -err3; stvx vr8,r0,r3 -err3; stvx vr9,r3,r9 -err3; stvx vr10,r3,r10 -err3; stvx vr11,r3,r11 +err3; stvx v8,r0,r3 +err3; stvx v9,r3,r9 +err3; stvx v10,r3,r10 +err3; stvx v11,r3,r11 addi r3,r3,64 9: bf cr7*4+2,10f -err3; lvx vr1,r0,r4 - VPERM(vr8,vr0,vr1,vr16) -err3; lvx vr0,r4,r9 - VPERM(vr9,vr1,vr0,vr16) +err3; lvx v1,r0,r4 + VPERM(v8,v0,v1,v16) +err3; lvx v0,r4,r9 + VPERM(v9,v1,v0,v16) addi r4,r4,32 -err3; stvx vr8,r0,r3 -err3; stvx vr9,r3,r9 +err3; stvx v8,r0,r3 +err3; stvx v9,r3,r9 addi r3,r3,32 10: bf cr7*4+3,11f -err3; lvx vr1,r0,r4 - VPERM(vr8,vr0,vr1,vr16) +err3; lvx v1,r0,r4 + VPERM(v8,v0,v1,v16) addi r4,r4,16 -err3; stvx vr8,r0,r3 +err3; stvx v8,r0,r3 addi r3,r3,16 /* Up to 15B to go */ diff --git a/arch/powerpc/lib/crtsavres.S b/arch/powerpc/lib/crtsavres.S index a5b30c71a8d3..18af0b3d3eb2 100644 --- a/arch/powerpc/lib/crtsavres.S +++ b/arch/powerpc/lib/crtsavres.S @@ -236,78 +236,78 @@ _GLOBAL(_rest32gpr_31_x) _GLOBAL(_savevr_20) li r11,-192 - stvx vr20,r11,r0 + stvx v20,r11,r0 _GLOBAL(_savevr_21) li r11,-176 - stvx vr21,r11,r0 + stvx v21,r11,r0 _GLOBAL(_savevr_22) li r11,-160 - stvx vr22,r11,r0 + stvx v22,r11,r0 _GLOBAL(_savevr_23) li r11,-144 - stvx vr23,r11,r0 + stvx v23,r11,r0 _GLOBAL(_savevr_24) li r11,-128 - stvx vr24,r11,r0 + stvx v24,r11,r0 _GLOBAL(_savevr_25) li r11,-112 - stvx vr25,r11,r0 + stvx v25,r11,r0 _GLOBAL(_savevr_26) li r11,-96 - stvx vr26,r11,r0 + stvx v26,r11,r0 _GLOBAL(_savevr_27) li r11,-80 - stvx vr27,r11,r0 + stvx v27,r11,r0 _GLOBAL(_savevr_28) li r11,-64 - stvx vr28,r11,r0 + stvx v28,r11,r0 _GLOBAL(_savevr_29) li r11,-48 - stvx vr29,r11,r0 + stvx v29,r11,r0 _GLOBAL(_savevr_30) li r11,-32 - stvx vr30,r11,r0 + stvx v30,r11,r0 _GLOBAL(_savevr_31) li r11,-16 - stvx vr31,r11,r0 + stvx v31,r11,r0 blr _GLOBAL(_restvr_20) li r11,-192 - lvx vr20,r11,r0 + lvx v20,r11,r0 _GLOBAL(_restvr_21) li r11,-176 - lvx vr21,r11,r0 + lvx v21,r11,r0 _GLOBAL(_restvr_22) li r11,-160 - lvx vr22,r11,r0 + lvx v22,r11,r0 _GLOBAL(_restvr_23) li r11,-144 - lvx vr23,r11,r0 + lvx v23,r11,r0 _GLOBAL(_restvr_24) li r11,-128 - lvx vr24,r11,r0 + lvx v24,r11,r0 _GLOBAL(_restvr_25) li r11,-112 - lvx vr25,r11,r0 + lvx v25,r11,r0 _GLOBAL(_restvr_26) li r11,-96 - lvx vr26,r11,r0 + lvx v26,r11,r0 _GLOBAL(_restvr_27) li r11,-80 - lvx vr27,r11,r0 + lvx v27,r11,r0 _GLOBAL(_restvr_28) li r11,-64 - lvx vr28,r11,r0 + lvx v28,r11,r0 _GLOBAL(_restvr_29) li r11,-48 - lvx vr29,r11,r0 + lvx v29,r11,r0 _GLOBAL(_restvr_30) li r11,-32 - lvx vr30,r11,r0 + lvx v30,r11,r0 _GLOBAL(_restvr_31) li r11,-16 - lvx vr31,r11,r0 + lvx v31,r11,r0 blr #endif /* CONFIG_ALTIVEC */ @@ -443,101 +443,101 @@ _restgpr0_31: .globl _savevr_20 _savevr_20: li r12,-192 - stvx vr20,r12,r0 + stvx v20,r12,r0 .globl _savevr_21 _savevr_21: li r12,-176 - stvx vr21,r12,r0 + stvx v21,r12,r0 .globl _savevr_22 _savevr_22: li r12,-160 - stvx vr22,r12,r0 + stvx v22,r12,r0 .globl _savevr_23 _savevr_23: li r12,-144 - stvx vr23,r12,r0 + stvx v23,r12,r0 .globl _savevr_24 _savevr_24: li r12,-128 - stvx vr24,r12,r0 + stvx v24,r12,r0 .globl _savevr_25 _savevr_25: li r12,-112 - stvx vr25,r12,r0 + stvx v25,r12,r0 .globl _savevr_26 _savevr_26: li r12,-96 - stvx vr26,r12,r0 + stvx v26,r12,r0 .globl _savevr_27 _savevr_27: li r12,-80 - stvx vr27,r12,r0 + stvx v27,r12,r0 .globl _savevr_28 _savevr_28: li r12,-64 - stvx vr28,r12,r0 + stvx v28,r12,r0 .globl _savevr_29 _savevr_29: li r12,-48 - stvx vr29,r12,r0 + stvx v29,r12,r0 .globl _savevr_30 _savevr_30: li r12,-32 - stvx vr30,r12,r0 + stvx v30,r12,r0 .globl _savevr_31 _savevr_31: li r12,-16 - stvx vr31,r12,r0 + stvx v31,r12,r0 blr .globl _restvr_20 _restvr_20: li r12,-192 - lvx vr20,r12,r0 + lvx v20,r12,r0 .globl _restvr_21 _restvr_21: li r12,-176 - lvx vr21,r12,r0 + lvx v21,r12,r0 .globl _restvr_22 _restvr_22: li r12,-160 - lvx vr22,r12,r0 + lvx v22,r12,r0 .globl _restvr_23 _restvr_23: li r12,-144 - lvx vr23,r12,r0 + lvx v23,r12,r0 .globl _restvr_24 _restvr_24: li r12,-128 - lvx vr24,r12,r0 + lvx v24,r12,r0 .globl _restvr_25 _restvr_25: li r12,-112 - lvx vr25,r12,r0 + lvx v25,r12,r0 .globl _restvr_26 _restvr_26: li r12,-96 - lvx vr26,r12,r0 + lvx v26,r12,r0 .globl _restvr_27 _restvr_27: li r12,-80 - lvx vr27,r12,r0 + lvx v27,r12,r0 .globl _restvr_28 _restvr_28: li r12,-64 - lvx vr28,r12,r0 + lvx v28,r12,r0 .globl _restvr_29 _restvr_29: li r12,-48 - lvx vr29,r12,r0 + lvx v29,r12,r0 .globl _restvr_30 _restvr_30: li r12,-32 - lvx vr30,r12,r0 + lvx v30,r12,r0 .globl _restvr_31 _restvr_31: li r12,-16 - lvx vr31,r12,r0 + lvx v31,r12,r0 blr #endif /* CONFIG_ALTIVEC */ diff --git a/arch/powerpc/lib/ldstfp.S b/arch/powerpc/lib/ldstfp.S index 85aec08ab234..659c7ca1f4f2 100644 --- a/arch/powerpc/lib/ldstfp.S +++ b/arch/powerpc/lib/ldstfp.S @@ -184,16 +184,16 @@ _GLOBAL(do_stfd) extab 2b,3b #ifdef CONFIG_ALTIVEC -/* Get the contents of vrN into vr0; N is in r3. */ +/* Get the contents of vrN into v0; N is in r3. */ _GLOBAL(get_vr) mflr r0 rlwinm r3,r3,3,0xf8 bcl 20,31,1f - blr /* vr0 is already in vr0 */ + blr /* v0 is already in v0 */ nop reg = 1 .rept 31 - vor vr0,reg,reg /* assembler doesn't know vmr? */ + vor v0,reg,reg /* assembler doesn't know vmr? */ blr reg = reg + 1 .endr @@ -203,16 +203,16 @@ reg = reg + 1 mtlr r0 bctr -/* Put the contents of vr0 into vrN; N is in r3. */ +/* Put the contents of v0 into vrN; N is in r3. */ _GLOBAL(put_vr) mflr r0 rlwinm r3,r3,3,0xf8 bcl 20,31,1f - blr /* vr0 is already in vr0 */ + blr /* v0 is already in v0 */ nop reg = 1 .rept 31 - vor reg,vr0,vr0 + vor reg,v0,v0 blr reg = reg + 1 .endr @@ -234,13 +234,13 @@ _GLOBAL(do_lvx) MTMSRD(r7) isync beq cr7,1f - stvx vr0,r1,r8 + stvx v0,r1,r8 1: li r9,-EFAULT -2: lvx vr0,0,r4 +2: lvx v0,0,r4 li r9,0 3: beq cr7,4f bl put_vr - lvx vr0,r1,r8 + lvx v0,r1,r8 4: PPC_LL r0,STKFRM+PPC_LR_STKOFF(r1) mtlr r0 MTMSRD(r6) @@ -262,13 +262,13 @@ _GLOBAL(do_stvx) MTMSRD(r7) isync beq cr7,1f - stvx vr0,r1,r8 + stvx v0,r1,r8 bl get_vr 1: li r9,-EFAULT -2: stvx vr0,0,r4 +2: stvx v0,0,r4 li r9,0 3: beq cr7,4f - lvx vr0,r1,r8 + lvx v0,r1,r8 4: PPC_LL r0,STKFRM+PPC_LR_STKOFF(r1) mtlr r0 MTMSRD(r6) @@ -304,7 +304,7 @@ _GLOBAL(put_vsr) mflr r0 rlwinm r3,r3,3,0x1f8 bcl 20,31,1f - blr /* vr0 is already in vr0 */ + blr /* v0 is already in v0 */ nop reg = 1 .rept 63 diff --git a/arch/powerpc/lib/memcpy_power7.S b/arch/powerpc/lib/memcpy_power7.S index 0830587df16e..786234fd4e91 100644 --- a/arch/powerpc/lib/memcpy_power7.S +++ b/arch/powerpc/lib/memcpy_power7.S @@ -321,29 +321,29 @@ _GLOBAL(memcpy_power7) li r11,48 bf cr7*4+3,5f - lvx vr1,r0,r4 + lvx v1,r0,r4 addi r4,r4,16 - stvx vr1,r0,r3 + stvx v1,r0,r3 addi r3,r3,16 5: bf cr7*4+2,6f - lvx vr1,r0,r4 - lvx vr0,r4,r9 + lvx v1,r0,r4 + lvx v0,r4,r9 addi r4,r4,32 - stvx vr1,r0,r3 - stvx vr0,r3,r9 + stvx v1,r0,r3 + stvx v0,r3,r9 addi r3,r3,32 6: bf cr7*4+1,7f - lvx vr3,r0,r4 - lvx vr2,r4,r9 - lvx vr1,r4,r10 - lvx vr0,r4,r11 + lvx v3,r0,r4 + lvx v2,r4,r9 + lvx v1,r4,r10 + lvx v0,r4,r11 addi r4,r4,64 - stvx vr3,r0,r3 - stvx vr2,r3,r9 - stvx vr1,r3,r10 - stvx vr0,r3,r11 + stvx v3,r0,r3 + stvx v2,r3,r9 + stvx v1,r3,r10 + stvx v0,r3,r11 addi r3,r3,64 7: sub r5,r5,r6 @@ -366,23 +366,23 @@ _GLOBAL(memcpy_power7) */ .align 5 8: - lvx vr7,r0,r4 - lvx vr6,r4,r9 - lvx vr5,r4,r10 - lvx vr4,r4,r11 - lvx vr3,r4,r12 - lvx vr2,r4,r14 - lvx vr1,r4,r15 - lvx vr0,r4,r16 + lvx v7,r0,r4 + lvx v6,r4,r9 + lvx v5,r4,r10 + lvx v4,r4,r11 + lvx v3,r4,r12 + lvx v2,r4,r14 + lvx v1,r4,r15 + lvx v0,r4,r16 addi r4,r4,128 - stvx vr7,r0,r3 - stvx vr6,r3,r9 - stvx vr5,r3,r10 - stvx vr4,r3,r11 - stvx vr3,r3,r12 - stvx vr2,r3,r14 - stvx vr1,r3,r15 - stvx vr0,r3,r16 + stvx v7,r0,r3 + stvx v6,r3,r9 + stvx v5,r3,r10 + stvx v4,r3,r11 + stvx v3,r3,r12 + stvx v2,r3,r14 + stvx v1,r3,r15 + stvx v0,r3,r16 addi r3,r3,128 bdnz 8b @@ -396,29 +396,29 @@ _GLOBAL(memcpy_power7) mtocrf 0x01,r6 bf cr7*4+1,9f - lvx vr3,r0,r4 - lvx vr2,r4,r9 - lvx vr1,r4,r10 - lvx vr0,r4,r11 + lvx v3,r0,r4 + lvx v2,r4,r9 + lvx v1,r4,r10 + lvx v0,r4,r11 addi r4,r4,64 - stvx vr3,r0,r3 - stvx vr2,r3,r9 - stvx vr1,r3,r10 - stvx vr0,r3,r11 + stvx v3,r0,r3 + stvx v2,r3,r9 + stvx v1,r3,r10 + stvx v0,r3,r11 addi r3,r3,64 9: bf cr7*4+2,10f - lvx vr1,r0,r4 - lvx vr0,r4,r9 + lvx v1,r0,r4 + lvx v0,r4,r9 addi r4,r4,32 - stvx vr1,r0,r3 - stvx vr0,r3,r9 + stvx v1,r0,r3 + stvx v0,r3,r9 addi r3,r3,32 10: bf cr7*4+3,11f - lvx vr1,r0,r4 + lvx v1,r0,r4 addi r4,r4,16 - stvx vr1,r0,r3 + stvx v1,r0,r3 addi r3,r3,16 /* Up to 15B to go */ @@ -494,42 +494,42 @@ _GLOBAL(memcpy_power7) li r10,32 li r11,48 - LVS(vr16,0,r4) /* Setup permute control vector */ - lvx vr0,0,r4 + LVS(v16,0,r4) /* Setup permute control vector */ + lvx v0,0,r4 addi r4,r4,16 bf cr7*4+3,5f - lvx vr1,r0,r4 - VPERM(vr8,vr0,vr1,vr16) + lvx v1,r0,r4 + VPERM(v8,v0,v1,v16) addi r4,r4,16 - stvx vr8,r0,r3 + stvx v8,r0,r3 addi r3,r3,16 - vor vr0,vr1,vr1 + vor v0,v1,v1 5: bf cr7*4+2,6f - lvx vr1,r0,r4 - VPERM(vr8,vr0,vr1,vr16) - lvx vr0,r4,r9 - VPERM(vr9,vr1,vr0,vr16) + lvx v1,r0,r4 + VPERM(v8,v0,v1,v16) + lvx v0,r4,r9 + VPERM(v9,v1,v0,v16) addi r4,r4,32 - stvx vr8,r0,r3 - stvx vr9,r3,r9 + stvx v8,r0,r3 + stvx v9,r3,r9 addi r3,r3,32 6: bf cr7*4+1,7f - lvx vr3,r0,r4 - VPERM(vr8,vr0,vr3,vr16) - lvx vr2,r4,r9 - VPERM(vr9,vr3,vr2,vr16) - lvx vr1,r4,r10 - VPERM(vr10,vr2,vr1,vr16) - lvx vr0,r4,r11 - VPERM(vr11,vr1,vr0,vr16) + lvx v3,r0,r4 + VPERM(v8,v0,v3,v16) + lvx v2,r4,r9 + VPERM(v9,v3,v2,v16) + lvx v1,r4,r10 + VPERM(v10,v2,v1,v16) + lvx v0,r4,r11 + VPERM(v11,v1,v0,v16) addi r4,r4,64 - stvx vr8,r0,r3 - stvx vr9,r3,r9 - stvx vr10,r3,r10 - stvx vr11,r3,r11 + stvx v8,r0,r3 + stvx v9,r3,r9 + stvx v10,r3,r10 + stvx v11,r3,r11 addi r3,r3,64 7: sub r5,r5,r6 @@ -552,31 +552,31 @@ _GLOBAL(memcpy_power7) */ .align 5 8: - lvx vr7,r0,r4 - VPERM(vr8,vr0,vr7,vr16) - lvx vr6,r4,r9 - VPERM(vr9,vr7,vr6,vr16) - lvx vr5,r4,r10 - VPERM(vr10,vr6,vr5,vr16) - lvx vr4,r4,r11 - VPERM(vr11,vr5,vr4,vr16) - lvx vr3,r4,r12 - VPERM(vr12,vr4,vr3,vr16) - lvx vr2,r4,r14 - VPERM(vr13,vr3,vr2,vr16) - lvx vr1,r4,r15 - VPERM(vr14,vr2,vr1,vr16) - lvx vr0,r4,r16 - VPERM(vr15,vr1,vr0,vr16) + lvx v7,r0,r4 + VPERM(v8,v0,v7,v16) + lvx v6,r4,r9 + VPERM(v9,v7,v6,v16) + lvx v5,r4,r10 + VPERM(v10,v6,v5,v16) + lvx v4,r4,r11 + VPERM(v11,v5,v4,v16) + lvx v3,r4,r12 + VPERM(v12,v4,v3,v16) + lvx v2,r4,r14 + VPERM(v13,v3,v2,v16) + lvx v1,r4,r15 + VPERM(v14,v2,v1,v16) + lvx v0,r4,r16 + VPERM(v15,v1,v0,v16) addi r4,r4,128 - stvx vr8,r0,r3 - stvx vr9,r3,r9 - stvx vr10,r3,r10 - stvx vr11,r3,r11 - stvx vr12,r3,r12 - stvx vr13,r3,r14 - stvx vr14,r3,r15 - stvx vr15,r3,r16 + stvx v8,r0,r3 + stvx v9,r3,r9 + stvx v10,r3,r10 + stvx v11,r3,r11 + stvx v12,r3,r12 + stvx v13,r3,r14 + stvx v14,r3,r15 + stvx v15,r3,r16 addi r3,r3,128 bdnz 8b @@ -590,36 +590,36 @@ _GLOBAL(memcpy_power7) mtocrf 0x01,r6 bf cr7*4+1,9f - lvx vr3,r0,r4 - VPERM(vr8,vr0,vr3,vr16) - lvx vr2,r4,r9 - VPERM(vr9,vr3,vr2,vr16) - lvx vr1,r4,r10 - VPERM(vr10,vr2,vr1,vr16) - lvx vr0,r4,r11 - VPERM(vr11,vr1,vr0,vr16) + lvx v3,r0,r4 + VPERM(v8,v0,v3,v16) + lvx v2,r4,r9 + VPERM(v9,v3,v2,v16) + lvx v1,r4,r10 + VPERM(v10,v2,v1,v16) + lvx v0,r4,r11 + VPERM(v11,v1,v0,v16) addi r4,r4,64 - stvx vr8,r0,r3 - stvx vr9,r3,r9 - stvx vr10,r3,r10 - stvx vr11,r3,r11 + stvx v8,r0,r3 + stvx v9,r3,r9 + stvx v10,r3,r10 + stvx v11,r3,r11 addi r3,r3,64 9: bf cr7*4+2,10f - lvx vr1,r0,r4 - VPERM(vr8,vr0,vr1,vr16) - lvx vr0,r4,r9 - VPERM(vr9,vr1,vr0,vr16) + lvx v1,r0,r4 + VPERM(v8,v0,v1,v16) + lvx v0,r4,r9 + VPERM(v9,v1,v0,v16) addi r4,r4,32 - stvx vr8,r0,r3 - stvx vr9,r3,r9 + stvx v8,r0,r3 + stvx v9,r3,r9 addi r3,r3,32 10: bf cr7*4+3,11f - lvx vr1,r0,r4 - VPERM(vr8,vr0,vr1,vr16) + lvx v1,r0,r4 + VPERM(v8,v0,v1,v16) addi r4,r4,16 - stvx vr8,r0,r3 + stvx v8,r0,r3 addi r3,r3,16 /* Up to 15B to go */ diff --git a/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h b/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h index d1dc37425510..50ae7d2091ce 100644 --- a/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h +++ b/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h @@ -4,39 +4,6 @@ #define r1 1 -#define vr0 0 -#define vr1 1 -#define vr2 2 -#define vr3 3 -#define vr4 4 -#define vr5 5 -#define vr6 6 -#define vr7 7 -#define vr8 8 -#define vr9 9 -#define vr10 10 -#define vr11 11 -#define vr12 12 -#define vr13 13 -#define vr14 14 -#define vr15 15 -#define vr16 16 -#define vr17 17 -#define vr18 18 -#define vr19 19 -#define vr20 20 -#define vr21 21 -#define vr22 22 -#define vr23 23 -#define vr24 24 -#define vr25 25 -#define vr26 26 -#define vr27 27 -#define vr28 28 -#define vr29 29 -#define vr30 30 -#define vr31 31 - #define R14 r14 #define R15 r15 #define R16 r16 -- cgit v1.2.3 From 45706bb53d118b5340a12926e26444d73b6491f9 Mon Sep 17 00:00:00 2001 From: Mahesh Salgaonkar Date: Fri, 19 Dec 2014 08:41:05 +0530 Subject: powerpc/book3s: Fix flush_tlb cpu_spec hook to take a generic argument. The flush_tlb hook in cpu_spec was introduced as a generic function hook to invalidate TLBs. But the current implementation of flush_tlb hook takes IS (invalidation selector) as an argument which is architecture dependent. Hence, It is not right to have a generic routine where caller has to pass non-generic argument. This patch fixes this and makes flush_tlb hook as high level API. Reported-by: Benjamin Herrenschmidt Signed-off-by: Mahesh Salgaonkar Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/cputable.h | 8 +++++- arch/powerpc/include/asm/mmu-hash64.h | 1 + arch/powerpc/kernel/cpu_setup_power.S | 10 ++----- arch/powerpc/kernel/cputable.c | 4 +-- arch/powerpc/kernel/mce_power.c | 53 +++++++++++++++++++++++++++++++++-- arch/powerpc/kvm/book3s_hv_ras.c | 4 +-- 6 files changed, 65 insertions(+), 15 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index 5cf5a6d10685..6367b8347dad 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -100,7 +100,7 @@ struct cpu_spec { /* * Processor specific routine to flush tlbs. */ - void (*flush_tlb)(unsigned long inval_selector); + void (*flush_tlb)(unsigned int action); }; @@ -114,6 +114,12 @@ extern void do_feature_fixups(unsigned long value, void *fixup_start, extern const char *powerpc_base_platform; +/* TLB flush actions. Used as argument to cpu_spec.flush_tlb() hook */ +enum { + TLB_INVAL_SCOPE_GLOBAL = 0, /* invalidate all TLBs */ + TLB_INVAL_SCOPE_LPID = 1, /* invalidate TLBs for current LPID */ +}; + #endif /* __ASSEMBLY__ */ /* CPU kernel features */ diff --git a/arch/powerpc/include/asm/mmu-hash64.h b/arch/powerpc/include/asm/mmu-hash64.h index 4f13c3ed7acf..1da6a81ce541 100644 --- a/arch/powerpc/include/asm/mmu-hash64.h +++ b/arch/powerpc/include/asm/mmu-hash64.h @@ -112,6 +112,7 @@ #define TLBIEL_INVAL_SET_SHIFT 12 #define POWER7_TLB_SETS 128 /* # sets in POWER7 TLB */ +#define POWER8_TLB_SETS 512 /* # sets in POWER8 TLB */ #ifndef __ASSEMBLY__ diff --git a/arch/powerpc/kernel/cpu_setup_power.S b/arch/powerpc/kernel/cpu_setup_power.S index 46733535cc0b..9c9b7411b28b 100644 --- a/arch/powerpc/kernel/cpu_setup_power.S +++ b/arch/powerpc/kernel/cpu_setup_power.S @@ -137,15 +137,11 @@ __init_HFSCR: /* * Clear the TLB using the specified IS form of tlbiel instruction * (invalidate by congruence class). P7 has 128 CCs., P8 has 512. - * - * r3 = IS field */ __init_tlb_power7: - li r3,0xc00 /* IS field = 0b11 */ -_GLOBAL(__flush_tlb_power7) li r6,128 mtctr r6 - mr r7,r3 /* IS field */ + li r7,0xc00 /* IS field = 0b11 */ ptesync 2: tlbiel r7 addi r7,r7,0x1000 @@ -154,11 +150,9 @@ _GLOBAL(__flush_tlb_power7) 1: blr __init_tlb_power8: - li r3,0xc00 /* IS field = 0b11 */ -_GLOBAL(__flush_tlb_power8) li r6,512 mtctr r6 - mr r7,r3 /* IS field */ + li r7,0xc00 /* IS field = 0b11 */ ptesync 2: tlbiel r7 addi r7,r7,0x1000 diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index f337666768a7..7ed126bc9b18 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -71,8 +71,8 @@ extern void __restore_cpu_power7(void); extern void __setup_cpu_power8(unsigned long offset, struct cpu_spec* spec); extern void __restore_cpu_power8(void); extern void __restore_cpu_a2(void); -extern void __flush_tlb_power7(unsigned long inval_selector); -extern void __flush_tlb_power8(unsigned long inval_selector); +extern void __flush_tlb_power7(unsigned int action); +extern void __flush_tlb_power8(unsigned int action); extern long __machine_check_early_realmode_p7(struct pt_regs *regs); extern long __machine_check_early_realmode_p8(struct pt_regs *regs); #endif /* CONFIG_PPC64 */ diff --git a/arch/powerpc/kernel/mce_power.c b/arch/powerpc/kernel/mce_power.c index b6f123ab90ed..2c647b1e62e4 100644 --- a/arch/powerpc/kernel/mce_power.c +++ b/arch/powerpc/kernel/mce_power.c @@ -28,6 +28,55 @@ #include #include +static void flush_tlb_206(unsigned int num_sets, unsigned int action) +{ + unsigned long rb; + unsigned int i; + + switch (action) { + case TLB_INVAL_SCOPE_GLOBAL: + rb = TLBIEL_INVAL_SET; + break; + case TLB_INVAL_SCOPE_LPID: + rb = TLBIEL_INVAL_SET_LPID; + break; + default: + BUG(); + break; + } + + asm volatile("ptesync" : : : "memory"); + for (i = 0; i < num_sets; i++) { + asm volatile("tlbiel %0" : : "r" (rb)); + rb += 1 << TLBIEL_INVAL_SET_SHIFT; + } + asm volatile("ptesync" : : : "memory"); +} + +/* + * Generic routine to flush TLB on power7. This routine is used as + * flush_tlb hook in cpu_spec for Power7 processor. + * + * action => TLB_INVAL_SCOPE_GLOBAL: Invalidate all TLBs. + * TLB_INVAL_SCOPE_LPID: Invalidate TLB for current LPID. + */ +void __flush_tlb_power7(unsigned int action) +{ + flush_tlb_206(POWER7_TLB_SETS, action); +} + +/* + * Generic routine to flush TLB on power8. This routine is used as + * flush_tlb hook in cpu_spec for power8 processor. + * + * action => TLB_INVAL_SCOPE_GLOBAL: Invalidate all TLBs. + * TLB_INVAL_SCOPE_LPID: Invalidate TLB for current LPID. + */ +void __flush_tlb_power8(unsigned int action) +{ + flush_tlb_206(POWER8_TLB_SETS, action); +} + /* flush SLBs and reload */ static void flush_and_reload_slb(void) { @@ -79,7 +128,7 @@ static long mce_handle_derror(uint64_t dsisr, uint64_t slb_error_bits) } if (dsisr & P7_DSISR_MC_TLB_MULTIHIT_MFTLB) { if (cur_cpu_spec && cur_cpu_spec->flush_tlb) - cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET); + cur_cpu_spec->flush_tlb(TLB_INVAL_SCOPE_GLOBAL); /* reset error bits */ dsisr &= ~P7_DSISR_MC_TLB_MULTIHIT_MFTLB; } @@ -110,7 +159,7 @@ static long mce_handle_common_ierror(uint64_t srr1) break; case P7_SRR1_MC_IFETCH_TLB_MULTIHIT: if (cur_cpu_spec && cur_cpu_spec->flush_tlb) { - cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET); + cur_cpu_spec->flush_tlb(TLB_INVAL_SCOPE_GLOBAL); handled = 1; } break; diff --git a/arch/powerpc/kvm/book3s_hv_ras.c b/arch/powerpc/kvm/book3s_hv_ras.c index 60081bd75847..93b5f5c9b445 100644 --- a/arch/powerpc/kvm/book3s_hv_ras.c +++ b/arch/powerpc/kvm/book3s_hv_ras.c @@ -84,7 +84,7 @@ static long kvmppc_realmode_mc_power7(struct kvm_vcpu *vcpu) } if (dsisr & DSISR_MC_TLB_MULTI) { if (cur_cpu_spec && cur_cpu_spec->flush_tlb) - cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET_LPID); + cur_cpu_spec->flush_tlb(TLB_INVAL_SCOPE_LPID); dsisr &= ~DSISR_MC_TLB_MULTI; } /* Any other errors we don't understand? */ @@ -102,7 +102,7 @@ static long kvmppc_realmode_mc_power7(struct kvm_vcpu *vcpu) break; case SRR1_MC_IFETCH_TLBMULTI: if (cur_cpu_spec && cur_cpu_spec->flush_tlb) - cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET_LPID); + cur_cpu_spec->flush_tlb(TLB_INVAL_SCOPE_LPID); break; default: handled = 0; -- cgit v1.2.3 From 28158cd1b75180343efa7c4d7d2f8e74ccc63b8f Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Wed, 11 Feb 2015 10:20:49 +1100 Subject: powerpc/eeh: Enhance pcibios_set_pcie_reset_state() Function pcibios_set_pcie_reset_state() is possibly called by pci_reset_function(), on which VFIO infrastructure depends to issue reset. pcibios_set_pcie_reset_state() is issuing reset on the parent PE of the indicated PCI device. The reset causes state lost on all PCI devices except the indicated one as the argument to pcibios_set_pcie_reset_state(). Also, sideband MMIO access from guest when issuing reset would cause unexpected EEH error. For above two issues, the patch applies following enhancements to pcibios_set_pcie_reset_state(): * For all PCI devices except the indicated one, save their state prior to reset and restore state after that. * Explicitly freeze PE prior to reset and unfreeze it after that, in order to avoid unexpected EEH error. Tested-by: Priya M. A Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/eeh.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 3b2252e7731b..19a897c810be 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -667,6 +667,55 @@ int eeh_pci_enable(struct eeh_pe *pe, int function) return rc; } +static void *eeh_disable_and_save_dev_state(void *data, void *userdata) +{ + struct eeh_dev *edev = data; + struct pci_dev *pdev = eeh_dev_to_pci_dev(edev); + struct pci_dev *dev = userdata; + + /* + * The caller should have disabled and saved the + * state for the specified device + */ + if (!pdev || pdev == dev) + return NULL; + + /* Ensure we have D0 power state */ + pci_set_power_state(pdev, PCI_D0); + + /* Save device state */ + pci_save_state(pdev); + + /* + * Disable device to avoid any DMA traffic and + * interrupt from the device + */ + pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE); + + return NULL; +} + +static void *eeh_restore_dev_state(void *data, void *userdata) +{ + struct eeh_dev *edev = data; + struct device_node *dn = eeh_dev_to_of_node(edev); + struct pci_dev *pdev = eeh_dev_to_pci_dev(edev); + struct pci_dev *dev = userdata; + + if (!pdev) + return NULL; + + /* Apply customization from firmware */ + if (dn && eeh_ops->restore_config) + eeh_ops->restore_config(dn); + + /* The caller should restore state for the specified device */ + if (pdev != dev) + pci_save_state(pdev); + + return NULL; +} + /** * pcibios_set_pcie_slot_reset - Set PCI-E reset state * @dev: pci device struct @@ -689,13 +738,19 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat switch (state) { case pcie_deassert_reset: eeh_ops->reset(pe, EEH_RESET_DEACTIVATE); + eeh_unfreeze_pe(pe, false); eeh_pe_state_clear(pe, EEH_PE_CFG_BLOCKED); + eeh_pe_dev_traverse(pe, eeh_restore_dev_state, dev); break; case pcie_hot_reset: + eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE); + eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev); eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED); eeh_ops->reset(pe, EEH_RESET_HOT); break; case pcie_warm_reset: + eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE); + eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev); eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED); eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL); break; -- cgit v1.2.3 From 52d99627003278bcd1a40ea45136a8148d351531 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Thu, 12 Mar 2015 20:32:50 +0800 Subject: powerpc: kill PPC_OF We have set CONFIG_PPC_OF to always 'y' in commit 0a498d96a332 ("powerpc: set CONFIG_PPC_OF=y always for ARCH=powerpc") nine years ago. And the arch/ppc also has gone away for many years. The OF functionality was also moved to a common place and be used by many archs. So it does make no sense to keep such a option in the current kernel. Just kill it. Signed-off-by: Kevin Hao Acked-by: Benjamin Herrenschmidt Signed-off-by: Michael Ellerman --- arch/powerpc/Kconfig | 3 --- arch/powerpc/Kconfig.debug | 2 +- arch/powerpc/kernel/Makefile | 4 ++-- 3 files changed, 3 insertions(+), 6 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 22b0940494bb..c102668b4225 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -189,9 +189,6 @@ config ARCH_MAY_HAVE_PC_FDC bool default PCI -config PPC_OF - def_bool y - config PPC_UDBG_16550 bool default n diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index ec2e40f2cc11..bfd823abff93 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -117,7 +117,7 @@ config BDI_SWITCH config BOOTX_TEXT bool "Support for early boot text console (BootX or OpenFirmware only)" - depends on PPC_OF && PPC_BOOK3S + depends on PPC_BOOK3S help Say Y here to see progress messages from the boot firmware in text mode. Requires either BootX or Open Firmware. diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 502cf69b6c89..c1ebbdaac28f 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -33,7 +33,8 @@ obj-y := cputable.o ptrace.o syscalls.o \ signal.o sysfs.o cacheinfo.o time.o \ prom.o traps.o setup-common.o \ udbg.o misc.o io.o dma.o \ - misc_$(CONFIG_WORD_SIZE).o vdso32/ + misc_$(CONFIG_WORD_SIZE).o vdso32/ \ + of_platform.o prom_parse.o obj-$(CONFIG_PPC64) += setup_64.o sys_ppc32.o \ signal_64.o ptrace32.o \ paca.o nvram_64.o firmware.o @@ -47,7 +48,6 @@ obj-$(CONFIG_PPC64) += vdso64/ obj-$(CONFIG_ALTIVEC) += vecemu.o obj-$(CONFIG_PPC_970_NAP) += idle_power4.o obj-$(CONFIG_PPC_P7_NAP) += idle_power7.o -obj-$(CONFIG_PPC_OF) += of_platform.o prom_parse.o procfs-y := proc_powerpc.o obj-$(CONFIG_PROC_FS) += $(procfs-y) rtaspci-$(CONFIG_PPC64)-$(CONFIG_PCI) := rtas_pci.o -- cgit v1.2.3 From 6eca8933d3ff17bff39d5f10a2a22366d8622fa6 Mon Sep 17 00:00:00 2001 From: Alex Dowad Date: Fri, 13 Mar 2015 20:14:46 +0200 Subject: powerpc/kernel: Rename copy_thread() 'arg' argument to 'kthread_arg' The 'arg' argument to copy_thread() is only ever used when forking a new kernel thread. Hence, rename it to 'kthread_arg' for clarity. Signed-off-by: Alex Dowad Signed-off-by: Michael Ellerman --- arch/powerpc/kernel/process.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index b4cc7bef6b16..febb50dd5328 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -1114,8 +1114,11 @@ static void setup_ksp_vsid(struct task_struct *p, unsigned long sp) */ extern unsigned long dscr_default; /* defined in arch/powerpc/kernel/sysfs.c */ +/* + * Copy architecture-specific thread state + */ int copy_thread(unsigned long clone_flags, unsigned long usp, - unsigned long arg, struct task_struct *p) + unsigned long kthread_arg, struct task_struct *p) { struct pt_regs *childregs, *kregs; extern void ret_from_fork(void); @@ -1127,6 +1130,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, sp -= sizeof(struct pt_regs); childregs = (struct pt_regs *) sp; if (unlikely(p->flags & PF_KTHREAD)) { + /* kernel thread */ struct thread_info *ti = (void *)task_stack_page(p); memset(childregs, 0, sizeof(struct pt_regs)); childregs->gpr[1] = sp + sizeof(struct pt_regs); @@ -1137,11 +1141,12 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, clear_tsk_thread_flag(p, TIF_32BIT); childregs->softe = 1; #endif - childregs->gpr[15] = arg; + childregs->gpr[15] = kthread_arg; p->thread.regs = NULL; /* no user register state */ ti->flags |= _TIF_RESTOREALL; f = ret_from_kernel_thread; } else { + /* user thread */ struct pt_regs *regs = current_pt_regs(); CHECK_FULL_REGS(regs); *childregs = *regs; -- cgit v1.2.3 From 78989f0a5592182a3d45d869ddaafc71c8f673af Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Fri, 6 Feb 2015 01:06:04 +0530 Subject: powerpc/nvram: Move generic code for nvram and pstore With minor checks, we can move most of the code for nvram under pseries to a common place to be re-used by other powerpc platforms like powernv. This patch moves such common code to arch/powerpc/kernel/nvram_64.c file. Signed-off-by: Hari Bathini [mpe: Move select of ZLIB_DEFLATE to PPC64 to fix the build] Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/nvram.h | 50 ++- arch/powerpc/include/asm/rtas.h | 4 + arch/powerpc/kernel/nvram_64.c | 656 ++++++++++++++++++++++++++++++++ arch/powerpc/platforms/Kconfig.cputype | 1 + arch/powerpc/platforms/pseries/Kconfig | 1 - arch/powerpc/platforms/pseries/nvram.c | 666 +-------------------------------- 6 files changed, 715 insertions(+), 663 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/nvram.h b/arch/powerpc/include/asm/nvram.h index b0fe0fe4e626..09a518bb7c03 100644 --- a/arch/powerpc/include/asm/nvram.h +++ b/arch/powerpc/include/asm/nvram.h @@ -9,12 +9,43 @@ #ifndef _ASM_POWERPC_NVRAM_H #define _ASM_POWERPC_NVRAM_H - +#include #include #include #include +/* + * Set oops header version to distinguish between old and new format header. + * lnx,oops-log partition max size is 4000, header version > 4000 will + * help in identifying new header. + */ +#define OOPS_HDR_VERSION 5000 + +struct err_log_info { + __be32 error_type; + __be32 seq_num; +}; + +struct nvram_os_partition { + const char *name; + int req_size; /* desired size, in bytes */ + int min_size; /* minimum acceptable size (0 means req_size) */ + long size; /* size of data portion (excluding err_log_info) */ + long index; /* offset of data portion of partition */ + bool os_partition; /* partition initialized by OS, not FW */ +}; + +struct oops_log_info { + __be16 version; + __be16 report_length; + __be64 timestamp; +} __attribute__((packed)); + +extern struct nvram_os_partition oops_log_partition; + #ifdef CONFIG_PPC_PSERIES +extern struct nvram_os_partition rtas_log_partition; + extern int nvram_write_error_log(char * buff, int length, unsigned int err_type, unsigned int err_seq); extern int nvram_read_error_log(char * buff, int length, @@ -50,6 +81,23 @@ extern void pmac_xpram_write(int xpaddr, u8 data); /* Synchronize NVRAM */ extern void nvram_sync(void); +/* Initialize NVRAM OS partition */ +extern int __init nvram_init_os_partition(struct nvram_os_partition *part); + +/* Initialize NVRAM oops partition */ +extern void __init nvram_init_oops_partition(int rtas_partition_exists); + +/* Read a NVRAM partition */ +extern int nvram_read_partition(struct nvram_os_partition *part, char *buff, + int length, unsigned int *err_type, + unsigned int *error_log_cnt); + +/* Write to NVRAM OS partition */ +extern int nvram_write_os_partition(struct nvram_os_partition *part, + char *buff, int length, + unsigned int err_type, + unsigned int error_log_cnt); + /* Determine NVRAM size */ extern ssize_t nvram_get_size(void); diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h index 2e23e92a4372..d1bd001566df 100644 --- a/arch/powerpc/include/asm/rtas.h +++ b/arch/powerpc/include/asm/rtas.h @@ -343,8 +343,12 @@ extern int early_init_dt_scan_rtas(unsigned long node, extern void pSeries_log_error(char *buf, unsigned int err_type, int fatal); #ifdef CONFIG_PPC_PSERIES +extern unsigned long last_rtas_event; +extern int clobbering_unread_rtas_event(void); extern int pseries_devicetree_update(s32 scope); extern void post_mobility_fixup(void); +#else +static inline int clobbering_unread_rtas_event(void) { return 0; } #endif #ifdef CONFIG_PPC_RTAS_DAEMON diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c index 34f7c9b7cd96..42e5c6a9c214 100644 --- a/arch/powerpc/kernel/nvram_64.c +++ b/arch/powerpc/kernel/nvram_64.c @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -54,6 +57,659 @@ struct nvram_partition { static LIST_HEAD(nvram_partitions); +#ifdef CONFIG_PPC_PSERIES +struct nvram_os_partition rtas_log_partition = { + .name = "ibm,rtas-log", + .req_size = 2079, + .min_size = 1055, + .index = -1, + .os_partition = true +}; +#endif + +struct nvram_os_partition oops_log_partition = { + .name = "lnx,oops-log", + .req_size = 4000, + .min_size = 2000, + .index = -1, + .os_partition = true +}; + +static const char *nvram_os_partitions[] = { +#ifdef CONFIG_PPC_PSERIES + "ibm,rtas-log", +#endif + "lnx,oops-log", + NULL +}; + +static void oops_to_nvram(struct kmsg_dumper *dumper, + enum kmsg_dump_reason reason); + +static struct kmsg_dumper nvram_kmsg_dumper = { + .dump = oops_to_nvram +}; + +/* + * For capturing and compressing an oops or panic report... + + * big_oops_buf[] holds the uncompressed text we're capturing. + * + * oops_buf[] holds the compressed text, preceded by a oops header. + * oops header has u16 holding the version of oops header (to differentiate + * between old and new format header) followed by u16 holding the length of + * the compressed* text (*Or uncompressed, if compression fails.) and u64 + * holding the timestamp. oops_buf[] gets written to NVRAM. + * + * oops_log_info points to the header. oops_data points to the compressed text. + * + * +- oops_buf + * | +- oops_data + * v v + * +-----------+-----------+-----------+------------------------+ + * | version | length | timestamp | text | + * | (2 bytes) | (2 bytes) | (8 bytes) | (oops_data_sz bytes) | + * +-----------+-----------+-----------+------------------------+ + * ^ + * +- oops_log_info + * + * We preallocate these buffers during init to avoid kmalloc during oops/panic. + */ +static size_t big_oops_buf_sz; +static char *big_oops_buf, *oops_buf; +static char *oops_data; +static size_t oops_data_sz; + +/* Compression parameters */ +#define COMPR_LEVEL 6 +#define WINDOW_BITS 12 +#define MEM_LEVEL 4 +static struct z_stream_s stream; + +#ifdef CONFIG_PSTORE +#ifdef CONFIG_PPC_PSERIES +static struct nvram_os_partition of_config_partition = { + .name = "of-config", + .index = -1, + .os_partition = false +}; +#endif + +static struct nvram_os_partition common_partition = { + .name = "common", + .index = -1, + .os_partition = false +}; + +static enum pstore_type_id nvram_type_ids[] = { + PSTORE_TYPE_DMESG, + PSTORE_TYPE_PPC_COMMON, + -1, + -1, + -1 +}; +static int read_type; +#endif + +/* nvram_write_os_partition + * + * We need to buffer the error logs into nvram to ensure that we have + * the failure information to decode. If we have a severe error there + * is no way to guarantee that the OS or the machine is in a state to + * get back to user land and write the error to disk. For example if + * the SCSI device driver causes a Machine Check by writing to a bad + * IO address, there is no way of guaranteeing that the device driver + * is in any state that is would also be able to write the error data + * captured to disk, thus we buffer it in NVRAM for analysis on the + * next boot. + * + * In NVRAM the partition containing the error log buffer will looks like: + * Header (in bytes): + * +-----------+----------+--------+------------+------------------+ + * | signature | checksum | length | name | data | + * |0 |1 |2 3|4 15|16 length-1| + * +-----------+----------+--------+------------+------------------+ + * + * The 'data' section would look like (in bytes): + * +--------------+------------+-----------------------------------+ + * | event_logged | sequence # | error log | + * |0 3|4 7|8 error_log_size-1| + * +--------------+------------+-----------------------------------+ + * + * event_logged: 0 if event has not been logged to syslog, 1 if it has + * sequence #: The unique sequence # for each event. (until it wraps) + * error log: The error log from event_scan + */ +int nvram_write_os_partition(struct nvram_os_partition *part, + char *buff, int length, + unsigned int err_type, + unsigned int error_log_cnt) +{ + int rc; + loff_t tmp_index; + struct err_log_info info; + + if (part->index == -1) + return -ESPIPE; + + if (length > part->size) + length = part->size; + + info.error_type = cpu_to_be32(err_type); + info.seq_num = cpu_to_be32(error_log_cnt); + + tmp_index = part->index; + + rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), + &tmp_index); + if (rc <= 0) { + pr_err("%s: Failed nvram_write (%d)\n", __func__, rc); + return rc; + } + + rc = ppc_md.nvram_write(buff, length, &tmp_index); + if (rc <= 0) { + pr_err("%s: Failed nvram_write (%d)\n", __func__, rc); + return rc; + } + + return 0; +} + +/* nvram_read_partition + * + * Reads nvram partition for at most 'length' + */ +int nvram_read_partition(struct nvram_os_partition *part, char *buff, + int length, unsigned int *err_type, + unsigned int *error_log_cnt) +{ + int rc; + loff_t tmp_index; + struct err_log_info info; + + if (part->index == -1) + return -1; + + if (length > part->size) + length = part->size; + + tmp_index = part->index; + + if (part->os_partition) { + rc = ppc_md.nvram_read((char *)&info, + sizeof(struct err_log_info), + &tmp_index); + if (rc <= 0) { + pr_err("%s: Failed nvram_read (%d)\n", __func__, rc); + return rc; + } + } + + rc = ppc_md.nvram_read(buff, length, &tmp_index); + if (rc <= 0) { + pr_err("%s: Failed nvram_read (%d)\n", __func__, rc); + return rc; + } + + if (part->os_partition) { + *error_log_cnt = be32_to_cpu(info.seq_num); + *err_type = be32_to_cpu(info.error_type); + } + + return 0; +} + +/* nvram_init_os_partition + * + * This sets up a partition with an "OS" signature. + * + * The general strategy is the following: + * 1.) If a partition with the indicated name already exists... + * - If it's large enough, use it. + * - Otherwise, recycle it and keep going. + * 2.) Search for a free partition that is large enough. + * 3.) If there's not a free partition large enough, recycle any obsolete + * OS partitions and try again. + * 4.) Will first try getting a chunk that will satisfy the requested size. + * 5.) If a chunk of the requested size cannot be allocated, then try finding + * a chunk that will satisfy the minum needed. + * + * Returns 0 on success, else -1. + */ +int __init nvram_init_os_partition(struct nvram_os_partition *part) +{ + loff_t p; + int size; + + /* Look for ours */ + p = nvram_find_partition(part->name, NVRAM_SIG_OS, &size); + + /* Found one but too small, remove it */ + if (p && size < part->min_size) { + pr_info("nvram: Found too small %s partition," + " removing it...\n", part->name); + nvram_remove_partition(part->name, NVRAM_SIG_OS, NULL); + p = 0; + } + + /* Create one if we didn't find */ + if (!p) { + p = nvram_create_partition(part->name, NVRAM_SIG_OS, + part->req_size, part->min_size); + if (p == -ENOSPC) { + pr_info("nvram: No room to create %s partition, " + "deleting any obsolete OS partitions...\n", + part->name); + nvram_remove_partition(NULL, NVRAM_SIG_OS, + nvram_os_partitions); + p = nvram_create_partition(part->name, NVRAM_SIG_OS, + part->req_size, part->min_size); + } + } + + if (p <= 0) { + pr_err("nvram: Failed to find or create %s" + " partition, err %d\n", part->name, (int)p); + return -1; + } + + part->index = p; + part->size = nvram_get_partition_size(p) - sizeof(struct err_log_info); + + return 0; +} + +/* Derived from logfs_compress() */ +static int nvram_compress(const void *in, void *out, size_t inlen, + size_t outlen) +{ + int err, ret; + + ret = -EIO; + err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS, + MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (err != Z_OK) + goto error; + + stream.next_in = in; + stream.avail_in = inlen; + stream.total_in = 0; + stream.next_out = out; + stream.avail_out = outlen; + stream.total_out = 0; + + err = zlib_deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) + goto error; + + err = zlib_deflateEnd(&stream); + if (err != Z_OK) + goto error; + + if (stream.total_out >= stream.total_in) + goto error; + + ret = stream.total_out; +error: + return ret; +} + +/* Compress the text from big_oops_buf into oops_buf. */ +static int zip_oops(size_t text_len) +{ + struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; + int zipped_len = nvram_compress(big_oops_buf, oops_data, text_len, + oops_data_sz); + if (zipped_len < 0) { + pr_err("nvram: compression failed; returned %d\n", zipped_len); + pr_err("nvram: logging uncompressed oops/panic report\n"); + return -1; + } + oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION); + oops_hdr->report_length = cpu_to_be16(zipped_len); + oops_hdr->timestamp = cpu_to_be64(get_seconds()); + return 0; +} + +#ifdef CONFIG_PSTORE +static int nvram_pstore_open(struct pstore_info *psi) +{ + /* Reset the iterator to start reading partitions again */ + read_type = -1; + return 0; +} + +/** + * nvram_pstore_write - pstore write callback for nvram + * @type: Type of message logged + * @reason: reason behind dump (oops/panic) + * @id: identifier to indicate the write performed + * @part: pstore writes data to registered buffer in parts, + * part number will indicate the same. + * @count: Indicates oops count + * @compressed: Flag to indicate the log is compressed + * @size: number of bytes written to the registered buffer + * @psi: registered pstore_info structure + * + * Called by pstore_dump() when an oops or panic report is logged in the + * printk buffer. + * Returns 0 on successful write. + */ +static int nvram_pstore_write(enum pstore_type_id type, + enum kmsg_dump_reason reason, + u64 *id, unsigned int part, int count, + bool compressed, size_t size, + struct pstore_info *psi) +{ + int rc; + unsigned int err_type = ERR_TYPE_KERNEL_PANIC; + struct oops_log_info *oops_hdr = (struct oops_log_info *) oops_buf; + + /* part 1 has the recent messages from printk buffer */ + if (part > 1 || (type != PSTORE_TYPE_DMESG)) + return -1; + + if (clobbering_unread_rtas_event()) + return -1; + + oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION); + oops_hdr->report_length = cpu_to_be16(size); + oops_hdr->timestamp = cpu_to_be64(get_seconds()); + + if (compressed) + err_type = ERR_TYPE_KERNEL_PANIC_GZ; + + rc = nvram_write_os_partition(&oops_log_partition, oops_buf, + (int) (sizeof(*oops_hdr) + size), err_type, count); + + if (rc != 0) + return rc; + + *id = part; + return 0; +} + +/* + * Reads the oops/panic report, rtas, of-config and common partition. + * Returns the length of the data we read from each partition. + * Returns 0 if we've been called before. + */ +static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, + int *count, struct timespec *time, char **buf, + bool *compressed, struct pstore_info *psi) +{ + struct oops_log_info *oops_hdr; + unsigned int err_type, id_no, size = 0; + struct nvram_os_partition *part = NULL; + char *buff = NULL; + int sig = 0; + loff_t p; + + read_type++; + + switch (nvram_type_ids[read_type]) { + case PSTORE_TYPE_DMESG: + part = &oops_log_partition; + *type = PSTORE_TYPE_DMESG; + break; + case PSTORE_TYPE_PPC_COMMON: + sig = NVRAM_SIG_SYS; + part = &common_partition; + *type = PSTORE_TYPE_PPC_COMMON; + *id = PSTORE_TYPE_PPC_COMMON; + time->tv_sec = 0; + time->tv_nsec = 0; + break; +#ifdef CONFIG_PPC_PSERIES + case PSTORE_TYPE_PPC_RTAS: + part = &rtas_log_partition; + *type = PSTORE_TYPE_PPC_RTAS; + time->tv_sec = last_rtas_event; + time->tv_nsec = 0; + break; + case PSTORE_TYPE_PPC_OF: + sig = NVRAM_SIG_OF; + part = &of_config_partition; + *type = PSTORE_TYPE_PPC_OF; + *id = PSTORE_TYPE_PPC_OF; + time->tv_sec = 0; + time->tv_nsec = 0; + break; +#endif + default: + return 0; + } + + if (!part->os_partition) { + p = nvram_find_partition(part->name, sig, &size); + if (p <= 0) { + pr_err("nvram: Failed to find partition %s, " + "err %d\n", part->name, (int)p); + return 0; + } + part->index = p; + part->size = size; + } + + buff = kmalloc(part->size, GFP_KERNEL); + + if (!buff) + return -ENOMEM; + + if (nvram_read_partition(part, buff, part->size, &err_type, &id_no)) { + kfree(buff); + return 0; + } + + *count = 0; + + if (part->os_partition) + *id = id_no; + + if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) { + size_t length, hdr_size; + + oops_hdr = (struct oops_log_info *)buff; + if (be16_to_cpu(oops_hdr->version) < OOPS_HDR_VERSION) { + /* Old format oops header had 2-byte record size */ + hdr_size = sizeof(u16); + length = be16_to_cpu(oops_hdr->version); + time->tv_sec = 0; + time->tv_nsec = 0; + } else { + hdr_size = sizeof(*oops_hdr); + length = be16_to_cpu(oops_hdr->report_length); + time->tv_sec = be64_to_cpu(oops_hdr->timestamp); + time->tv_nsec = 0; + } + *buf = kmalloc(length, GFP_KERNEL); + if (*buf == NULL) + return -ENOMEM; + memcpy(*buf, buff + hdr_size, length); + kfree(buff); + + if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) + *compressed = true; + else + *compressed = false; + return length; + } + + *buf = buff; + return part->size; +} + +static struct pstore_info nvram_pstore_info = { + .owner = THIS_MODULE, + .name = "nvram", + .open = nvram_pstore_open, + .read = nvram_pstore_read, + .write = nvram_pstore_write, +}; + +static int nvram_pstore_init(void) +{ + int rc = 0; + + nvram_type_ids[2] = PSTORE_TYPE_PPC_RTAS; + nvram_type_ids[3] = PSTORE_TYPE_PPC_OF; + + nvram_pstore_info.buf = oops_data; + nvram_pstore_info.bufsize = oops_data_sz; + + spin_lock_init(&nvram_pstore_info.buf_lock); + + rc = pstore_register(&nvram_pstore_info); + if (rc != 0) + pr_err("nvram: pstore_register() failed, defaults to " + "kmsg_dump; returned %d\n", rc); + + return rc; +} +#else +static int nvram_pstore_init(void) +{ + return -1; +} +#endif + +void __init nvram_init_oops_partition(int rtas_partition_exists) +{ + int rc; + + rc = nvram_init_os_partition(&oops_log_partition); + if (rc != 0) { +#ifdef CONFIG_PPC_PSERIES + if (!rtas_partition_exists) { + pr_err("nvram: Failed to initialize oops partition!"); + return; + } + pr_notice("nvram: Using %s partition to log both" + " RTAS errors and oops/panic reports\n", + rtas_log_partition.name); + memcpy(&oops_log_partition, &rtas_log_partition, + sizeof(rtas_log_partition)); +#else + pr_err("nvram: Failed to initialize oops partition!"); + return; +#endif + } + oops_buf = kmalloc(oops_log_partition.size, GFP_KERNEL); + if (!oops_buf) { + pr_err("nvram: No memory for %s partition\n", + oops_log_partition.name); + return; + } + oops_data = oops_buf + sizeof(struct oops_log_info); + oops_data_sz = oops_log_partition.size - sizeof(struct oops_log_info); + + rc = nvram_pstore_init(); + + if (!rc) + return; + + /* + * Figure compression (preceded by elimination of each line's + * severity prefix) will reduce the oops/panic report to at most + * 45% of its original size. + */ + big_oops_buf_sz = (oops_data_sz * 100) / 45; + big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); + if (big_oops_buf) { + stream.workspace = kmalloc(zlib_deflate_workspacesize( + WINDOW_BITS, MEM_LEVEL), GFP_KERNEL); + if (!stream.workspace) { + pr_err("nvram: No memory for compression workspace; " + "skipping compression of %s partition data\n", + oops_log_partition.name); + kfree(big_oops_buf); + big_oops_buf = NULL; + } + } else { + pr_err("No memory for uncompressed %s data; " + "skipping compression\n", oops_log_partition.name); + stream.workspace = NULL; + } + + rc = kmsg_dump_register(&nvram_kmsg_dumper); + if (rc != 0) { + pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc); + kfree(oops_buf); + kfree(big_oops_buf); + kfree(stream.workspace); + } +} + +/* + * This is our kmsg_dump callback, called after an oops or panic report + * has been written to the printk buffer. We want to capture as much + * of the printk buffer as possible. First, capture as much as we can + * that we think will compress sufficiently to fit in the lnx,oops-log + * partition. If that's too much, go back and capture uncompressed text. + */ +static void oops_to_nvram(struct kmsg_dumper *dumper, + enum kmsg_dump_reason reason) +{ + struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; + static unsigned int oops_count = 0; + static bool panicking = false; + static DEFINE_SPINLOCK(lock); + unsigned long flags; + size_t text_len; + unsigned int err_type = ERR_TYPE_KERNEL_PANIC_GZ; + int rc = -1; + + switch (reason) { + case KMSG_DUMP_RESTART: + case KMSG_DUMP_HALT: + case KMSG_DUMP_POWEROFF: + /* These are almost always orderly shutdowns. */ + return; + case KMSG_DUMP_OOPS: + break; + case KMSG_DUMP_PANIC: + panicking = true; + break; + case KMSG_DUMP_EMERG: + if (panicking) + /* Panic report already captured. */ + return; + break; + default: + pr_err("%s: ignoring unrecognized KMSG_DUMP_* reason %d\n", + __func__, (int) reason); + return; + } + + if (clobbering_unread_rtas_event()) + return; + + if (!spin_trylock_irqsave(&lock, flags)) + return; + + if (big_oops_buf) { + kmsg_dump_get_buffer(dumper, false, + big_oops_buf, big_oops_buf_sz, &text_len); + rc = zip_oops(text_len); + } + if (rc != 0) { + kmsg_dump_rewind(dumper); + kmsg_dump_get_buffer(dumper, false, + oops_data, oops_data_sz, &text_len); + err_type = ERR_TYPE_KERNEL_PANIC; + oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION); + oops_hdr->report_length = cpu_to_be16(text_len); + oops_hdr->timestamp = cpu_to_be64(get_seconds()); + } + + (void) nvram_write_os_partition(&oops_log_partition, oops_buf, + (int) (sizeof(*oops_hdr) + text_len), err_type, + ++oops_count); + + spin_unlock_irqrestore(&lock, flags); +} + static loff_t dev_nvram_llseek(struct file *file, loff_t offset, int origin) { int size; diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 76483e3acd60..80614c6088bc 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -2,6 +2,7 @@ config PPC64 bool "64-bit kernel" default n select HAVE_VIRT_CPU_ACCOUNTING + select ZLIB_DEFLATE help This option selects whether a 32-bit or a 64-bit kernel will be built. diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index a758a9c3bbba..54c87d5d349d 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -16,7 +16,6 @@ config PPC_PSERIES select PPC_UDBG_16550 select PPC_NATIVE select PPC_PCI_CHOICE if EXPERT - select ZLIB_DEFLATE select PPC_DOORBELL select HAVE_CONTEXT_TRACKING select HOTPLUG_CPU if SMP diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index 054a0ed5c7ee..533807efed61 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -30,129 +29,17 @@ /* Max bytes to read/write in one go */ #define NVRW_CNT 0x20 -/* - * Set oops header version to distinguish between old and new format header. - * lnx,oops-log partition max size is 4000, header version > 4000 will - * help in identifying new header. - */ -#define OOPS_HDR_VERSION 5000 - static unsigned int nvram_size; static int nvram_fetch, nvram_store; static char nvram_buf[NVRW_CNT]; /* assume this is in the first 4GB */ static DEFINE_SPINLOCK(nvram_lock); -struct err_log_info { - __be32 error_type; - __be32 seq_num; -}; - -struct nvram_os_partition { - const char *name; - int req_size; /* desired size, in bytes */ - int min_size; /* minimum acceptable size (0 means req_size) */ - long size; /* size of data portion (excluding err_log_info) */ - long index; /* offset of data portion of partition */ - bool os_partition; /* partition initialized by OS, not FW */ -}; - -static struct nvram_os_partition rtas_log_partition = { - .name = "ibm,rtas-log", - .req_size = 2079, - .min_size = 1055, - .index = -1, - .os_partition = true -}; - -static struct nvram_os_partition oops_log_partition = { - .name = "lnx,oops-log", - .req_size = 4000, - .min_size = 2000, - .index = -1, - .os_partition = true -}; - -static const char *pseries_nvram_os_partitions[] = { - "ibm,rtas-log", - "lnx,oops-log", - NULL -}; - -struct oops_log_info { - __be16 version; - __be16 report_length; - __be64 timestamp; -} __attribute__((packed)); - -static void oops_to_nvram(struct kmsg_dumper *dumper, - enum kmsg_dump_reason reason); - -static struct kmsg_dumper nvram_kmsg_dumper = { - .dump = oops_to_nvram -}; - /* See clobbering_unread_rtas_event() */ #define NVRAM_RTAS_READ_TIMEOUT 5 /* seconds */ static unsigned long last_unread_rtas_event; /* timestamp */ -/* - * For capturing and compressing an oops or panic report... - - * big_oops_buf[] holds the uncompressed text we're capturing. - * - * oops_buf[] holds the compressed text, preceded by a oops header. - * oops header has u16 holding the version of oops header (to differentiate - * between old and new format header) followed by u16 holding the length of - * the compressed* text (*Or uncompressed, if compression fails.) and u64 - * holding the timestamp. oops_buf[] gets written to NVRAM. - * - * oops_log_info points to the header. oops_data points to the compressed text. - * - * +- oops_buf - * | +- oops_data - * v v - * +-----------+-----------+-----------+------------------------+ - * | version | length | timestamp | text | - * | (2 bytes) | (2 bytes) | (8 bytes) | (oops_data_sz bytes) | - * +-----------+-----------+-----------+------------------------+ - * ^ - * +- oops_log_info - * - * We preallocate these buffers during init to avoid kmalloc during oops/panic. - */ -static size_t big_oops_buf_sz; -static char *big_oops_buf, *oops_buf; -static char *oops_data; -static size_t oops_data_sz; - -/* Compression parameters */ -#define COMPR_LEVEL 6 -#define WINDOW_BITS 12 -#define MEM_LEVEL 4 -static struct z_stream_s stream; - #ifdef CONFIG_PSTORE -static struct nvram_os_partition of_config_partition = { - .name = "of-config", - .index = -1, - .os_partition = false -}; - -static struct nvram_os_partition common_partition = { - .name = "common", - .index = -1, - .os_partition = false -}; - -static enum pstore_type_id nvram_type_ids[] = { - PSTORE_TYPE_DMESG, - PSTORE_TYPE_PPC_RTAS, - PSTORE_TYPE_PPC_OF, - PSTORE_TYPE_PPC_COMMON, - -1 -}; -static int read_type; -static unsigned long last_rtas_event; +unsigned long last_rtas_event; #endif static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) @@ -246,73 +133,11 @@ static ssize_t pSeries_nvram_get_size(void) return nvram_size ? nvram_size : -ENODEV; } - -/* nvram_write_os_partition, nvram_write_error_log +/* nvram_write_error_log * * We need to buffer the error logs into nvram to ensure that we have - * the failure information to decode. If we have a severe error there - * is no way to guarantee that the OS or the machine is in a state to - * get back to user land and write the error to disk. For example if - * the SCSI device driver causes a Machine Check by writing to a bad - * IO address, there is no way of guaranteeing that the device driver - * is in any state that is would also be able to write the error data - * captured to disk, thus we buffer it in NVRAM for analysis on the - * next boot. - * - * In NVRAM the partition containing the error log buffer will looks like: - * Header (in bytes): - * +-----------+----------+--------+------------+------------------+ - * | signature | checksum | length | name | data | - * |0 |1 |2 3|4 15|16 length-1| - * +-----------+----------+--------+------------+------------------+ - * - * The 'data' section would look like (in bytes): - * +--------------+------------+-----------------------------------+ - * | event_logged | sequence # | error log | - * |0 3|4 7|8 error_log_size-1| - * +--------------+------------+-----------------------------------+ - * - * event_logged: 0 if event has not been logged to syslog, 1 if it has - * sequence #: The unique sequence # for each event. (until it wraps) - * error log: The error log from event_scan + * the failure information to decode. */ -static int nvram_write_os_partition(struct nvram_os_partition *part, - char *buff, int length, - unsigned int err_type, - unsigned int error_log_cnt) -{ - int rc; - loff_t tmp_index; - struct err_log_info info; - - if (part->index == -1) { - return -ESPIPE; - } - - if (length > part->size) { - length = part->size; - } - - info.error_type = cpu_to_be32(err_type); - info.seq_num = cpu_to_be32(error_log_cnt); - - tmp_index = part->index; - - rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index); - if (rc <= 0) { - pr_err("%s: Failed nvram_write (%d)\n", __func__, rc); - return rc; - } - - rc = ppc_md.nvram_write(buff, length, &tmp_index); - if (rc <= 0) { - pr_err("%s: Failed nvram_write (%d)\n", __func__, rc); - return rc; - } - - return 0; -} - int nvram_write_error_log(char * buff, int length, unsigned int err_type, unsigned int error_log_cnt) { @@ -328,50 +153,6 @@ int nvram_write_error_log(char * buff, int length, return rc; } -/* nvram_read_partition - * - * Reads nvram partition for at most 'length' - */ -static int nvram_read_partition(struct nvram_os_partition *part, char *buff, - int length, unsigned int *err_type, - unsigned int *error_log_cnt) -{ - int rc; - loff_t tmp_index; - struct err_log_info info; - - if (part->index == -1) - return -1; - - if (length > part->size) - length = part->size; - - tmp_index = part->index; - - if (part->os_partition) { - rc = ppc_md.nvram_read((char *)&info, - sizeof(struct err_log_info), - &tmp_index); - if (rc <= 0) { - pr_err("%s: Failed nvram_read (%d)\n", __func__, rc); - return rc; - } - } - - rc = ppc_md.nvram_read(buff, length, &tmp_index); - if (rc <= 0) { - pr_err("%s: Failed nvram_read (%d)\n", __func__, rc); - return rc; - } - - if (part->os_partition) { - *error_log_cnt = be32_to_cpu(info.seq_num); - *err_type = be32_to_cpu(info.error_type); - } - - return 0; -} - /* nvram_read_error_log * * Reads nvram for error log for at most 'length' @@ -407,67 +188,6 @@ int nvram_clear_error_log(void) return 0; } -/* pseries_nvram_init_os_partition - * - * This sets up a partition with an "OS" signature. - * - * The general strategy is the following: - * 1.) If a partition with the indicated name already exists... - * - If it's large enough, use it. - * - Otherwise, recycle it and keep going. - * 2.) Search for a free partition that is large enough. - * 3.) If there's not a free partition large enough, recycle any obsolete - * OS partitions and try again. - * 4.) Will first try getting a chunk that will satisfy the requested size. - * 5.) If a chunk of the requested size cannot be allocated, then try finding - * a chunk that will satisfy the minum needed. - * - * Returns 0 on success, else -1. - */ -static int __init pseries_nvram_init_os_partition(struct nvram_os_partition - *part) -{ - loff_t p; - int size; - - /* Look for ours */ - p = nvram_find_partition(part->name, NVRAM_SIG_OS, &size); - - /* Found one but too small, remove it */ - if (p && size < part->min_size) { - pr_info("nvram: Found too small %s partition," - " removing it...\n", part->name); - nvram_remove_partition(part->name, NVRAM_SIG_OS, NULL); - p = 0; - } - - /* Create one if we didn't find */ - if (!p) { - p = nvram_create_partition(part->name, NVRAM_SIG_OS, - part->req_size, part->min_size); - if (p == -ENOSPC) { - pr_info("nvram: No room to create %s partition, " - "deleting any obsolete OS partitions...\n", - part->name); - nvram_remove_partition(NULL, NVRAM_SIG_OS, - pseries_nvram_os_partitions); - p = nvram_create_partition(part->name, NVRAM_SIG_OS, - part->req_size, part->min_size); - } - } - - if (p <= 0) { - pr_err("nvram: Failed to find or create %s" - " partition, err %d\n", part->name, (int)p); - return -1; - } - - part->index = p; - part->size = nvram_get_partition_size(p) - sizeof(struct err_log_info); - - return 0; -} - /* * Are we using the ibm,rtas-log for oops/panic reports? And if so, * would logging this oops/panic overwrite an RTAS event that rtas_errd @@ -476,7 +196,7 @@ static int __init pseries_nvram_init_os_partition(struct nvram_os_partition * We assume that if rtas_errd hasn't read the RTAS event in * NVRAM_RTAS_READ_TIMEOUT seconds, it's probably not going to. */ -static int clobbering_unread_rtas_event(void) +int clobbering_unread_rtas_event(void) { return (oops_log_partition.index == rtas_log_partition.index && last_unread_rtas_event @@ -484,313 +204,6 @@ static int clobbering_unread_rtas_event(void) NVRAM_RTAS_READ_TIMEOUT); } -/* Derived from logfs_compress() */ -static int nvram_compress(const void *in, void *out, size_t inlen, - size_t outlen) -{ - int err, ret; - - ret = -EIO; - err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS, - MEM_LEVEL, Z_DEFAULT_STRATEGY); - if (err != Z_OK) - goto error; - - stream.next_in = in; - stream.avail_in = inlen; - stream.total_in = 0; - stream.next_out = out; - stream.avail_out = outlen; - stream.total_out = 0; - - err = zlib_deflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) - goto error; - - err = zlib_deflateEnd(&stream); - if (err != Z_OK) - goto error; - - if (stream.total_out >= stream.total_in) - goto error; - - ret = stream.total_out; -error: - return ret; -} - -/* Compress the text from big_oops_buf into oops_buf. */ -static int zip_oops(size_t text_len) -{ - struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; - int zipped_len = nvram_compress(big_oops_buf, oops_data, text_len, - oops_data_sz); - if (zipped_len < 0) { - pr_err("nvram: compression failed; returned %d\n", zipped_len); - pr_err("nvram: logging uncompressed oops/panic report\n"); - return -1; - } - oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION); - oops_hdr->report_length = cpu_to_be16(zipped_len); - oops_hdr->timestamp = cpu_to_be64(get_seconds()); - return 0; -} - -#ifdef CONFIG_PSTORE -static int nvram_pstore_open(struct pstore_info *psi) -{ - /* Reset the iterator to start reading partitions again */ - read_type = -1; - return 0; -} - -/** - * nvram_pstore_write - pstore write callback for nvram - * @type: Type of message logged - * @reason: reason behind dump (oops/panic) - * @id: identifier to indicate the write performed - * @part: pstore writes data to registered buffer in parts, - * part number will indicate the same. - * @count: Indicates oops count - * @compressed: Flag to indicate the log is compressed - * @size: number of bytes written to the registered buffer - * @psi: registered pstore_info structure - * - * Called by pstore_dump() when an oops or panic report is logged in the - * printk buffer. - * Returns 0 on successful write. - */ -static int nvram_pstore_write(enum pstore_type_id type, - enum kmsg_dump_reason reason, - u64 *id, unsigned int part, int count, - bool compressed, size_t size, - struct pstore_info *psi) -{ - int rc; - unsigned int err_type = ERR_TYPE_KERNEL_PANIC; - struct oops_log_info *oops_hdr = (struct oops_log_info *) oops_buf; - - /* part 1 has the recent messages from printk buffer */ - if (part > 1 || type != PSTORE_TYPE_DMESG || - clobbering_unread_rtas_event()) - return -1; - - oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION); - oops_hdr->report_length = cpu_to_be16(size); - oops_hdr->timestamp = cpu_to_be64(get_seconds()); - - if (compressed) - err_type = ERR_TYPE_KERNEL_PANIC_GZ; - - rc = nvram_write_os_partition(&oops_log_partition, oops_buf, - (int) (sizeof(*oops_hdr) + size), err_type, count); - - if (rc != 0) - return rc; - - *id = part; - return 0; -} - -/* - * Reads the oops/panic report, rtas, of-config and common partition. - * Returns the length of the data we read from each partition. - * Returns 0 if we've been called before. - */ -static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, - int *count, struct timespec *time, char **buf, - bool *compressed, struct pstore_info *psi) -{ - struct oops_log_info *oops_hdr; - unsigned int err_type, id_no, size = 0; - struct nvram_os_partition *part = NULL; - char *buff = NULL; - int sig = 0; - loff_t p; - - read_type++; - - switch (nvram_type_ids[read_type]) { - case PSTORE_TYPE_DMESG: - part = &oops_log_partition; - *type = PSTORE_TYPE_DMESG; - break; - case PSTORE_TYPE_PPC_RTAS: - part = &rtas_log_partition; - *type = PSTORE_TYPE_PPC_RTAS; - time->tv_sec = last_rtas_event; - time->tv_nsec = 0; - break; - case PSTORE_TYPE_PPC_OF: - sig = NVRAM_SIG_OF; - part = &of_config_partition; - *type = PSTORE_TYPE_PPC_OF; - *id = PSTORE_TYPE_PPC_OF; - time->tv_sec = 0; - time->tv_nsec = 0; - break; - case PSTORE_TYPE_PPC_COMMON: - sig = NVRAM_SIG_SYS; - part = &common_partition; - *type = PSTORE_TYPE_PPC_COMMON; - *id = PSTORE_TYPE_PPC_COMMON; - time->tv_sec = 0; - time->tv_nsec = 0; - break; - default: - return 0; - } - - if (!part->os_partition) { - p = nvram_find_partition(part->name, sig, &size); - if (p <= 0) { - pr_err("nvram: Failed to find partition %s, " - "err %d\n", part->name, (int)p); - return 0; - } - part->index = p; - part->size = size; - } - - buff = kmalloc(part->size, GFP_KERNEL); - - if (!buff) - return -ENOMEM; - - if (nvram_read_partition(part, buff, part->size, &err_type, &id_no)) { - kfree(buff); - return 0; - } - - *count = 0; - - if (part->os_partition) - *id = id_no; - - if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) { - size_t length, hdr_size; - - oops_hdr = (struct oops_log_info *)buff; - if (be16_to_cpu(oops_hdr->version) < OOPS_HDR_VERSION) { - /* Old format oops header had 2-byte record size */ - hdr_size = sizeof(u16); - length = be16_to_cpu(oops_hdr->version); - time->tv_sec = 0; - time->tv_nsec = 0; - } else { - hdr_size = sizeof(*oops_hdr); - length = be16_to_cpu(oops_hdr->report_length); - time->tv_sec = be64_to_cpu(oops_hdr->timestamp); - time->tv_nsec = 0; - } - *buf = kmalloc(length, GFP_KERNEL); - if (*buf == NULL) - return -ENOMEM; - memcpy(*buf, buff + hdr_size, length); - kfree(buff); - - if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) - *compressed = true; - else - *compressed = false; - return length; - } - - *buf = buff; - return part->size; -} - -static struct pstore_info nvram_pstore_info = { - .owner = THIS_MODULE, - .name = "nvram", - .open = nvram_pstore_open, - .read = nvram_pstore_read, - .write = nvram_pstore_write, -}; - -static int nvram_pstore_init(void) -{ - int rc = 0; - - nvram_pstore_info.buf = oops_data; - nvram_pstore_info.bufsize = oops_data_sz; - - spin_lock_init(&nvram_pstore_info.buf_lock); - - rc = pstore_register(&nvram_pstore_info); - if (rc != 0) - pr_err("nvram: pstore_register() failed, defaults to " - "kmsg_dump; returned %d\n", rc); - - return rc; -} -#else -static int nvram_pstore_init(void) -{ - return -1; -} -#endif - -static void __init nvram_init_oops_partition(int rtas_partition_exists) -{ - int rc; - - rc = pseries_nvram_init_os_partition(&oops_log_partition); - if (rc != 0) { - if (!rtas_partition_exists) - return; - pr_notice("nvram: Using %s partition to log both" - " RTAS errors and oops/panic reports\n", - rtas_log_partition.name); - memcpy(&oops_log_partition, &rtas_log_partition, - sizeof(rtas_log_partition)); - } - oops_buf = kmalloc(oops_log_partition.size, GFP_KERNEL); - if (!oops_buf) { - pr_err("nvram: No memory for %s partition\n", - oops_log_partition.name); - return; - } - oops_data = oops_buf + sizeof(struct oops_log_info); - oops_data_sz = oops_log_partition.size - sizeof(struct oops_log_info); - - rc = nvram_pstore_init(); - - if (!rc) - return; - - /* - * Figure compression (preceded by elimination of each line's - * severity prefix) will reduce the oops/panic report to at most - * 45% of its original size. - */ - big_oops_buf_sz = (oops_data_sz * 100) / 45; - big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); - if (big_oops_buf) { - stream.workspace = kmalloc(zlib_deflate_workspacesize( - WINDOW_BITS, MEM_LEVEL), GFP_KERNEL); - if (!stream.workspace) { - pr_err("nvram: No memory for compression workspace; " - "skipping compression of %s partition data\n", - oops_log_partition.name); - kfree(big_oops_buf); - big_oops_buf = NULL; - } - } else { - pr_err("No memory for uncompressed %s data; " - "skipping compression\n", oops_log_partition.name); - stream.workspace = NULL; - } - - rc = kmsg_dump_register(&nvram_kmsg_dumper); - if (rc != 0) { - pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc); - kfree(oops_buf); - kfree(big_oops_buf); - kfree(stream.workspace); - } -} - static int __init pseries_nvram_init_log_partitions(void) { int rc; @@ -798,7 +211,7 @@ static int __init pseries_nvram_init_log_partitions(void) /* Scan nvram for partitions */ nvram_scan_partitions(); - rc = pseries_nvram_init_os_partition(&rtas_log_partition); + rc = nvram_init_os_partition(&rtas_log_partition); nvram_init_oops_partition(rc == 0); return 0; } @@ -834,72 +247,3 @@ int __init pSeries_nvram_init(void) return 0; } - -/* - * This is our kmsg_dump callback, called after an oops or panic report - * has been written to the printk buffer. We want to capture as much - * of the printk buffer as possible. First, capture as much as we can - * that we think will compress sufficiently to fit in the lnx,oops-log - * partition. If that's too much, go back and capture uncompressed text. - */ -static void oops_to_nvram(struct kmsg_dumper *dumper, - enum kmsg_dump_reason reason) -{ - struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; - static unsigned int oops_count = 0; - static bool panicking = false; - static DEFINE_SPINLOCK(lock); - unsigned long flags; - size_t text_len; - unsigned int err_type = ERR_TYPE_KERNEL_PANIC_GZ; - int rc = -1; - - switch (reason) { - case KMSG_DUMP_RESTART: - case KMSG_DUMP_HALT: - case KMSG_DUMP_POWEROFF: - /* These are almost always orderly shutdowns. */ - return; - case KMSG_DUMP_OOPS: - break; - case KMSG_DUMP_PANIC: - panicking = true; - break; - case KMSG_DUMP_EMERG: - if (panicking) - /* Panic report already captured. */ - return; - break; - default: - pr_err("%s: ignoring unrecognized KMSG_DUMP_* reason %d\n", - __func__, (int) reason); - return; - } - - if (clobbering_unread_rtas_event()) - return; - - if (!spin_trylock_irqsave(&lock, flags)) - return; - - if (big_oops_buf) { - kmsg_dump_get_buffer(dumper, false, - big_oops_buf, big_oops_buf_sz, &text_len); - rc = zip_oops(text_len); - } - if (rc != 0) { - kmsg_dump_rewind(dumper); - kmsg_dump_get_buffer(dumper, false, - oops_data, oops_data_sz, &text_len); - err_type = ERR_TYPE_KERNEL_PANIC; - oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION); - oops_hdr->report_length = cpu_to_be16(text_len); - oops_hdr->timestamp = cpu_to_be64(get_seconds()); - } - - (void) nvram_write_os_partition(&oops_log_partition, oops_buf, - (int) (sizeof(*oops_hdr) + text_len), err_type, - ++oops_count); - - spin_unlock_irqrestore(&lock, flags); -} -- cgit v1.2.3 From f7618299b4ab425956099508cba7b3a39a056d87 Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Fri, 6 Feb 2015 01:06:52 +0530 Subject: powerpc/powernv: Add pstore support on powernv This patch extends pstore, a generic interface to platform dependent persistent storage, support for powernv platform to capture certain useful information, during dying moments. Such support is already in place for pseries platform. This patch re-uses most of that code. It is a common practice to compile kernels with both CONFIG_PPC_PSERIES=y and CONFIG_PPC_POWERNV=y. The code in nvram_init_oops_partition() routine still works as intended, as the caller is platform specific code which passes the appropriate value for "rtas_partition_exists" parameter. In all other places, where CONFIG_PPC_PSERIES or CONFIG_PPC_POWERNV flag is used in this patchset, it is to reduce the kernel size in cases where this flag is not set and doesn't have any impact logic wise. Signed-off-by: Hari Bathini Signed-off-by: Michael Ellerman --- arch/powerpc/kernel/nvram_64.c | 25 +++++++++++++++++++++++-- arch/powerpc/platforms/powernv/opal-nvram.c | 10 ++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c index 42e5c6a9c214..293da889055b 100644 --- a/arch/powerpc/kernel/nvram_64.c +++ b/arch/powerpc/kernel/nvram_64.c @@ -127,6 +127,14 @@ static size_t oops_data_sz; static struct z_stream_s stream; #ifdef CONFIG_PSTORE +#ifdef CONFIG_PPC_POWERNV +static struct nvram_os_partition skiboot_partition = { + .name = "ibm,skiboot", + .index = -1, + .os_partition = false +}; +#endif + #ifdef CONFIG_PPC_PSERIES static struct nvram_os_partition of_config_partition = { .name = "of-config", @@ -476,6 +484,16 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, time->tv_sec = 0; time->tv_nsec = 0; break; +#endif +#ifdef CONFIG_PPC_POWERNV + case PSTORE_TYPE_PPC_OPAL: + sig = NVRAM_SIG_FW; + part = &skiboot_partition; + *type = PSTORE_TYPE_PPC_OPAL; + *id = PSTORE_TYPE_PPC_OPAL; + time->tv_sec = 0; + time->tv_nsec = 0; + break; #endif default: return 0; @@ -552,8 +570,11 @@ static int nvram_pstore_init(void) { int rc = 0; - nvram_type_ids[2] = PSTORE_TYPE_PPC_RTAS; - nvram_type_ids[3] = PSTORE_TYPE_PPC_OF; + if (machine_is(pseries)) { + nvram_type_ids[2] = PSTORE_TYPE_PPC_RTAS; + nvram_type_ids[3] = PSTORE_TYPE_PPC_OF; + } else + nvram_type_ids[2] = PSTORE_TYPE_PPC_OPAL; nvram_pstore_info.buf = oops_data; nvram_pstore_info.bufsize = oops_data_sz; diff --git a/arch/powerpc/platforms/powernv/opal-nvram.c b/arch/powerpc/platforms/powernv/opal-nvram.c index f9896fd5d04a..9db4398ded5d 100644 --- a/arch/powerpc/platforms/powernv/opal-nvram.c +++ b/arch/powerpc/platforms/powernv/opal-nvram.c @@ -16,6 +16,7 @@ #include #include +#include #include static unsigned int nvram_size; @@ -62,6 +63,15 @@ static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index) return count; } +static int __init opal_nvram_init_log_partitions(void) +{ + /* Scan nvram for partitions */ + nvram_scan_partitions(); + nvram_init_oops_partition(0); + return 0; +} +machine_arch_initcall(powernv, opal_nvram_init_log_partitions); + void __init opal_nvram_init(void) { struct device_node *np; -- cgit v1.2.3 From e4a9616c548f67537a8d020a45a327f6a4d583ee Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Fri, 6 Feb 2015 01:07:17 +0530 Subject: powerpc/rtas: Make timestamp related code y2038-safe While we are here, let us make timestamp related code y2038-safe. Suggested-by: Arnd Bergmann Signed-off-by: Hari Bathini Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/rtas.h | 3 ++- arch/powerpc/kernel/nvram_64.c | 6 +++--- arch/powerpc/platforms/pseries/nvram.c | 10 +++++----- 3 files changed, 10 insertions(+), 9 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h index d1bd001566df..c3c99eb35448 100644 --- a/arch/powerpc/include/asm/rtas.h +++ b/arch/powerpc/include/asm/rtas.h @@ -4,6 +4,7 @@ #include #include +#include /* * Definitions for talking to the RTAS on CHRP machines. @@ -343,7 +344,7 @@ extern int early_init_dt_scan_rtas(unsigned long node, extern void pSeries_log_error(char *buf, unsigned int err_type, int fatal); #ifdef CONFIG_PPC_PSERIES -extern unsigned long last_rtas_event; +extern time64_t last_rtas_event; extern int clobbering_unread_rtas_event(void); extern int pseries_devicetree_update(s32 scope); extern void post_mobility_fixup(void); diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c index 293da889055b..1e703f8ebad4 100644 --- a/arch/powerpc/kernel/nvram_64.c +++ b/arch/powerpc/kernel/nvram_64.c @@ -376,7 +376,7 @@ static int zip_oops(size_t text_len) } oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION); oops_hdr->report_length = cpu_to_be16(zipped_len); - oops_hdr->timestamp = cpu_to_be64(get_seconds()); + oops_hdr->timestamp = cpu_to_be64(ktime_get_real_seconds()); return 0; } @@ -423,7 +423,7 @@ static int nvram_pstore_write(enum pstore_type_id type, oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION); oops_hdr->report_length = cpu_to_be16(size); - oops_hdr->timestamp = cpu_to_be64(get_seconds()); + oops_hdr->timestamp = cpu_to_be64(ktime_get_real_seconds()); if (compressed) err_type = ERR_TYPE_KERNEL_PANIC_GZ; @@ -721,7 +721,7 @@ static void oops_to_nvram(struct kmsg_dumper *dumper, err_type = ERR_TYPE_KERNEL_PANIC; oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION); oops_hdr->report_length = cpu_to_be16(text_len); - oops_hdr->timestamp = cpu_to_be64(get_seconds()); + oops_hdr->timestamp = cpu_to_be64(ktime_get_real_seconds()); } (void) nvram_write_os_partition(&oops_log_partition, oops_buf, diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index 533807efed61..9f8184175c86 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -36,10 +36,10 @@ static DEFINE_SPINLOCK(nvram_lock); /* See clobbering_unread_rtas_event() */ #define NVRAM_RTAS_READ_TIMEOUT 5 /* seconds */ -static unsigned long last_unread_rtas_event; /* timestamp */ +static time64_t last_unread_rtas_event; /* timestamp */ #ifdef CONFIG_PSTORE -unsigned long last_rtas_event; +time64_t last_rtas_event; #endif static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) @@ -144,9 +144,9 @@ int nvram_write_error_log(char * buff, int length, int rc = nvram_write_os_partition(&rtas_log_partition, buff, length, err_type, error_log_cnt); if (!rc) { - last_unread_rtas_event = get_seconds(); + last_unread_rtas_event = ktime_get_real_seconds(); #ifdef CONFIG_PSTORE - last_rtas_event = get_seconds(); + last_rtas_event = ktime_get_real_seconds(); #endif } @@ -200,7 +200,7 @@ int clobbering_unread_rtas_event(void) { return (oops_log_partition.index == rtas_log_partition.index && last_unread_rtas_event - && get_seconds() - last_unread_rtas_event <= + && ktime_get_real_seconds() - last_unread_rtas_event <= NVRAM_RTAS_READ_TIMEOUT); } -- cgit v1.2.3 From f57333a7677750f1264341d57bf1771000330458 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 20 Mar 2015 10:10:18 +1100 Subject: powerpc/powernv: Fix return value from power7_nap() et al. The power7_nap(), power7_sleep() and power7_winkle() functions are called from pnv_smp_cpu_kill_self(), which expects them to return the SRR1 value set by the hardware on wakeup, or 0 if no nap/sleep/winkle occurred. However, in the case where an interrupt needs to be replayed, the logic in power7_powersave_common (the common code for power7_nap et al.) doesn't set r3 to 0 in this case. Instead what we get as the return value is the selector for the type of power-saving mode requested (1, 2 or 3). In fact this should not affect the operation of pnv_smp_cpu_kill_self(), but it is better to get this correct, so this adds an instruction to set r3 to 0 in this case. Signed-off-by: Paul Mackerras Signed-off-by: Michael Ellerman --- arch/powerpc/kernel/idle_power7.S | 1 + 1 file changed, 1 insertion(+) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/idle_power7.S b/arch/powerpc/kernel/idle_power7.S index 05adc8bbdef8..eeaa0d5f69d5 100644 --- a/arch/powerpc/kernel/idle_power7.S +++ b/arch/powerpc/kernel/idle_power7.S @@ -94,6 +94,7 @@ _GLOBAL(power7_powersave_common) beq 1f addi r1,r1,INT_FRAME_SIZE ld r0,16(r1) + li r3,0 /* Return 0 (no nap) */ mtlr r0 blr -- cgit v1.2.3 From cca87d303c85b257a7b0fd34f9d6fce1c59880a2 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Tue, 17 Mar 2015 16:15:02 +1100 Subject: powerpc/pci: Refactor pci_dn Currently, the PCI config accessors are implemented based on device node. Unfortunately, SRIOV VFs won't have the corresponding device nodes. pci_dn will be used in replacement with device node for SRIOV VFs. So we have to use pci_dn in PCI config accessors. The patch refactors pci_dn in following aspects to make it ready to be used in PCI config accessors as we do in subsequent patch: * pci_dn is organized as a hierarchy tree. PCI device's pci_dn is put to the child list of pci_dn of its upstream bridge or PHB. VF's pci_dn will be put to the child list of pci_dn of PF's bridge. * For one particular PCI device (VF or not), its pci_dn can be found from pdev->dev.archdata.pci_data, PCI_DN(devnode), or parent's list. The fast path (fetching pci_dn through PCI device instance) is populated during early fixup time. [bhelgaas: changelog] Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/device.h | 6 ++ arch/powerpc/include/asm/pci-bridge.h | 11 ++- arch/powerpc/kernel/pci_dn.c | 130 ++++++++++++++++++++++++++++++++-- 3 files changed, 141 insertions(+), 6 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h index 38faeded7d59..9f1371bab5fc 100644 --- a/arch/powerpc/include/asm/device.h +++ b/arch/powerpc/include/asm/device.h @@ -8,6 +8,9 @@ struct dma_map_ops; struct device_node; +#ifdef CONFIG_PPC64 +struct pci_dn; +#endif /* * Arch extensions to struct device. @@ -34,6 +37,9 @@ struct dev_archdata { #ifdef CONFIG_SWIOTLB dma_addr_t max_direct_dma_addr; #endif +#ifdef CONFIG_PPC64 + struct pci_dn *pci_data; +#endif #ifdef CONFIG_EEH struct eeh_dev *edev; #endif diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 546d036fe925..706710b571c3 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -89,6 +89,7 @@ struct pci_controller { #ifdef CONFIG_PPC64 unsigned long buid; + struct pci_dn *pci_data; #endif /* CONFIG_PPC64 */ void *private_data; @@ -154,9 +155,12 @@ static inline int isa_vaddr_is_ioport(void __iomem *address) struct iommu_table; struct pci_dn { + int flags; + int busno; /* pci bus number */ int devfn; /* pci device and function number */ + struct pci_dn *parent; struct pci_controller *phb; /* for pci devices */ struct iommu_table *iommu_table; /* for phb's or bridges */ struct device_node *node; /* back-pointer to the device_node */ @@ -171,14 +175,17 @@ struct pci_dn { #ifdef CONFIG_PPC_POWERNV int pe_number; #endif + struct list_head child_list; + struct list_head list; }; /* Get the pointer to a device_node's pci_dn */ #define PCI_DN(dn) ((struct pci_dn *) (dn)->data) +extern struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus, + int devfn); extern struct pci_dn *pci_get_pdn(struct pci_dev *pdev); - -extern void * update_dn_pci_info(struct device_node *dn, void *data); +extern void *update_dn_pci_info(struct device_node *dn, void *data); static inline int pci_device_from_OF_node(struct device_node *np, u8 *bus, u8 *devfn) diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c index 83df3075d3df..0ab2dadaf842 100644 --- a/arch/powerpc/kernel/pci_dn.c +++ b/arch/powerpc/kernel/pci_dn.c @@ -32,12 +32,108 @@ #include #include +/* + * The function is used to find the firmware data of one + * specific PCI device, which is attached to the indicated + * PCI bus. For VFs, their firmware data is linked to that + * one of PF's bridge. For other devices, their firmware + * data is linked to that of their bridge. + */ +static struct pci_dn *pci_bus_to_pdn(struct pci_bus *bus) +{ + struct pci_bus *pbus; + struct device_node *dn; + struct pci_dn *pdn; + + /* + * We probably have virtual bus which doesn't + * have associated bridge. + */ + pbus = bus; + while (pbus) { + if (pci_is_root_bus(pbus) || pbus->self) + break; + + pbus = pbus->parent; + } + + /* + * Except virtual bus, all PCI buses should + * have device nodes. + */ + dn = pci_bus_to_OF_node(pbus); + pdn = dn ? PCI_DN(dn) : NULL; + + return pdn; +} + +struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus, + int devfn) +{ + struct device_node *dn = NULL; + struct pci_dn *parent, *pdn; + struct pci_dev *pdev = NULL; + + /* Fast path: fetch from PCI device */ + list_for_each_entry(pdev, &bus->devices, bus_list) { + if (pdev->devfn == devfn) { + if (pdev->dev.archdata.pci_data) + return pdev->dev.archdata.pci_data; + + dn = pci_device_to_OF_node(pdev); + break; + } + } + + /* Fast path: fetch from device node */ + pdn = dn ? PCI_DN(dn) : NULL; + if (pdn) + return pdn; + + /* Slow path: fetch from firmware data hierarchy */ + parent = pci_bus_to_pdn(bus); + if (!parent) + return NULL; + + list_for_each_entry(pdn, &parent->child_list, list) { + if (pdn->busno == bus->number && + pdn->devfn == devfn) + return pdn; + } + + return NULL; +} + struct pci_dn *pci_get_pdn(struct pci_dev *pdev) { - struct device_node *dn = pci_device_to_OF_node(pdev); - if (!dn) + struct device_node *dn; + struct pci_dn *parent, *pdn; + + /* Search device directly */ + if (pdev->dev.archdata.pci_data) + return pdev->dev.archdata.pci_data; + + /* Check device node */ + dn = pci_device_to_OF_node(pdev); + pdn = dn ? PCI_DN(dn) : NULL; + if (pdn) + return pdn; + + /* + * VFs don't have device nodes. We hook their + * firmware data to PF's bridge. + */ + parent = pci_bus_to_pdn(pdev->bus); + if (!parent) return NULL; - return PCI_DN(dn); + + list_for_each_entry(pdn, &parent->child_list, list) { + if (pdn->busno == pdev->bus->number && + pdn->devfn == pdev->devfn) + return pdn; + } + + return NULL; } /* @@ -49,6 +145,7 @@ void *update_dn_pci_info(struct device_node *dn, void *data) struct pci_controller *phb = data; const __be32 *type = of_get_property(dn, "ibm,pci-config-space-type", NULL); const __be32 *regs; + struct device_node *parent; struct pci_dn *pdn; pdn = zalloc_maybe_bootmem(sizeof(*pdn), GFP_KERNEL); @@ -70,6 +167,15 @@ void *update_dn_pci_info(struct device_node *dn, void *data) } pdn->pci_ext_config_space = (type && of_read_number(type, 1) == 1); + + /* Attach to parent node */ + INIT_LIST_HEAD(&pdn->child_list); + INIT_LIST_HEAD(&pdn->list); + parent = of_get_parent(dn); + pdn->parent = parent ? PCI_DN(parent) : NULL; + if (pdn->parent) + list_add_tail(&pdn->list, &pdn->parent->child_list); + return NULL; } @@ -147,8 +253,11 @@ void pci_devs_phb_init_dynamic(struct pci_controller *phb) /* PHB nodes themselves must not match */ update_dn_pci_info(dn, phb); pdn = dn->data; - if (pdn) + if (pdn) { pdn->devfn = pdn->busno = -1; + pdn->phb = phb; + phb->pci_data = pdn; + } /* Update dn->phb ptrs for new phb and children devices */ traverse_pci_devices(dn, update_dn_pci_info, phb); @@ -171,3 +280,16 @@ void __init pci_devs_phb_init(void) list_for_each_entry_safe(phb, tmp, &hose_list, list_node) pci_devs_phb_init_dynamic(phb); } + +static void pci_dev_pdn_setup(struct pci_dev *pdev) +{ + struct pci_dn *pdn; + + if (pdev->dev.archdata.pci_data) + return; + + /* Setup the fast path */ + pdn = pci_get_pdn(pdev); + pdev->dev.archdata.pci_data = pdn; +} +DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, pci_dev_pdn_setup); -- cgit v1.2.3 From c035ff1d2eaa03ab40839041e955a86a8e412eb4 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Tue, 17 Mar 2015 16:15:04 +1100 Subject: powerpc/pci: Trace more information from pci_dn Originally, EEH probes on device_node or pci_dev and populates the corresponding eeh_dev. In the subsequent patches, EEH will probes on pci_dn and populates the corresponding eeh_dev. So we have to cache some information in pci_dn, either from device_node or SRIOV PF's enablement platform hook, to populate the eeh_dev properly. The motivation to probe pci_dn, instead of device node or pci_dev, to populate eeh_dev is SRIOV VFs are dynamically created and we don't have the corresponding device nodes for them. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/pci-bridge.h | 3 +++ arch/powerpc/kernel/pci_dn.c | 10 ++++++++++ 2 files changed, 13 insertions(+) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 706710b571c3..01b730a8a5a3 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -159,6 +159,9 @@ struct pci_dn { int busno; /* pci bus number */ int devfn; /* pci device and function number */ + int vendor_id; /* Vendor ID */ + int device_id; /* Device ID */ + int class_code; /* Device class code */ struct pci_dn *parent; struct pci_controller *phb; /* for pci devices */ diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c index 0ab2dadaf842..d139f72ff9d5 100644 --- a/arch/powerpc/kernel/pci_dn.c +++ b/arch/powerpc/kernel/pci_dn.c @@ -166,6 +166,15 @@ void *update_dn_pci_info(struct device_node *dn, void *data) pdn->devfn = (addr >> 8) & 0xff; } + /* vendor/device IDs and class code */ + regs = of_get_property(dn, "vendor-id", NULL); + pdn->vendor_id = regs ? of_read_number(regs, 1) : 0; + regs = of_get_property(dn, "device-id", NULL); + pdn->device_id = regs ? of_read_number(regs, 1) : 0; + regs = of_get_property(dn, "class-code", NULL); + pdn->class_code = regs ? of_read_number(regs, 1) : 0; + + /* Extended config space */ pdn->pci_ext_config_space = (type && of_read_number(type, 1) == 1); /* Attach to parent node */ @@ -255,6 +264,7 @@ void pci_devs_phb_init_dynamic(struct pci_controller *phb) pdn = dn->data; if (pdn) { pdn->devfn = pdn->busno = -1; + pdn->vendor_id = pdn->device_id = pdn->class_code = 0; pdn->phb = phb; phb->pci_data = pdn; } -- cgit v1.2.3 From e8e9b34cef237d4d6fdc0d350cd8a95d1adb9ee9 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Tue, 17 Mar 2015 16:15:05 +1100 Subject: powerpc/eeh: Create eeh_dev from pci_dn instead of device_node The patch adds function traverse_pci_dn(), which is similar to traverse_pci_devices() except it takes pci_dn, not device_node as parameter. The pci_dev.c has been reworked to create eeh_dev from pci_dn, instead of device_node. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/eeh.h | 11 ++++++++-- arch/powerpc/include/asm/pci-bridge.h | 8 ++++++- arch/powerpc/include/asm/ppc-pci.h | 5 +++++ arch/powerpc/kernel/eeh_dev.c | 14 ++++++------ arch/powerpc/kernel/pci_dn.c | 40 ++++++++++++++++++++++++++++++++++ arch/powerpc/platforms/pseries/setup.c | 2 +- 6 files changed, 69 insertions(+), 11 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 55abfd09e47f..2106f83da2d5 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -30,6 +30,7 @@ struct pci_dev; struct pci_bus; struct device_node; +struct pci_dn; #ifdef CONFIG_EEH @@ -137,6 +138,7 @@ struct eeh_dev { struct list_head list; /* Form link list in the PE */ struct pci_controller *phb; /* Associated PHB */ struct device_node *dn; /* Associated device node */ + struct pci_dn *pdn; /* Associated PCI device node */ struct pci_dev *pdev; /* Associated PCI device */ struct pci_bus *bus; /* PCI bus for partial hotplug */ }; @@ -146,6 +148,11 @@ static inline struct device_node *eeh_dev_to_of_node(struct eeh_dev *edev) return edev ? edev->dn : NULL; } +static inline struct pci_dn *eeh_dev_to_pdn(struct eeh_dev *edev) +{ + return edev ? edev->pdn : NULL; +} + static inline struct pci_dev *eeh_dev_to_pci_dev(struct eeh_dev *edev) { return edev ? edev->pdev : NULL; @@ -272,7 +279,7 @@ void eeh_pe_restore_bars(struct eeh_pe *pe); const char *eeh_pe_loc_get(struct eeh_pe *pe); struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe); -void *eeh_dev_init(struct device_node *dn, void *data); +void *eeh_dev_init(struct pci_dn *pdn, void *data); void eeh_dev_phb_init_dynamic(struct pci_controller *phb); int eeh_init(void); int __init eeh_ops_register(struct eeh_ops *ops); @@ -323,7 +330,7 @@ static inline int eeh_init(void) return 0; } -static inline void *eeh_dev_init(struct device_node *dn, void *data) +static inline void *eeh_dev_init(struct pci_dn *pdn, void *data) { return NULL; } diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 01b730a8a5a3..7b74499b728c 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -213,8 +213,14 @@ static inline struct eeh_dev *of_node_to_eeh_dev(struct device_node *dn) return PCI_DN(dn)->edev; } + +static inline struct eeh_dev *pdn_to_eeh_dev(struct pci_dn *pdn) +{ + return pdn ? pdn->edev : NULL; +} #else -#define of_node_to_eeh_dev(x) (NULL) +#define of_node_to_eeh_dev(x) (NULL) +#define pdn_to_eeh_dev(x) (NULL) #endif /** Find the bus corresponding to the indicated device node */ diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h index db1e2b8eff3c..ade75238ceb5 100644 --- a/arch/powerpc/include/asm/ppc-pci.h +++ b/arch/powerpc/include/asm/ppc-pci.h @@ -33,9 +33,14 @@ extern struct pci_dev *isa_bridge_pcidev; /* may be NULL if no ISA bus */ /* PCI device_node operations */ struct device_node; +struct pci_dn; + typedef void *(*traverse_func)(struct device_node *me, void *data); void *traverse_pci_devices(struct device_node *start, traverse_func pre, void *data); +void *traverse_pci_dn(struct pci_dn *root, + void *(*fn)(struct pci_dn *, void *), + void *data); extern void pci_devs_phb_init(void); extern void pci_devs_phb_init_dynamic(struct pci_controller *phb); diff --git a/arch/powerpc/kernel/eeh_dev.c b/arch/powerpc/kernel/eeh_dev.c index e5274ee9a75f..aabba94ff9cb 100644 --- a/arch/powerpc/kernel/eeh_dev.c +++ b/arch/powerpc/kernel/eeh_dev.c @@ -43,13 +43,13 @@ /** * eeh_dev_init - Create EEH device according to OF node - * @dn: device node + * @pdn: PCI device node * @data: PHB * * It will create EEH device according to the given OF node. The function * might be called by PCI emunation, DR, PHB hotplug. */ -void *eeh_dev_init(struct device_node *dn, void *data) +void *eeh_dev_init(struct pci_dn *pdn, void *data) { struct pci_controller *phb = data; struct eeh_dev *edev; @@ -63,8 +63,8 @@ void *eeh_dev_init(struct device_node *dn, void *data) } /* Associate EEH device with OF node */ - PCI_DN(dn)->edev = edev; - edev->dn = dn; + pdn->edev = edev; + edev->pdn = pdn; edev->phb = phb; INIT_LIST_HEAD(&edev->list); @@ -80,16 +80,16 @@ void *eeh_dev_init(struct device_node *dn, void *data) */ void eeh_dev_phb_init_dynamic(struct pci_controller *phb) { - struct device_node *dn = phb->dn; + struct pci_dn *root = phb->pci_data; /* EEH PE for PHB */ eeh_phb_pe_create(phb); /* EEH device for PHB */ - eeh_dev_init(dn, phb); + eeh_dev_init(root, phb); /* EEH devices for children OF nodes */ - traverse_pci_devices(dn, eeh_dev_init, phb); + traverse_pci_dn(root, eeh_dev_init, phb); } /** diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c index d139f72ff9d5..65b98367005c 100644 --- a/arch/powerpc/kernel/pci_dn.c +++ b/arch/powerpc/kernel/pci_dn.c @@ -246,6 +246,46 @@ void *traverse_pci_devices(struct device_node *start, traverse_func pre, return NULL; } +static struct pci_dn *pci_dn_next_one(struct pci_dn *root, + struct pci_dn *pdn) +{ + struct list_head *next = pdn->child_list.next; + + if (next != &pdn->child_list) + return list_entry(next, struct pci_dn, list); + + while (1) { + if (pdn == root) + return NULL; + + next = pdn->list.next; + if (next != &pdn->parent->child_list) + break; + + pdn = pdn->parent; + } + + return list_entry(next, struct pci_dn, list); +} + +void *traverse_pci_dn(struct pci_dn *root, + void *(*fn)(struct pci_dn *, void *), + void *data) +{ + struct pci_dn *pdn = root; + void *ret; + + /* Only scan the child nodes */ + for (pdn = pci_dn_next_one(root, pdn); pdn; + pdn = pci_dn_next_one(root, pdn)) { + ret = fn(pdn, data); + if (ret) + return ret; + } + + return NULL; +} + /** * pci_devs_phb_init_dynamic - setup pci devices under this PHB * phb: pci-to-host bridge (top-level bridge connecting to cpu) diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index e445b6701f50..70304070a260 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -265,7 +265,7 @@ static int pci_dn_reconfig_notifier(struct notifier_block *nb, unsigned long act update_dn_pci_info(np, pci->phb); /* Create EEH device for the OF node */ - eeh_dev_init(np, pci->phb); + eeh_dev_init(PCI_DN(np), pci->phb); } break; default: -- cgit v1.2.3 From ff57b454ddb938d98d48d8df356357000fedc88c Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Tue, 17 Mar 2015 16:15:06 +1100 Subject: powerpc/eeh: Do probe on pci_dn Originally, EEH core probes on device_node or pci_dev to populate EEH devices and PEs, which conflicts with the fact: SRIOV VFs are usually enabled and created by PF's driver and they don't have the corresponding device_nodes. Instead, SRIOV VFs have dynamically created pci_dn, which can be used for EEH probe. The patch reworks EEH probe for PowerNV and pSeries platforms to do probing based on pci_dn, instead of pci_dev or device_node any more. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/eeh.h | 11 +- arch/powerpc/kernel/eeh.c | 63 ++++-------- arch/powerpc/kernel/of_platform.c | 2 +- arch/powerpc/kernel/pci-hotplug.c | 2 +- arch/powerpc/platforms/powernv/eeh-powernv.c | 146 ++++++++++++++++++++------- arch/powerpc/platforms/pseries/eeh_pseries.c | 82 ++++++--------- arch/powerpc/platforms/pseries/pci_dlpar.c | 2 +- drivers/pci/hotplug/rpadlpar_core.c | 2 +- 8 files changed, 172 insertions(+), 138 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 2106f83da2d5..87797811808f 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -207,8 +207,7 @@ struct eeh_ops { char *name; int (*init)(void); int (*post_init)(void); - void* (*of_probe)(struct device_node *dn, void *flag); - int (*dev_probe)(struct pci_dev *dev, void *flag); + void* (*probe)(struct pci_dn *pdn, void *data); int (*set_option)(struct eeh_pe *pe, int option); int (*get_pe_addr)(struct eeh_pe *pe); int (*get_state)(struct eeh_pe *pe, int *state); @@ -287,8 +286,8 @@ int __exit eeh_ops_unregister(const char *name); int eeh_check_failure(const volatile void __iomem *token); int eeh_dev_check_failure(struct eeh_dev *edev); void eeh_addr_cache_build(void); -void eeh_add_device_early(struct device_node *); -void eeh_add_device_tree_early(struct device_node *); +void eeh_add_device_early(struct pci_dn *); +void eeh_add_device_tree_early(struct pci_dn *); void eeh_add_device_late(struct pci_dev *); void eeh_add_device_tree_late(struct pci_bus *); void eeh_add_sysfs_files(struct pci_bus *); @@ -346,9 +345,9 @@ static inline int eeh_check_failure(const volatile void __iomem *token) static inline void eeh_addr_cache_build(void) { } -static inline void eeh_add_device_early(struct device_node *dn) { } +static inline void eeh_add_device_early(struct pci_dn *pdn) { } -static inline void eeh_add_device_tree_early(struct device_node *dn) { } +static inline void eeh_add_device_tree_early(struct pci_dn *pdn) { } static inline void eeh_add_device_late(struct pci_dev *dev) { } diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 19a897c810be..9504c2f0bb54 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -969,7 +969,7 @@ static struct notifier_block eeh_reboot_nb = { int eeh_init(void) { struct pci_controller *hose, *tmp; - struct device_node *phb; + struct pci_dn *pdn; static int cnt = 0; int ret = 0; @@ -1004,20 +1004,9 @@ int eeh_init(void) return ret; /* Enable EEH for all adapters */ - if (eeh_has_flag(EEH_PROBE_MODE_DEVTREE)) { - list_for_each_entry_safe(hose, tmp, - &hose_list, list_node) { - phb = hose->dn; - traverse_pci_devices(phb, eeh_ops->of_probe, NULL); - } - } else if (eeh_has_flag(EEH_PROBE_MODE_DEV)) { - list_for_each_entry_safe(hose, tmp, - &hose_list, list_node) - pci_walk_bus(hose->bus, eeh_ops->dev_probe, NULL); - } else { - pr_warn("%s: Invalid probe mode %x", - __func__, eeh_subsystem_flags); - return -EINVAL; + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { + pdn = hose->pci_data; + traverse_pci_dn(pdn, eeh_ops->probe, NULL); } /* @@ -1043,7 +1032,7 @@ core_initcall_sync(eeh_init); /** * eeh_add_device_early - Enable EEH for the indicated device_node - * @dn: device node for which to set up EEH + * @pdn: PCI device node for which to set up EEH * * This routine must be used to perform EEH initialization for PCI * devices that were added after system boot (e.g. hotplug, dlpar). @@ -1053,44 +1042,41 @@ core_initcall_sync(eeh_init); * on the CEC architecture, type of the device, on earlier boot * command-line arguments & etc. */ -void eeh_add_device_early(struct device_node *dn) +void eeh_add_device_early(struct pci_dn *pdn) { struct pci_controller *phb; + struct eeh_dev *edev = pdn_to_eeh_dev(pdn); - /* - * If we're doing EEH probe based on PCI device, we - * would delay the probe until late stage because - * the PCI device isn't available this moment. - */ - if (!eeh_has_flag(EEH_PROBE_MODE_DEVTREE)) - return; - - if (!of_node_to_eeh_dev(dn)) + if (!edev) return; - phb = of_node_to_eeh_dev(dn)->phb; /* USB Bus children of PCI devices will not have BUID's */ - if (NULL == phb || 0 == phb->buid) + phb = edev->phb; + if (NULL == phb || + (eeh_has_flag(EEH_PROBE_MODE_DEVTREE) && 0 == phb->buid)) return; - eeh_ops->of_probe(dn, NULL); + eeh_ops->probe(pdn, NULL); } /** * eeh_add_device_tree_early - Enable EEH for the indicated device - * @dn: device node + * @pdn: PCI device node * * This routine must be used to perform EEH initialization for the * indicated PCI device that was added after system boot (e.g. * hotplug, dlpar). */ -void eeh_add_device_tree_early(struct device_node *dn) +void eeh_add_device_tree_early(struct pci_dn *pdn) { - struct device_node *sib; + struct pci_dn *n; + + if (!pdn) + return; - for_each_child_of_node(dn, sib) - eeh_add_device_tree_early(sib); - eeh_add_device_early(dn); + list_for_each_entry(n, &pdn->child_list, list) + eeh_add_device_tree_early(n); + eeh_add_device_early(pdn); } EXPORT_SYMBOL_GPL(eeh_add_device_tree_early); @@ -1144,13 +1130,6 @@ void eeh_add_device_late(struct pci_dev *dev) edev->pdev = dev; dev->dev.archdata.edev = edev; - /* - * We have to do the EEH probe here because the PCI device - * hasn't been created yet in the early stage. - */ - if (eeh_has_flag(EEH_PROBE_MODE_DEV)) - eeh_ops->dev_probe(dev, NULL); - eeh_addr_cache_insert_dev(dev); } diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c index 2f35a72642c6..b60a67d92ebd 100644 --- a/arch/powerpc/kernel/of_platform.c +++ b/arch/powerpc/kernel/of_platform.c @@ -72,7 +72,7 @@ static int of_pci_phb_probe(struct platform_device *dev) /* Register devices with EEH */ if (dev->dev.of_node->child) - eeh_add_device_tree_early(dev->dev.of_node); + eeh_add_device_tree_early(PCI_DN(dev->dev.of_node)); /* Scan the bus */ pcibios_scan_phb(phb); diff --git a/arch/powerpc/kernel/pci-hotplug.c b/arch/powerpc/kernel/pci-hotplug.c index 5b789177aa29..18d9575729a3 100644 --- a/arch/powerpc/kernel/pci-hotplug.c +++ b/arch/powerpc/kernel/pci-hotplug.c @@ -75,7 +75,7 @@ void pcibios_add_pci_devices(struct pci_bus * bus) struct pci_dev *dev; struct device_node *dn = pci_bus_to_OF_node(bus); - eeh_add_device_tree_early(dn); + eeh_add_device_tree_early(PCI_DN(dn)); mode = PCI_PROBE_NORMAL; if (ppc_md.pci_probe_mode) diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c index 8eac8c57ee86..dcc524fe2a30 100644 --- a/arch/powerpc/platforms/powernv/eeh-powernv.c +++ b/arch/powerpc/platforms/powernv/eeh-powernv.c @@ -286,10 +286,82 @@ static int pnv_eeh_post_init(void) return ret; } +static int pnv_eeh_cap_start(struct pci_dn *pdn) +{ + u32 status; + + if (!pdn) + return 0; + + pnv_pci_cfg_read(pdn, PCI_STATUS, 2, &status); + if (!(status & PCI_STATUS_CAP_LIST)) + return 0; + + return PCI_CAPABILITY_LIST; +} + +static int pnv_eeh_find_cap(struct pci_dn *pdn, int cap) +{ + int pos = pnv_eeh_cap_start(pdn); + int cnt = 48; /* Maximal number of capabilities */ + u32 id; + + if (!pos) + return 0; + + while (cnt--) { + pnv_pci_cfg_read(pdn, pos, 1, &pos); + if (pos < 0x40) + break; + + pos &= ~3; + pnv_pci_cfg_read(pdn, pos + PCI_CAP_LIST_ID, 1, &id); + if (id == 0xff) + break; + + /* Found */ + if (id == cap) + return pos; + + /* Next one */ + pos += PCI_CAP_LIST_NEXT; + } + + return 0; +} + +static int pnv_eeh_find_ecap(struct pci_dn *pdn, int cap) +{ + struct eeh_dev *edev = pdn_to_eeh_dev(pdn); + u32 header; + int pos = 256, ttl = (4096 - 256) / 8; + + if (!edev || !edev->pcie_cap) + return 0; + if (pnv_pci_cfg_read(pdn, pos, 4, &header) != PCIBIOS_SUCCESSFUL) + return 0; + else if (!header) + return 0; + + while (ttl-- > 0) { + if (PCI_EXT_CAP_ID(header) == cap && pos) + return pos; + + pos = PCI_EXT_CAP_NEXT(header); + if (pos < 256) + break; + + if (pnv_pci_cfg_read(pdn, pos, 4, &header) != PCIBIOS_SUCCESSFUL) + break; + } + + return 0; +} + /** - * pnv_eeh_dev_probe - Do probe on PCI device - * @dev: PCI device - * @flag: unused + * pnv_eeh_probe - Do probe on PCI device + * @pdn: PCI device node + * @data: unused * * When EEH module is installed during system boot, all PCI devices * are checked one by one to see if it supports EEH. The function @@ -303,12 +375,12 @@ static int pnv_eeh_post_init(void) * was possiblly triggered by EEH core, the binding between EEH device * and the PCI device isn't built yet. */ -static int pnv_eeh_dev_probe(struct pci_dev *dev, void *flag) +static void *pnv_eeh_probe(struct pci_dn *pdn, void *data) { - struct pci_controller *hose = pci_bus_to_host(dev->bus); + struct pci_controller *hose = pdn->phb; struct pnv_phb *phb = hose->private_data; - struct device_node *dn = pci_device_to_OF_node(dev); - struct eeh_dev *edev = of_node_to_eeh_dev(dn); + struct eeh_dev *edev = pdn_to_eeh_dev(pdn); + uint32_t pcie_flags; int ret; /* @@ -317,40 +389,42 @@ static int pnv_eeh_dev_probe(struct pci_dev *dev, void *flag) * the root bridge. So it's not reasonable to continue * the probing. */ - if (!dn || !edev || edev->pe) - return 0; + if (!edev || edev->pe) + return NULL; /* Skip for PCI-ISA bridge */ - if ((dev->class >> 8) == PCI_CLASS_BRIDGE_ISA) - return 0; + if ((pdn->class_code >> 8) == PCI_CLASS_BRIDGE_ISA) + return NULL; /* Initialize eeh device */ - edev->class_code = dev->class; + edev->class_code = pdn->class_code; edev->mode &= 0xFFFFFF00; - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) + edev->pcix_cap = pnv_eeh_find_cap(pdn, PCI_CAP_ID_PCIX); + edev->pcie_cap = pnv_eeh_find_cap(pdn, PCI_CAP_ID_EXP); + edev->aer_cap = pnv_eeh_find_ecap(pdn, PCI_EXT_CAP_ID_ERR); + if ((edev->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) { edev->mode |= EEH_DEV_BRIDGE; - edev->pcix_cap = pci_find_capability(dev, PCI_CAP_ID_PCIX); - if (pci_is_pcie(dev)) { - edev->pcie_cap = pci_pcie_cap(dev); - - if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) - edev->mode |= EEH_DEV_ROOT_PORT; - else if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) - edev->mode |= EEH_DEV_DS_PORT; - - edev->aer_cap = pci_find_ext_capability(dev, - PCI_EXT_CAP_ID_ERR); + if (edev->pcie_cap) { + pnv_pci_cfg_read(pdn, edev->pcie_cap + PCI_EXP_FLAGS, + 2, &pcie_flags); + pcie_flags = (pcie_flags & PCI_EXP_FLAGS_TYPE) >> 4; + if (pcie_flags == PCI_EXP_TYPE_ROOT_PORT) + edev->mode |= EEH_DEV_ROOT_PORT; + else if (pcie_flags == PCI_EXP_TYPE_DOWNSTREAM) + edev->mode |= EEH_DEV_DS_PORT; + } } - edev->config_addr = ((dev->bus->number << 8) | dev->devfn); - edev->pe_config_addr = phb->bdfn_to_pe(phb, dev->bus, dev->devfn & 0xff); + edev->config_addr = (pdn->busno << 8) | (pdn->devfn); + edev->pe_config_addr = phb->ioda.pe_rmap[edev->config_addr]; /* Create PE */ ret = eeh_add_to_parent_pe(edev); if (ret) { - pr_warn("%s: Can't add PCI dev %s to parent PE (%d)\n", - __func__, pci_name(dev), ret); - return ret; + pr_warn("%s: Can't add PCI dev %04x:%02x:%02x.%01x to parent PE (%d)\n", + __func__, hose->global_number, pdn->busno, + PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn), ret); + return NULL; } /* @@ -369,8 +443,10 @@ static int pnv_eeh_dev_probe(struct pci_dev *dev, void *flag) * Broadcom Austin 4-ports NICs (14e4:1657) * Broadcom Shiner 2-ports 10G NICs (14e4:168e) */ - if ((dev->vendor == PCI_VENDOR_ID_BROADCOM && dev->device == 0x1657) || - (dev->vendor == PCI_VENDOR_ID_BROADCOM && dev->device == 0x168e)) + if ((pdn->vendor_id == PCI_VENDOR_ID_BROADCOM && + pdn->device_id == 0x1657) || + (pdn->vendor_id == PCI_VENDOR_ID_BROADCOM && + pdn->device_id == 0x168e)) edev->pe->state |= EEH_PE_CFG_RESTRICTED; /* @@ -380,7 +456,8 @@ static int pnv_eeh_dev_probe(struct pci_dev *dev, void *flag) * to PE reset. */ if (!edev->pe->bus) - edev->pe->bus = dev->bus; + edev->pe->bus = pci_find_bus(hose->global_number, + pdn->busno); /* * Enable EEH explicitly so that we will do EEH check @@ -391,7 +468,7 @@ static int pnv_eeh_dev_probe(struct pci_dev *dev, void *flag) /* Save memory bars */ eeh_save_bars(edev); - return 0; + return NULL; } /** @@ -1432,8 +1509,7 @@ static struct eeh_ops pnv_eeh_ops = { .name = "powernv", .init = pnv_eeh_init, .post_init = pnv_eeh_post_init, - .of_probe = NULL, - .dev_probe = pnv_eeh_dev_probe, + .probe = pnv_eeh_probe, .set_option = pnv_eeh_set_option, .get_pe_addr = pnv_eeh_get_pe_addr, .get_state = pnv_eeh_get_state, diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index a6c7e19f5eb3..a2946f72d5e7 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -118,9 +118,8 @@ static int pseries_eeh_init(void) return 0; } -static int pseries_eeh_cap_start(struct device_node *dn) +static int pseries_eeh_cap_start(struct pci_dn *pdn) { - struct pci_dn *pdn = PCI_DN(dn); u32 status; if (!pdn) @@ -134,10 +133,9 @@ static int pseries_eeh_cap_start(struct device_node *dn) } -static int pseries_eeh_find_cap(struct device_node *dn, int cap) +static int pseries_eeh_find_cap(struct pci_dn *pdn, int cap) { - struct pci_dn *pdn = PCI_DN(dn); - int pos = pseries_eeh_cap_start(dn); + int pos = pseries_eeh_cap_start(pdn); int cnt = 48; /* Maximal number of capabilities */ u32 id; @@ -160,10 +158,9 @@ static int pseries_eeh_find_cap(struct device_node *dn, int cap) return 0; } -static int pseries_eeh_find_ecap(struct device_node *dn, int cap) +static int pseries_eeh_find_ecap(struct pci_dn *pdn, int cap) { - struct pci_dn *pdn = PCI_DN(dn); - struct eeh_dev *edev = of_node_to_eeh_dev(dn); + struct eeh_dev *edev = pdn_to_eeh_dev(pdn); u32 header; int pos = 256; int ttl = (4096 - 256) / 8; @@ -191,53 +188,44 @@ static int pseries_eeh_find_ecap(struct device_node *dn, int cap) } /** - * pseries_eeh_of_probe - EEH probe on the given device - * @dn: OF node - * @flag: Unused + * pseries_eeh_probe - EEH probe on the given device + * @pdn: PCI device node + * @data: Unused * * When EEH module is installed during system boot, all PCI devices * are checked one by one to see if it supports EEH. The function * is introduced for the purpose. */ -static void *pseries_eeh_of_probe(struct device_node *dn, void *flag) +static void *pseries_eeh_probe(struct pci_dn *pdn, void *data) { struct eeh_dev *edev; struct eeh_pe pe; - struct pci_dn *pdn = PCI_DN(dn); - const __be32 *classp, *vendorp, *devicep; - u32 class_code; - const __be32 *regs; u32 pcie_flags; int enable = 0; int ret; /* Retrieve OF node and eeh device */ - edev = of_node_to_eeh_dev(dn); - if (edev->pe || !of_device_is_available(dn)) + edev = pdn_to_eeh_dev(pdn); + if (!edev || edev->pe) return NULL; - /* Retrieve class/vendor/device IDs */ - classp = of_get_property(dn, "class-code", NULL); - vendorp = of_get_property(dn, "vendor-id", NULL); - devicep = of_get_property(dn, "device-id", NULL); - - /* Skip for bad OF node or PCI-ISA bridge */ - if (!classp || !vendorp || !devicep) - return NULL; - if (dn->type && !strcmp(dn->type, "isa")) + /* Check class/vendor/device IDs */ + if (!pdn->vendor_id || !pdn->device_id || !pdn->class_code) return NULL; - class_code = of_read_number(classp, 1); + /* Skip for PCI-ISA bridge */ + if ((pdn->class_code >> 8) == PCI_CLASS_BRIDGE_ISA) + return NULL; /* * Update class code and mode of eeh device. We need * correctly reflects that current device is root port * or PCIe switch downstream port. */ - edev->class_code = class_code; - edev->pcix_cap = pseries_eeh_find_cap(dn, PCI_CAP_ID_PCIX); - edev->pcie_cap = pseries_eeh_find_cap(dn, PCI_CAP_ID_EXP); - edev->aer_cap = pseries_eeh_find_ecap(dn, PCI_EXT_CAP_ID_ERR); + edev->class_code = pdn->class_code; + edev->pcix_cap = pseries_eeh_find_cap(pdn, PCI_CAP_ID_PCIX); + edev->pcie_cap = pseries_eeh_find_cap(pdn, PCI_CAP_ID_EXP); + edev->aer_cap = pseries_eeh_find_ecap(pdn, PCI_EXT_CAP_ID_ERR); edev->mode &= 0xFFFFFF00; if ((edev->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) { edev->mode |= EEH_DEV_BRIDGE; @@ -252,24 +240,16 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag) } } - /* Retrieve the device address */ - regs = of_get_property(dn, "reg", NULL); - if (!regs) { - pr_warn("%s: OF node property %s::reg not found\n", - __func__, dn->full_name); - return NULL; - } - /* Initialize the fake PE */ memset(&pe, 0, sizeof(struct eeh_pe)); pe.phb = edev->phb; - pe.config_addr = of_read_number(regs, 1); + pe.config_addr = (pdn->busno << 16) | (pdn->devfn << 8); /* Enable EEH on the device */ ret = eeh_ops->set_option(&pe, EEH_OPT_ENABLE); if (!ret) { - edev->config_addr = of_read_number(regs, 1); /* Retrieve PE address */ + edev->config_addr = (pdn->busno << 16) | (pdn->devfn << 8); edev->pe_config_addr = eeh_ops->get_pe_addr(&pe); pe.addr = edev->pe_config_addr; @@ -285,16 +265,17 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag) eeh_add_flag(EEH_ENABLED); eeh_add_to_parent_pe(edev); - pr_debug("%s: EEH enabled on %s PHB#%d-PE#%x, config addr#%x\n", - __func__, dn->full_name, pe.phb->global_number, - pe.addr, pe.config_addr); - } else if (dn->parent && of_node_to_eeh_dev(dn->parent) && - (of_node_to_eeh_dev(dn->parent))->pe) { + pr_debug("%s: EEH enabled on %02x:%02x.%01x PHB#%d-PE#%x\n", + __func__, pdn->busno, PCI_SLOT(pdn->devfn), + PCI_FUNC(pdn->devfn), pe.phb->global_number, + pe.addr); + } else if (pdn->parent && pdn_to_eeh_dev(pdn->parent) && + (pdn_to_eeh_dev(pdn->parent))->pe) { /* This device doesn't support EEH, but it may have an * EEH parent, in which case we mark it as supported. */ - edev->config_addr = of_node_to_eeh_dev(dn->parent)->config_addr; - edev->pe_config_addr = of_node_to_eeh_dev(dn->parent)->pe_config_addr; + edev->config_addr = pdn_to_eeh_dev(pdn->parent)->config_addr; + edev->pe_config_addr = pdn_to_eeh_dev(pdn->parent)->pe_config_addr; eeh_add_to_parent_pe(edev); } } @@ -707,8 +688,7 @@ static int pseries_eeh_write_config(struct device_node *dn, int where, int size, static struct eeh_ops pseries_eeh_ops = { .name = "pseries", .init = pseries_eeh_init, - .of_probe = pseries_eeh_of_probe, - .dev_probe = NULL, + .probe = pseries_eeh_probe, .set_option = pseries_eeh_set_option, .get_pe_addr = pseries_eeh_get_pe_addr, .get_state = pseries_eeh_get_state, diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index 89e23811199c..f735f4fee48c 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -82,7 +82,7 @@ struct pci_controller *init_phb_dynamic(struct device_node *dn) eeh_dev_phb_init_dynamic(phb); if (dn->child) - eeh_add_device_tree_early(dn); + eeh_add_device_tree_early(PCI_DN(dn)); pcibios_scan_phb(phb); pcibios_finish_adding_to_bus(phb->bus); diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index 7660232ef460..e12bafdc42e0 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -146,7 +146,7 @@ static void dlpar_pci_add_bus(struct device_node *dn) struct pci_controller *phb = pdn->phb; struct pci_dev *dev = NULL; - eeh_add_device_tree_early(dn); + eeh_add_device_tree_early(pdn); /* Add EADS device to PHB bus, adding new entry to bus->devices */ dev = of_create_pci_dev(dn, phb->bus, pdn->devfn); -- cgit v1.2.3 From 0bd785873c6a6c9bd50d2ae19862f69ee5759fb9 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Tue, 17 Mar 2015 16:15:07 +1100 Subject: powerpc/eeh: Replace device_node with pci_dn in eeh_ops There are 3 EEH operations whose arguments contain device_node: read_config(), write_config() and restore_config(). The patch replaces device_node with pci_dn. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/eeh.h | 6 +- arch/powerpc/kernel/eeh.c | 40 ++++++++------ arch/powerpc/kernel/eeh_pe.c | 82 ++++++++++++++-------------- arch/powerpc/platforms/powernv/eeh-powernv.c | 40 ++++++-------- arch/powerpc/platforms/pseries/eeh_pseries.c | 16 ++---- 5 files changed, 87 insertions(+), 97 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 87797811808f..f847fb716653 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -217,10 +217,10 @@ struct eeh_ops { int (*configure_bridge)(struct eeh_pe *pe); int (*err_inject)(struct eeh_pe *pe, int type, int func, unsigned long addr, unsigned long mask); - int (*read_config)(struct device_node *dn, int where, int size, u32 *val); - int (*write_config)(struct device_node *dn, int where, int size, u32 val); + int (*read_config)(struct pci_dn *pdn, int where, int size, u32 *val); + int (*write_config)(struct pci_dn *pdn, int where, int size, u32 val); int (*next_error)(struct eeh_pe **pe); - int (*restore_config)(struct device_node *dn); + int (*restore_config)(struct pci_dn *pdn); }; extern int eeh_subsystem_flags; diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 9504c2f0bb54..1fd2566c87f1 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -164,30 +164,34 @@ __setup("eeh=", eeh_setup); */ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len) { - struct device_node *dn = eeh_dev_to_of_node(edev); + struct pci_dn *pdn = eeh_dev_to_pdn(edev); u32 cfg; int cap, i; int n = 0, l = 0; char buffer[128]; - n += scnprintf(buf+n, len-n, "%s\n", dn->full_name); - pr_warn("EEH: of node=%s\n", dn->full_name); + n += scnprintf(buf+n, len-n, "%04x:%02x:%02x:%01x\n", + edev->phb->global_number, pdn->busno, + PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn)); + pr_warn("EEH: of node=%04x:%02x:%02x:%01x\n", + edev->phb->global_number, pdn->busno, + PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn)); - eeh_ops->read_config(dn, PCI_VENDOR_ID, 4, &cfg); + eeh_ops->read_config(pdn, PCI_VENDOR_ID, 4, &cfg); n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg); pr_warn("EEH: PCI device/vendor: %08x\n", cfg); - eeh_ops->read_config(dn, PCI_COMMAND, 4, &cfg); + eeh_ops->read_config(pdn, PCI_COMMAND, 4, &cfg); n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg); pr_warn("EEH: PCI cmd/status register: %08x\n", cfg); /* Gather bridge-specific registers */ if (edev->mode & EEH_DEV_BRIDGE) { - eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg); + eeh_ops->read_config(pdn, PCI_SEC_STATUS, 2, &cfg); n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg); pr_warn("EEH: Bridge secondary status: %04x\n", cfg); - eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &cfg); + eeh_ops->read_config(pdn, PCI_BRIDGE_CONTROL, 2, &cfg); n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg); pr_warn("EEH: Bridge control: %04x\n", cfg); } @@ -195,11 +199,11 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len) /* Dump out the PCI-X command and status regs */ cap = edev->pcix_cap; if (cap) { - eeh_ops->read_config(dn, cap, 4, &cfg); + eeh_ops->read_config(pdn, cap, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg); pr_warn("EEH: PCI-X cmd: %08x\n", cfg); - eeh_ops->read_config(dn, cap+4, 4, &cfg); + eeh_ops->read_config(pdn, cap+4, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg); pr_warn("EEH: PCI-X status: %08x\n", cfg); } @@ -211,7 +215,7 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len) pr_warn("EEH: PCI-E capabilities and status follow:\n"); for (i=0; i<=8; i++) { - eeh_ops->read_config(dn, cap+4*i, 4, &cfg); + eeh_ops->read_config(pdn, cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); if ((i % 4) == 0) { @@ -238,7 +242,7 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len) pr_warn("EEH: PCI-E AER capability register set follows:\n"); for (i=0; i<=13; i++) { - eeh_ops->read_config(dn, cap+4*i, 4, &cfg); + eeh_ops->read_config(pdn, cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); if ((i % 4) == 0) { @@ -698,7 +702,7 @@ static void *eeh_disable_and_save_dev_state(void *data, void *userdata) static void *eeh_restore_dev_state(void *data, void *userdata) { struct eeh_dev *edev = data; - struct device_node *dn = eeh_dev_to_of_node(edev); + struct pci_dn *pdn = eeh_dev_to_pdn(edev); struct pci_dev *pdev = eeh_dev_to_pci_dev(edev); struct pci_dev *dev = userdata; @@ -706,8 +710,8 @@ static void *eeh_restore_dev_state(void *data, void *userdata) return NULL; /* Apply customization from firmware */ - if (dn && eeh_ops->restore_config) - eeh_ops->restore_config(dn); + if (pdn && eeh_ops->restore_config) + eeh_ops->restore_config(pdn); /* The caller should restore state for the specified device */ if (pdev != dev) @@ -870,15 +874,15 @@ out: */ void eeh_save_bars(struct eeh_dev *edev) { + struct pci_dn *pdn; int i; - struct device_node *dn; - if (!edev) + pdn = eeh_dev_to_pdn(edev); + if (!pdn) return; - dn = eeh_dev_to_of_node(edev); for (i = 0; i < 16; i++) - eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]); + eeh_ops->read_config(pdn, i * 4, 4, &edev->config_space[i]); /* * For PCI bridges including root port, we need enable bus diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c index 1e4946c36f9e..209cd753bf46 100644 --- a/arch/powerpc/kernel/eeh_pe.c +++ b/arch/powerpc/kernel/eeh_pe.c @@ -291,27 +291,25 @@ struct eeh_pe *eeh_pe_get(struct eeh_dev *edev) */ static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev) { - struct device_node *dn; struct eeh_dev *parent; + struct pci_dn *pdn = eeh_dev_to_pdn(edev); /* * It might have the case for the indirect parent * EEH device already having associated PE, but * the direct parent EEH device doesn't have yet. */ - dn = edev->dn->parent; - while (dn) { + pdn = pdn ? pdn->parent : NULL; + while (pdn) { /* We're poking out of PCI territory */ - if (!PCI_DN(dn)) return NULL; - - parent = of_node_to_eeh_dev(dn); - /* We're poking out of PCI territory */ - if (!parent) return NULL; + parent = pdn_to_eeh_dev(pdn); + if (!parent) + return NULL; if (parent->pe) return parent->pe; - dn = dn->parent; + pdn = pdn->parent; } return NULL; @@ -653,9 +651,9 @@ void eeh_pe_state_clear(struct eeh_pe *pe, int state) * blocked on normal path during the stage. So we need utilize * eeh operations, which is always permitted. */ -static void eeh_bridge_check_link(struct eeh_dev *edev, - struct device_node *dn) +static void eeh_bridge_check_link(struct eeh_dev *edev) { + struct pci_dn *pdn = eeh_dev_to_pdn(edev); int cap; uint32_t val; int timeout = 0; @@ -675,32 +673,32 @@ static void eeh_bridge_check_link(struct eeh_dev *edev, /* Check slot status */ cap = edev->pcie_cap; - eeh_ops->read_config(dn, cap + PCI_EXP_SLTSTA, 2, &val); + eeh_ops->read_config(pdn, cap + PCI_EXP_SLTSTA, 2, &val); if (!(val & PCI_EXP_SLTSTA_PDS)) { pr_debug(" No card in the slot (0x%04x) !\n", val); return; } /* Check power status if we have the capability */ - eeh_ops->read_config(dn, cap + PCI_EXP_SLTCAP, 2, &val); + eeh_ops->read_config(pdn, cap + PCI_EXP_SLTCAP, 2, &val); if (val & PCI_EXP_SLTCAP_PCP) { - eeh_ops->read_config(dn, cap + PCI_EXP_SLTCTL, 2, &val); + eeh_ops->read_config(pdn, cap + PCI_EXP_SLTCTL, 2, &val); if (val & PCI_EXP_SLTCTL_PCC) { pr_debug(" In power-off state, power it on ...\n"); val &= ~(PCI_EXP_SLTCTL_PCC | PCI_EXP_SLTCTL_PIC); val |= (0x0100 & PCI_EXP_SLTCTL_PIC); - eeh_ops->write_config(dn, cap + PCI_EXP_SLTCTL, 2, val); + eeh_ops->write_config(pdn, cap + PCI_EXP_SLTCTL, 2, val); msleep(2 * 1000); } } /* Enable link */ - eeh_ops->read_config(dn, cap + PCI_EXP_LNKCTL, 2, &val); + eeh_ops->read_config(pdn, cap + PCI_EXP_LNKCTL, 2, &val); val &= ~PCI_EXP_LNKCTL_LD; - eeh_ops->write_config(dn, cap + PCI_EXP_LNKCTL, 2, val); + eeh_ops->write_config(pdn, cap + PCI_EXP_LNKCTL, 2, val); /* Check link */ - eeh_ops->read_config(dn, cap + PCI_EXP_LNKCAP, 4, &val); + eeh_ops->read_config(pdn, cap + PCI_EXP_LNKCAP, 4, &val); if (!(val & PCI_EXP_LNKCAP_DLLLARC)) { pr_debug(" No link reporting capability (0x%08x) \n", val); msleep(1000); @@ -713,7 +711,7 @@ static void eeh_bridge_check_link(struct eeh_dev *edev, msleep(20); timeout += 20; - eeh_ops->read_config(dn, cap + PCI_EXP_LNKSTA, 2, &val); + eeh_ops->read_config(pdn, cap + PCI_EXP_LNKSTA, 2, &val); if (val & PCI_EXP_LNKSTA_DLLLA) break; } @@ -728,9 +726,9 @@ static void eeh_bridge_check_link(struct eeh_dev *edev, #define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF)) #define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)]) -static void eeh_restore_bridge_bars(struct eeh_dev *edev, - struct device_node *dn) +static void eeh_restore_bridge_bars(struct eeh_dev *edev) { + struct pci_dn *pdn = eeh_dev_to_pdn(edev); int i; /* @@ -738,49 +736,49 @@ static void eeh_restore_bridge_bars(struct eeh_dev *edev, * Bus numbers and windows: 0x18 - 0x30 */ for (i = 4; i < 13; i++) - eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]); + eeh_ops->write_config(pdn, i*4, 4, edev->config_space[i]); /* Rom: 0x38 */ - eeh_ops->write_config(dn, 14*4, 4, edev->config_space[14]); + eeh_ops->write_config(pdn, 14*4, 4, edev->config_space[14]); /* Cache line & Latency timer: 0xC 0xD */ - eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1, + eeh_ops->write_config(pdn, PCI_CACHE_LINE_SIZE, 1, SAVED_BYTE(PCI_CACHE_LINE_SIZE)); - eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1, + eeh_ops->write_config(pdn, PCI_LATENCY_TIMER, 1, SAVED_BYTE(PCI_LATENCY_TIMER)); /* Max latency, min grant, interrupt ping and line: 0x3C */ - eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]); + eeh_ops->write_config(pdn, 15*4, 4, edev->config_space[15]); /* PCI Command: 0x4 */ - eeh_ops->write_config(dn, PCI_COMMAND, 4, edev->config_space[1]); + eeh_ops->write_config(pdn, PCI_COMMAND, 4, edev->config_space[1]); /* Check the PCIe link is ready */ - eeh_bridge_check_link(edev, dn); + eeh_bridge_check_link(edev); } -static void eeh_restore_device_bars(struct eeh_dev *edev, - struct device_node *dn) +static void eeh_restore_device_bars(struct eeh_dev *edev) { + struct pci_dn *pdn = eeh_dev_to_pdn(edev); int i; u32 cmd; for (i = 4; i < 10; i++) - eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]); + eeh_ops->write_config(pdn, i*4, 4, edev->config_space[i]); /* 12 == Expansion ROM Address */ - eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]); + eeh_ops->write_config(pdn, 12*4, 4, edev->config_space[12]); - eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1, + eeh_ops->write_config(pdn, PCI_CACHE_LINE_SIZE, 1, SAVED_BYTE(PCI_CACHE_LINE_SIZE)); - eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1, + eeh_ops->write_config(pdn, PCI_LATENCY_TIMER, 1, SAVED_BYTE(PCI_LATENCY_TIMER)); /* max latency, min grant, interrupt pin and line */ - eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]); + eeh_ops->write_config(pdn, 15*4, 4, edev->config_space[15]); /* * Restore PERR & SERR bits, some devices require it, * don't touch the other command bits */ - eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd); + eeh_ops->read_config(pdn, PCI_COMMAND, 4, &cmd); if (edev->config_space[1] & PCI_COMMAND_PARITY) cmd |= PCI_COMMAND_PARITY; else @@ -789,7 +787,7 @@ static void eeh_restore_device_bars(struct eeh_dev *edev, cmd |= PCI_COMMAND_SERR; else cmd &= ~PCI_COMMAND_SERR; - eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd); + eeh_ops->write_config(pdn, PCI_COMMAND, 4, cmd); } /** @@ -804,16 +802,16 @@ static void eeh_restore_device_bars(struct eeh_dev *edev, static void *eeh_restore_one_device_bars(void *data, void *flag) { struct eeh_dev *edev = (struct eeh_dev *)data; - struct device_node *dn = eeh_dev_to_of_node(edev); + struct pci_dn *pdn = eeh_dev_to_pdn(edev); /* Do special restore for bridges */ if (edev->mode & EEH_DEV_BRIDGE) - eeh_restore_bridge_bars(edev, dn); + eeh_restore_bridge_bars(edev); else - eeh_restore_device_bars(edev, dn); + eeh_restore_device_bars(edev); - if (eeh_ops->restore_config) - eeh_ops->restore_config(dn); + if (eeh_ops->restore_config && pdn) + eeh_ops->restore_config(pdn); return NULL; } diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c index dcc524fe2a30..ce738ab3d5a9 100644 --- a/arch/powerpc/platforms/powernv/eeh-powernv.c +++ b/arch/powerpc/platforms/powernv/eeh-powernv.c @@ -842,8 +842,8 @@ out: static int pnv_eeh_bridge_reset(struct pci_dev *dev, int option) { - struct device_node *dn = pci_device_to_OF_node(dev); - struct eeh_dev *edev = of_node_to_eeh_dev(dn); + struct pci_dn *pdn = pci_get_pdn_by_devfn(dev->bus, dev->devfn); + struct eeh_dev *edev = pdn_to_eeh_dev(pdn); int aer = edev ? edev->aer_cap : 0; u32 ctrl; @@ -856,32 +856,32 @@ static int pnv_eeh_bridge_reset(struct pci_dev *dev, int option) case EEH_RESET_HOT: /* Don't report linkDown event */ if (aer) { - eeh_ops->read_config(dn, aer + PCI_ERR_UNCOR_MASK, + eeh_ops->read_config(pdn, aer + PCI_ERR_UNCOR_MASK, 4, &ctrl); ctrl |= PCI_ERR_UNC_SURPDN; - eeh_ops->write_config(dn, aer + PCI_ERR_UNCOR_MASK, + eeh_ops->write_config(pdn, aer + PCI_ERR_UNCOR_MASK, 4, ctrl); } - eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &ctrl); + eeh_ops->read_config(pdn, PCI_BRIDGE_CONTROL, 2, &ctrl); ctrl |= PCI_BRIDGE_CTL_BUS_RESET; - eeh_ops->write_config(dn, PCI_BRIDGE_CONTROL, 2, ctrl); + eeh_ops->write_config(pdn, PCI_BRIDGE_CONTROL, 2, ctrl); msleep(EEH_PE_RST_HOLD_TIME); break; case EEH_RESET_DEACTIVATE: - eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &ctrl); + eeh_ops->read_config(pdn, PCI_BRIDGE_CONTROL, 2, &ctrl); ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; - eeh_ops->write_config(dn, PCI_BRIDGE_CONTROL, 2, ctrl); + eeh_ops->write_config(pdn, PCI_BRIDGE_CONTROL, 2, ctrl); msleep(EEH_PE_RST_SETTLE_TIME); /* Continue reporting linkDown event */ if (aer) { - eeh_ops->read_config(dn, aer + PCI_ERR_UNCOR_MASK, + eeh_ops->read_config(pdn, aer + PCI_ERR_UNCOR_MASK, 4, &ctrl); ctrl &= ~PCI_ERR_UNC_SURPDN; - eeh_ops->write_config(dn, aer + PCI_ERR_UNCOR_MASK, + eeh_ops->write_config(pdn, aer + PCI_ERR_UNCOR_MASK, 4, ctrl); } @@ -1099,9 +1099,9 @@ static int pnv_eeh_err_inject(struct eeh_pe *pe, int type, int func, return 0; } -static inline bool pnv_eeh_cfg_blocked(struct device_node *dn) +static inline bool pnv_eeh_cfg_blocked(struct pci_dn *pdn) { - struct eeh_dev *edev = of_node_to_eeh_dev(dn); + struct eeh_dev *edev = pdn_to_eeh_dev(pdn); if (!edev || !edev->pe) return false; @@ -1112,15 +1112,13 @@ static inline bool pnv_eeh_cfg_blocked(struct device_node *dn) return false; } -static int pnv_eeh_read_config(struct device_node *dn, +static int pnv_eeh_read_config(struct pci_dn *pdn, int where, int size, u32 *val) { - struct pci_dn *pdn = PCI_DN(dn); - if (!pdn) return PCIBIOS_DEVICE_NOT_FOUND; - if (pnv_eeh_cfg_blocked(dn)) { + if (pnv_eeh_cfg_blocked(pdn)) { *val = 0xFFFFFFFF; return PCIBIOS_SET_FAILED; } @@ -1128,15 +1126,13 @@ static int pnv_eeh_read_config(struct device_node *dn, return pnv_pci_cfg_read(pdn, where, size, val); } -static int pnv_eeh_write_config(struct device_node *dn, +static int pnv_eeh_write_config(struct pci_dn *pdn, int where, int size, u32 val) { - struct pci_dn *pdn = PCI_DN(dn); - if (!pdn) return PCIBIOS_DEVICE_NOT_FOUND; - if (pnv_eeh_cfg_blocked(dn)) + if (pnv_eeh_cfg_blocked(pdn)) return PCIBIOS_SET_FAILED; return pnv_pci_cfg_write(pdn, where, size, val); @@ -1484,9 +1480,9 @@ static int pnv_eeh_next_error(struct eeh_pe **pe) return ret; } -static int pnv_eeh_restore_config(struct device_node *dn) +static int pnv_eeh_restore_config(struct pci_dn *pdn) { - struct eeh_dev *edev = of_node_to_eeh_dev(dn); + struct eeh_dev *edev = pdn_to_eeh_dev(pdn); struct pnv_phb *phb; s64 ret; diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index a2946f72d5e7..2039397cc75d 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -651,37 +651,29 @@ static int pseries_eeh_configure_bridge(struct eeh_pe *pe) /** * pseries_eeh_read_config - Read PCI config space - * @dn: device node + * @pdn: PCI device node * @where: PCI address * @size: size to read * @val: return value * * Read config space from the speicifed device */ -static int pseries_eeh_read_config(struct device_node *dn, int where, int size, u32 *val) +static int pseries_eeh_read_config(struct pci_dn *pdn, int where, int size, u32 *val) { - struct pci_dn *pdn; - - pdn = PCI_DN(dn); - return rtas_read_config(pdn, where, size, val); } /** * pseries_eeh_write_config - Write PCI config space - * @dn: device node + * @pdn: PCI device node * @where: PCI address * @size: size to write * @val: value to be written * * Write config space to the specified device */ -static int pseries_eeh_write_config(struct device_node *dn, int where, int size, u32 val) +static int pseries_eeh_write_config(struct pci_dn *pdn, int where, int size, u32 val) { - struct pci_dn *pdn; - - pdn = PCI_DN(dn); - return rtas_write_config(pdn, where, size, val); } -- cgit v1.2.3 From c6406d8fbb014bebdfb5bf3c244548958aec7379 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Tue, 17 Mar 2015 16:15:08 +1100 Subject: powerpc/eeh: Remove device_node dependency The patch removes struct eeh_dev::dn and the corresponding helper functions: eeh_dev_to_of_node() and of_node_to_eeh_dev(). Instead, eeh_dev_to_pdn() and pdn_to_eeh_dev() should be used to get the pdn, which might contain device_node on PowerNV platform. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/eeh.h | 7 ------- arch/powerpc/include/asm/pci-bridge.h | 14 -------------- arch/powerpc/kernel/eeh.c | 24 +++++++++++++----------- arch/powerpc/kernel/eeh_cache.c | 25 +++++++++++-------------- arch/powerpc/kernel/eeh_driver.c | 22 ---------------------- arch/powerpc/kernel/eeh_pe.c | 34 +++++++++++++++++++++++++--------- arch/powerpc/kernel/pci_of_scan.c | 2 +- arch/powerpc/kernel/rtas_pci.c | 2 +- arch/powerpc/platforms/pseries/msi.c | 6 ++++-- 9 files changed, 55 insertions(+), 81 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index f847fb716653..a52db28ecc1e 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -29,7 +29,6 @@ struct pci_dev; struct pci_bus; -struct device_node; struct pci_dn; #ifdef CONFIG_EEH @@ -137,17 +136,11 @@ struct eeh_dev { struct eeh_pe *pe; /* Associated PE */ struct list_head list; /* Form link list in the PE */ struct pci_controller *phb; /* Associated PHB */ - struct device_node *dn; /* Associated device node */ struct pci_dn *pdn; /* Associated PCI device node */ struct pci_dev *pdev; /* Associated PCI device */ struct pci_bus *bus; /* PCI bus for partial hotplug */ }; -static inline struct device_node *eeh_dev_to_of_node(struct eeh_dev *edev) -{ - return edev ? edev->dn : NULL; -} - static inline struct pci_dn *eeh_dev_to_pdn(struct eeh_dev *edev) { return edev ? edev->pdn : NULL; diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 7b74499b728c..2c6dc2a3d14a 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -201,25 +201,11 @@ static inline int pci_device_from_OF_node(struct device_node *np, } #if defined(CONFIG_EEH) -static inline struct eeh_dev *of_node_to_eeh_dev(struct device_node *dn) -{ - /* - * For those OF nodes whose parent isn't PCI bridge, they - * don't have PCI_DN actually. So we have to skip them for - * any EEH operations. - */ - if (!dn || !PCI_DN(dn)) - return NULL; - - return PCI_DN(dn)->edev; -} - static inline struct eeh_dev *pdn_to_eeh_dev(struct pci_dn *pdn) { return pdn ? pdn->edev : NULL; } #else -#define of_node_to_eeh_dev(x) (NULL) #define pdn_to_eeh_dev(x) (NULL) #endif diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 1fd2566c87f1..76253eb146be 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -418,11 +418,11 @@ int eeh_dev_check_failure(struct eeh_dev *edev) int ret; int active_flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE); unsigned long flags; - struct device_node *dn; + struct pci_dn *pdn; struct pci_dev *dev; struct eeh_pe *pe, *parent_pe, *phb_pe; int rc = 0; - const char *location; + const char *location = NULL; eeh_stats.total_mmio_ffs++; @@ -433,15 +433,14 @@ int eeh_dev_check_failure(struct eeh_dev *edev) eeh_stats.no_dn++; return 0; } - dn = eeh_dev_to_of_node(edev); dev = eeh_dev_to_pci_dev(edev); pe = eeh_dev_to_pe(edev); /* Access to IO BARs might get this far and still not want checking. */ if (!pe) { eeh_stats.ignored_check++; - pr_debug("EEH: Ignored check for %s %s\n", - eeh_pci_name(dev), dn->full_name); + pr_debug("EEH: Ignored check for %s\n", + eeh_pci_name(dev)); return 0; } @@ -477,10 +476,13 @@ int eeh_dev_check_failure(struct eeh_dev *edev) if (pe->state & EEH_PE_ISOLATED) { pe->check_count++; if (pe->check_count % EEH_MAX_FAILS == 0) { - location = of_get_property(dn, "ibm,loc-code", NULL); + pdn = eeh_dev_to_pdn(edev); + if (pdn->node) + location = of_get_property(pdn->node, "ibm,loc-code", NULL); printk(KERN_ERR "EEH: %d reads ignored for recovering device at " "location=%s driver=%s pci addr=%s\n", - pe->check_count, location, + pe->check_count, + location ? location : "unknown", eeh_driver_name(dev), eeh_pci_name(dev)); printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n", eeh_driver_name(dev)); @@ -1035,7 +1037,7 @@ int eeh_init(void) core_initcall_sync(eeh_init); /** - * eeh_add_device_early - Enable EEH for the indicated device_node + * eeh_add_device_early - Enable EEH for the indicated device node * @pdn: PCI device node for which to set up EEH * * This routine must be used to perform EEH initialization for PCI @@ -1093,7 +1095,7 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_early); */ void eeh_add_device_late(struct pci_dev *dev) { - struct device_node *dn; + struct pci_dn *pdn; struct eeh_dev *edev; if (!dev || !eeh_enabled()) @@ -1101,8 +1103,8 @@ void eeh_add_device_late(struct pci_dev *dev) pr_debug("EEH: Adding device %s\n", pci_name(dev)); - dn = pci_device_to_OF_node(dev); - edev = of_node_to_eeh_dev(dn); + pdn = pci_get_pdn_by_devfn(dev->bus, dev->devfn); + edev = pdn_to_eeh_dev(pdn); if (edev->pdev == dev) { pr_debug("EEH: Already referenced !\n"); return; diff --git a/arch/powerpc/kernel/eeh_cache.c b/arch/powerpc/kernel/eeh_cache.c index 07d8a2423a61..eeabeabea49c 100644 --- a/arch/powerpc/kernel/eeh_cache.c +++ b/arch/powerpc/kernel/eeh_cache.c @@ -171,30 +171,27 @@ eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo, static void __eeh_addr_cache_insert_dev(struct pci_dev *dev) { - struct device_node *dn; + struct pci_dn *pdn; struct eeh_dev *edev; int i; - dn = pci_device_to_OF_node(dev); - if (!dn) { + pdn = pci_get_pdn_by_devfn(dev->bus, dev->devfn); + if (!pdn) { pr_warn("PCI: no pci dn found for dev=%s\n", pci_name(dev)); return; } - edev = of_node_to_eeh_dev(dn); + edev = pdn_to_eeh_dev(pdn); if (!edev) { - pr_warn("PCI: no EEH dev found for dn=%s\n", - dn->full_name); + pr_warn("PCI: no EEH dev found for %s\n", + pci_name(dev)); return; } /* Skip any devices for which EEH is not enabled. */ if (!edev->pe) { -#ifdef DEBUG - pr_info("PCI: skip building address cache for=%s - %s\n", - pci_name(dev), dn->full_name); -#endif + dev_dbg(&dev->dev, "EEH: Skip building address cache\n"); return; } @@ -282,18 +279,18 @@ void eeh_addr_cache_rmv_dev(struct pci_dev *dev) */ void eeh_addr_cache_build(void) { - struct device_node *dn; + struct pci_dn *pdn; struct eeh_dev *edev; struct pci_dev *dev = NULL; spin_lock_init(&pci_io_addr_cache_root.piar_lock); for_each_pci_dev(dev) { - dn = pci_device_to_OF_node(dev); - if (!dn) + pdn = pci_get_pdn_by_devfn(dev->bus, dev->devfn); + if (!pdn) continue; - edev = of_node_to_eeh_dev(dn); + edev = pdn_to_eeh_dev(pdn); if (!edev) continue; diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index d099540c0f56..24768ff3cb73 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -83,28 +83,6 @@ static inline void eeh_pcid_put(struct pci_dev *pdev) module_put(pdev->driver->driver.owner); } -#if 0 -static void print_device_node_tree(struct pci_dn *pdn, int dent) -{ - int i; - struct device_node *pc; - - if (!pdn) - return; - for (i = 0; i < dent; i++) - printk(" "); - printk("dn=%s mode=%x \tcfg_addr=%x pe_addr=%x \tfull=%s\n", - pdn->node->name, pdn->eeh_mode, pdn->eeh_config_addr, - pdn->eeh_pe_config_addr, pdn->node->full_name); - dent += 3; - pc = pdn->node->child; - while (pc) { - print_device_node_tree(PCI_DN(pc), dent); - pc = pc->sibling; - } -} -#endif - /** * eeh_disable_irq - Disable interrupt for the recovering device * @dev: PCI device diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c index 209cd753bf46..f33ceccf6876 100644 --- a/arch/powerpc/kernel/eeh_pe.c +++ b/arch/powerpc/kernel/eeh_pe.c @@ -348,9 +348,12 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) /* Put the edev to PE */ list_add_tail(&edev->list, &pe->edevs); - pr_debug("EEH: Add %s to Bus PE#%x\n", - edev->dn->full_name, pe->addr); - + pr_debug("EEH: Add %04x:%02x:%02x.%01x to Bus PE#%x\n", + edev->phb->global_number, + edev->config_addr >> 8, + PCI_SLOT(edev->config_addr & 0xFF), + PCI_FUNC(edev->config_addr & 0xFF), + pe->addr); return 0; } else if (pe && (pe->type & EEH_PE_INVALID)) { list_add_tail(&edev->list, &pe->edevs); @@ -366,9 +369,14 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) parent->type &= ~(EEH_PE_INVALID | EEH_PE_KEEP); parent = parent->parent; } - pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", - edev->dn->full_name, pe->addr, pe->parent->addr); + pr_debug("EEH: Add %04x:%02x:%02x.%01x to Device " + "PE#%x, Parent PE#%x\n", + edev->phb->global_number, + edev->config_addr >> 8, + PCI_SLOT(edev->config_addr & 0xFF), + PCI_FUNC(edev->config_addr & 0xFF), + pe->addr, pe->parent->addr); return 0; } @@ -407,8 +415,13 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) list_add_tail(&pe->child, &parent->child_list); list_add_tail(&edev->list, &pe->edevs); edev->pe = pe; - pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", - edev->dn->full_name, pe->addr, pe->parent->addr); + pr_debug("EEH: Add %04x:%02x:%02x.%01x to " + "Device PE#%x, Parent PE#%x\n", + edev->phb->global_number, + edev->config_addr >> 8, + PCI_SLOT(edev->config_addr & 0xFF), + PCI_FUNC(edev->config_addr & 0xFF), + pe->addr, pe->parent->addr); return 0; } @@ -428,8 +441,11 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev) int cnt; if (!edev->pe) { - pr_debug("%s: No PE found for EEH device %s\n", - __func__, edev->dn->full_name); + pr_debug("%s: No PE found for device %04x:%02x:%02x.%01x\n", + __func__, edev->phb->global_number, + edev->config_addr >> 8, + PCI_SLOT(edev->config_addr & 0xFF), + PCI_FUNC(edev->config_addr & 0xFF)); return -EEXIST; } diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c index e6245e9c7d8d..7122dfece393 100644 --- a/arch/powerpc/kernel/pci_of_scan.c +++ b/arch/powerpc/kernel/pci_of_scan.c @@ -305,7 +305,7 @@ static struct pci_dev *of_scan_pci_dev(struct pci_bus *bus, const __be32 *reg; int reglen, devfn; #ifdef CONFIG_EEH - struct eeh_dev *edev = of_node_to_eeh_dev(dn); + struct eeh_dev *edev = pdn_to_eeh_dev(PCI_DN(dn)); #endif pr_debug(" * %s\n", dn->full_name); diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c index ce230da2c015..af29df2517f7 100644 --- a/arch/powerpc/kernel/rtas_pci.c +++ b/arch/powerpc/kernel/rtas_pci.c @@ -113,7 +113,7 @@ static int rtas_pci_read_config(struct pci_bus *bus, ret = rtas_read_config(pdn, where, size, val); if (*val == EEH_IO_ERROR_VALUE(size) && - eeh_dev_check_failure(of_node_to_eeh_dev(dn))) + eeh_dev_check_failure(pdn_to_eeh_dev(pdn))) return PCIBIOS_DEVICE_NOT_FOUND; return ret; diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c index 691a154c286d..c8d24f9a6948 100644 --- a/arch/powerpc/platforms/pseries/msi.c +++ b/arch/powerpc/platforms/pseries/msi.c @@ -195,6 +195,7 @@ static struct device_node *find_pe_total_msi(struct pci_dev *dev, int *total) static struct device_node *find_pe_dn(struct pci_dev *dev, int *total) { struct device_node *dn; + struct pci_dn *pdn; struct eeh_dev *edev; /* Found our PE and assume 8 at that point. */ @@ -204,10 +205,11 @@ static struct device_node *find_pe_dn(struct pci_dev *dev, int *total) return NULL; /* Get the top level device in the PE */ - edev = of_node_to_eeh_dev(dn); + edev = pdn_to_eeh_dev(PCI_DN(dn)); if (edev->pe) edev = list_first_entry(&edev->pe->edevs, struct eeh_dev, list); - dn = eeh_dev_to_of_node(edev); + pdn = eeh_dev_to_pdn(edev); + dn = pdn ? pdn->node : NULL; if (!dn) return NULL; -- cgit v1.2.3 From c03e73740d24fbe990291cd9ac2d6ae0d95b975f Mon Sep 17 00:00:00 2001 From: Tyrel Datwyler Date: Fri, 27 Mar 2015 12:47:25 -0700 Subject: powerpc/pseries: Simplify check for suspendability during suspend/migration During suspend/migration operation we must wait for the VASI state reported by the hypervisor to become Suspending prior to making the ibm,suspend-me RTAS call. Calling routines to rtas_ibm_supend_me() pass a vasi_state variable that exposes the VASI state to the caller. This is unnecessary as the caller only really cares about the following three conditions; if there is an error we should bailout, success indicating we have suspended and woken back up so proceed to device tree update, or we are not suspendable yet so try calling rtas_ibm_suspend_me again shortly. This patch removes the extraneous vasi_state variable and simply uses the return code to communicate how to proceed. We either succeed, fail, or get -EAGAIN in which case we sleep for a second before trying to call rtas_ibm_suspend_me again. The behaviour of ppc_rtas() remains the same, but migrate_store() now returns the propogated error code on failure. Previously -1 was returned from migrate_store() in the failure case which equates to -EPERM and was clearly wrong. Signed-off-by: Tyrel Datwyler Cc: Nathan Fontenont Cc: Cyril Bur Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/rtas.h | 2 +- arch/powerpc/kernel/rtas.c | 26 +++++++++++++------------- arch/powerpc/platforms/pseries/mobility.c | 9 +++------ 3 files changed, 17 insertions(+), 20 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h index c3c99eb35448..398106f12617 100644 --- a/arch/powerpc/include/asm/rtas.h +++ b/arch/powerpc/include/asm/rtas.h @@ -328,7 +328,7 @@ extern int rtas_suspend_cpu(struct rtas_suspend_me_data *data); extern int rtas_suspend_last_cpu(struct rtas_suspend_me_data *data); extern int rtas_online_cpus_mask(cpumask_var_t cpus); extern int rtas_offline_cpus_mask(cpumask_var_t cpus); -extern int rtas_ibm_suspend_me(u64 handle, int *vasi_return); +extern int rtas_ibm_suspend_me(u64 handle); struct rtc_time; extern unsigned long rtas_get_boot_time(void); diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index 21c45a2d0706..b9a7b8981ef7 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -897,7 +897,7 @@ int rtas_offline_cpus_mask(cpumask_var_t cpus) } EXPORT_SYMBOL(rtas_offline_cpus_mask); -int rtas_ibm_suspend_me(u64 handle, int *vasi_return) +int rtas_ibm_suspend_me(u64 handle) { long state; long rc; @@ -919,13 +919,11 @@ int rtas_ibm_suspend_me(u64 handle, int *vasi_return) printk(KERN_ERR "rtas_ibm_suspend_me: vasi_state returned %ld\n",rc); return rc; } else if (state == H_VASI_ENABLED) { - *vasi_return = RTAS_NOT_SUSPENDABLE; - return 0; + return -EAGAIN; } else if (state != H_VASI_SUSPENDING) { printk(KERN_ERR "rtas_ibm_suspend_me: vasi_state returned state %ld\n", state); - *vasi_return = -1; - return 0; + return -EIO; } if (!alloc_cpumask_var(&offline_mask, GFP_TEMPORARY)) @@ -972,7 +970,7 @@ out: return atomic_read(&data.error); } #else /* CONFIG_PPC_PSERIES */ -int rtas_ibm_suspend_me(u64 handle, int *vasi_return) +int rtas_ibm_suspend_me(u64 handle) { return -ENOSYS; } @@ -1022,7 +1020,6 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) unsigned long flags; char *buff_copy, *errbuf = NULL; int nargs, nret, token; - int rc; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -1054,15 +1051,18 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) if (token == ibm_suspend_me_token) { /* - * rtas_ibm_suspend_me assumes args are in cpu endian, or at least the - * hcall within it requires it. + * rtas_ibm_suspend_me assumes the streamid handle is in cpu + * endian, or at least the hcall within it requires it. */ - int vasi_rc = 0; + int rc = 0; u64 handle = ((u64)be32_to_cpu(args.args[0]) << 32) | be32_to_cpu(args.args[1]); - rc = rtas_ibm_suspend_me(handle, &vasi_rc); - args.rets[0] = cpu_to_be32(vasi_rc); - if (rc) + rc = rtas_ibm_suspend_me(handle); + if (rc == -EAGAIN) + args.rets[0] = cpu_to_be32(RTAS_NOT_SUSPENDABLE); + else if (rc == -EIO) + args.rets[0] = cpu_to_be32(-1); + else if (rc) return rc; goto copy_return; } diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c index 03a428e87b14..38db1b9f2ac3 100644 --- a/arch/powerpc/platforms/pseries/mobility.c +++ b/arch/powerpc/platforms/pseries/mobility.c @@ -318,22 +318,19 @@ static ssize_t migrate_store(struct class *class, struct class_attribute *attr, { u64 streamid; int rc; - int vasi_rc = 0; rc = kstrtou64(buf, 0, &streamid); if (rc) return rc; do { - rc = rtas_ibm_suspend_me(streamid, &vasi_rc); - if (!rc && vasi_rc == RTAS_NOT_SUSPENDABLE) + rc = rtas_ibm_suspend_me(streamid); + if (rc == -EAGAIN) ssleep(1); - } while (!rc && vasi_rc == RTAS_NOT_SUSPENDABLE); + } while (rc == -EAGAIN); if (rc) return rc; - if (vasi_rc) - return vasi_rc; post_mobility_fixup(); return count; -- cgit v1.2.3 From 529d235a0e190ded1d21ccc80a73e625ebcad09b Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Sat, 28 Mar 2015 21:35:16 +1100 Subject: powerpc: Add a proper syscall for switching endianness We currently have a "special" syscall for switching endianness. This is syscall number 0x1ebe, which is handled explicitly in the 64-bit syscall exception entry. That has a few problems, firstly the syscall number is outside of the usual range, which confuses various tools. For example strace doesn't recognise the syscall at all. Secondly it's handled explicitly as a special case in the syscall exception entry, which is complicated enough without it. As a first step toward removing the special syscall, we need to add a regular syscall that implements the same functionality. The logic is simple, it simply toggles the MSR_LE bit in the userspace MSR. This is the same as the special syscall, with the caveat that the special syscall clobbers fewer registers. This version clobbers r9-r12, XER, CTR, and CR0-1,5-7. Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/systbl.h | 1 + arch/powerpc/include/asm/unistd.h | 2 +- arch/powerpc/include/uapi/asm/unistd.h | 1 + arch/powerpc/kernel/entry_64.S | 5 +++++ arch/powerpc/kernel/syscalls.c | 17 +++++++++++++++++ arch/powerpc/kernel/systbl.S | 2 ++ arch/powerpc/kernel/systbl_chk.c | 2 ++ arch/powerpc/platforms/cell/spu_callbacks.c | 1 + 8 files changed, 30 insertions(+), 1 deletion(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/systbl.h b/arch/powerpc/include/asm/systbl.h index 91062eef582f..f1863a138b4a 100644 --- a/arch/powerpc/include/asm/systbl.h +++ b/arch/powerpc/include/asm/systbl.h @@ -367,3 +367,4 @@ SYSCALL_SPU(getrandom) SYSCALL_SPU(memfd_create) SYSCALL_SPU(bpf) COMPAT_SYS(execveat) +PPC64ONLY(switch_endian) diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h index 36b79c31eedd..f4f8b667d75b 100644 --- a/arch/powerpc/include/asm/unistd.h +++ b/arch/powerpc/include/asm/unistd.h @@ -12,7 +12,7 @@ #include -#define __NR_syscalls 363 +#define __NR_syscalls 364 #define __NR__exit __NR_exit #define NR_syscalls __NR_syscalls diff --git a/arch/powerpc/include/uapi/asm/unistd.h b/arch/powerpc/include/uapi/asm/unistd.h index ef5b5b1f3123..e4aa173dae62 100644 --- a/arch/powerpc/include/uapi/asm/unistd.h +++ b/arch/powerpc/include/uapi/asm/unistd.h @@ -385,5 +385,6 @@ #define __NR_memfd_create 360 #define __NR_bpf 361 #define __NR_execveat 362 +#define __NR_switch_endian 363 #endif /* _UAPI_ASM_POWERPC_UNISTD_H_ */ diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index d180caf2d6de..afbc20019c2e 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -356,6 +356,11 @@ _GLOBAL(ppc64_swapcontext) bl sys_swapcontext b .Lsyscall_exit +_GLOBAL(ppc_switch_endian) + bl save_nvgprs + bl sys_switch_endian + b .Lsyscall_exit + _GLOBAL(ret_from_fork) bl schedule_tail REST_NVGPRS(r1) diff --git a/arch/powerpc/kernel/syscalls.c b/arch/powerpc/kernel/syscalls.c index b2702e87db0d..5fa92706444b 100644 --- a/arch/powerpc/kernel/syscalls.c +++ b/arch/powerpc/kernel/syscalls.c @@ -121,3 +121,20 @@ long ppc_fadvise64_64(int fd, int advice, u32 offset_high, u32 offset_low, return sys_fadvise64(fd, (u64)offset_high << 32 | offset_low, (u64)len_high << 32 | len_low, advice); } + +long sys_switch_endian(void) +{ + struct thread_info *ti; + + current->thread.regs->msr ^= MSR_LE; + + /* + * Set TIF_RESTOREALL so that r3 isn't clobbered on return to + * userspace. That also has the effect of restoring the non-volatile + * GPRs, so we saved them on the way in here. + */ + ti = current_thread_info(); + ti->flags |= _TIF_RESTOREALL; + + return 0; +} diff --git a/arch/powerpc/kernel/systbl.S b/arch/powerpc/kernel/systbl.S index 7ab5d434e2ee..4d6b1d3a747f 100644 --- a/arch/powerpc/kernel/systbl.S +++ b/arch/powerpc/kernel/systbl.S @@ -22,6 +22,7 @@ #define PPC_SYS(func) .llong DOTSYM(ppc_##func),DOTSYM(ppc_##func) #define OLDSYS(func) .llong DOTSYM(sys_ni_syscall),DOTSYM(sys_ni_syscall) #define SYS32ONLY(func) .llong DOTSYM(sys_ni_syscall),DOTSYM(compat_sys_##func) +#define PPC64ONLY(func) .llong DOTSYM(ppc_##func),DOTSYM(sys_ni_syscall) #define SYSX(f, f3264, f32) .llong DOTSYM(f),DOTSYM(f3264) #else #define SYSCALL(func) .long sys_##func @@ -29,6 +30,7 @@ #define PPC_SYS(func) .long ppc_##func #define OLDSYS(func) .long sys_##func #define SYS32ONLY(func) .long sys_##func +#define PPC64ONLY(func) .long sys_ni_syscall #define SYSX(f, f3264, f32) .long f32 #endif #define SYSCALL_SPU(func) SYSCALL(func) diff --git a/arch/powerpc/kernel/systbl_chk.c b/arch/powerpc/kernel/systbl_chk.c index 238aa63ced8f..2384129f5893 100644 --- a/arch/powerpc/kernel/systbl_chk.c +++ b/arch/powerpc/kernel/systbl_chk.c @@ -21,9 +21,11 @@ #ifdef CONFIG_PPC64 #define OLDSYS(func) -1 #define SYS32ONLY(func) -1 +#define PPC64ONLY(func) __NR_##func #else #define OLDSYS(func) __NR_old##func #define SYS32ONLY(func) __NR_##func +#define PPC64ONLY(func) -1 #endif #define SYSX(f, f3264, f32) -1 diff --git a/arch/powerpc/platforms/cell/spu_callbacks.c b/arch/powerpc/platforms/cell/spu_callbacks.c index b0ec78e8ad68..a494028b2cdf 100644 --- a/arch/powerpc/platforms/cell/spu_callbacks.c +++ b/arch/powerpc/platforms/cell/spu_callbacks.c @@ -39,6 +39,7 @@ static void *spu_syscall_table[] = { #define PPC_SYS(func) sys_ni_syscall, #define OLDSYS(func) sys_ni_syscall, #define SYS32ONLY(func) sys_ni_syscall, +#define PPC64ONLY(func) sys_ni_syscall, #define SYSX(f, f3264, f32) sys_ni_syscall, #define SYSCALL_SPU(func) sys_##func, -- cgit v1.2.3 From a8b2f8288a3fdef8d93efef2b1ead7563004275e Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Wed, 25 Mar 2015 16:23:52 +0800 Subject: powerpc/pci: Create pci_dn for VFs pci_dn is the extension of PCI device node and is created from device node. Unfortunately, VFs are enabled dynamically by PF's driver and they don't have corresponding device nodes and pci_dn, which is required to access VFs' config spaces. The patch creates pci_dn for VFs in pcibios_sriov_enable() on their PF, and removes pci_dn for VFs in pcibios_sriov_disable() on their PF. When VF's pci_dn is created, it's put to the child list of the pci_dn of PF's upstream bridge. The pci_dn is linked to pci_dev during early fixup time to setup the fast path. [bhelgaas: add ifdef around add_one_dev_pci_info(), use dev_printk()] Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/pci-bridge.h | 3 + arch/powerpc/kernel/pci_dn.c | 116 ++++++++++++++++++++++++++++++ arch/powerpc/platforms/powernv/pci-ioda.c | 16 +++++ 3 files changed, 135 insertions(+) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 2c6dc2a3d14a..ece30f589398 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -156,6 +156,7 @@ struct iommu_table; struct pci_dn { int flags; +#define PCI_DN_FLAG_IOV_VF 0x01 int busno; /* pci bus number */ int devfn; /* pci device and function number */ @@ -188,6 +189,8 @@ struct pci_dn { extern struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus, int devfn); extern struct pci_dn *pci_get_pdn(struct pci_dev *pdev); +extern struct pci_dn *add_dev_pci_data(struct pci_dev *pdev); +extern void remove_dev_pci_data(struct pci_dev *pdev); extern void *update_dn_pci_info(struct device_node *dn, void *data); static inline int pci_device_from_OF_node(struct device_node *np, diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c index 65b98367005c..e5f1d78ef7cf 100644 --- a/arch/powerpc/kernel/pci_dn.c +++ b/arch/powerpc/kernel/pci_dn.c @@ -136,6 +136,122 @@ struct pci_dn *pci_get_pdn(struct pci_dev *pdev) return NULL; } +#ifdef CONFIG_PCI_IOV +static struct pci_dn *add_one_dev_pci_data(struct pci_dn *parent, + struct pci_dev *pdev, + int busno, int devfn) +{ + struct pci_dn *pdn; + + /* Except PHB, we always have the parent */ + if (!parent) + return NULL; + + pdn = kzalloc(sizeof(*pdn), GFP_KERNEL); + if (!pdn) { + dev_warn(&pdev->dev, "%s: Out of memory!\n", __func__); + return NULL; + } + + pdn->phb = parent->phb; + pdn->parent = parent; + pdn->busno = busno; + pdn->devfn = devfn; +#ifdef CONFIG_PPC_POWERNV + pdn->pe_number = IODA_INVALID_PE; +#endif + INIT_LIST_HEAD(&pdn->child_list); + INIT_LIST_HEAD(&pdn->list); + list_add_tail(&pdn->list, &parent->child_list); + + /* + * If we already have PCI device instance, lets + * bind them. + */ + if (pdev) + pdev->dev.archdata.pci_data = pdn; + + return pdn; +} +#endif + +struct pci_dn *add_dev_pci_data(struct pci_dev *pdev) +{ +#ifdef CONFIG_PCI_IOV + struct pci_dn *parent, *pdn; + int i; + + /* Only support IOV for now */ + if (!pdev->is_physfn) + return pci_get_pdn(pdev); + + /* Check if VFs have been populated */ + pdn = pci_get_pdn(pdev); + if (!pdn || (pdn->flags & PCI_DN_FLAG_IOV_VF)) + return NULL; + + pdn->flags |= PCI_DN_FLAG_IOV_VF; + parent = pci_bus_to_pdn(pdev->bus); + if (!parent) + return NULL; + + for (i = 0; i < pci_sriov_get_totalvfs(pdev); i++) { + pdn = add_one_dev_pci_data(parent, NULL, + pci_iov_virtfn_bus(pdev, i), + pci_iov_virtfn_devfn(pdev, i)); + if (!pdn) { + dev_warn(&pdev->dev, "%s: Cannot create firmware data for VF#%d\n", + __func__, i); + return NULL; + } + } +#endif /* CONFIG_PCI_IOV */ + + return pci_get_pdn(pdev); +} + +void remove_dev_pci_data(struct pci_dev *pdev) +{ +#ifdef CONFIG_PCI_IOV + struct pci_dn *parent; + struct pci_dn *pdn, *tmp; + int i; + + /* Only support IOV PF for now */ + if (!pdev->is_physfn) + return; + + /* Check if VFs have been populated */ + pdn = pci_get_pdn(pdev); + if (!pdn || !(pdn->flags & PCI_DN_FLAG_IOV_VF)) + return; + + pdn->flags &= ~PCI_DN_FLAG_IOV_VF; + parent = pci_bus_to_pdn(pdev->bus); + if (!parent) + return; + + /* + * We might introduce flag to pci_dn in future + * so that we can release VF's firmware data in + * a batch mode. + */ + for (i = 0; i < pci_sriov_get_totalvfs(pdev); i++) { + list_for_each_entry_safe(pdn, tmp, + &parent->child_list, list) { + if (pdn->busno != pci_iov_virtfn_bus(pdev, i) || + pdn->devfn != pci_iov_virtfn_devfn(pdev, i)) + continue; + + if (!list_empty(&pdn->list)) + list_del(&pdn->list); + + kfree(pdn); + } + } +#endif /* CONFIG_PCI_IOV */ +} + /* * Traverse_func that inits the PCI fields of the device node. * NOTE: this *must* be done before read/write config to the device. diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 26fe09936935..7f58f199f2c1 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -974,6 +974,22 @@ static void pnv_pci_ioda_setup_PEs(void) } } +#ifdef CONFIG_PCI_IOV +int pcibios_sriov_disable(struct pci_dev *pdev) +{ + /* Release PCI data */ + remove_dev_pci_data(pdev); + return 0; +} + +int pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs) +{ + /* Allocate PCI data */ + add_dev_pci_data(pdev); + return 0; +} +#endif /* CONFIG_PCI_IOV */ + static void pnv_pci_ioda_dma_dev_setup(struct pnv_phb *phb, struct pci_dev *pdev) { struct pci_dn *pdn = pci_get_pdn(pdev); -- cgit v1.2.3 From c3b80fb0f22f464f35a970d65e76d2fe904d4923 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Wed, 25 Mar 2015 16:23:53 +0800 Subject: powerpc/pci: Don't unset PCI resources for VFs Flag PCI_REASSIGN_ALL_RSRC is used to ignore resources information setup by firmware, so that kernel would re-assign all resources of pci devices. On powerpc arch, this happens in a header fixup function pcibios_fixup_resources(), which will clean up the resources if this flag is set. This works fine for PFs, since after clean up, kernel will re-assign the resources in pcibios_resource_survey(). Below is a simple call flow on how it works: pcibios_init pcibios_scan_phb pci_scan_child_bus ... pci_device_add pci_fixup_device(pci_fixup_header) pcibios_fixup_resources # header fixup for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) dev->resource[i].start = 0 pcibios_resource_survey # re-assign pcibios_allocate_resources However, the VF resources won't be re-assigned, since the VF resources are completely determined by the PF resources, and the PF resources have already been reassigned. This means we need to leave VF's resources un-cleared in pcibios_fixup_resources(). In this patch, we skip the resource unset process in pcibios_fixup_resources(), if the pci_dev is a VF. Signed-off-by: Wei Yang Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/pci-common.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 2a525c938158..82031011522f 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -788,6 +788,10 @@ static void pcibios_fixup_resources(struct pci_dev *dev) pci_name(dev)); return; } + + if (dev->is_virtfn) + return; + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { struct resource *res = dev->resource + i; struct pci_bus_region reg; -- cgit v1.2.3 From 6e628c7d33d99406cef374972c89389edcc3570f Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Wed, 25 Mar 2015 16:23:55 +0800 Subject: powerpc/powernv: Reserve additional space for IOV BAR according to the number of total_pe On PHB3, PF IOV BAR will be covered by M64 BAR to have better PE isolation. M64 BAR is a type of hardware resource in PHB3, which could map a range of MMIO to PE numbers on powernv platform. And this range is divided equally by the number of total_pe with each divided range mapping to a PE number. Also, the M64 BAR must map a MMIO range with power-of-two size. The total_pe number is usually different from total_VFs, which can lead to a conflict between MMIO space and the PE number. For example, if total_VFs is 128 and total_pe is 256, the second half of M64 BAR will be part of other PCI device, which may already belong to other PEs. This patch prevents the conflict by reserving additional space for the PF IOV BAR, which is total_pe number of VF's BAR size. [bhelgaas: make dev_printk() output more consistent, index resource[] conventionally] Signed-off-by: Wei Yang Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/machdep.h | 4 +++ arch/powerpc/include/asm/pci-bridge.h | 3 +++ arch/powerpc/kernel/pci-common.c | 6 +++++ arch/powerpc/platforms/powernv/pci-ioda.c | 43 +++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index 098d51e924ea..b303833fa3fb 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -250,6 +250,10 @@ struct machdep_calls { /* Reset the secondary bus of bridge */ void (*pcibios_reset_secondary_bus)(struct pci_dev *dev); +#ifdef CONFIG_PCI_IOV + void (*pcibios_fixup_sriov)(struct pci_dev *pdev); +#endif /* CONFIG_PCI_IOV */ + /* Called to shutdown machine specific hardware not already controlled * by other drivers. */ diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index ece30f589398..7b8ebc5929ff 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -178,6 +178,9 @@ struct pci_dn { #define IODA_INVALID_PE (-1) #ifdef CONFIG_PPC_POWERNV int pe_number; +#ifdef CONFIG_PCI_IOV + u16 vfs_expanded; /* number of VFs IOV BAR expanded */ +#endif /* CONFIG_PCI_IOV */ #endif struct list_head child_list; struct list_head list; diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 82031011522f..375bf7099912 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -990,6 +990,12 @@ int pcibios_add_device(struct pci_dev *dev) */ if (dev->bus->is_added) pcibios_setup_device(dev); + +#ifdef CONFIG_PCI_IOV + if (ppc_md.pcibios_fixup_sriov) + ppc_md.pcibios_fixup_sriov(dev); +#endif /* CONFIG_PCI_IOV */ + return 0; } diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 9447ee9b4aa3..1da45aa76a03 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -1749,6 +1749,46 @@ static void pnv_pci_init_ioda_msis(struct pnv_phb *phb) static void pnv_pci_init_ioda_msis(struct pnv_phb *phb) { } #endif /* CONFIG_PCI_MSI */ +#ifdef CONFIG_PCI_IOV +static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) +{ + struct pci_controller *hose; + struct pnv_phb *phb; + struct resource *res; + int i; + resource_size_t size; + struct pci_dn *pdn; + + if (!pdev->is_physfn || pdev->is_added) + return; + + hose = pci_bus_to_host(pdev->bus); + phb = hose->private_data; + + pdn = pci_get_pdn(pdev); + pdn->vfs_expanded = 0; + + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + res = &pdev->resource[i + PCI_IOV_RESOURCES]; + if (!res->flags || res->parent) + continue; + if (!pnv_pci_is_mem_pref_64(res->flags)) { + dev_warn(&pdev->dev, "Skipping expanding VF BAR%d: %pR\n", + i, res); + continue; + } + + dev_dbg(&pdev->dev, " Fixing VF BAR%d: %pR to\n", i, res); + size = pci_iov_resource_size(pdev, i + PCI_IOV_RESOURCES); + res->end = res->start + size * phb->ioda.total_pe - 1; + dev_dbg(&pdev->dev, " %pR\n", res); + dev_info(&pdev->dev, "VF BAR%d: %pR (expanded to %d VFs for PE alignment)", + i, res, phb->ioda.total_pe); + } + pdn->vfs_expanded = phb->ioda.total_pe; +} +#endif /* CONFIG_PCI_IOV */ + /* * This function is supposed to be called on basis of PE from top * to bottom style. So the the I/O or MMIO segment assigned to @@ -2122,6 +2162,9 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np, ppc_md.pcibios_enable_device_hook = pnv_pci_enable_device_hook; ppc_md.pcibios_window_alignment = pnv_pci_window_alignment; ppc_md.pcibios_reset_secondary_bus = pnv_pci_reset_secondary_bus; +#ifdef CONFIG_PCI_IOV + ppc_md.pcibios_fixup_sriov = pnv_pci_ioda_fixup_iov_resources; +#endif /* CONFIG_PCI_IOV */ pci_add_flags(PCI_REASSIGN_ALL_RSRC); /* Reset IODA tables to a clean state */ -- cgit v1.2.3 From 5350ab3fd794f899079d9f6b2b6fe1a7917087ef Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Wed, 25 Mar 2015 16:23:56 +0800 Subject: powerpc/powernv: Implement pcibios_iov_resource_alignment() on powernv Implement pcibios_iov_resource_alignment() on powernv platform. On PowerNV platform, there are 3 cases for the IOV BAR: 1. initial state, the IOV BAR size is multiple times of VF BAR size 2. after expanded, the IOV BAR size is expanded to meet the M64 segment size 3. sizing stage, the IOV BAR is truncated to 0 pnv_pci_iov_resource_alignment() handle these three cases respectively. [bhelgaas: adjust to drop "align" parameter, return pci_iov_resource_size() if no ppc_md machdep_call version] Signed-off-by: Wei Yang Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/machdep.h | 1 + arch/powerpc/kernel/pci-common.c | 10 ++++++++++ arch/powerpc/platforms/powernv/pci-ioda.c | 20 ++++++++++++++++++++ 3 files changed, 31 insertions(+) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index b303833fa3fb..1b268044f290 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -252,6 +252,7 @@ struct machdep_calls { #ifdef CONFIG_PCI_IOV void (*pcibios_fixup_sriov)(struct pci_dev *pdev); + resource_size_t (*pcibios_iov_resource_alignment)(struct pci_dev *, int resno); #endif /* CONFIG_PCI_IOV */ /* Called to shutdown machine specific hardware not already controlled diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 375bf7099912..9a306ff304ae 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -130,6 +130,16 @@ void pcibios_reset_secondary_bus(struct pci_dev *dev) pci_reset_secondary_bus(dev); } +#ifdef CONFIG_PCI_IOV +resource_size_t pcibios_iov_resource_alignment(struct pci_dev *pdev, int resno) +{ + if (ppc_md.pcibios_iov_resource_alignment) + return ppc_md.pcibios_iov_resource_alignment(pdev, resno); + + return pci_iov_resource_size(pdev, resno); +} +#endif /* CONFIG_PCI_IOV */ + static resource_size_t pcibios_io_size(const struct pci_controller *hose) { #ifdef CONFIG_PPC64 diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 1da45aa76a03..217eaad23cde 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -1965,6 +1965,25 @@ static resource_size_t pnv_pci_window_alignment(struct pci_bus *bus, return phb->ioda.io_segsize; } +#ifdef CONFIG_PCI_IOV +static resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, + int resno) +{ + struct pci_dn *pdn = pci_get_pdn(pdev); + resource_size_t align, iov_align; + + iov_align = resource_size(&pdev->resource[resno]); + if (iov_align) + return iov_align; + + align = pci_iov_resource_size(pdev, resno); + if (pdn->vfs_expanded) + return pdn->vfs_expanded * align; + + return align; +} +#endif /* CONFIG_PCI_IOV */ + /* Prevent enabling devices for which we couldn't properly * assign a PE */ @@ -2164,6 +2183,7 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np, ppc_md.pcibios_reset_secondary_bus = pnv_pci_reset_secondary_bus; #ifdef CONFIG_PCI_IOV ppc_md.pcibios_fixup_sriov = pnv_pci_ioda_fixup_iov_resources; + ppc_md.pcibios_iov_resource_alignment = pnv_pci_iov_resource_alignment; #endif /* CONFIG_PCI_IOV */ pci_add_flags(PCI_REASSIGN_ALL_RSRC); -- cgit v1.2.3 From 781a868f3136c6eb8e8c5c19d148416d7da86610 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Wed, 25 Mar 2015 16:23:57 +0800 Subject: powerpc/powernv: Shift VF resource with an offset On PowerNV platform, resource position in M64 BAR implies the PE# the resource belongs to. In some cases, adjustment of a resource is necessary to locate it to a correct position in M64 BAR . This patch adds pnv_pci_vf_resource_shift() to shift the 'real' PF IOV BAR address according to an offset. Note: After doing so, there would be a "hole" in the /proc/iomem when offset is a positive value. It looks like the device return some mmio back to the system, which actually no one could use it. [bhelgaas: rework loops, rework overlap check, index resource[] conventionally, remove pci_regs.h include, squashed with next patch] Signed-off-by: Wei Yang Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/pci-bridge.h | 4 + arch/powerpc/kernel/pci_dn.c | 13 + arch/powerpc/platforms/powernv/pci-ioda.c | 528 +++++++++++++++++++++++++++++- arch/powerpc/platforms/powernv/pci.c | 18 + arch/powerpc/platforms/powernv/pci.h | 7 + 5 files changed, 553 insertions(+), 17 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 7b8ebc5929ff..8716db48e946 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -180,6 +180,10 @@ struct pci_dn { int pe_number; #ifdef CONFIG_PCI_IOV u16 vfs_expanded; /* number of VFs IOV BAR expanded */ + u16 num_vfs; /* number of VFs enabled*/ + int offset; /* PE# for the first VF PE */ +#define IODA_INVALID_M64 (-1) + int m64_wins[PCI_SRIOV_NUM_BARS]; #endif /* CONFIG_PCI_IOV */ #endif struct list_head child_list; diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c index e5f1d78ef7cf..b3b4df91b792 100644 --- a/arch/powerpc/kernel/pci_dn.c +++ b/arch/powerpc/kernel/pci_dn.c @@ -217,6 +217,19 @@ void remove_dev_pci_data(struct pci_dev *pdev) struct pci_dn *pdn, *tmp; int i; + /* + * VF and VF PE are created/released dynamically, so we need to + * bind/unbind them. Otherwise the VF and VF PE would be mismatched + * when re-enabling SR-IOV. + */ + if (pdev->is_virtfn) { + pdn = pci_get_pdn(pdev); +#ifdef CONFIG_PPC_POWERNV + pdn->pe_number = IODA_INVALID_PE; +#endif + return; + } + /* Only support IOV PF for now */ if (!pdev->is_physfn) return; diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 217eaad23cde..5187d164cfe1 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -44,6 +44,9 @@ #include "powernv.h" #include "pci.h" +/* 256M DMA window, 4K TCE pages, 8 bytes TCE */ +#define TCE32_TABLE_SIZE ((0x10000000 / 0x1000) * 8) + static void pe_level_printk(const struct pnv_ioda_pe *pe, const char *level, const char *fmt, ...) { @@ -56,11 +59,18 @@ static void pe_level_printk(const struct pnv_ioda_pe *pe, const char *level, vaf.fmt = fmt; vaf.va = &args; - if (pe->pdev) + if (pe->flags & PNV_IODA_PE_DEV) strlcpy(pfix, dev_name(&pe->pdev->dev), sizeof(pfix)); - else + else if (pe->flags & (PNV_IODA_PE_BUS | PNV_IODA_PE_BUS_ALL)) sprintf(pfix, "%04x:%02x ", pci_domain_nr(pe->pbus), pe->pbus->number); +#ifdef CONFIG_PCI_IOV + else if (pe->flags & PNV_IODA_PE_VF) + sprintf(pfix, "%04x:%02x:%2x.%d", + pci_domain_nr(pe->parent_dev->bus), + (pe->rid & 0xff00) >> 8, + PCI_SLOT(pe->rid), PCI_FUNC(pe->rid)); +#endif /* CONFIG_PCI_IOV*/ printk("%spci %s: [PE# %.3d] %pV", level, pfix, pe->pe_number, &vaf); @@ -591,7 +601,7 @@ static int pnv_ioda_set_peltv(struct pnv_phb *phb, bool is_add) { struct pnv_ioda_pe *slave; - struct pci_dev *pdev; + struct pci_dev *pdev = NULL; int ret; /* @@ -630,8 +640,12 @@ static int pnv_ioda_set_peltv(struct pnv_phb *phb, if (pe->flags & (PNV_IODA_PE_BUS_ALL | PNV_IODA_PE_BUS)) pdev = pe->pbus->self; - else + else if (pe->flags & PNV_IODA_PE_DEV) pdev = pe->pdev->bus->self; +#ifdef CONFIG_PCI_IOV + else if (pe->flags & PNV_IODA_PE_VF) + pdev = pe->parent_dev->bus->self; +#endif /* CONFIG_PCI_IOV */ while (pdev) { struct pci_dn *pdn = pci_get_pdn(pdev); struct pnv_ioda_pe *parent; @@ -649,6 +663,87 @@ static int pnv_ioda_set_peltv(struct pnv_phb *phb, return 0; } +#ifdef CONFIG_PCI_IOV +static int pnv_ioda_deconfigure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe) +{ + struct pci_dev *parent; + uint8_t bcomp, dcomp, fcomp; + int64_t rc; + long rid_end, rid; + + /* Currently, we just deconfigure VF PE. Bus PE will always there.*/ + if (pe->pbus) { + int count; + + dcomp = OPAL_IGNORE_RID_DEVICE_NUMBER; + fcomp = OPAL_IGNORE_RID_FUNCTION_NUMBER; + parent = pe->pbus->self; + if (pe->flags & PNV_IODA_PE_BUS_ALL) + count = pe->pbus->busn_res.end - pe->pbus->busn_res.start + 1; + else + count = 1; + + switch(count) { + case 1: bcomp = OpalPciBusAll; break; + case 2: bcomp = OpalPciBus7Bits; break; + case 4: bcomp = OpalPciBus6Bits; break; + case 8: bcomp = OpalPciBus5Bits; break; + case 16: bcomp = OpalPciBus4Bits; break; + case 32: bcomp = OpalPciBus3Bits; break; + default: + dev_err(&pe->pbus->dev, "Number of subordinate buses %d unsupported\n", + count); + /* Do an exact match only */ + bcomp = OpalPciBusAll; + } + rid_end = pe->rid + (count << 8); + } else { + if (pe->flags & PNV_IODA_PE_VF) + parent = pe->parent_dev; + else + parent = pe->pdev->bus->self; + bcomp = OpalPciBusAll; + dcomp = OPAL_COMPARE_RID_DEVICE_NUMBER; + fcomp = OPAL_COMPARE_RID_FUNCTION_NUMBER; + rid_end = pe->rid + 1; + } + + /* Clear the reverse map */ + for (rid = pe->rid; rid < rid_end; rid++) + phb->ioda.pe_rmap[rid] = 0; + + /* Release from all parents PELT-V */ + while (parent) { + struct pci_dn *pdn = pci_get_pdn(parent); + if (pdn && pdn->pe_number != IODA_INVALID_PE) { + rc = opal_pci_set_peltv(phb->opal_id, pdn->pe_number, + pe->pe_number, OPAL_REMOVE_PE_FROM_DOMAIN); + /* XXX What to do in case of error ? */ + } + parent = parent->bus->self; + } + + opal_pci_eeh_freeze_set(phb->opal_id, pe->pe_number, + OPAL_EEH_ACTION_CLEAR_FREEZE_ALL); + + /* Disassociate PE in PELT */ + rc = opal_pci_set_peltv(phb->opal_id, pe->pe_number, + pe->pe_number, OPAL_REMOVE_PE_FROM_DOMAIN); + if (rc) + pe_warn(pe, "OPAL error %ld remove self from PELTV\n", rc); + rc = opal_pci_set_pe(phb->opal_id, pe->pe_number, pe->rid, + bcomp, dcomp, fcomp, OPAL_UNMAP_PE); + if (rc) + pe_err(pe, "OPAL error %ld trying to setup PELT table\n", rc); + + pe->pbus = NULL; + pe->pdev = NULL; + pe->parent_dev = NULL; + + return 0; +} +#endif /* CONFIG_PCI_IOV */ + static int pnv_ioda_configure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe) { struct pci_dev *parent; @@ -675,15 +770,19 @@ static int pnv_ioda_configure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe) case 16: bcomp = OpalPciBus4Bits; break; case 32: bcomp = OpalPciBus3Bits; break; default: - pr_err("%s: Number of subordinate busses %d" - " unsupported\n", - pci_name(pe->pbus->self), count); + dev_err(&pe->pbus->dev, "Number of subordinate buses %d unsupported\n", + count); /* Do an exact match only */ bcomp = OpalPciBusAll; } rid_end = pe->rid + (count << 8); } else { - parent = pe->pdev->bus->self; +#ifdef CONFIG_PCI_IOV + if (pe->flags & PNV_IODA_PE_VF) + parent = pe->parent_dev; + else +#endif /* CONFIG_PCI_IOV */ + parent = pe->pdev->bus->self; bcomp = OpalPciBusAll; dcomp = OPAL_COMPARE_RID_DEVICE_NUMBER; fcomp = OPAL_COMPARE_RID_FUNCTION_NUMBER; @@ -774,6 +873,78 @@ static unsigned int pnv_ioda_dma_weight(struct pci_dev *dev) return 10; } +#ifdef CONFIG_PCI_IOV +static int pnv_pci_vf_resource_shift(struct pci_dev *dev, int offset) +{ + struct pci_dn *pdn = pci_get_pdn(dev); + int i; + struct resource *res, res2; + resource_size_t size; + u16 num_vfs; + + if (!dev->is_physfn) + return -EINVAL; + + /* + * "offset" is in VFs. The M64 windows are sized so that when they + * are segmented, each segment is the same size as the IOV BAR. + * Each segment is in a separate PE, and the high order bits of the + * address are the PE number. Therefore, each VF's BAR is in a + * separate PE, and changing the IOV BAR start address changes the + * range of PEs the VFs are in. + */ + num_vfs = pdn->num_vfs; + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + res = &dev->resource[i + PCI_IOV_RESOURCES]; + if (!res->flags || !res->parent) + continue; + + if (!pnv_pci_is_mem_pref_64(res->flags)) + continue; + + /* + * The actual IOV BAR range is determined by the start address + * and the actual size for num_vfs VFs BAR. This check is to + * make sure that after shifting, the range will not overlap + * with another device. + */ + size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES); + res2.flags = res->flags; + res2.start = res->start + (size * offset); + res2.end = res2.start + (size * num_vfs) - 1; + + if (res2.end > res->end) { + dev_err(&dev->dev, "VF BAR%d: %pR would extend past %pR (trying to enable %d VFs shifted by %d)\n", + i, &res2, res, num_vfs, offset); + return -EBUSY; + } + } + + /* + * After doing so, there would be a "hole" in the /proc/iomem when + * offset is a positive value. It looks like the device return some + * mmio back to the system, which actually no one could use it. + */ + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + res = &dev->resource[i + PCI_IOV_RESOURCES]; + if (!res->flags || !res->parent) + continue; + + if (!pnv_pci_is_mem_pref_64(res->flags)) + continue; + + size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES); + res2 = *res; + res->start += size * offset; + + dev_info(&dev->dev, "VF BAR%d: %pR shifted to %pR (enabling %d VFs shifted by %d)\n", + i, &res2, res, num_vfs, offset); + pci_update_resource(dev, i + PCI_IOV_RESOURCES); + } + return 0; +} +#endif /* CONFIG_PCI_IOV */ + #if 0 static struct pnv_ioda_pe *pnv_ioda_setup_dev_PE(struct pci_dev *dev) { @@ -979,8 +1150,316 @@ static void pnv_pci_ioda_setup_PEs(void) } #ifdef CONFIG_PCI_IOV +static int pnv_pci_vf_release_m64(struct pci_dev *pdev) +{ + struct pci_bus *bus; + struct pci_controller *hose; + struct pnv_phb *phb; + struct pci_dn *pdn; + int i; + + bus = pdev->bus; + hose = pci_bus_to_host(bus); + phb = hose->private_data; + pdn = pci_get_pdn(pdev); + + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + if (pdn->m64_wins[i] == IODA_INVALID_M64) + continue; + opal_pci_phb_mmio_enable(phb->opal_id, + OPAL_M64_WINDOW_TYPE, pdn->m64_wins[i], 0); + clear_bit(pdn->m64_wins[i], &phb->ioda.m64_bar_alloc); + pdn->m64_wins[i] = IODA_INVALID_M64; + } + + return 0; +} + +static int pnv_pci_vf_assign_m64(struct pci_dev *pdev) +{ + struct pci_bus *bus; + struct pci_controller *hose; + struct pnv_phb *phb; + struct pci_dn *pdn; + unsigned int win; + struct resource *res; + int i; + int64_t rc; + + bus = pdev->bus; + hose = pci_bus_to_host(bus); + phb = hose->private_data; + pdn = pci_get_pdn(pdev); + + /* Initialize the m64_wins to IODA_INVALID_M64 */ + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) + pdn->m64_wins[i] = IODA_INVALID_M64; + + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + res = &pdev->resource[i + PCI_IOV_RESOURCES]; + if (!res->flags || !res->parent) + continue; + + if (!pnv_pci_is_mem_pref_64(res->flags)) + continue; + + do { + win = find_next_zero_bit(&phb->ioda.m64_bar_alloc, + phb->ioda.m64_bar_idx + 1, 0); + + if (win >= phb->ioda.m64_bar_idx + 1) + goto m64_failed; + } while (test_and_set_bit(win, &phb->ioda.m64_bar_alloc)); + + pdn->m64_wins[i] = win; + + /* Map the M64 here */ + rc = opal_pci_set_phb_mem_window(phb->opal_id, + OPAL_M64_WINDOW_TYPE, + pdn->m64_wins[i], + res->start, + 0, /* unused */ + resource_size(res)); + if (rc != OPAL_SUCCESS) { + dev_err(&pdev->dev, "Failed to map M64 window #%d: %lld\n", + win, rc); + goto m64_failed; + } + + rc = opal_pci_phb_mmio_enable(phb->opal_id, + OPAL_M64_WINDOW_TYPE, pdn->m64_wins[i], 1); + if (rc != OPAL_SUCCESS) { + dev_err(&pdev->dev, "Failed to enable M64 window #%d: %llx\n", + win, rc); + goto m64_failed; + } + } + return 0; + +m64_failed: + pnv_pci_vf_release_m64(pdev); + return -EBUSY; +} + +static void pnv_pci_ioda2_release_dma_pe(struct pci_dev *dev, struct pnv_ioda_pe *pe) +{ + struct pci_bus *bus; + struct pci_controller *hose; + struct pnv_phb *phb; + struct iommu_table *tbl; + unsigned long addr; + int64_t rc; + + bus = dev->bus; + hose = pci_bus_to_host(bus); + phb = hose->private_data; + tbl = pe->tce32_table; + addr = tbl->it_base; + + opal_pci_map_pe_dma_window(phb->opal_id, pe->pe_number, + pe->pe_number << 1, 1, __pa(addr), + 0, 0x1000); + + rc = opal_pci_map_pe_dma_window_real(pe->phb->opal_id, + pe->pe_number, + (pe->pe_number << 1) + 1, + pe->tce_bypass_base, + 0); + if (rc) + pe_warn(pe, "OPAL error %ld release DMA window\n", rc); + + iommu_free_table(tbl, of_node_full_name(dev->dev.of_node)); + free_pages(addr, get_order(TCE32_TABLE_SIZE)); + pe->tce32_table = NULL; +} + +static void pnv_ioda_release_vf_PE(struct pci_dev *pdev) +{ + struct pci_bus *bus; + struct pci_controller *hose; + struct pnv_phb *phb; + struct pnv_ioda_pe *pe, *pe_n; + struct pci_dn *pdn; + + bus = pdev->bus; + hose = pci_bus_to_host(bus); + phb = hose->private_data; + + if (!pdev->is_physfn) + return; + + pdn = pci_get_pdn(pdev); + list_for_each_entry_safe(pe, pe_n, &phb->ioda.pe_list, list) { + if (pe->parent_dev != pdev) + continue; + + pnv_pci_ioda2_release_dma_pe(pdev, pe); + + /* Remove from list */ + mutex_lock(&phb->ioda.pe_list_mutex); + list_del(&pe->list); + mutex_unlock(&phb->ioda.pe_list_mutex); + + pnv_ioda_deconfigure_pe(phb, pe); + + pnv_ioda_free_pe(phb, pe->pe_number); + } +} + +void pnv_pci_sriov_disable(struct pci_dev *pdev) +{ + struct pci_bus *bus; + struct pci_controller *hose; + struct pnv_phb *phb; + struct pci_dn *pdn; + struct pci_sriov *iov; + u16 num_vfs; + + bus = pdev->bus; + hose = pci_bus_to_host(bus); + phb = hose->private_data; + pdn = pci_get_pdn(pdev); + iov = pdev->sriov; + num_vfs = pdn->num_vfs; + + /* Release VF PEs */ + pnv_ioda_release_vf_PE(pdev); + + if (phb->type == PNV_PHB_IODA2) { + pnv_pci_vf_resource_shift(pdev, -pdn->offset); + + /* Release M64 windows */ + pnv_pci_vf_release_m64(pdev); + + /* Release PE numbers */ + bitmap_clear(phb->ioda.pe_alloc, pdn->offset, num_vfs); + pdn->offset = 0; + } +} + +static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, + struct pnv_ioda_pe *pe); +static void pnv_ioda_setup_vf_PE(struct pci_dev *pdev, u16 num_vfs) +{ + struct pci_bus *bus; + struct pci_controller *hose; + struct pnv_phb *phb; + struct pnv_ioda_pe *pe; + int pe_num; + u16 vf_index; + struct pci_dn *pdn; + + bus = pdev->bus; + hose = pci_bus_to_host(bus); + phb = hose->private_data; + pdn = pci_get_pdn(pdev); + + if (!pdev->is_physfn) + return; + + /* Reserve PE for each VF */ + for (vf_index = 0; vf_index < num_vfs; vf_index++) { + pe_num = pdn->offset + vf_index; + + pe = &phb->ioda.pe_array[pe_num]; + pe->pe_number = pe_num; + pe->phb = phb; + pe->flags = PNV_IODA_PE_VF; + pe->pbus = NULL; + pe->parent_dev = pdev; + pe->tce32_seg = -1; + pe->mve_number = -1; + pe->rid = (pci_iov_virtfn_bus(pdev, vf_index) << 8) | + pci_iov_virtfn_devfn(pdev, vf_index); + + pe_info(pe, "VF %04d:%02d:%02d.%d associated with PE#%d\n", + hose->global_number, pdev->bus->number, + PCI_SLOT(pci_iov_virtfn_devfn(pdev, vf_index)), + PCI_FUNC(pci_iov_virtfn_devfn(pdev, vf_index)), pe_num); + + if (pnv_ioda_configure_pe(phb, pe)) { + /* XXX What do we do here ? */ + if (pe_num) + pnv_ioda_free_pe(phb, pe_num); + pe->pdev = NULL; + continue; + } + + pe->tce32_table = kzalloc_node(sizeof(struct iommu_table), + GFP_KERNEL, hose->node); + pe->tce32_table->data = pe; + + /* Put PE to the list */ + mutex_lock(&phb->ioda.pe_list_mutex); + list_add_tail(&pe->list, &phb->ioda.pe_list); + mutex_unlock(&phb->ioda.pe_list_mutex); + + pnv_pci_ioda2_setup_dma_pe(phb, pe); + } +} + +int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) +{ + struct pci_bus *bus; + struct pci_controller *hose; + struct pnv_phb *phb; + struct pci_dn *pdn; + int ret; + + bus = pdev->bus; + hose = pci_bus_to_host(bus); + phb = hose->private_data; + pdn = pci_get_pdn(pdev); + + if (phb->type == PNV_PHB_IODA2) { + /* Calculate available PE for required VFs */ + mutex_lock(&phb->ioda.pe_alloc_mutex); + pdn->offset = bitmap_find_next_zero_area( + phb->ioda.pe_alloc, phb->ioda.total_pe, + 0, num_vfs, 0); + if (pdn->offset >= phb->ioda.total_pe) { + mutex_unlock(&phb->ioda.pe_alloc_mutex); + dev_info(&pdev->dev, "Failed to enable VF%d\n", num_vfs); + pdn->offset = 0; + return -EBUSY; + } + bitmap_set(phb->ioda.pe_alloc, pdn->offset, num_vfs); + pdn->num_vfs = num_vfs; + mutex_unlock(&phb->ioda.pe_alloc_mutex); + + /* Assign M64 window accordingly */ + ret = pnv_pci_vf_assign_m64(pdev); + if (ret) { + dev_info(&pdev->dev, "Not enough M64 window resources\n"); + goto m64_failed; + } + + /* + * When using one M64 BAR to map one IOV BAR, we need to shift + * the IOV BAR according to the PE# allocated to the VFs. + * Otherwise, the PE# for the VF will conflict with others. + */ + ret = pnv_pci_vf_resource_shift(pdev, pdn->offset); + if (ret) + goto m64_failed; + } + + /* Setup VF PEs */ + pnv_ioda_setup_vf_PE(pdev, num_vfs); + + return 0; + +m64_failed: + bitmap_clear(phb->ioda.pe_alloc, pdn->offset, num_vfs); + pdn->offset = 0; + + return ret; +} + int pcibios_sriov_disable(struct pci_dev *pdev) { + pnv_pci_sriov_disable(pdev); + /* Release PCI data */ remove_dev_pci_data(pdev); return 0; @@ -990,6 +1469,8 @@ int pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs) { /* Allocate PCI data */ add_dev_pci_data(pdev); + + pnv_pci_sriov_enable(pdev, num_vfs); return 0; } #endif /* CONFIG_PCI_IOV */ @@ -1186,9 +1667,6 @@ static void pnv_pci_ioda_setup_dma_pe(struct pnv_phb *phb, int64_t rc; void *addr; - /* 256M DMA window, 4K TCE pages, 8 bytes TCE */ -#define TCE32_TABLE_SIZE ((0x10000000 / 0x1000) * 8) - /* XXX FIXME: Handle 64-bit only DMA devices */ /* XXX FIXME: Provide 64-bit DMA facilities & non-4K TCE tables etc.. */ /* XXX FIXME: Allocate multi-level tables on PHB3 */ @@ -1251,12 +1729,19 @@ static void pnv_pci_ioda_setup_dma_pe(struct pnv_phb *phb, TCE_PCI_SWINV_PAIR); } iommu_init_table(tbl, phb->hose->node); - iommu_register_group(tbl, phb->hose->global_number, pe->pe_number); - if (pe->pdev) + if (pe->flags & PNV_IODA_PE_DEV) { + iommu_register_group(tbl, phb->hose->global_number, + pe->pe_number); set_iommu_table_base_and_group(&pe->pdev->dev, tbl); - else + } else if (pe->flags & (PNV_IODA_PE_BUS | PNV_IODA_PE_BUS_ALL)) { + iommu_register_group(tbl, phb->hose->global_number, + pe->pe_number); pnv_ioda_setup_bus_dma(pe, pe->pbus, true); + } else if (pe->flags & PNV_IODA_PE_VF) { + iommu_register_group(tbl, phb->hose->global_number, + pe->pe_number); + } return; fail: @@ -1383,12 +1868,19 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, tbl->it_type |= (TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE); } iommu_init_table(tbl, phb->hose->node); - iommu_register_group(tbl, phb->hose->global_number, pe->pe_number); - if (pe->pdev) + if (pe->flags & PNV_IODA_PE_DEV) { + iommu_register_group(tbl, phb->hose->global_number, + pe->pe_number); set_iommu_table_base_and_group(&pe->pdev->dev, tbl); - else + } else if (pe->flags & (PNV_IODA_PE_BUS | PNV_IODA_PE_BUS_ALL)) { + iommu_register_group(tbl, phb->hose->global_number, + pe->pe_number); pnv_ioda_setup_bus_dma(pe, pe->pbus, true); + } else if (pe->flags & PNV_IODA_PE_VF) { + iommu_register_group(tbl, phb->hose->global_number, + pe->pe_number); + } /* Also create a bypass window */ if (!pnv_iommu_bypass_disabled) @@ -2068,6 +2560,7 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np, phb->hub_id = hub_id; phb->opal_id = phb_id; phb->type = ioda_type; + mutex_init(&phb->ioda.pe_alloc_mutex); /* Detect specific models for error handling */ if (of_device_is_compatible(np, "ibm,p7ioc-pciex")) @@ -2127,6 +2620,7 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np, INIT_LIST_HEAD(&phb->ioda.pe_dma_list); INIT_LIST_HEAD(&phb->ioda.pe_list); + mutex_init(&phb->ioda.pe_list_mutex); /* Calculate how many 32-bit TCE segments we have */ phb->ioda.tce32_count = phb->ioda.m32_pci_base >> 28; diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index 946aa3d62c3c..02badcef5cea 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -714,6 +714,24 @@ static void pnv_pci_dma_dev_setup(struct pci_dev *pdev) { struct pci_controller *hose = pci_bus_to_host(pdev->bus); struct pnv_phb *phb = hose->private_data; +#ifdef CONFIG_PCI_IOV + struct pnv_ioda_pe *pe; + struct pci_dn *pdn; + + /* Fix the VF pdn PE number */ + if (pdev->is_virtfn) { + pdn = pci_get_pdn(pdev); + WARN_ON(pdn->pe_number != IODA_INVALID_PE); + list_for_each_entry(pe, &phb->ioda.pe_list, list) { + if (pe->rid == ((pdev->bus->number << 8) | + (pdev->devfn & 0xff))) { + pdn->pe_number = pe->pe_number; + pe->pdev = pdev; + break; + } + } + } +#endif /* CONFIG_PCI_IOV */ /* If we have no phb structure, try to setup a fallback based on * the device-tree (RTAS PCI for example) diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 84280474e18f..070ee888fc95 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -23,6 +23,7 @@ enum pnv_phb_model { #define PNV_IODA_PE_BUS_ALL (1 << 2) /* PE has subordinate buses */ #define PNV_IODA_PE_MASTER (1 << 3) /* Master PE in compound case */ #define PNV_IODA_PE_SLAVE (1 << 4) /* Slave PE in compound case */ +#define PNV_IODA_PE_VF (1 << 5) /* PE for one VF */ /* Data associated with a PE, including IOMMU tracking etc.. */ struct pnv_phb; @@ -34,6 +35,9 @@ struct pnv_ioda_pe { * entire bus (& children). In the former case, pdev * is populated, in the later case, pbus is. */ +#ifdef CONFIG_PCI_IOV + struct pci_dev *parent_dev; +#endif struct pci_dev *pdev; struct pci_bus *pbus; @@ -145,6 +149,8 @@ struct pnv_phb { /* PE allocation bitmap */ unsigned long *pe_alloc; + /* PE allocation mutex */ + struct mutex pe_alloc_mutex; /* M32 & IO segment maps */ unsigned int *m32_segmap; @@ -159,6 +165,7 @@ struct pnv_phb { * on the sequence of creation */ struct list_head pe_list; + struct mutex pe_list_mutex; /* Reverse map of PEs, will have to extend if * we are to support more than 256 PEs, indexed -- cgit v1.2.3 From 433185d2b4e9c25f2a444424c05af72fbadd4275 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Fri, 27 Mar 2015 11:22:17 +1100 Subject: powerpc/eeh: Fix PE#0 check in eeh_add_to_parent_pe() The function eeh_add_parent_pe() is used to create a PE or add one edev to its parent PE. Current code checks if PE#0 is valid for the later case. Actually, we should validate PE#0 for both cases when EEH core regards PE#0 as invalid one (without flag EEH_VALID_PE_ZERO). Otherwise, not all EEH devices can be added to its parent PE#0 for EEH on P7IOC. The patch fixes the issue by validating PE#0 for the two cases. So far, we don't have PE#0 for EEH on P7IOC, but it will show up when we enable M64 for P7IOC. The patch also makes the error message more meaningful. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/eeh_pe.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c index f33ceccf6876..35f0b62259bb 100644 --- a/arch/powerpc/kernel/eeh_pe.c +++ b/arch/powerpc/kernel/eeh_pe.c @@ -328,6 +328,13 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) { struct eeh_pe *pe, *parent; + /* Check if the PE number is valid */ + if (!eeh_has_flag(EEH_VALID_PE_ZERO) && !edev->pe_config_addr) { + pr_err("%s: Invalid PE#0 for edev 0x%x on PHB#%d\n", + __func__, edev->config_addr, edev->phb->global_number); + return -EINVAL; + } + /* * Search the PE has been existing or not according * to the PE address. If that has been existing, the @@ -336,12 +343,6 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) */ pe = eeh_pe_get(edev); if (pe && !(pe->type & EEH_PE_INVALID)) { - if (!edev->pe_config_addr) { - pr_err("%s: PE with addr 0x%x already exists\n", - __func__, edev->config_addr); - return -EEXIST; - } - /* Mark the PE as type of PCI bus */ pe->type = EEH_PE_BUS; edev->pe = pe; -- cgit v1.2.3 From bf4981a00636347ddcef3fc008e4dd979380a851 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 19 Mar 2015 15:15:20 +1100 Subject: powerpc: Remove the celleb support The celleb code has seen no actual development for ~7 years. We (maintainers) have no access to test hardware, and it is highly likely the code has bit-rotted. As far as we're aware the hardware was never widely available, and is certainly no longer available, and no one on the list has shown any interest in it over the years. So remove it. If anyone has one and cares please speak up. Signed-off-by: Michael Ellerman Acked-by: Benjamin Herrenschmidt Acked-by: Jeremy Kerr --- arch/powerpc/Kconfig.debug | 7 - arch/powerpc/boot/Makefile | 2 - arch/powerpc/configs/cell_defconfig | 3 - arch/powerpc/configs/celleb_defconfig | 152 ------- arch/powerpc/configs/ppc64_defconfig | 3 - arch/powerpc/include/asm/firmware.h | 10 +- arch/powerpc/include/asm/smp.h | 1 - arch/powerpc/kernel/udbg.c | 2 - arch/powerpc/platforms/Kconfig | 5 - arch/powerpc/platforms/cell/Kconfig | 11 - arch/powerpc/platforms/cell/Makefile | 15 - arch/powerpc/platforms/cell/beat.c | 264 ------------ arch/powerpc/platforms/cell/beat.h | 39 -- arch/powerpc/platforms/cell/beat_htab.c | 445 -------------------- arch/powerpc/platforms/cell/beat_hvCall.S | 285 ------------- arch/powerpc/platforms/cell/beat_interrupt.c | 253 ------------ arch/powerpc/platforms/cell/beat_interrupt.h | 30 -- arch/powerpc/platforms/cell/beat_iommu.c | 115 ------ arch/powerpc/platforms/cell/beat_spu_priv1.c | 205 ---------- arch/powerpc/platforms/cell/beat_syscall.h | 164 -------- arch/powerpc/platforms/cell/beat_udbg.c | 98 ----- arch/powerpc/platforms/cell/beat_wrapper.h | 290 ------------- arch/powerpc/platforms/cell/celleb_pci.c | 499 ----------------------- arch/powerpc/platforms/cell/celleb_pci.h | 46 --- arch/powerpc/platforms/cell/celleb_scc.h | 232 ----------- arch/powerpc/platforms/cell/celleb_scc_epci.c | 428 -------------------- arch/powerpc/platforms/cell/celleb_scc_pciex.c | 538 ------------------------- arch/powerpc/platforms/cell/celleb_scc_sio.c | 99 ----- arch/powerpc/platforms/cell/celleb_scc_uhc.c | 95 ----- arch/powerpc/platforms/cell/celleb_setup.c | 243 ----------- arch/powerpc/platforms/cell/iommu.c | 2 - 31 files changed, 1 insertion(+), 4580 deletions(-) delete mode 100644 arch/powerpc/configs/celleb_defconfig delete mode 100644 arch/powerpc/platforms/cell/beat.c delete mode 100644 arch/powerpc/platforms/cell/beat.h delete mode 100644 arch/powerpc/platforms/cell/beat_htab.c delete mode 100644 arch/powerpc/platforms/cell/beat_hvCall.S delete mode 100644 arch/powerpc/platforms/cell/beat_interrupt.c delete mode 100644 arch/powerpc/platforms/cell/beat_interrupt.h delete mode 100644 arch/powerpc/platforms/cell/beat_iommu.c delete mode 100644 arch/powerpc/platforms/cell/beat_spu_priv1.c delete mode 100644 arch/powerpc/platforms/cell/beat_syscall.h delete mode 100644 arch/powerpc/platforms/cell/beat_udbg.c delete mode 100644 arch/powerpc/platforms/cell/beat_wrapper.h delete mode 100644 arch/powerpc/platforms/cell/celleb_pci.c delete mode 100644 arch/powerpc/platforms/cell/celleb_pci.h delete mode 100644 arch/powerpc/platforms/cell/celleb_scc.h delete mode 100644 arch/powerpc/platforms/cell/celleb_scc_epci.c delete mode 100644 arch/powerpc/platforms/cell/celleb_scc_pciex.c delete mode 100644 arch/powerpc/platforms/cell/celleb_scc_sio.c delete mode 100644 arch/powerpc/platforms/cell/celleb_scc_uhc.c delete mode 100644 arch/powerpc/platforms/cell/celleb_setup.c (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index bfd823abff93..0efa8f90a8f1 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -193,13 +193,6 @@ config PPC_EARLY_DEBUG_PAS_REALMODE Select this to enable early debugging for PA Semi. Output will be on UART0. -config PPC_EARLY_DEBUG_BEAT - bool "Beat HV Console" - depends on PPC_CELLEB - select PPC_UDBG_BEAT - help - Select this to enable early debugging for Celleb with Beat. - config PPC_EARLY_DEBUG_44x bool "Early serial debugging for IBM/AMCC 44x CPUs" depends on 44x diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 73a19fac4850..73eddda53b8e 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -110,7 +110,6 @@ src-plat-$(CONFIG_EPAPR_BOOT) += epapr.c epapr-wrapper.c src-plat-$(CONFIG_PPC_PSERIES) += pseries-head.S src-plat-$(CONFIG_PPC_POWERNV) += pseries-head.S src-plat-$(CONFIG_PPC_IBM_CELL_BLADE) += pseries-head.S -src-plat-$(CONFIG_PPC_CELLEB) += pseries-head.S src-plat-$(CONFIG_PPC_CELL_QPACE) += pseries-head.S src-wlib := $(sort $(src-wlib-y)) @@ -215,7 +214,6 @@ image-$(CONFIG_PPC_POWERNV) += zImage.pseries image-$(CONFIG_PPC_MAPLE) += zImage.maple image-$(CONFIG_PPC_IBM_CELL_BLADE) += zImage.pseries image-$(CONFIG_PPC_PS3) += dtbImage.ps3 -image-$(CONFIG_PPC_CELLEB) += zImage.pseries image-$(CONFIG_PPC_CELL_QPACE) += zImage.pseries image-$(CONFIG_PPC_CHRP) += zImage.chrp image-$(CONFIG_PPC_EFIKA) += zImage.chrp diff --git a/arch/powerpc/configs/cell_defconfig b/arch/powerpc/configs/cell_defconfig index 9788b3c2d563..9227b517560a 100644 --- a/arch/powerpc/configs/cell_defconfig +++ b/arch/powerpc/configs/cell_defconfig @@ -28,7 +28,6 @@ CONFIG_PS3_ROM=m CONFIG_PS3_FLASH=m CONFIG_PS3_LPM=m CONFIG_PPC_IBM_CELL_BLADE=y -CONFIG_PPC_CELLEB=y CONFIG_RTAS_FLASH=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y @@ -113,7 +112,6 @@ CONFIG_IDE=y CONFIG_BLK_DEV_GENERIC=y CONFIG_BLK_DEV_AEC62XX=y CONFIG_BLK_DEV_SIIMAGE=y -CONFIG_BLK_DEV_CELLEB=y CONFIG_BLK_DEV_SD=y CONFIG_BLK_DEV_SR=m CONFIG_CHR_DEV_SG=y @@ -156,7 +154,6 @@ CONFIG_SERIAL_TXX9_NR_UARTS=2 CONFIG_SERIAL_TXX9_CONSOLE=y CONFIG_SERIAL_OF_PLATFORM=y CONFIG_HVC_RTAS=y -CONFIG_HVC_BEAT=y CONFIG_IPMI_HANDLER=m CONFIG_IPMI_DEVICE_INTERFACE=m CONFIG_IPMI_SI=m diff --git a/arch/powerpc/configs/celleb_defconfig b/arch/powerpc/configs/celleb_defconfig deleted file mode 100644 index ff454dcd2dd3..000000000000 --- a/arch/powerpc/configs/celleb_defconfig +++ /dev/null @@ -1,152 +0,0 @@ -CONFIG_PPC64=y -CONFIG_TUNE_CELL=y -CONFIG_ALTIVEC=y -CONFIG_SMP=y -CONFIG_NR_CPUS=4 -CONFIG_SYSVIPC=y -CONFIG_FHANDLE=y -CONFIG_NO_HZ=y -CONFIG_HIGH_RES_TIMERS=y -CONFIG_IKCONFIG=y -CONFIG_IKCONFIG_PROC=y -CONFIG_LOG_BUF_SHIFT=15 -CONFIG_BLK_DEV_INITRD=y -# CONFIG_COMPAT_BRK is not set -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_MODVERSIONS=y -CONFIG_MODULE_SRCVERSION_ALL=y -CONFIG_PARTITION_ADVANCED=y -# CONFIG_PPC_POWERNV is not set -# CONFIG_PPC_PSERIES is not set -# CONFIG_PPC_PMAC is not set -CONFIG_PPC_CELLEB=y -CONFIG_SPU_FS=y -# CONFIG_CBE_THERM is not set -CONFIG_UDBG_RTAS_CONSOLE=y -# CONFIG_RTAS_PROC is not set -CONFIG_BINFMT_MISC=m -CONFIG_KEXEC=y -CONFIG_NUMA=y -CONFIG_NET=y -CONFIG_PACKET=y -CONFIG_UNIX=y -CONFIG_INET=y -CONFIG_IP_MULTICAST=y -CONFIG_SYN_COOKIES=y -CONFIG_IPV6=y -CONFIG_INET6_AH=m -CONFIG_INET6_ESP=m -CONFIG_INET6_IPCOMP=m -CONFIG_IPV6_TUNNEL=m -CONFIG_NETFILTER=y -CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" -CONFIG_BLK_DEV_LOOP=y -CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_SIZE=131072 -CONFIG_IDE=y -CONFIG_BLK_DEV_IDECD=m -CONFIG_BLK_DEV_GENERIC=y -CONFIG_BLK_DEV_CELLEB=y -CONFIG_SCSI=m -# CONFIG_SCSI_PROC_FS is not set -CONFIG_BLK_DEV_SD=m -CONFIG_BLK_DEV_SR=m -CONFIG_CHR_DEV_SG=m -CONFIG_MD=y -CONFIG_BLK_DEV_MD=m -CONFIG_MD_LINEAR=m -CONFIG_MD_RAID0=m -CONFIG_MD_RAID1=m -CONFIG_BLK_DEV_DM=m -CONFIG_DM_CRYPT=m -CONFIG_DM_SNAPSHOT=m -CONFIG_DM_MIRROR=m -CONFIG_DM_ZERO=m -CONFIG_DM_MULTIPATH=m -CONFIG_NETDEVICES=y -CONFIG_SPIDER_NET=y -# CONFIG_INPUT_MOUSEDEV_PSAUX is not set -# CONFIG_INPUT_KEYBOARD is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_SERIO_I8042 is not set -# CONFIG_LEGACY_PTYS is not set -CONFIG_SERIAL_NONSTANDARD=y -CONFIG_SERIAL_TXX9_NR_UARTS=3 -CONFIG_SERIAL_TXX9_CONSOLE=y -CONFIG_HVC_RTAS=y -CONFIG_HVC_BEAT=y -# CONFIG_HW_RANDOM is not set -CONFIG_GEN_RTC=y -CONFIG_I2C=y -# CONFIG_HWMON is not set -CONFIG_WATCHDOG=y -# CONFIG_VGA_CONSOLE is not set -CONFIG_USB_HIDDEV=y -CONFIG_USB=y -CONFIG_USB_MON=y -CONFIG_USB_EHCI_HCD=m -# CONFIG_USB_EHCI_HCD_PPC_OF is not set -CONFIG_USB_OHCI_HCD=m -CONFIG_USB_STORAGE=m -CONFIG_EXT2_FS=y -CONFIG_EXT2_FS_XATTR=y -CONFIG_EXT2_FS_POSIX_ACL=y -CONFIG_EXT2_FS_SECURITY=y -CONFIG_EXT2_FS_XIP=y -CONFIG_EXT3_FS=y -CONFIG_EXT3_FS_POSIX_ACL=y -CONFIG_EXT3_FS_SECURITY=y -CONFIG_ISO9660_FS=m -CONFIG_JOLIET=y -CONFIG_UDF_FS=m -CONFIG_MSDOS_FS=m -CONFIG_VFAT_FS=m -CONFIG_PROC_KCORE=y -CONFIG_TMPFS=y -CONFIG_HUGETLBFS=y -CONFIG_NFS_FS=m -CONFIG_NFS_V3_ACL=y -CONFIG_NFSD=m -CONFIG_NFSD_V3=y -CONFIG_NFSD_V3_ACL=y -CONFIG_NLS_ISO8859_1=m -CONFIG_NLS_ISO8859_2=m -CONFIG_NLS_ISO8859_3=m -CONFIG_NLS_ISO8859_4=m -CONFIG_NLS_ISO8859_5=m -CONFIG_NLS_ISO8859_6=m -CONFIG_NLS_ISO8859_7=m -CONFIG_NLS_ISO8859_9=m -CONFIG_NLS_ISO8859_13=m -CONFIG_NLS_ISO8859_14=m -CONFIG_NLS_ISO8859_15=m -CONFIG_LIBCRC32C=m -CONFIG_DEBUG_FS=y -CONFIG_MAGIC_SYSRQ=y -CONFIG_DEBUG_KERNEL=y -CONFIG_DEBUG_MUTEXES=y -CONFIG_XMON=y -CONFIG_XMON_DEFAULT=y -CONFIG_CRYPTO_NULL=m -CONFIG_CRYPTO_TEST=m -CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m -CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_MICHAEL_MIC=m -CONFIG_CRYPTO_SHA256=m -CONFIG_CRYPTO_SHA512=m -CONFIG_CRYPTO_TGR192=m -CONFIG_CRYPTO_WP512=m -CONFIG_CRYPTO_ANUBIS=m -CONFIG_CRYPTO_ARC4=m -CONFIG_CRYPTO_BLOWFISH=m -CONFIG_CRYPTO_CAST5=m -CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_KHAZAD=m -CONFIG_CRYPTO_SERPENT=m -CONFIG_CRYPTO_TEA=m -CONFIG_CRYPTO_TWOFISH=m -# CONFIG_CRYPTO_HW is not set diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 3315c9f0828a..aad501ae3834 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -36,7 +36,6 @@ CONFIG_PS3_ROM=m CONFIG_PS3_FLASH=m CONFIG_PS3_LPM=m CONFIG_PPC_IBM_CELL_BLADE=y -CONFIG_PPC_CELLEB=y CONFIG_PPC_CELL_QPACE=y CONFIG_RTAS_FLASH=m CONFIG_IBMEBUS=y @@ -89,7 +88,6 @@ CONFIG_IDE=y CONFIG_BLK_DEV_IDECD=y CONFIG_BLK_DEV_GENERIC=y CONFIG_BLK_DEV_AMD74XX=y -CONFIG_BLK_DEV_CELLEB=y CONFIG_BLK_DEV_IDE_PMAC=y CONFIG_BLK_DEV_IDE_PMAC_ATA100FIRST=y CONFIG_BLK_DEV_SD=y @@ -196,7 +194,6 @@ CONFIG_SERIAL_TXX9_CONSOLE=y CONFIG_SERIAL_JSM=m CONFIG_HVC_CONSOLE=y CONFIG_HVC_RTAS=y -CONFIG_HVC_BEAT=y CONFIG_HVCS=m CONFIG_VIRTIO_CONSOLE=m CONFIG_IBM_BSR=m diff --git a/arch/powerpc/include/asm/firmware.h b/arch/powerpc/include/asm/firmware.h index 681bc0314b6b..e05808a328db 100644 --- a/arch/powerpc/include/asm/firmware.h +++ b/arch/powerpc/include/asm/firmware.h @@ -42,7 +42,7 @@ #define FW_FEATURE_SPLPAR ASM_CONST(0x0000000000100000) #define FW_FEATURE_LPAR ASM_CONST(0x0000000000400000) #define FW_FEATURE_PS3_LV1 ASM_CONST(0x0000000000800000) -#define FW_FEATURE_BEAT ASM_CONST(0x0000000001000000) +/* Free ASM_CONST(0x0000000001000000) */ #define FW_FEATURE_CMO ASM_CONST(0x0000000002000000) #define FW_FEATURE_VPHN ASM_CONST(0x0000000004000000) #define FW_FEATURE_XCMO ASM_CONST(0x0000000008000000) @@ -75,8 +75,6 @@ enum { FW_FEATURE_POWERNV_ALWAYS = 0, FW_FEATURE_PS3_POSSIBLE = FW_FEATURE_LPAR | FW_FEATURE_PS3_LV1, FW_FEATURE_PS3_ALWAYS = FW_FEATURE_LPAR | FW_FEATURE_PS3_LV1, - FW_FEATURE_CELLEB_POSSIBLE = FW_FEATURE_LPAR | FW_FEATURE_BEAT, - FW_FEATURE_CELLEB_ALWAYS = 0, FW_FEATURE_NATIVE_POSSIBLE = 0, FW_FEATURE_NATIVE_ALWAYS = 0, FW_FEATURE_POSSIBLE = @@ -89,9 +87,6 @@ enum { #ifdef CONFIG_PPC_PS3 FW_FEATURE_PS3_POSSIBLE | #endif -#ifdef CONFIG_PPC_CELLEB - FW_FEATURE_CELLEB_POSSIBLE | -#endif #ifdef CONFIG_PPC_NATIVE FW_FEATURE_NATIVE_ALWAYS | #endif @@ -106,9 +101,6 @@ enum { #ifdef CONFIG_PPC_PS3 FW_FEATURE_PS3_ALWAYS & #endif -#ifdef CONFIG_PPC_CELLEB - FW_FEATURE_CELLEB_ALWAYS & -#endif #ifdef CONFIG_PPC_NATIVE FW_FEATURE_NATIVE_ALWAYS & #endif diff --git a/arch/powerpc/include/asm/smp.h b/arch/powerpc/include/asm/smp.h index d607df5081a7..7c19959cd705 100644 --- a/arch/powerpc/include/asm/smp.h +++ b/arch/powerpc/include/asm/smp.h @@ -125,7 +125,6 @@ extern irqreturn_t smp_ipi_demux(void); void smp_init_pSeries(void); void smp_init_cell(void); -void smp_init_celleb(void); void smp_setup_cpu_maps(void); extern int __cpu_disable(void); diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c index b7aa07279a63..7cc38b5b58bc 100644 --- a/arch/powerpc/kernel/udbg.c +++ b/arch/powerpc/kernel/udbg.c @@ -46,8 +46,6 @@ void __init udbg_early_init(void) #elif defined(CONFIG_PPC_EARLY_DEBUG_MAPLE) /* Maple real mode debug */ udbg_init_maple_realmode(); -#elif defined(CONFIG_PPC_EARLY_DEBUG_BEAT) - udbg_init_debug_beat(); #elif defined(CONFIG_PPC_EARLY_DEBUG_PAS_REALMODE) udbg_init_pas_realmode(); #elif defined(CONFIG_PPC_EARLY_DEBUG_BOOTX) diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 391b3f6b54a3..b7f9c408bf24 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -72,11 +72,6 @@ config PPC_SMP_MUXED_IPI cpu. This will enable the generic code to multiplex the 4 messages on to one ipi. -config PPC_UDBG_BEAT - bool "BEAT based debug console" - depends on PPC_CELLEB - default n - config IPIC bool default n diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig index 870b6dbd4d18..2f23133ab3d1 100644 --- a/arch/powerpc/platforms/cell/Kconfig +++ b/arch/powerpc/platforms/cell/Kconfig @@ -33,17 +33,6 @@ config PPC_IBM_CELL_BLADE select PPC_UDBG_16550 select UDBG_RTAS_CONSOLE -config PPC_CELLEB - bool "Toshiba's Cell Reference Set 'Celleb' Architecture" - depends on PPC64 && PPC_BOOK3S - select PPC_CELL_NATIVE - select PPC_OF_PLATFORM_PCI - select PCI - select HAS_TXX9_SERIAL - select PPC_UDBG_BEAT - select USB_OHCI_BIG_ENDIAN_MMIO - select USB_EHCI_BIG_ENDIAN_MMIO - config PPC_CELL_QPACE bool "IBM Cell - QPACE" depends on PPC64 && PPC_BOOK3S diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile index 2d16884f67b9..34699bddfddd 100644 --- a/arch/powerpc/platforms/cell/Makefile +++ b/arch/powerpc/platforms/cell/Makefile @@ -29,18 +29,3 @@ obj-$(CONFIG_AXON_MSI) += axon_msi.o # qpace setup obj-$(CONFIG_PPC_CELL_QPACE) += qpace_setup.o - -# celleb stuff -ifeq ($(CONFIG_PPC_CELLEB),y) -obj-y += celleb_setup.o \ - celleb_pci.o celleb_scc_epci.o \ - celleb_scc_pciex.o \ - celleb_scc_uhc.o \ - spider-pci.o beat.o beat_htab.o \ - beat_hvCall.o beat_interrupt.o \ - beat_iommu.o - -obj-$(CONFIG_PPC_UDBG_BEAT) += beat_udbg.o -obj-$(CONFIG_SERIAL_TXX9) += celleb_scc_sio.o -obj-$(CONFIG_SPU_BASE) += beat_spu_priv1.o -endif diff --git a/arch/powerpc/platforms/cell/beat.c b/arch/powerpc/platforms/cell/beat.c deleted file mode 100644 index affcf566d460..000000000000 --- a/arch/powerpc/platforms/cell/beat.c +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Simple routines for Celleb/Beat - * - * (C) Copyright 2006-2007 TOSHIBA CORPORATION - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "beat_wrapper.h" -#include "beat.h" -#include "beat_interrupt.h" - -static int beat_pm_poweroff_flag; - -void beat_restart(char *cmd) -{ - beat_shutdown_logical_partition(!beat_pm_poweroff_flag); -} - -void beat_power_off(void) -{ - beat_shutdown_logical_partition(0); -} - -u64 beat_halt_code = 0x1000000000000000UL; -EXPORT_SYMBOL(beat_halt_code); - -void beat_halt(void) -{ - beat_shutdown_logical_partition(beat_halt_code); -} - -int beat_set_rtc_time(struct rtc_time *rtc_time) -{ - u64 tim; - tim = mktime(rtc_time->tm_year+1900, - rtc_time->tm_mon+1, rtc_time->tm_mday, - rtc_time->tm_hour, rtc_time->tm_min, rtc_time->tm_sec); - if (beat_rtc_write(tim)) - return -1; - return 0; -} - -void beat_get_rtc_time(struct rtc_time *rtc_time) -{ - u64 tim; - - if (beat_rtc_read(&tim)) - tim = 0; - to_tm(tim, rtc_time); - rtc_time->tm_year -= 1900; - rtc_time->tm_mon -= 1; -} - -#define BEAT_NVRAM_SIZE 4096 - -ssize_t beat_nvram_read(char *buf, size_t count, loff_t *index) -{ - unsigned int i; - unsigned long len; - char *p = buf; - - if (*index >= BEAT_NVRAM_SIZE) - return -ENODEV; - i = *index; - if (i + count > BEAT_NVRAM_SIZE) - count = BEAT_NVRAM_SIZE - i; - - for (; count != 0; count -= len) { - len = count; - if (len > BEAT_NVRW_CNT) - len = BEAT_NVRW_CNT; - if (beat_eeprom_read(i, len, p)) - return -EIO; - - p += len; - i += len; - } - *index = i; - return p - buf; -} - -ssize_t beat_nvram_write(char *buf, size_t count, loff_t *index) -{ - unsigned int i; - unsigned long len; - char *p = buf; - - if (*index >= BEAT_NVRAM_SIZE) - return -ENODEV; - i = *index; - if (i + count > BEAT_NVRAM_SIZE) - count = BEAT_NVRAM_SIZE - i; - - for (; count != 0; count -= len) { - len = count; - if (len > BEAT_NVRW_CNT) - len = BEAT_NVRW_CNT; - if (beat_eeprom_write(i, len, p)) - return -EIO; - - p += len; - i += len; - } - *index = i; - return p - buf; -} - -ssize_t beat_nvram_get_size(void) -{ - return BEAT_NVRAM_SIZE; -} - -int beat_set_xdabr(unsigned long dabr, unsigned long dabrx) -{ - if (beat_set_dabr(dabr, dabrx)) - return -1; - return 0; -} - -int64_t beat_get_term_char(u64 vterm, u64 *len, u64 *t1, u64 *t2) -{ - u64 db[2]; - s64 ret; - - ret = beat_get_characters_from_console(vterm, len, (u8 *)db); - if (ret == 0) { - *t1 = db[0]; - *t2 = db[1]; - } - return ret; -} -EXPORT_SYMBOL(beat_get_term_char); - -int64_t beat_put_term_char(u64 vterm, u64 len, u64 t1, u64 t2) -{ - u64 db[2]; - - db[0] = t1; - db[1] = t2; - return beat_put_characters_to_console(vterm, len, (u8 *)db); -} -EXPORT_SYMBOL(beat_put_term_char); - -void beat_power_save(void) -{ - beat_pause(0); -} - -#ifdef CONFIG_KEXEC -void beat_kexec_cpu_down(int crash, int secondary) -{ - beatic_deinit_IRQ(); -} -#endif - -static irqreturn_t beat_power_event(int virq, void *arg) -{ - printk(KERN_DEBUG "Beat: power button pressed\n"); - beat_pm_poweroff_flag = 1; - ctrl_alt_del(); - return IRQ_HANDLED; -} - -static irqreturn_t beat_reset_event(int virq, void *arg) -{ - printk(KERN_DEBUG "Beat: reset button pressed\n"); - beat_pm_poweroff_flag = 0; - ctrl_alt_del(); - return IRQ_HANDLED; -} - -static struct beat_event_list { - const char *typecode; - irq_handler_t handler; - unsigned int virq; -} beat_event_list[] = { - { "power", beat_power_event, 0 }, - { "reset", beat_reset_event, 0 }, -}; - -static int __init beat_register_event(void) -{ - u64 path[4], data[2]; - int rc, i; - unsigned int virq; - - for (i = 0; i < ARRAY_SIZE(beat_event_list); i++) { - struct beat_event_list *ev = &beat_event_list[i]; - - if (beat_construct_event_receive_port(data) != 0) { - printk(KERN_ERR "Beat: " - "cannot construct event receive port for %s\n", - ev->typecode); - return -EINVAL; - } - - virq = irq_create_mapping(NULL, data[0]); - if (virq == NO_IRQ) { - printk(KERN_ERR "Beat: failed to get virtual IRQ" - " for event receive port for %s\n", - ev->typecode); - beat_destruct_event_receive_port(data[0]); - return -EIO; - } - ev->virq = virq; - - rc = request_irq(virq, ev->handler, 0, - ev->typecode, NULL); - if (rc != 0) { - printk(KERN_ERR "Beat: failed to request virtual IRQ" - " for event receive port for %s\n", - ev->typecode); - beat_destruct_event_receive_port(data[0]); - return rc; - } - - path[0] = 0x1000000065780000ul; /* 1,ex */ - path[1] = 0x627574746f6e0000ul; /* button */ - path[2] = 0; - strncpy((char *)&path[2], ev->typecode, 8); - path[3] = 0; - data[1] = 0; - - beat_create_repository_node(path, data); - } - return 0; -} - -static int __init beat_event_init(void) -{ - if (!firmware_has_feature(FW_FEATURE_BEAT)) - return -EINVAL; - - beat_pm_poweroff_flag = 0; - return beat_register_event(); -} - -device_initcall(beat_event_init); diff --git a/arch/powerpc/platforms/cell/beat.h b/arch/powerpc/platforms/cell/beat.h deleted file mode 100644 index bfcb8e351ae5..000000000000 --- a/arch/powerpc/platforms/cell/beat.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Guest OS Interfaces. - * - * (C) Copyright 2006 TOSHIBA CORPORATION - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef _CELLEB_BEAT_H -#define _CELLEB_BEAT_H - -int64_t beat_get_term_char(uint64_t, uint64_t *, uint64_t *, uint64_t *); -int64_t beat_put_term_char(uint64_t, uint64_t, uint64_t, uint64_t); -int64_t beat_repository_encode(int, const char *, uint64_t[4]); -void beat_restart(char *); -void beat_power_off(void); -void beat_halt(void); -int beat_set_rtc_time(struct rtc_time *); -void beat_get_rtc_time(struct rtc_time *); -ssize_t beat_nvram_get_size(void); -ssize_t beat_nvram_read(char *, size_t, loff_t *); -ssize_t beat_nvram_write(char *, size_t, loff_t *); -int beat_set_xdabr(unsigned long, unsigned long); -void beat_power_save(void); -void beat_kexec_cpu_down(int, int); - -#endif /* _CELLEB_BEAT_H */ diff --git a/arch/powerpc/platforms/cell/beat_htab.c b/arch/powerpc/platforms/cell/beat_htab.c deleted file mode 100644 index bee9232fe619..000000000000 --- a/arch/powerpc/platforms/cell/beat_htab.c +++ /dev/null @@ -1,445 +0,0 @@ -/* - * "Cell Reference Set" HTAB support. - * - * (C) Copyright 2006-2007 TOSHIBA CORPORATION - * - * This code is based on arch/powerpc/platforms/pseries/lpar.c: - * Copyright (C) 2001 Todd Inglett, IBM Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#undef DEBUG_LOW - -#include -#include - -#include -#include -#include -#include -#include - -#include "beat_wrapper.h" - -#ifdef DEBUG_LOW -#define DBG_LOW(fmt...) do { udbg_printf(fmt); } while (0) -#else -#define DBG_LOW(fmt...) do { } while (0) -#endif - -static DEFINE_RAW_SPINLOCK(beat_htab_lock); - -static inline unsigned int beat_read_mask(unsigned hpte_group) -{ - unsigned long rmask = 0; - u64 hpte_v[5]; - - beat_read_htab_entries(0, hpte_group + 0, hpte_v); - if (!(hpte_v[0] & HPTE_V_BOLTED)) - rmask |= 0x8000; - if (!(hpte_v[1] & HPTE_V_BOLTED)) - rmask |= 0x4000; - if (!(hpte_v[2] & HPTE_V_BOLTED)) - rmask |= 0x2000; - if (!(hpte_v[3] & HPTE_V_BOLTED)) - rmask |= 0x1000; - beat_read_htab_entries(0, hpte_group + 4, hpte_v); - if (!(hpte_v[0] & HPTE_V_BOLTED)) - rmask |= 0x0800; - if (!(hpte_v[1] & HPTE_V_BOLTED)) - rmask |= 0x0400; - if (!(hpte_v[2] & HPTE_V_BOLTED)) - rmask |= 0x0200; - if (!(hpte_v[3] & HPTE_V_BOLTED)) - rmask |= 0x0100; - hpte_group = ~hpte_group & (htab_hash_mask * HPTES_PER_GROUP); - beat_read_htab_entries(0, hpte_group + 0, hpte_v); - if (!(hpte_v[0] & HPTE_V_BOLTED)) - rmask |= 0x80; - if (!(hpte_v[1] & HPTE_V_BOLTED)) - rmask |= 0x40; - if (!(hpte_v[2] & HPTE_V_BOLTED)) - rmask |= 0x20; - if (!(hpte_v[3] & HPTE_V_BOLTED)) - rmask |= 0x10; - beat_read_htab_entries(0, hpte_group + 4, hpte_v); - if (!(hpte_v[0] & HPTE_V_BOLTED)) - rmask |= 0x08; - if (!(hpte_v[1] & HPTE_V_BOLTED)) - rmask |= 0x04; - if (!(hpte_v[2] & HPTE_V_BOLTED)) - rmask |= 0x02; - if (!(hpte_v[3] & HPTE_V_BOLTED)) - rmask |= 0x01; - return rmask; -} - -static long beat_lpar_hpte_insert(unsigned long hpte_group, - unsigned long vpn, unsigned long pa, - unsigned long rflags, unsigned long vflags, - int psize, int apsize, int ssize) -{ - unsigned long lpar_rc; - u64 hpte_v, hpte_r, slot; - - if (vflags & HPTE_V_SECONDARY) - return -1; - - if (!(vflags & HPTE_V_BOLTED)) - DBG_LOW("hpte_insert(group=%lx, va=%016lx, pa=%016lx, " - "rflags=%lx, vflags=%lx, psize=%d)\n", - hpte_group, va, pa, rflags, vflags, psize); - - hpte_v = hpte_encode_v(vpn, psize, apsize, MMU_SEGSIZE_256M) | - vflags | HPTE_V_VALID; - hpte_r = hpte_encode_r(pa, psize, apsize) | rflags; - - if (!(vflags & HPTE_V_BOLTED)) - DBG_LOW(" hpte_v=%016lx, hpte_r=%016lx\n", hpte_v, hpte_r); - - if (rflags & _PAGE_NO_CACHE) - hpte_r &= ~HPTE_R_M; - - raw_spin_lock(&beat_htab_lock); - lpar_rc = beat_read_mask(hpte_group); - if (lpar_rc == 0) { - if (!(vflags & HPTE_V_BOLTED)) - DBG_LOW(" full\n"); - raw_spin_unlock(&beat_htab_lock); - return -1; - } - - lpar_rc = beat_insert_htab_entry(0, hpte_group, lpar_rc << 48, - hpte_v, hpte_r, &slot); - raw_spin_unlock(&beat_htab_lock); - - /* - * Since we try and ioremap PHBs we don't own, the pte insert - * will fail. However we must catch the failure in hash_page - * or we will loop forever, so return -2 in this case. - */ - if (unlikely(lpar_rc != 0)) { - if (!(vflags & HPTE_V_BOLTED)) - DBG_LOW(" lpar err %lx\n", lpar_rc); - return -2; - } - if (!(vflags & HPTE_V_BOLTED)) - DBG_LOW(" -> slot: %lx\n", slot); - - /* We have to pass down the secondary bucket bit here as well */ - return (slot ^ hpte_group) & 15; -} - -static long beat_lpar_hpte_remove(unsigned long hpte_group) -{ - DBG_LOW("hpte_remove(group=%lx)\n", hpte_group); - return -1; -} - -static unsigned long beat_lpar_hpte_getword0(unsigned long slot) -{ - unsigned long dword0; - unsigned long lpar_rc; - u64 dword[5]; - - lpar_rc = beat_read_htab_entries(0, slot & ~3UL, dword); - - dword0 = dword[slot&3]; - - BUG_ON(lpar_rc != 0); - - return dword0; -} - -static void beat_lpar_hptab_clear(void) -{ - unsigned long size_bytes = 1UL << ppc64_pft_size; - unsigned long hpte_count = size_bytes >> 4; - int i; - u64 dummy0, dummy1; - - /* TODO: Use bulk call */ - for (i = 0; i < hpte_count; i++) - beat_write_htab_entry(0, i, 0, 0, -1UL, -1UL, &dummy0, &dummy1); -} - -/* - * NOTE: for updatepp ops we are fortunate that the linux "newpp" bits and - * the low 3 bits of flags happen to line up. So no transform is needed. - * We can probably optimize here and assume the high bits of newpp are - * already zero. For now I am paranoid. - */ -static long beat_lpar_hpte_updatepp(unsigned long slot, - unsigned long newpp, - unsigned long vpn, - int psize, int apsize, - int ssize, unsigned long flags) -{ - unsigned long lpar_rc; - u64 dummy0, dummy1; - unsigned long want_v; - - want_v = hpte_encode_avpn(vpn, psize, MMU_SEGSIZE_256M); - - DBG_LOW(" update: " - "avpnv=%016lx, slot=%016lx, psize: %d, newpp %016lx ... ", - want_v & HPTE_V_AVPN, slot, psize, newpp); - - raw_spin_lock(&beat_htab_lock); - dummy0 = beat_lpar_hpte_getword0(slot); - if ((dummy0 & ~0x7FUL) != (want_v & ~0x7FUL)) { - DBG_LOW("not found !\n"); - raw_spin_unlock(&beat_htab_lock); - return -1; - } - - lpar_rc = beat_write_htab_entry(0, slot, 0, newpp, 0, 7, &dummy0, - &dummy1); - raw_spin_unlock(&beat_htab_lock); - if (lpar_rc != 0 || dummy0 == 0) { - DBG_LOW("not found !\n"); - return -1; - } - - DBG_LOW("ok %lx %lx\n", dummy0, dummy1); - - BUG_ON(lpar_rc != 0); - - return 0; -} - -static long beat_lpar_hpte_find(unsigned long vpn, int psize) -{ - unsigned long hash; - unsigned long i, j; - long slot; - unsigned long want_v, hpte_v; - - hash = hpt_hash(vpn, mmu_psize_defs[psize].shift, MMU_SEGSIZE_256M); - want_v = hpte_encode_avpn(vpn, psize, MMU_SEGSIZE_256M); - - for (j = 0; j < 2; j++) { - slot = (hash & htab_hash_mask) * HPTES_PER_GROUP; - for (i = 0; i < HPTES_PER_GROUP; i++) { - hpte_v = beat_lpar_hpte_getword0(slot); - - if (HPTE_V_COMPARE(hpte_v, want_v) - && (hpte_v & HPTE_V_VALID) - && (!!(hpte_v & HPTE_V_SECONDARY) == j)) { - /* HPTE matches */ - if (j) - slot = -slot; - return slot; - } - ++slot; - } - hash = ~hash; - } - - return -1; -} - -static void beat_lpar_hpte_updateboltedpp(unsigned long newpp, - unsigned long ea, - int psize, int ssize) -{ - unsigned long vpn; - unsigned long lpar_rc, slot, vsid; - u64 dummy0, dummy1; - - vsid = get_kernel_vsid(ea, MMU_SEGSIZE_256M); - vpn = hpt_vpn(ea, vsid, MMU_SEGSIZE_256M); - - raw_spin_lock(&beat_htab_lock); - slot = beat_lpar_hpte_find(vpn, psize); - BUG_ON(slot == -1); - - lpar_rc = beat_write_htab_entry(0, slot, 0, newpp, 0, 7, - &dummy0, &dummy1); - raw_spin_unlock(&beat_htab_lock); - - BUG_ON(lpar_rc != 0); -} - -static void beat_lpar_hpte_invalidate(unsigned long slot, unsigned long vpn, - int psize, int apsize, - int ssize, int local) -{ - unsigned long want_v; - unsigned long lpar_rc; - u64 dummy1, dummy2; - unsigned long flags; - - DBG_LOW(" inval : slot=%lx, va=%016lx, psize: %d, local: %d\n", - slot, va, psize, local); - want_v = hpte_encode_avpn(vpn, psize, MMU_SEGSIZE_256M); - - raw_spin_lock_irqsave(&beat_htab_lock, flags); - dummy1 = beat_lpar_hpte_getword0(slot); - - if ((dummy1 & ~0x7FUL) != (want_v & ~0x7FUL)) { - DBG_LOW("not found !\n"); - raw_spin_unlock_irqrestore(&beat_htab_lock, flags); - return; - } - - lpar_rc = beat_write_htab_entry(0, slot, 0, 0, HPTE_V_VALID, 0, - &dummy1, &dummy2); - raw_spin_unlock_irqrestore(&beat_htab_lock, flags); - - BUG_ON(lpar_rc != 0); -} - -void __init hpte_init_beat(void) -{ - ppc_md.hpte_invalidate = beat_lpar_hpte_invalidate; - ppc_md.hpte_updatepp = beat_lpar_hpte_updatepp; - ppc_md.hpte_updateboltedpp = beat_lpar_hpte_updateboltedpp; - ppc_md.hpte_insert = beat_lpar_hpte_insert; - ppc_md.hpte_remove = beat_lpar_hpte_remove; - ppc_md.hpte_clear_all = beat_lpar_hptab_clear; -} - -static long beat_lpar_hpte_insert_v3(unsigned long hpte_group, - unsigned long vpn, unsigned long pa, - unsigned long rflags, unsigned long vflags, - int psize, int apsize, int ssize) -{ - unsigned long lpar_rc; - u64 hpte_v, hpte_r, slot; - - if (vflags & HPTE_V_SECONDARY) - return -1; - - if (!(vflags & HPTE_V_BOLTED)) - DBG_LOW("hpte_insert(group=%lx, vpn=%016lx, pa=%016lx, " - "rflags=%lx, vflags=%lx, psize=%d)\n", - hpte_group, vpn, pa, rflags, vflags, psize); - - hpte_v = hpte_encode_v(vpn, psize, apsize, MMU_SEGSIZE_256M) | - vflags | HPTE_V_VALID; - hpte_r = hpte_encode_r(pa, psize, apsize) | rflags; - - if (!(vflags & HPTE_V_BOLTED)) - DBG_LOW(" hpte_v=%016lx, hpte_r=%016lx\n", hpte_v, hpte_r); - - if (rflags & _PAGE_NO_CACHE) - hpte_r &= ~HPTE_R_M; - - /* insert into not-volted entry */ - lpar_rc = beat_insert_htab_entry3(0, hpte_group, hpte_v, hpte_r, - HPTE_V_BOLTED, 0, &slot); - /* - * Since we try and ioremap PHBs we don't own, the pte insert - * will fail. However we must catch the failure in hash_page - * or we will loop forever, so return -2 in this case. - */ - if (unlikely(lpar_rc != 0)) { - if (!(vflags & HPTE_V_BOLTED)) - DBG_LOW(" lpar err %lx\n", lpar_rc); - return -2; - } - if (!(vflags & HPTE_V_BOLTED)) - DBG_LOW(" -> slot: %lx\n", slot); - - /* We have to pass down the secondary bucket bit here as well */ - return (slot ^ hpte_group) & 15; -} - -/* - * NOTE: for updatepp ops we are fortunate that the linux "newpp" bits and - * the low 3 bits of flags happen to line up. So no transform is needed. - * We can probably optimize here and assume the high bits of newpp are - * already zero. For now I am paranoid. - */ -static long beat_lpar_hpte_updatepp_v3(unsigned long slot, - unsigned long newpp, - unsigned long vpn, - int psize, int apsize, - int ssize, unsigned long flags) -{ - unsigned long lpar_rc; - unsigned long want_v; - unsigned long pss; - - want_v = hpte_encode_avpn(vpn, psize, MMU_SEGSIZE_256M); - pss = (psize == MMU_PAGE_4K) ? -1UL : mmu_psize_defs[psize].penc[psize]; - - DBG_LOW(" update: " - "avpnv=%016lx, slot=%016lx, psize: %d, newpp %016lx ... ", - want_v & HPTE_V_AVPN, slot, psize, newpp); - - lpar_rc = beat_update_htab_permission3(0, slot, want_v, pss, 7, newpp); - - if (lpar_rc == 0xfffffff7) { - DBG_LOW("not found !\n"); - return -1; - } - - DBG_LOW("ok\n"); - - BUG_ON(lpar_rc != 0); - - return 0; -} - -static void beat_lpar_hpte_invalidate_v3(unsigned long slot, unsigned long vpn, - int psize, int apsize, - int ssize, int local) -{ - unsigned long want_v; - unsigned long lpar_rc; - unsigned long pss; - - DBG_LOW(" inval : slot=%lx, vpn=%016lx, psize: %d, local: %d\n", - slot, vpn, psize, local); - want_v = hpte_encode_avpn(vpn, psize, MMU_SEGSIZE_256M); - pss = (psize == MMU_PAGE_4K) ? -1UL : mmu_psize_defs[psize].penc[psize]; - - lpar_rc = beat_invalidate_htab_entry3(0, slot, want_v, pss); - - /* E_busy can be valid output: page may be already replaced */ - BUG_ON(lpar_rc != 0 && lpar_rc != 0xfffffff7); -} - -static int64_t _beat_lpar_hptab_clear_v3(void) -{ - return beat_clear_htab3(0); -} - -static void beat_lpar_hptab_clear_v3(void) -{ - _beat_lpar_hptab_clear_v3(); -} - -void __init hpte_init_beat_v3(void) -{ - if (_beat_lpar_hptab_clear_v3() == 0) { - ppc_md.hpte_invalidate = beat_lpar_hpte_invalidate_v3; - ppc_md.hpte_updatepp = beat_lpar_hpte_updatepp_v3; - ppc_md.hpte_updateboltedpp = beat_lpar_hpte_updateboltedpp; - ppc_md.hpte_insert = beat_lpar_hpte_insert_v3; - ppc_md.hpte_remove = beat_lpar_hpte_remove; - ppc_md.hpte_clear_all = beat_lpar_hptab_clear_v3; - } else { - ppc_md.hpte_invalidate = beat_lpar_hpte_invalidate; - ppc_md.hpte_updatepp = beat_lpar_hpte_updatepp; - ppc_md.hpte_updateboltedpp = beat_lpar_hpte_updateboltedpp; - ppc_md.hpte_insert = beat_lpar_hpte_insert; - ppc_md.hpte_remove = beat_lpar_hpte_remove; - ppc_md.hpte_clear_all = beat_lpar_hptab_clear; - } -} diff --git a/arch/powerpc/platforms/cell/beat_hvCall.S b/arch/powerpc/platforms/cell/beat_hvCall.S deleted file mode 100644 index 96c801907126..000000000000 --- a/arch/powerpc/platforms/cell/beat_hvCall.S +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Beat hypervisor call I/F - * - * (C) Copyright 2007 TOSHIBA CORPORATION - * - * This code is based on arch/powerpc/platforms/pseries/hvCall.S. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include - -/* Not implemented on Beat, now */ -#define HCALL_INST_PRECALL -#define HCALL_INST_POSTCALL - - .text - -#define HVSC .long 0x44000022 - -/* Note: takes only 7 input parameters at maximum */ -_GLOBAL(beat_hcall_norets) - HMT_MEDIUM - - mfcr r0 - stw r0,8(r1) - - HCALL_INST_PRECALL - - mr r11,r3 - mr r3,r4 - mr r4,r5 - mr r5,r6 - mr r6,r7 - mr r7,r8 - mr r8,r9 - - HVSC /* invoke the hypervisor */ - - HCALL_INST_POSTCALL - - lwz r0,8(r1) - mtcrf 0xff,r0 - - blr /* return r3 = status */ - -/* Note: takes 8 input parameters at maximum */ -_GLOBAL(beat_hcall_norets8) - HMT_MEDIUM - - mfcr r0 - stw r0,8(r1) - - HCALL_INST_PRECALL - - mr r11,r3 - mr r3,r4 - mr r4,r5 - mr r5,r6 - mr r6,r7 - mr r7,r8 - mr r8,r9 - ld r10,STK_PARAM(R10)(r1) - - HVSC /* invoke the hypervisor */ - - HCALL_INST_POSTCALL - - lwz r0,8(r1) - mtcrf 0xff,r0 - - blr /* return r3 = status */ - -/* Note: takes only 6 input parameters, 1 output parameters at maximum */ -_GLOBAL(beat_hcall1) - HMT_MEDIUM - - mfcr r0 - stw r0,8(r1) - - HCALL_INST_PRECALL - - std r4,STK_PARAM(R4)(r1) /* save ret buffer */ - - mr r11,r3 - mr r3,r5 - mr r4,r6 - mr r5,r7 - mr r6,r8 - mr r7,r9 - mr r8,r10 - - HVSC /* invoke the hypervisor */ - - HCALL_INST_POSTCALL - - ld r12,STK_PARAM(R4)(r1) - std r4, 0(r12) - - lwz r0,8(r1) - mtcrf 0xff,r0 - - blr /* return r3 = status */ - -/* Note: takes only 6 input parameters, 2 output parameters at maximum */ -_GLOBAL(beat_hcall2) - HMT_MEDIUM - - mfcr r0 - stw r0,8(r1) - - HCALL_INST_PRECALL - - std r4,STK_PARAM(R4)(r1) /* save ret buffer */ - - mr r11,r3 - mr r3,r5 - mr r4,r6 - mr r5,r7 - mr r6,r8 - mr r7,r9 - mr r8,r10 - - HVSC /* invoke the hypervisor */ - - HCALL_INST_POSTCALL - - ld r12,STK_PARAM(R4)(r1) - std r4, 0(r12) - std r5, 8(r12) - - lwz r0,8(r1) - mtcrf 0xff,r0 - - blr /* return r3 = status */ - -/* Note: takes only 6 input parameters, 3 output parameters at maximum */ -_GLOBAL(beat_hcall3) - HMT_MEDIUM - - mfcr r0 - stw r0,8(r1) - - HCALL_INST_PRECALL - - std r4,STK_PARAM(R4)(r1) /* save ret buffer */ - - mr r11,r3 - mr r3,r5 - mr r4,r6 - mr r5,r7 - mr r6,r8 - mr r7,r9 - mr r8,r10 - - HVSC /* invoke the hypervisor */ - - HCALL_INST_POSTCALL - - ld r12,STK_PARAM(R4)(r1) - std r4, 0(r12) - std r5, 8(r12) - std r6, 16(r12) - - lwz r0,8(r1) - mtcrf 0xff,r0 - - blr /* return r3 = status */ - -/* Note: takes only 6 input parameters, 4 output parameters at maximum */ -_GLOBAL(beat_hcall4) - HMT_MEDIUM - - mfcr r0 - stw r0,8(r1) - - HCALL_INST_PRECALL - - std r4,STK_PARAM(R4)(r1) /* save ret buffer */ - - mr r11,r3 - mr r3,r5 - mr r4,r6 - mr r5,r7 - mr r6,r8 - mr r7,r9 - mr r8,r10 - - HVSC /* invoke the hypervisor */ - - HCALL_INST_POSTCALL - - ld r12,STK_PARAM(R4)(r1) - std r4, 0(r12) - std r5, 8(r12) - std r6, 16(r12) - std r7, 24(r12) - - lwz r0,8(r1) - mtcrf 0xff,r0 - - blr /* return r3 = status */ - -/* Note: takes only 6 input parameters, 5 output parameters at maximum */ -_GLOBAL(beat_hcall5) - HMT_MEDIUM - - mfcr r0 - stw r0,8(r1) - - HCALL_INST_PRECALL - - std r4,STK_PARAM(R4)(r1) /* save ret buffer */ - - mr r11,r3 - mr r3,r5 - mr r4,r6 - mr r5,r7 - mr r6,r8 - mr r7,r9 - mr r8,r10 - - HVSC /* invoke the hypervisor */ - - HCALL_INST_POSTCALL - - ld r12,STK_PARAM(R4)(r1) - std r4, 0(r12) - std r5, 8(r12) - std r6, 16(r12) - std r7, 24(r12) - std r8, 32(r12) - - lwz r0,8(r1) - mtcrf 0xff,r0 - - blr /* return r3 = status */ - -/* Note: takes only 6 input parameters, 6 output parameters at maximum */ -_GLOBAL(beat_hcall6) - HMT_MEDIUM - - mfcr r0 - stw r0,8(r1) - - HCALL_INST_PRECALL - - std r4,STK_PARAM(R4)(r1) /* save ret buffer */ - - mr r11,r3 - mr r3,r5 - mr r4,r6 - mr r5,r7 - mr r6,r8 - mr r7,r9 - mr r8,r10 - - HVSC /* invoke the hypervisor */ - - HCALL_INST_POSTCALL - - ld r12,STK_PARAM(R4)(r1) - std r4, 0(r12) - std r5, 8(r12) - std r6, 16(r12) - std r7, 24(r12) - std r8, 32(r12) - std r9, 40(r12) - - lwz r0,8(r1) - mtcrf 0xff,r0 - - blr /* return r3 = status */ diff --git a/arch/powerpc/platforms/cell/beat_interrupt.c b/arch/powerpc/platforms/cell/beat_interrupt.c deleted file mode 100644 index 9e5dfbcc00af..000000000000 --- a/arch/powerpc/platforms/cell/beat_interrupt.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Celleb/Beat Interrupt controller - * - * (C) Copyright 2006-2007 TOSHIBA CORPORATION - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include - -#include - -#include "beat_interrupt.h" -#include "beat_wrapper.h" - -#define MAX_IRQS NR_IRQS -static DEFINE_RAW_SPINLOCK(beatic_irq_mask_lock); -static uint64_t beatic_irq_mask_enable[(MAX_IRQS+255)/64]; -static uint64_t beatic_irq_mask_ack[(MAX_IRQS+255)/64]; - -static struct irq_domain *beatic_host; - -/* - * In this implementation, "virq" == "IRQ plug number", - * "(irq_hw_number_t)hwirq" == "IRQ outlet number". - */ - -/* assumption: locked */ -static inline void beatic_update_irq_mask(unsigned int irq_plug) -{ - int off; - unsigned long masks[4]; - - off = (irq_plug / 256) * 4; - masks[0] = beatic_irq_mask_enable[off + 0] - & beatic_irq_mask_ack[off + 0]; - masks[1] = beatic_irq_mask_enable[off + 1] - & beatic_irq_mask_ack[off + 1]; - masks[2] = beatic_irq_mask_enable[off + 2] - & beatic_irq_mask_ack[off + 2]; - masks[3] = beatic_irq_mask_enable[off + 3] - & beatic_irq_mask_ack[off + 3]; - if (beat_set_interrupt_mask(irq_plug&~255UL, - masks[0], masks[1], masks[2], masks[3]) != 0) - panic("Failed to set mask IRQ!"); -} - -static void beatic_mask_irq(struct irq_data *d) -{ - unsigned long flags; - - raw_spin_lock_irqsave(&beatic_irq_mask_lock, flags); - beatic_irq_mask_enable[d->irq/64] &= ~(1UL << (63 - (d->irq%64))); - beatic_update_irq_mask(d->irq); - raw_spin_unlock_irqrestore(&beatic_irq_mask_lock, flags); -} - -static void beatic_unmask_irq(struct irq_data *d) -{ - unsigned long flags; - - raw_spin_lock_irqsave(&beatic_irq_mask_lock, flags); - beatic_irq_mask_enable[d->irq/64] |= 1UL << (63 - (d->irq%64)); - beatic_update_irq_mask(d->irq); - raw_spin_unlock_irqrestore(&beatic_irq_mask_lock, flags); -} - -static void beatic_ack_irq(struct irq_data *d) -{ - unsigned long flags; - - raw_spin_lock_irqsave(&beatic_irq_mask_lock, flags); - beatic_irq_mask_ack[d->irq/64] &= ~(1UL << (63 - (d->irq%64))); - beatic_update_irq_mask(d->irq); - raw_spin_unlock_irqrestore(&beatic_irq_mask_lock, flags); -} - -static void beatic_end_irq(struct irq_data *d) -{ - s64 err; - unsigned long flags; - - err = beat_downcount_of_interrupt(d->irq); - if (err != 0) { - if ((err & 0xFFFFFFFF) != 0xFFFFFFF5) /* -11: wrong state */ - panic("Failed to downcount IRQ! Error = %16llx", err); - - printk(KERN_ERR "IRQ over-downcounted, plug %d\n", d->irq); - } - raw_spin_lock_irqsave(&beatic_irq_mask_lock, flags); - beatic_irq_mask_ack[d->irq/64] |= 1UL << (63 - (d->irq%64)); - beatic_update_irq_mask(d->irq); - raw_spin_unlock_irqrestore(&beatic_irq_mask_lock, flags); -} - -static struct irq_chip beatic_pic = { - .name = "CELL-BEAT", - .irq_unmask = beatic_unmask_irq, - .irq_mask = beatic_mask_irq, - .irq_eoi = beatic_end_irq, -}; - -/* - * Dispose binding hardware IRQ number (hw) and Virtuql IRQ number (virq), - * update flags. - * - * Note that the number (virq) is already assigned at upper layer. - */ -static void beatic_pic_host_unmap(struct irq_domain *h, unsigned int virq) -{ - beat_destruct_irq_plug(virq); -} - -/* - * Create or update binding hardware IRQ number (hw) and Virtuql - * IRQ number (virq). This is called only once for a given mapping. - * - * Note that the number (virq) is already assigned at upper layer. - */ -static int beatic_pic_host_map(struct irq_domain *h, unsigned int virq, - irq_hw_number_t hw) -{ - int64_t err; - - err = beat_construct_and_connect_irq_plug(virq, hw); - if (err < 0) - return -EIO; - - irq_set_status_flags(virq, IRQ_LEVEL); - irq_set_chip_and_handler(virq, &beatic_pic, handle_fasteoi_irq); - return 0; -} - -/* - * Translate device-tree interrupt spec to irq_hw_number_t style (ulong), - * to pass away to irq_create_mapping(). - * - * Called from irq_create_of_mapping() only. - * Note: We have only 1 entry to translate. - */ -static int beatic_pic_host_xlate(struct irq_domain *h, struct device_node *ct, - const u32 *intspec, unsigned int intsize, - irq_hw_number_t *out_hwirq, - unsigned int *out_flags) -{ - const u64 *intspec2 = (const u64 *)intspec; - - *out_hwirq = *intspec2; - *out_flags |= IRQ_TYPE_LEVEL_LOW; - return 0; -} - -static int beatic_pic_host_match(struct irq_domain *h, struct device_node *np) -{ - /* Match all */ - return 1; -} - -static const struct irq_domain_ops beatic_pic_host_ops = { - .map = beatic_pic_host_map, - .unmap = beatic_pic_host_unmap, - .xlate = beatic_pic_host_xlate, - .match = beatic_pic_host_match, -}; - -/* - * Get an IRQ number - * Note: returns VIRQ - */ -static inline unsigned int beatic_get_irq_plug(void) -{ - int i; - uint64_t pending[4], ub; - - for (i = 0; i < MAX_IRQS; i += 256) { - beat_detect_pending_interrupts(i, pending); - __asm__ ("cntlzd %0,%1":"=r"(ub): - "r"(pending[0] & beatic_irq_mask_enable[i/64+0] - & beatic_irq_mask_ack[i/64+0])); - if (ub != 64) - return i + ub + 0; - __asm__ ("cntlzd %0,%1":"=r"(ub): - "r"(pending[1] & beatic_irq_mask_enable[i/64+1] - & beatic_irq_mask_ack[i/64+1])); - if (ub != 64) - return i + ub + 64; - __asm__ ("cntlzd %0,%1":"=r"(ub): - "r"(pending[2] & beatic_irq_mask_enable[i/64+2] - & beatic_irq_mask_ack[i/64+2])); - if (ub != 64) - return i + ub + 128; - __asm__ ("cntlzd %0,%1":"=r"(ub): - "r"(pending[3] & beatic_irq_mask_enable[i/64+3] - & beatic_irq_mask_ack[i/64+3])); - if (ub != 64) - return i + ub + 192; - } - - return NO_IRQ; -} -unsigned int beatic_get_irq(void) -{ - unsigned int ret; - - ret = beatic_get_irq_plug(); - if (ret != NO_IRQ) - beatic_ack_irq(irq_get_irq_data(ret)); - return ret; -} - -/* - */ -void __init beatic_init_IRQ(void) -{ - int i; - - memset(beatic_irq_mask_enable, 0, sizeof(beatic_irq_mask_enable)); - memset(beatic_irq_mask_ack, 255, sizeof(beatic_irq_mask_ack)); - for (i = 0; i < MAX_IRQS; i += 256) - beat_set_interrupt_mask(i, 0L, 0L, 0L, 0L); - - /* Set out get_irq function */ - ppc_md.get_irq = beatic_get_irq; - - /* Allocate an irq host */ - beatic_host = irq_domain_add_nomap(NULL, ~0, &beatic_pic_host_ops, NULL); - BUG_ON(beatic_host == NULL); - irq_set_default_host(beatic_host); -} - -void beatic_deinit_IRQ(void) -{ - int i; - - for (i = 1; i < nr_irqs; i++) - beat_destruct_irq_plug(i); -} diff --git a/arch/powerpc/platforms/cell/beat_interrupt.h b/arch/powerpc/platforms/cell/beat_interrupt.h deleted file mode 100644 index a7e52f91a078..000000000000 --- a/arch/powerpc/platforms/cell/beat_interrupt.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Celleb/Beat Interrupt controller - * - * (C) Copyright 2006 TOSHIBA CORPORATION - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef ASM_BEAT_PIC_H -#define ASM_BEAT_PIC_H -#ifdef __KERNEL__ - -extern void beatic_init_IRQ(void); -extern unsigned int beatic_get_irq(void); -extern void beatic_deinit_IRQ(void); - -#endif -#endif /* ASM_BEAT_PIC_H */ diff --git a/arch/powerpc/platforms/cell/beat_iommu.c b/arch/powerpc/platforms/cell/beat_iommu.c deleted file mode 100644 index 3ce685568935..000000000000 --- a/arch/powerpc/platforms/cell/beat_iommu.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Support for IOMMU on Celleb platform. - * - * (C) Copyright 2006-2007 TOSHIBA CORPORATION - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include - -#include - -#include "beat_wrapper.h" - -#define DMA_FLAGS 0xf800000000000000UL /* r/w permitted, coherency required, - strongest order */ - -static int __init find_dma_window(u64 *io_space_id, u64 *ioid, - u64 *base, u64 *size, u64 *io_page_size) -{ - struct device_node *dn; - const unsigned long *dma_window; - - for_each_node_by_type(dn, "ioif") { - dma_window = of_get_property(dn, "toshiba,dma-window", NULL); - if (dma_window) { - *io_space_id = (dma_window[0] >> 32) & 0xffffffffUL; - *ioid = dma_window[0] & 0x7ffUL; - *base = dma_window[1]; - *size = dma_window[2]; - *io_page_size = 1 << dma_window[3]; - of_node_put(dn); - return 1; - } - } - return 0; -} - -static unsigned long celleb_dma_direct_offset; - -static void __init celleb_init_direct_mapping(void) -{ - u64 lpar_addr, io_addr; - u64 io_space_id, ioid, dma_base, dma_size, io_page_size; - - if (!find_dma_window(&io_space_id, &ioid, &dma_base, &dma_size, - &io_page_size)) { - pr_info("No dma window found !\n"); - return; - } - - for (lpar_addr = 0; lpar_addr < dma_size; lpar_addr += io_page_size) { - io_addr = lpar_addr + dma_base; - (void)beat_put_iopte(io_space_id, io_addr, lpar_addr, - ioid, DMA_FLAGS); - } - - celleb_dma_direct_offset = dma_base; -} - -static void celleb_dma_dev_setup(struct device *dev) -{ - set_dma_ops(dev, &dma_direct_ops); - set_dma_offset(dev, 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, - unsigned long action, void *data) -{ - struct device *dev = data; - - /* We are only intereted in device addition */ - if (action != BUS_NOTIFY_ADD_DEVICE) - return 0; - - celleb_dma_dev_setup(dev); - - return 0; -} - -static struct notifier_block celleb_of_bus_notifier = { - .notifier_call = celleb_of_bus_notify -}; - -static int __init celleb_init_iommu(void) -{ - celleb_init_direct_mapping(); - ppc_md.pci_dma_dev_setup = celleb_pci_dma_dev_setup; - bus_register_notifier(&platform_bus_type, &celleb_of_bus_notifier); - - return 0; -} - -machine_arch_initcall(celleb_beat, celleb_init_iommu); diff --git a/arch/powerpc/platforms/cell/beat_spu_priv1.c b/arch/powerpc/platforms/cell/beat_spu_priv1.c deleted file mode 100644 index 13f52589d3a9..000000000000 --- a/arch/powerpc/platforms/cell/beat_spu_priv1.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * spu hypervisor abstraction for Beat - * - * (C) Copyright 2006-2007 TOSHIBA CORPORATION - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include - -#include "beat_wrapper.h" - -static inline void _int_mask_set(struct spu *spu, int class, u64 mask) -{ - spu->shadow_int_mask_RW[class] = mask; - beat_set_irq_mask_for_spe(spu->spe_id, class, mask); -} - -static inline u64 _int_mask_get(struct spu *spu, int class) -{ - return spu->shadow_int_mask_RW[class]; -} - -static void int_mask_set(struct spu *spu, int class, u64 mask) -{ - _int_mask_set(spu, class, mask); -} - -static u64 int_mask_get(struct spu *spu, int class) -{ - return _int_mask_get(spu, class); -} - -static void int_mask_and(struct spu *spu, int class, u64 mask) -{ - u64 old_mask; - old_mask = _int_mask_get(spu, class); - _int_mask_set(spu, class, old_mask & mask); -} - -static void int_mask_or(struct spu *spu, int class, u64 mask) -{ - u64 old_mask; - old_mask = _int_mask_get(spu, class); - _int_mask_set(spu, class, old_mask | mask); -} - -static void int_stat_clear(struct spu *spu, int class, u64 stat) -{ - beat_clear_interrupt_status_of_spe(spu->spe_id, class, stat); -} - -static u64 int_stat_get(struct spu *spu, int class) -{ - u64 int_stat; - beat_get_interrupt_status_of_spe(spu->spe_id, class, &int_stat); - return int_stat; -} - -static void cpu_affinity_set(struct spu *spu, int cpu) -{ - return; -} - -static u64 mfc_dar_get(struct spu *spu) -{ - u64 dar; - beat_get_spe_privileged_state_1_registers( - spu->spe_id, - offsetof(struct spu_priv1, mfc_dar_RW), &dar); - return dar; -} - -static u64 mfc_dsisr_get(struct spu *spu) -{ - u64 dsisr; - beat_get_spe_privileged_state_1_registers( - spu->spe_id, - offsetof(struct spu_priv1, mfc_dsisr_RW), &dsisr); - return dsisr; -} - -static void mfc_dsisr_set(struct spu *spu, u64 dsisr) -{ - beat_set_spe_privileged_state_1_registers( - spu->spe_id, - offsetof(struct spu_priv1, mfc_dsisr_RW), dsisr); -} - -static void mfc_sdr_setup(struct spu *spu) -{ - return; -} - -static void mfc_sr1_set(struct spu *spu, u64 sr1) -{ - beat_set_spe_privileged_state_1_registers( - spu->spe_id, - offsetof(struct spu_priv1, mfc_sr1_RW), sr1); -} - -static u64 mfc_sr1_get(struct spu *spu) -{ - u64 sr1; - beat_get_spe_privileged_state_1_registers( - spu->spe_id, - offsetof(struct spu_priv1, mfc_sr1_RW), &sr1); - return sr1; -} - -static void mfc_tclass_id_set(struct spu *spu, u64 tclass_id) -{ - beat_set_spe_privileged_state_1_registers( - spu->spe_id, - offsetof(struct spu_priv1, mfc_tclass_id_RW), tclass_id); -} - -static u64 mfc_tclass_id_get(struct spu *spu) -{ - u64 tclass_id; - beat_get_spe_privileged_state_1_registers( - spu->spe_id, - offsetof(struct spu_priv1, mfc_tclass_id_RW), &tclass_id); - return tclass_id; -} - -static void tlb_invalidate(struct spu *spu) -{ - beat_set_spe_privileged_state_1_registers( - spu->spe_id, - offsetof(struct spu_priv1, tlb_invalidate_entry_W), 0ul); -} - -static void resource_allocation_groupID_set(struct spu *spu, u64 id) -{ - beat_set_spe_privileged_state_1_registers( - spu->spe_id, - offsetof(struct spu_priv1, resource_allocation_groupID_RW), - id); -} - -static u64 resource_allocation_groupID_get(struct spu *spu) -{ - u64 id; - beat_get_spe_privileged_state_1_registers( - spu->spe_id, - offsetof(struct spu_priv1, resource_allocation_groupID_RW), - &id); - return id; -} - -static void resource_allocation_enable_set(struct spu *spu, u64 enable) -{ - beat_set_spe_privileged_state_1_registers( - spu->spe_id, - offsetof(struct spu_priv1, resource_allocation_enable_RW), - enable); -} - -static u64 resource_allocation_enable_get(struct spu *spu) -{ - u64 enable; - beat_get_spe_privileged_state_1_registers( - spu->spe_id, - offsetof(struct spu_priv1, resource_allocation_enable_RW), - &enable); - return enable; -} - -const struct spu_priv1_ops spu_priv1_beat_ops = { - .int_mask_and = int_mask_and, - .int_mask_or = int_mask_or, - .int_mask_set = int_mask_set, - .int_mask_get = int_mask_get, - .int_stat_clear = int_stat_clear, - .int_stat_get = int_stat_get, - .cpu_affinity_set = cpu_affinity_set, - .mfc_dar_get = mfc_dar_get, - .mfc_dsisr_get = mfc_dsisr_get, - .mfc_dsisr_set = mfc_dsisr_set, - .mfc_sdr_setup = mfc_sdr_setup, - .mfc_sr1_set = mfc_sr1_set, - .mfc_sr1_get = mfc_sr1_get, - .mfc_tclass_id_set = mfc_tclass_id_set, - .mfc_tclass_id_get = mfc_tclass_id_get, - .tlb_invalidate = tlb_invalidate, - .resource_allocation_groupID_set = resource_allocation_groupID_set, - .resource_allocation_groupID_get = resource_allocation_groupID_get, - .resource_allocation_enable_set = resource_allocation_enable_set, - .resource_allocation_enable_get = resource_allocation_enable_get, -}; diff --git a/arch/powerpc/platforms/cell/beat_syscall.h b/arch/powerpc/platforms/cell/beat_syscall.h deleted file mode 100644 index 8580dc7e1798..000000000000 --- a/arch/powerpc/platforms/cell/beat_syscall.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Beat hypervisor call numbers - * - * (C) Copyright 2004-2007 TOSHIBA CORPORATION - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef BEAT_BEAT_syscall_H -#define BEAT_BEAT_syscall_H - -#ifdef __ASSEMBLY__ -#define __BEAT_ADD_VENDOR_ID(__x, __v) ((__v)<<60|(__x)) -#else -#define __BEAT_ADD_VENDOR_ID(__x, __v) ((u64)(__v)<<60|(__x)) -#endif -#define HV_allocate_memory __BEAT_ADD_VENDOR_ID(0, 0) -#define HV_construct_virtual_address_space __BEAT_ADD_VENDOR_ID(2, 0) -#define HV_destruct_virtual_address_space __BEAT_ADD_VENDOR_ID(10, 0) -#define HV_get_virtual_address_space_id_of_ppe __BEAT_ADD_VENDOR_ID(4, 0) -#define HV_query_logical_partition_address_region_info \ - __BEAT_ADD_VENDOR_ID(6, 0) -#define HV_release_memory __BEAT_ADD_VENDOR_ID(13, 0) -#define HV_select_virtual_address_space __BEAT_ADD_VENDOR_ID(7, 0) -#define HV_load_range_registers __BEAT_ADD_VENDOR_ID(68, 0) -#define HV_set_ppe_l2cache_rmt_entry __BEAT_ADD_VENDOR_ID(70, 0) -#define HV_set_ppe_tlb_rmt_entry __BEAT_ADD_VENDOR_ID(71, 0) -#define HV_set_spe_tlb_rmt_entry __BEAT_ADD_VENDOR_ID(72, 0) -#define HV_get_io_address_translation_fault_info __BEAT_ADD_VENDOR_ID(14, 0) -#define HV_get_iopte __BEAT_ADD_VENDOR_ID(16, 0) -#define HV_preload_iopt_cache __BEAT_ADD_VENDOR_ID(17, 0) -#define HV_put_iopte __BEAT_ADD_VENDOR_ID(15, 0) -#define HV_connect_event_ports __BEAT_ADD_VENDOR_ID(21, 0) -#define HV_construct_event_receive_port __BEAT_ADD_VENDOR_ID(18, 0) -#define HV_destruct_event_receive_port __BEAT_ADD_VENDOR_ID(19, 0) -#define HV_destruct_event_send_port __BEAT_ADD_VENDOR_ID(22, 0) -#define HV_get_state_of_event_send_port __BEAT_ADD_VENDOR_ID(25, 0) -#define HV_request_to_connect_event_ports __BEAT_ADD_VENDOR_ID(20, 0) -#define HV_send_event_externally __BEAT_ADD_VENDOR_ID(23, 0) -#define HV_send_event_locally __BEAT_ADD_VENDOR_ID(24, 0) -#define HV_construct_and_connect_irq_plug __BEAT_ADD_VENDOR_ID(28, 0) -#define HV_destruct_irq_plug __BEAT_ADD_VENDOR_ID(29, 0) -#define HV_detect_pending_interrupts __BEAT_ADD_VENDOR_ID(26, 0) -#define HV_end_of_interrupt __BEAT_ADD_VENDOR_ID(27, 0) -#define HV_assign_control_signal_notification_port __BEAT_ADD_VENDOR_ID(45, 0) -#define HV_end_of_control_signal_processing __BEAT_ADD_VENDOR_ID(48, 0) -#define HV_get_control_signal __BEAT_ADD_VENDOR_ID(46, 0) -#define HV_set_irq_mask_for_spe __BEAT_ADD_VENDOR_ID(61, 0) -#define HV_shutdown_logical_partition __BEAT_ADD_VENDOR_ID(44, 0) -#define HV_connect_message_ports __BEAT_ADD_VENDOR_ID(35, 0) -#define HV_destruct_message_port __BEAT_ADD_VENDOR_ID(36, 0) -#define HV_receive_message __BEAT_ADD_VENDOR_ID(37, 0) -#define HV_get_message_port_info __BEAT_ADD_VENDOR_ID(34, 0) -#define HV_request_to_connect_message_ports __BEAT_ADD_VENDOR_ID(33, 0) -#define HV_send_message __BEAT_ADD_VENDOR_ID(32, 0) -#define HV_get_logical_ppe_id __BEAT_ADD_VENDOR_ID(69, 0) -#define HV_pause __BEAT_ADD_VENDOR_ID(9, 0) -#define HV_destruct_shared_memory_handle __BEAT_ADD_VENDOR_ID(51, 0) -#define HV_get_shared_memory_info __BEAT_ADD_VENDOR_ID(52, 0) -#define HV_permit_sharing_memory __BEAT_ADD_VENDOR_ID(50, 0) -#define HV_request_to_attach_shared_memory __BEAT_ADD_VENDOR_ID(49, 0) -#define HV_enable_logical_spe_execution __BEAT_ADD_VENDOR_ID(55, 0) -#define HV_construct_logical_spe __BEAT_ADD_VENDOR_ID(53, 0) -#define HV_disable_logical_spe_execution __BEAT_ADD_VENDOR_ID(56, 0) -#define HV_destruct_logical_spe __BEAT_ADD_VENDOR_ID(54, 0) -#define HV_sense_spe_execution_status __BEAT_ADD_VENDOR_ID(58, 0) -#define HV_insert_htab_entry __BEAT_ADD_VENDOR_ID(101, 0) -#define HV_read_htab_entries __BEAT_ADD_VENDOR_ID(95, 0) -#define HV_write_htab_entry __BEAT_ADD_VENDOR_ID(94, 0) -#define HV_assign_io_address_translation_fault_port \ - __BEAT_ADD_VENDOR_ID(100, 0) -#define HV_set_interrupt_mask __BEAT_ADD_VENDOR_ID(73, 0) -#define HV_get_logical_partition_id __BEAT_ADD_VENDOR_ID(74, 0) -#define HV_create_repository_node2 __BEAT_ADD_VENDOR_ID(90, 0) -#define HV_create_repository_node __BEAT_ADD_VENDOR_ID(90, 0) /* alias */ -#define HV_get_repository_node_value2 __BEAT_ADD_VENDOR_ID(91, 0) -#define HV_get_repository_node_value __BEAT_ADD_VENDOR_ID(91, 0) /* alias */ -#define HV_modify_repository_node_value2 __BEAT_ADD_VENDOR_ID(92, 0) -#define HV_modify_repository_node_value __BEAT_ADD_VENDOR_ID(92, 0) /* alias */ -#define HV_remove_repository_node2 __BEAT_ADD_VENDOR_ID(93, 0) -#define HV_remove_repository_node __BEAT_ADD_VENDOR_ID(93, 0) /* alias */ -#define HV_cancel_shared_memory __BEAT_ADD_VENDOR_ID(104, 0) -#define HV_clear_interrupt_status_of_spe __BEAT_ADD_VENDOR_ID(206, 0) -#define HV_construct_spe_irq_outlet __BEAT_ADD_VENDOR_ID(80, 0) -#define HV_destruct_spe_irq_outlet __BEAT_ADD_VENDOR_ID(81, 0) -#define HV_disconnect_ipspc_service __BEAT_ADD_VENDOR_ID(88, 0) -#define HV_execute_ipspc_command __BEAT_ADD_VENDOR_ID(86, 0) -#define HV_get_interrupt_status_of_spe __BEAT_ADD_VENDOR_ID(205, 0) -#define HV_get_spe_privileged_state_1_registers __BEAT_ADD_VENDOR_ID(208, 0) -#define HV_permit_use_of_ipspc_service __BEAT_ADD_VENDOR_ID(85, 0) -#define HV_reinitialize_logical_spe __BEAT_ADD_VENDOR_ID(82, 0) -#define HV_request_ipspc_service __BEAT_ADD_VENDOR_ID(84, 0) -#define HV_stop_ipspc_command __BEAT_ADD_VENDOR_ID(87, 0) -#define HV_set_spe_privileged_state_1_registers __BEAT_ADD_VENDOR_ID(204, 0) -#define HV_get_status_of_ipspc_service __BEAT_ADD_VENDOR_ID(203, 0) -#define HV_put_characters_to_console __BEAT_ADD_VENDOR_ID(0x101, 1) -#define HV_get_characters_from_console __BEAT_ADD_VENDOR_ID(0x102, 1) -#define HV_get_base_clock __BEAT_ADD_VENDOR_ID(0x111, 1) -#define HV_set_base_clock __BEAT_ADD_VENDOR_ID(0x112, 1) -#define HV_get_frame_cycle __BEAT_ADD_VENDOR_ID(0x114, 1) -#define HV_disable_console __BEAT_ADD_VENDOR_ID(0x115, 1) -#define HV_disable_all_console __BEAT_ADD_VENDOR_ID(0x116, 1) -#define HV_oneshot_timer __BEAT_ADD_VENDOR_ID(0x117, 1) -#define HV_set_dabr __BEAT_ADD_VENDOR_ID(0x118, 1) -#define HV_get_dabr __BEAT_ADD_VENDOR_ID(0x119, 1) -#define HV_start_hv_stats __BEAT_ADD_VENDOR_ID(0x21c, 1) -#define HV_stop_hv_stats __BEAT_ADD_VENDOR_ID(0x21d, 1) -#define HV_get_hv_stats __BEAT_ADD_VENDOR_ID(0x21e, 1) -#define HV_get_hv_error_stats __BEAT_ADD_VENDOR_ID(0x221, 1) -#define HV_get_stats __BEAT_ADD_VENDOR_ID(0x224, 1) -#define HV_get_heap_stats __BEAT_ADD_VENDOR_ID(0x225, 1) -#define HV_get_memory_stats __BEAT_ADD_VENDOR_ID(0x227, 1) -#define HV_get_memory_detail __BEAT_ADD_VENDOR_ID(0x228, 1) -#define HV_set_priority_of_irq_outlet __BEAT_ADD_VENDOR_ID(0x122, 1) -#define HV_get_physical_spe_by_reservation_id __BEAT_ADD_VENDOR_ID(0x128, 1) -#define HV_get_spe_context __BEAT_ADD_VENDOR_ID(0x129, 1) -#define HV_set_spe_context __BEAT_ADD_VENDOR_ID(0x12a, 1) -#define HV_downcount_of_interrupt __BEAT_ADD_VENDOR_ID(0x12e, 1) -#define HV_peek_spe_context __BEAT_ADD_VENDOR_ID(0x12f, 1) -#define HV_read_bpa_register __BEAT_ADD_VENDOR_ID(0x131, 1) -#define HV_write_bpa_register __BEAT_ADD_VENDOR_ID(0x132, 1) -#define HV_map_context_table_of_spe __BEAT_ADD_VENDOR_ID(0x137, 1) -#define HV_get_slb_for_logical_spe __BEAT_ADD_VENDOR_ID(0x138, 1) -#define HV_set_slb_for_logical_spe __BEAT_ADD_VENDOR_ID(0x139, 1) -#define HV_init_pm __BEAT_ADD_VENDOR_ID(0x150, 1) -#define HV_set_pm_signal __BEAT_ADD_VENDOR_ID(0x151, 1) -#define HV_get_pm_signal __BEAT_ADD_VENDOR_ID(0x152, 1) -#define HV_set_pm_config __BEAT_ADD_VENDOR_ID(0x153, 1) -#define HV_get_pm_config __BEAT_ADD_VENDOR_ID(0x154, 1) -#define HV_get_inner_trace_data __BEAT_ADD_VENDOR_ID(0x155, 1) -#define HV_set_ext_trace_buffer __BEAT_ADD_VENDOR_ID(0x156, 1) -#define HV_get_ext_trace_buffer __BEAT_ADD_VENDOR_ID(0x157, 1) -#define HV_set_pm_interrupt __BEAT_ADD_VENDOR_ID(0x158, 1) -#define HV_get_pm_interrupt __BEAT_ADD_VENDOR_ID(0x159, 1) -#define HV_kick_pm __BEAT_ADD_VENDOR_ID(0x160, 1) -#define HV_construct_pm_context __BEAT_ADD_VENDOR_ID(0x164, 1) -#define HV_destruct_pm_context __BEAT_ADD_VENDOR_ID(0x165, 1) -#define HV_be_slow __BEAT_ADD_VENDOR_ID(0x170, 1) -#define HV_assign_ipspc_server_connection_status_notification_port \ - __BEAT_ADD_VENDOR_ID(0x173, 1) -#define HV_get_raid_of_physical_spe __BEAT_ADD_VENDOR_ID(0x174, 1) -#define HV_set_physical_spe_to_rag __BEAT_ADD_VENDOR_ID(0x175, 1) -#define HV_release_physical_spe_from_rag __BEAT_ADD_VENDOR_ID(0x176, 1) -#define HV_rtc_read __BEAT_ADD_VENDOR_ID(0x190, 1) -#define HV_rtc_write __BEAT_ADD_VENDOR_ID(0x191, 1) -#define HV_eeprom_read __BEAT_ADD_VENDOR_ID(0x192, 1) -#define HV_eeprom_write __BEAT_ADD_VENDOR_ID(0x193, 1) -#define HV_insert_htab_entry3 __BEAT_ADD_VENDOR_ID(0x104, 1) -#define HV_invalidate_htab_entry3 __BEAT_ADD_VENDOR_ID(0x105, 1) -#define HV_update_htab_permission3 __BEAT_ADD_VENDOR_ID(0x106, 1) -#define HV_clear_htab3 __BEAT_ADD_VENDOR_ID(0x107, 1) -#endif diff --git a/arch/powerpc/platforms/cell/beat_udbg.c b/arch/powerpc/platforms/cell/beat_udbg.c deleted file mode 100644 index 350735bc8888..000000000000 --- a/arch/powerpc/platforms/cell/beat_udbg.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * udbg function for Beat - * - * (C) Copyright 2006 TOSHIBA CORPORATION - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include - -#include -#include -#include - -#include "beat.h" - -#define celleb_vtermno 0 - -static void udbg_putc_beat(char c) -{ - unsigned long rc; - - if (c == '\n') - udbg_putc_beat('\r'); - - rc = beat_put_term_char(celleb_vtermno, 1, (uint64_t)c << 56, 0); -} - -/* Buffered chars getc */ -static u64 inbuflen; -static u64 inbuf[2]; /* must be 2 u64s */ - -static int udbg_getc_poll_beat(void) -{ - /* The interface is tricky because it may return up to 16 chars. - * We save them statically for future calls to udbg_getc(). - */ - char ch, *buf = (char *)inbuf; - int i; - long rc; - if (inbuflen == 0) { - /* get some more chars. */ - inbuflen = 0; - rc = beat_get_term_char(celleb_vtermno, &inbuflen, - inbuf+0, inbuf+1); - if (rc != 0) - inbuflen = 0; /* otherwise inbuflen is garbage */ - } - if (inbuflen <= 0 || inbuflen > 16) { - /* Catch error case as well as other oddities (corruption) */ - inbuflen = 0; - return -1; - } - ch = buf[0]; - for (i = 1; i < inbuflen; i++) /* shuffle them down. */ - buf[i-1] = buf[i]; - inbuflen--; - return ch; -} - -static int udbg_getc_beat(void) -{ - int ch; - for (;;) { - ch = udbg_getc_poll_beat(); - if (ch == -1) { - /* This shouldn't be needed...but... */ - volatile unsigned long delay; - for (delay = 0; delay < 2000000; delay++) - ; - } else { - return ch; - } - } -} - -/* call this from early_init() for a working debug console on - * vterm capable LPAR machines - */ -void __init udbg_init_debug_beat(void) -{ - udbg_putc = udbg_putc_beat; - udbg_getc = udbg_getc_beat; - udbg_getc_poll = udbg_getc_poll_beat; -} diff --git a/arch/powerpc/platforms/cell/beat_wrapper.h b/arch/powerpc/platforms/cell/beat_wrapper.h deleted file mode 100644 index c1109969f242..000000000000 --- a/arch/powerpc/platforms/cell/beat_wrapper.h +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Beat hypervisor call I/F - * - * (C) Copyright 2007 TOSHIBA CORPORATION - * - * This code is based on arch/powerpc/platforms/pseries/plpar_wrapper.h. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#ifndef BEAT_HCALL -#include -#include "beat_syscall.h" - -/* defined in hvCall.S */ -extern s64 beat_hcall_norets(u64 opcode, ...); -extern s64 beat_hcall_norets8(u64 opcode, u64 arg1, u64 arg2, u64 arg3, - u64 arg4, u64 arg5, u64 arg6, u64 arg7, u64 arg8); -extern s64 beat_hcall1(u64 opcode, u64 retbuf[1], ...); -extern s64 beat_hcall2(u64 opcode, u64 retbuf[2], ...); -extern s64 beat_hcall3(u64 opcode, u64 retbuf[3], ...); -extern s64 beat_hcall4(u64 opcode, u64 retbuf[4], ...); -extern s64 beat_hcall5(u64 opcode, u64 retbuf[5], ...); -extern s64 beat_hcall6(u64 opcode, u64 retbuf[6], ...); - -static inline s64 beat_downcount_of_interrupt(u64 plug_id) -{ - return beat_hcall_norets(HV_downcount_of_interrupt, plug_id); -} - -static inline s64 beat_set_interrupt_mask(u64 index, - u64 val0, u64 val1, u64 val2, u64 val3) -{ - return beat_hcall_norets(HV_set_interrupt_mask, index, - val0, val1, val2, val3); -} - -static inline s64 beat_destruct_irq_plug(u64 plug_id) -{ - return beat_hcall_norets(HV_destruct_irq_plug, plug_id); -} - -static inline s64 beat_construct_and_connect_irq_plug(u64 plug_id, - u64 outlet_id) -{ - return beat_hcall_norets(HV_construct_and_connect_irq_plug, plug_id, - outlet_id); -} - -static inline s64 beat_detect_pending_interrupts(u64 index, u64 *retbuf) -{ - return beat_hcall4(HV_detect_pending_interrupts, retbuf, index); -} - -static inline s64 beat_pause(u64 style) -{ - return beat_hcall_norets(HV_pause, style); -} - -static inline s64 beat_read_htab_entries(u64 htab_id, u64 index, u64 *retbuf) -{ - return beat_hcall5(HV_read_htab_entries, retbuf, htab_id, index); -} - -static inline s64 beat_insert_htab_entry(u64 htab_id, u64 group, - u64 bitmask, u64 hpte_v, u64 hpte_r, u64 *slot) -{ - u64 dummy[3]; - s64 ret; - - ret = beat_hcall3(HV_insert_htab_entry, dummy, htab_id, group, - bitmask, hpte_v, hpte_r); - *slot = dummy[0]; - return ret; -} - -static inline s64 beat_write_htab_entry(u64 htab_id, u64 slot, - u64 hpte_v, u64 hpte_r, u64 mask_v, u64 mask_r, - u64 *ret_v, u64 *ret_r) -{ - u64 dummy[2]; - s64 ret; - - ret = beat_hcall2(HV_write_htab_entry, dummy, htab_id, slot, - hpte_v, hpte_r, mask_v, mask_r); - *ret_v = dummy[0]; - *ret_r = dummy[1]; - return ret; -} - -static inline s64 beat_insert_htab_entry3(u64 htab_id, u64 group, - u64 hpte_v, u64 hpte_r, u64 mask_v, u64 value_v, u64 *slot) -{ - u64 dummy[1]; - s64 ret; - - ret = beat_hcall1(HV_insert_htab_entry3, dummy, htab_id, group, - hpte_v, hpte_r, mask_v, value_v); - *slot = dummy[0]; - return ret; -} - -static inline s64 beat_invalidate_htab_entry3(u64 htab_id, u64 group, - u64 va, u64 pss) -{ - return beat_hcall_norets(HV_invalidate_htab_entry3, - htab_id, group, va, pss); -} - -static inline s64 beat_update_htab_permission3(u64 htab_id, u64 group, - u64 va, u64 pss, u64 ptel_mask, u64 ptel_value) -{ - return beat_hcall_norets(HV_update_htab_permission3, - htab_id, group, va, pss, ptel_mask, ptel_value); -} - -static inline s64 beat_clear_htab3(u64 htab_id) -{ - return beat_hcall_norets(HV_clear_htab3, htab_id); -} - -static inline void beat_shutdown_logical_partition(u64 code) -{ - (void)beat_hcall_norets(HV_shutdown_logical_partition, code); -} - -static inline s64 beat_rtc_write(u64 time_from_epoch) -{ - return beat_hcall_norets(HV_rtc_write, time_from_epoch); -} - -static inline s64 beat_rtc_read(u64 *time_from_epoch) -{ - u64 dummy[1]; - s64 ret; - - ret = beat_hcall1(HV_rtc_read, dummy); - *time_from_epoch = dummy[0]; - return ret; -} - -#define BEAT_NVRW_CNT (sizeof(u64) * 6) - -static inline s64 beat_eeprom_write(u64 index, u64 length, u8 *buffer) -{ - u64 b[6]; - - if (length > BEAT_NVRW_CNT) - return -1; - memcpy(b, buffer, sizeof(b)); - return beat_hcall_norets8(HV_eeprom_write, index, length, - b[0], b[1], b[2], b[3], b[4], b[5]); -} - -static inline s64 beat_eeprom_read(u64 index, u64 length, u8 *buffer) -{ - u64 b[6]; - s64 ret; - - if (length > BEAT_NVRW_CNT) - return -1; - ret = beat_hcall6(HV_eeprom_read, b, index, length); - memcpy(buffer, b, length); - return ret; -} - -static inline s64 beat_set_dabr(u64 value, u64 style) -{ - return beat_hcall_norets(HV_set_dabr, value, style); -} - -static inline s64 beat_get_characters_from_console(u64 termno, u64 *len, - u8 *buffer) -{ - u64 dummy[3]; - s64 ret; - - ret = beat_hcall3(HV_get_characters_from_console, dummy, termno, len); - *len = dummy[0]; - memcpy(buffer, dummy + 1, *len); - return ret; -} - -static inline s64 beat_put_characters_to_console(u64 termno, u64 len, - u8 *buffer) -{ - u64 b[2]; - - memcpy(b, buffer, len); - return beat_hcall_norets(HV_put_characters_to_console, termno, len, - b[0], b[1]); -} - -static inline s64 beat_get_spe_privileged_state_1_registers( - u64 id, u64 offsetof, u64 *value) -{ - u64 dummy[1]; - s64 ret; - - ret = beat_hcall1(HV_get_spe_privileged_state_1_registers, dummy, id, - offsetof); - *value = dummy[0]; - return ret; -} - -static inline s64 beat_set_irq_mask_for_spe(u64 id, u64 class, u64 mask) -{ - return beat_hcall_norets(HV_set_irq_mask_for_spe, id, class, mask); -} - -static inline s64 beat_clear_interrupt_status_of_spe(u64 id, u64 class, - u64 mask) -{ - return beat_hcall_norets(HV_clear_interrupt_status_of_spe, - id, class, mask); -} - -static inline s64 beat_set_spe_privileged_state_1_registers( - u64 id, u64 offsetof, u64 value) -{ - return beat_hcall_norets(HV_set_spe_privileged_state_1_registers, - id, offsetof, value); -} - -static inline s64 beat_get_interrupt_status_of_spe(u64 id, u64 class, u64 *val) -{ - u64 dummy[1]; - s64 ret; - - ret = beat_hcall1(HV_get_interrupt_status_of_spe, dummy, id, class); - *val = dummy[0]; - return ret; -} - -static inline s64 beat_put_iopte(u64 ioas_id, u64 io_addr, u64 real_addr, - u64 ioid, u64 flags) -{ - return beat_hcall_norets(HV_put_iopte, ioas_id, io_addr, real_addr, - ioid, flags); -} - -static inline s64 beat_construct_event_receive_port(u64 *port) -{ - u64 dummy[1]; - s64 ret; - - ret = beat_hcall1(HV_construct_event_receive_port, dummy); - *port = dummy[0]; - return ret; -} - -static inline s64 beat_destruct_event_receive_port(u64 port) -{ - s64 ret; - - ret = beat_hcall_norets(HV_destruct_event_receive_port, port); - return ret; -} - -static inline s64 beat_create_repository_node(u64 path[4], u64 data[2]) -{ - s64 ret; - - ret = beat_hcall_norets(HV_create_repository_node2, - path[0], path[1], path[2], path[3], data[0], data[1]); - return ret; -} - -static inline s64 beat_get_repository_node_value(u64 lpid, u64 path[4], - u64 data[2]) -{ - s64 ret; - - ret = beat_hcall2(HV_get_repository_node_value2, data, - lpid, path[0], path[1], path[2], path[3]); - return ret; -} - -#endif diff --git a/arch/powerpc/platforms/cell/celleb_pci.c b/arch/powerpc/platforms/cell/celleb_pci.c deleted file mode 100644 index 9b11b5dd8b7c..000000000000 --- a/arch/powerpc/platforms/cell/celleb_pci.c +++ /dev/null @@ -1,499 +0,0 @@ -/* - * Support for PCI on Celleb platform. - * - * (C) Copyright 2006-2007 TOSHIBA CORPORATION - * - * This code is based on arch/powerpc/kernel/rtas_pci.c: - * Copyright (C) 2001 Dave Engebretsen, IBM Corporation - * Copyright (C) 2003 Anton Blanchard , IBM - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "celleb_pci.h" - -#define MAX_PCI_DEVICES 32 -#define MAX_PCI_FUNCTIONS 8 -#define MAX_PCI_BASE_ADDRS 3 /* use 64 bit address */ - -/* definition for fake pci configuration area for GbE, .... ,and etc. */ - -struct celleb_pci_resource { - struct resource r[MAX_PCI_BASE_ADDRS]; -}; - -struct celleb_pci_private { - unsigned char *fake_config[MAX_PCI_DEVICES][MAX_PCI_FUNCTIONS]; - struct celleb_pci_resource *res[MAX_PCI_DEVICES][MAX_PCI_FUNCTIONS]; -}; - -static inline u8 celleb_fake_config_readb(void *addr) -{ - u8 *p = addr; - return *p; -} - -static inline u16 celleb_fake_config_readw(void *addr) -{ - __le16 *p = addr; - return le16_to_cpu(*p); -} - -static inline u32 celleb_fake_config_readl(void *addr) -{ - __le32 *p = addr; - return le32_to_cpu(*p); -} - -static inline void celleb_fake_config_writeb(u32 val, void *addr) -{ - u8 *p = addr; - *p = val; -} - -static inline void celleb_fake_config_writew(u32 val, void *addr) -{ - __le16 val16; - __le16 *p = addr; - val16 = cpu_to_le16(val); - *p = val16; -} - -static inline void celleb_fake_config_writel(u32 val, void *addr) -{ - __le32 val32; - __le32 *p = addr; - val32 = cpu_to_le32(val); - *p = val32; -} - -static unsigned char *get_fake_config_start(struct pci_controller *hose, - int devno, int fn) -{ - struct celleb_pci_private *private = hose->private_data; - - if (private == NULL) - return NULL; - - return private->fake_config[devno][fn]; -} - -static struct celleb_pci_resource *get_resource_start( - struct pci_controller *hose, - int devno, int fn) -{ - struct celleb_pci_private *private = hose->private_data; - - if (private == NULL) - return NULL; - - return private->res[devno][fn]; -} - - -static void celleb_config_read_fake(unsigned char *config, int where, - int size, u32 *val) -{ - char *p = config + where; - - switch (size) { - case 1: - *val = celleb_fake_config_readb(p); - break; - case 2: - *val = celleb_fake_config_readw(p); - break; - case 4: - *val = celleb_fake_config_readl(p); - break; - } -} - -static void celleb_config_write_fake(unsigned char *config, int where, - int size, u32 val) -{ - char *p = config + where; - - switch (size) { - case 1: - celleb_fake_config_writeb(val, p); - break; - case 2: - celleb_fake_config_writew(val, p); - break; - case 4: - celleb_fake_config_writel(val, p); - break; - } -} - -static int celleb_fake_pci_read_config(struct pci_bus *bus, - unsigned int devfn, int where, int size, u32 *val) -{ - char *config; - struct pci_controller *hose = pci_bus_to_host(bus); - unsigned int devno = devfn >> 3; - unsigned int fn = devfn & 0x7; - - /* allignment check */ - BUG_ON(where % size); - - pr_debug(" fake read: bus=0x%x, ", bus->number); - config = get_fake_config_start(hose, devno, fn); - - pr_debug("devno=0x%x, where=0x%x, size=0x%x, ", devno, where, size); - if (!config) { - pr_debug("failed\n"); - return PCIBIOS_DEVICE_NOT_FOUND; - } - - celleb_config_read_fake(config, where, size, val); - pr_debug("val=0x%x\n", *val); - - return PCIBIOS_SUCCESSFUL; -} - - -static int celleb_fake_pci_write_config(struct pci_bus *bus, - unsigned int devfn, int where, int size, u32 val) -{ - char *config; - struct pci_controller *hose = pci_bus_to_host(bus); - struct celleb_pci_resource *res; - unsigned int devno = devfn >> 3; - unsigned int fn = devfn & 0x7; - - /* allignment check */ - BUG_ON(where % size); - - config = get_fake_config_start(hose, devno, fn); - - if (!config) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (val == ~0) { - int i = (where - PCI_BASE_ADDRESS_0) >> 3; - - switch (where) { - case PCI_BASE_ADDRESS_0: - case PCI_BASE_ADDRESS_2: - if (size != 4) - return PCIBIOS_DEVICE_NOT_FOUND; - res = get_resource_start(hose, devno, fn); - if (!res) - return PCIBIOS_DEVICE_NOT_FOUND; - celleb_config_write_fake(config, where, size, - (res->r[i].end - res->r[i].start)); - return PCIBIOS_SUCCESSFUL; - case PCI_BASE_ADDRESS_1: - case PCI_BASE_ADDRESS_3: - case PCI_BASE_ADDRESS_4: - case PCI_BASE_ADDRESS_5: - break; - default: - break; - } - } - - celleb_config_write_fake(config, where, size, val); - pr_debug(" fake write: where=%x, size=%d, val=%x\n", - where, size, val); - - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops celleb_fake_pci_ops = { - .read = celleb_fake_pci_read_config, - .write = celleb_fake_pci_write_config, -}; - -static inline void celleb_setup_pci_base_addrs(struct pci_controller *hose, - unsigned int devno, unsigned int fn, - unsigned int num_base_addr) -{ - u32 val; - unsigned char *config; - struct celleb_pci_resource *res; - - config = get_fake_config_start(hose, devno, fn); - res = get_resource_start(hose, devno, fn); - - if (!config || !res) - return; - - switch (num_base_addr) { - case 3: - val = (res->r[2].start & 0xfffffff0) - | PCI_BASE_ADDRESS_MEM_TYPE_64; - celleb_config_write_fake(config, PCI_BASE_ADDRESS_4, 4, val); - val = res->r[2].start >> 32; - celleb_config_write_fake(config, PCI_BASE_ADDRESS_5, 4, val); - /* FALLTHROUGH */ - case 2: - val = (res->r[1].start & 0xfffffff0) - | PCI_BASE_ADDRESS_MEM_TYPE_64; - celleb_config_write_fake(config, PCI_BASE_ADDRESS_2, 4, val); - val = res->r[1].start >> 32; - celleb_config_write_fake(config, PCI_BASE_ADDRESS_3, 4, val); - /* FALLTHROUGH */ - case 1: - val = (res->r[0].start & 0xfffffff0) - | PCI_BASE_ADDRESS_MEM_TYPE_64; - celleb_config_write_fake(config, PCI_BASE_ADDRESS_0, 4, val); - val = res->r[0].start >> 32; - celleb_config_write_fake(config, PCI_BASE_ADDRESS_1, 4, val); - break; - } - - val = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; - celleb_config_write_fake(config, PCI_COMMAND, 2, val); -} - -static int __init celleb_setup_fake_pci_device(struct device_node *node, - struct pci_controller *hose) -{ - unsigned int rlen; - int num_base_addr = 0; - u32 val; - const u32 *wi0, *wi1, *wi2, *wi3, *wi4; - unsigned int devno, fn; - struct celleb_pci_private *private = hose->private_data; - unsigned char **config = NULL; - struct celleb_pci_resource **res = NULL; - const char *name; - const unsigned long *li; - int size, result; - - if (private == NULL) { - printk(KERN_ERR "PCI: " - "memory space for pci controller is not assigned\n"); - goto error; - } - - name = of_get_property(node, "model", &rlen); - if (!name) { - printk(KERN_ERR "PCI: model property not found.\n"); - goto error; - } - - wi4 = of_get_property(node, "reg", &rlen); - if (wi4 == NULL) - goto error; - - devno = ((wi4[0] >> 8) & 0xff) >> 3; - fn = (wi4[0] >> 8) & 0x7; - - pr_debug("PCI: celleb_setup_fake_pci() %s devno=%x fn=%x\n", name, - devno, fn); - - size = 256; - config = &private->fake_config[devno][fn]; - *config = zalloc_maybe_bootmem(size, GFP_KERNEL); - if (*config == NULL) { - printk(KERN_ERR "PCI: " - "not enough memory for fake configuration space\n"); - goto error; - } - pr_debug("PCI: fake config area assigned 0x%016lx\n", - (unsigned long)*config); - - size = sizeof(struct celleb_pci_resource); - res = &private->res[devno][fn]; - *res = zalloc_maybe_bootmem(size, GFP_KERNEL); - if (*res == NULL) { - printk(KERN_ERR - "PCI: not enough memory for resource data space\n"); - goto error; - } - pr_debug("PCI: res assigned 0x%016lx\n", (unsigned long)*res); - - wi0 = of_get_property(node, "device-id", NULL); - wi1 = of_get_property(node, "vendor-id", NULL); - wi2 = of_get_property(node, "class-code", NULL); - wi3 = of_get_property(node, "revision-id", NULL); - if (!wi0 || !wi1 || !wi2 || !wi3) { - printk(KERN_ERR "PCI: Missing device tree properties.\n"); - goto error; - } - - celleb_config_write_fake(*config, PCI_DEVICE_ID, 2, wi0[0] & 0xffff); - celleb_config_write_fake(*config, PCI_VENDOR_ID, 2, wi1[0] & 0xffff); - pr_debug("class-code = 0x%08x\n", wi2[0]); - - celleb_config_write_fake(*config, PCI_CLASS_PROG, 1, wi2[0] & 0xff); - celleb_config_write_fake(*config, PCI_CLASS_DEVICE, 2, - (wi2[0] >> 8) & 0xffff); - celleb_config_write_fake(*config, PCI_REVISION_ID, 1, wi3[0]); - - while (num_base_addr < MAX_PCI_BASE_ADDRS) { - result = of_address_to_resource(node, - num_base_addr, &(*res)->r[num_base_addr]); - if (result) - break; - num_base_addr++; - } - - celleb_setup_pci_base_addrs(hose, devno, fn, num_base_addr); - - li = of_get_property(node, "interrupts", &rlen); - if (!li) { - printk(KERN_ERR "PCI: interrupts not found.\n"); - goto error; - } - val = li[0]; - celleb_config_write_fake(*config, PCI_INTERRUPT_PIN, 1, 1); - celleb_config_write_fake(*config, PCI_INTERRUPT_LINE, 1, val); - -#ifdef DEBUG - pr_debug("PCI: %s irq=%ld\n", name, li[0]); - for (i = 0; i < 6; i++) { - celleb_config_read_fake(*config, - PCI_BASE_ADDRESS_0 + 0x4 * i, 4, - &val); - pr_debug("PCI: %s fn=%d base_address_%d=0x%x\n", - name, fn, i, val); - } -#endif - - celleb_config_write_fake(*config, PCI_HEADER_TYPE, 1, - PCI_HEADER_TYPE_NORMAL); - - return 0; - -error: - if (mem_init_done) { - if (config) - kfree(*config); - if (res) - kfree(*res); - } else { - if (config && *config) { - size = 256; - memblock_free(__pa(*config), size); - } - if (res && *res) { - size = sizeof(struct celleb_pci_resource); - memblock_free(__pa(*res), size); - } - } - - return 1; -} - -static int __init phb_set_bus_ranges(struct device_node *dev, - struct pci_controller *phb) -{ - const int *bus_range; - unsigned int len; - - bus_range = of_get_property(dev, "bus-range", &len); - if (bus_range == NULL || len < 2 * sizeof(int)) - return 1; - - phb->first_busno = bus_range[0]; - phb->last_busno = bus_range[1]; - - return 0; -} - -static void __init celleb_alloc_private_mem(struct pci_controller *hose) -{ - hose->private_data = - zalloc_maybe_bootmem(sizeof(struct celleb_pci_private), - GFP_KERNEL); -} - -static int __init celleb_setup_fake_pci(struct device_node *dev, - struct pci_controller *phb) -{ - struct device_node *node; - - phb->ops = &celleb_fake_pci_ops; - celleb_alloc_private_mem(phb); - - for (node = of_get_next_child(dev, NULL); - node != NULL; node = of_get_next_child(dev, node)) - celleb_setup_fake_pci_device(node, phb); - - return 0; -} - -static struct celleb_phb_spec celleb_fake_pci_spec __initdata = { - .setup = celleb_setup_fake_pci, -}; - -static const struct of_device_id celleb_phb_match[] __initconst = { - { - .name = "pci-pseudo", - .data = &celleb_fake_pci_spec, - }, { - .name = "epci", - .data = &celleb_epci_spec, - }, { - .name = "pcie", - .data = &celleb_pciex_spec, - }, { - }, -}; - -int __init celleb_setup_phb(struct pci_controller *phb) -{ - struct device_node *dev = phb->dn; - const struct of_device_id *match; - const struct celleb_phb_spec *phb_spec; - int rc; - - match = of_match_node(celleb_phb_match, dev); - if (!match) - return 1; - - phb_set_bus_ranges(dev, phb); - phb->buid = 1; - - phb_spec = match->data; - rc = (*phb_spec->setup)(dev, phb); - if (rc) - return 1; - - if (phb_spec->ops) - iowa_register_bus(phb, phb_spec->ops, - phb_spec->iowa_init, - phb_spec->iowa_data); - return 0; -} - -int celleb_pci_probe_mode(struct pci_bus *bus) -{ - return PCI_PROBE_DEVTREE; -} diff --git a/arch/powerpc/platforms/cell/celleb_pci.h b/arch/powerpc/platforms/cell/celleb_pci.h deleted file mode 100644 index a801fcc5f389..000000000000 --- a/arch/powerpc/platforms/cell/celleb_pci.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * pci prototypes for Celleb platform - * - * (C) Copyright 2006-2007 TOSHIBA CORPORATION - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef _CELLEB_PCI_H -#define _CELLEB_PCI_H - -#include - -#include -#include -#include -#include - -struct iowa_bus; - -struct celleb_phb_spec { - int (*setup)(struct device_node *, struct pci_controller *); - struct ppc_pci_io *ops; - int (*iowa_init)(struct iowa_bus *, void *); - void *iowa_data; -}; - -extern int celleb_setup_phb(struct pci_controller *); -extern int celleb_pci_probe_mode(struct pci_bus *); - -extern struct celleb_phb_spec celleb_epci_spec; -extern struct celleb_phb_spec celleb_pciex_spec; - -#endif /* _CELLEB_PCI_H */ diff --git a/arch/powerpc/platforms/cell/celleb_scc.h b/arch/powerpc/platforms/cell/celleb_scc.h deleted file mode 100644 index b596a711c348..000000000000 --- a/arch/powerpc/platforms/cell/celleb_scc.h +++ /dev/null @@ -1,232 +0,0 @@ -/* - * SCC (Super Companion Chip) definitions - * - * (C) Copyright 2004-2006 TOSHIBA CORPORATION - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef _CELLEB_SCC_H -#define _CELLEB_SCC_H - -#define PCI_VENDOR_ID_TOSHIBA_2 0x102f -#define PCI_DEVICE_ID_TOSHIBA_SCC_PCIEXC_BRIDGE 0x01b0 -#define PCI_DEVICE_ID_TOSHIBA_SCC_EPCI_BRIDGE 0x01b1 -#define PCI_DEVICE_ID_TOSHIBA_SCC_BRIDGE 0x01b2 -#define PCI_DEVICE_ID_TOSHIBA_SCC_GBE 0x01b3 -#define PCI_DEVICE_ID_TOSHIBA_SCC_ATA 0x01b4 -#define PCI_DEVICE_ID_TOSHIBA_SCC_USB2 0x01b5 -#define PCI_DEVICE_ID_TOSHIBA_SCC_USB 0x01b6 -#define PCI_DEVICE_ID_TOSHIBA_SCC_ENCDEC 0x01b7 - -#define SCC_EPCI_REG 0x0000d000 - -/* EPCI registers */ -#define SCC_EPCI_CNF10_REG 0x010 -#define SCC_EPCI_CNF14_REG 0x014 -#define SCC_EPCI_CNF18_REG 0x018 -#define SCC_EPCI_PVBAT 0x100 -#define SCC_EPCI_VPMBAT 0x104 -#define SCC_EPCI_VPIBAT 0x108 -#define SCC_EPCI_VCSR 0x110 -#define SCC_EPCI_VIENAB 0x114 -#define SCC_EPCI_VISTAT 0x118 -#define SCC_EPCI_VRDCOUNT 0x124 -#define SCC_EPCI_BAM0 0x12c -#define SCC_EPCI_BAM1 0x134 -#define SCC_EPCI_BAM2 0x13c -#define SCC_EPCI_IADR 0x164 -#define SCC_EPCI_CLKRST 0x800 -#define SCC_EPCI_INTSET 0x804 -#define SCC_EPCI_STATUS 0x808 -#define SCC_EPCI_ABTSET 0x80c -#define SCC_EPCI_WATRP 0x810 -#define SCC_EPCI_DUMYRADR 0x814 -#define SCC_EPCI_SWRESP 0x818 -#define SCC_EPCI_CNTOPT 0x81c -#define SCC_EPCI_ECMODE 0xf00 -#define SCC_EPCI_IOM_AC_NUM 5 -#define SCC_EPCI_IOM_ACTE(n) (0xf10 + (n) * 4) -#define SCC_EPCI_IOT_AC_NUM 4 -#define SCC_EPCI_IOT_ACTE(n) (0xf30 + (n) * 4) -#define SCC_EPCI_MAEA 0xf50 -#define SCC_EPCI_MAEC 0xf54 -#define SCC_EPCI_CKCTRL 0xff0 - -/* bits for SCC_EPCI_VCSR */ -#define SCC_EPCI_VCSR_FRE 0x00020000 -#define SCC_EPCI_VCSR_FWE 0x00010000 -#define SCC_EPCI_VCSR_DR 0x00000400 -#define SCC_EPCI_VCSR_SR 0x00000008 -#define SCC_EPCI_VCSR_AT 0x00000004 - -/* bits for SCC_EPCI_VIENAB/SCC_EPCI_VISTAT */ -#define SCC_EPCI_VISTAT_PMPE 0x00000008 -#define SCC_EPCI_VISTAT_PMFE 0x00000004 -#define SCC_EPCI_VISTAT_PRA 0x00000002 -#define SCC_EPCI_VISTAT_PRD 0x00000001 -#define SCC_EPCI_VISTAT_ALL 0x0000000f - -#define SCC_EPCI_VIENAB_PMPEE 0x00000008 -#define SCC_EPCI_VIENAB_PMFEE 0x00000004 -#define SCC_EPCI_VIENAB_PRA 0x00000002 -#define SCC_EPCI_VIENAB_PRD 0x00000001 -#define SCC_EPCI_VIENAB_ALL 0x0000000f - -/* bits for SCC_EPCI_CLKRST */ -#define SCC_EPCI_CLKRST_CKS_MASK 0x00030000 -#define SCC_EPCI_CLKRST_CKS_2 0x00000000 -#define SCC_EPCI_CLKRST_CKS_4 0x00010000 -#define SCC_EPCI_CLKRST_CKS_8 0x00020000 -#define SCC_EPCI_CLKRST_PCICRST 0x00000400 -#define SCC_EPCI_CLKRST_BC 0x00000200 -#define SCC_EPCI_CLKRST_PCIRST 0x00000100 -#define SCC_EPCI_CLKRST_PCKEN 0x00000001 - -/* bits for SCC_EPCI_INTSET/SCC_EPCI_STATUS */ -#define SCC_EPCI_INT_2M 0x01000000 -#define SCC_EPCI_INT_RERR 0x00200000 -#define SCC_EPCI_INT_SERR 0x00100000 -#define SCC_EPCI_INT_PRTER 0x00080000 -#define SCC_EPCI_INT_SER 0x00040000 -#define SCC_EPCI_INT_PER 0x00020000 -#define SCC_EPCI_INT_PAI 0x00010000 -#define SCC_EPCI_INT_1M 0x00000100 -#define SCC_EPCI_INT_PME 0x00000010 -#define SCC_EPCI_INT_INTD 0x00000008 -#define SCC_EPCI_INT_INTC 0x00000004 -#define SCC_EPCI_INT_INTB 0x00000002 -#define SCC_EPCI_INT_INTA 0x00000001 -#define SCC_EPCI_INT_DEVINT 0x0000000f -#define SCC_EPCI_INT_ALL 0x003f001f -#define SCC_EPCI_INT_ALLERR 0x003f0000 - -/* bits for SCC_EPCI_CKCTRL */ -#define SCC_EPCI_CKCTRL_CRST0 0x00010000 -#define SCC_EPCI_CKCTRL_CRST1 0x00020000 -#define SCC_EPCI_CKCTRL_OCLKEN 0x00000100 -#define SCC_EPCI_CKCTRL_LCLKEN 0x00000001 - -#define SCC_EPCI_IDSEL_AD_TO_SLOT(ad) ((ad) - 10) -#define SCC_EPCI_MAX_DEVNU SCC_EPCI_IDSEL_AD_TO_SLOT(32) - -/* bits for SCC_EPCI_CNTOPT */ -#define SCC_EPCI_CNTOPT_O2PMB 0x00000002 - -/* SCC PCIEXC SMMIO registers */ -#define PEXCADRS 0x000 -#define PEXCWDATA 0x004 -#define PEXCRDATA 0x008 -#define PEXDADRS 0x010 -#define PEXDCMND 0x014 -#define PEXDWDATA 0x018 -#define PEXDRDATA 0x01c -#define PEXREQID 0x020 -#define PEXTIDMAP 0x024 -#define PEXINTMASK 0x028 -#define PEXINTSTS 0x02c -#define PEXAERRMASK 0x030 -#define PEXAERRSTS 0x034 -#define PEXPRERRMASK 0x040 -#define PEXPRERRSTS 0x044 -#define PEXPRERRID01 0x048 -#define PEXPRERRID23 0x04c -#define PEXVDMASK 0x050 -#define PEXVDSTS 0x054 -#define PEXRCVCPLIDA 0x060 -#define PEXLENERRIDA 0x068 -#define PEXPHYPLLST 0x070 -#define PEXDMRDEN0 0x100 -#define PEXDMRDADR0 0x104 -#define PEXDMRDENX 0x110 -#define PEXDMRDADRX 0x114 -#define PEXECMODE 0xf00 -#define PEXMAEA(n) (0xf50 + (8 * n)) -#define PEXMAEC(n) (0xf54 + (8 * n)) -#define PEXCCRCTRL 0xff0 - -/* SCC PCIEXC bits and shifts for PEXCADRS */ -#define PEXCADRS_BYTE_EN_SHIFT 20 -#define PEXCADRS_CMD_SHIFT 16 -#define PEXCADRS_CMD_READ (0xa << PEXCADRS_CMD_SHIFT) -#define PEXCADRS_CMD_WRITE (0xb << PEXCADRS_CMD_SHIFT) - -/* SCC PCIEXC shifts for PEXDADRS */ -#define PEXDADRS_BUSNO_SHIFT 20 -#define PEXDADRS_DEVNO_SHIFT 15 -#define PEXDADRS_FUNCNO_SHIFT 12 - -/* SCC PCIEXC bits and shifts for PEXDCMND */ -#define PEXDCMND_BYTE_EN_SHIFT 4 -#define PEXDCMND_IO_READ 0x2 -#define PEXDCMND_IO_WRITE 0x3 -#define PEXDCMND_CONFIG_READ 0xa -#define PEXDCMND_CONFIG_WRITE 0xb - -/* SCC PCIEXC bits for PEXPHYPLLST */ -#define PEXPHYPLLST_PEXPHYAPLLST 0x00000001 - -/* SCC PCIEXC bits for PEXECMODE */ -#define PEXECMODE_ALL_THROUGH 0x00000000 -#define PEXECMODE_ALL_8BIT 0x00550155 -#define PEXECMODE_ALL_16BIT 0x00aa02aa - -/* SCC PCIEXC bits for PEXCCRCTRL */ -#define PEXCCRCTRL_PEXIPCOREEN 0x00040000 -#define PEXCCRCTRL_PEXIPCONTEN 0x00020000 -#define PEXCCRCTRL_PEXPHYPLLEN 0x00010000 -#define PEXCCRCTRL_PCIEXCAOCKEN 0x00000100 - -/* SCC PCIEXC port configuration registers */ -#define PEXTCERRCHK 0x21c -#define PEXTAMAPB0 0x220 -#define PEXTAMAPL0 0x224 -#define PEXTAMAPB(n) (PEXTAMAPB0 + 8 * (n)) -#define PEXTAMAPL(n) (PEXTAMAPL0 + 8 * (n)) -#define PEXCHVC0P 0x500 -#define PEXCHVC0NP 0x504 -#define PEXCHVC0C 0x508 -#define PEXCDVC0P 0x50c -#define PEXCDVC0NP 0x510 -#define PEXCDVC0C 0x514 -#define PEXCHVCXP 0x518 -#define PEXCHVCXNP 0x51c -#define PEXCHVCXC 0x520 -#define PEXCDVCXP 0x524 -#define PEXCDVCXNP 0x528 -#define PEXCDVCXC 0x52c -#define PEXCTTRG 0x530 -#define PEXTSCTRL 0x700 -#define PEXTSSTS 0x704 -#define PEXSKPCTRL 0x708 - -/* UHC registers */ -#define SCC_UHC_CKRCTRL 0xff0 -#define SCC_UHC_ECMODE 0xf00 - -/* bits for SCC_UHC_CKRCTRL */ -#define SCC_UHC_F48MCKLEN 0x00000001 -#define SCC_UHC_P_SUSPEND 0x00000002 -#define SCC_UHC_PHY_SUSPEND_SEL 0x00000004 -#define SCC_UHC_HCLKEN 0x00000100 -#define SCC_UHC_USBEN 0x00010000 -#define SCC_UHC_USBCEN 0x00020000 -#define SCC_UHC_PHYEN 0x00040000 - -/* bits for SCC_UHC_ECMODE */ -#define SCC_UHC_ECMODE_BY_BYTE 0x00000555 -#define SCC_UHC_ECMODE_BY_WORD 0x00000aaa - -#endif /* _CELLEB_SCC_H */ diff --git a/arch/powerpc/platforms/cell/celleb_scc_epci.c b/arch/powerpc/platforms/cell/celleb_scc_epci.c deleted file mode 100644 index 9438bbed402f..000000000000 --- a/arch/powerpc/platforms/cell/celleb_scc_epci.c +++ /dev/null @@ -1,428 +0,0 @@ -/* - * Support for SCC external PCI - * - * (C) Copyright 2004-2007 TOSHIBA CORPORATION - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#undef DEBUG - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "celleb_scc.h" -#include "celleb_pci.h" - -#define MAX_PCI_DEVICES 32 -#define MAX_PCI_FUNCTIONS 8 - -#define iob() __asm__ __volatile__("eieio; sync":::"memory") - -static inline PCI_IO_ADDR celleb_epci_get_epci_base( - struct pci_controller *hose) -{ - /* - * Note: - * Celleb epci uses cfg_addr as a base address for - * epci control registers. - */ - - return hose->cfg_addr; -} - -static inline PCI_IO_ADDR celleb_epci_get_epci_cfg( - struct pci_controller *hose) -{ - /* - * Note: - * Celleb epci uses cfg_data as a base address for - * configuration area for epci devices. - */ - - return hose->cfg_data; -} - -static inline void clear_and_disable_master_abort_interrupt( - struct pci_controller *hose) -{ - PCI_IO_ADDR epci_base; - PCI_IO_ADDR reg; - epci_base = celleb_epci_get_epci_base(hose); - reg = epci_base + PCI_COMMAND; - out_be32(reg, in_be32(reg) | (PCI_STATUS_REC_MASTER_ABORT << 16)); -} - -static int celleb_epci_check_abort(struct pci_controller *hose, - PCI_IO_ADDR addr) -{ - PCI_IO_ADDR reg; - PCI_IO_ADDR epci_base; - u32 val; - - iob(); - epci_base = celleb_epci_get_epci_base(hose); - - reg = epci_base + PCI_COMMAND; - val = in_be32(reg); - - if (val & (PCI_STATUS_REC_MASTER_ABORT << 16)) { - out_be32(reg, - (val & 0xffff) | (PCI_STATUS_REC_MASTER_ABORT << 16)); - - /* clear PCI Controller error, FRE, PMFE */ - reg = epci_base + SCC_EPCI_STATUS; - out_be32(reg, SCC_EPCI_INT_PAI); - - reg = epci_base + SCC_EPCI_VCSR; - val = in_be32(reg) & 0xffff; - val |= SCC_EPCI_VCSR_FRE; - out_be32(reg, val); - - reg = epci_base + SCC_EPCI_VISTAT; - out_be32(reg, SCC_EPCI_VISTAT_PMFE); - return PCIBIOS_DEVICE_NOT_FOUND; - } - - return PCIBIOS_SUCCESSFUL; -} - -static PCI_IO_ADDR celleb_epci_make_config_addr(struct pci_bus *bus, - struct pci_controller *hose, unsigned int devfn, int where) -{ - PCI_IO_ADDR addr; - - if (bus != hose->bus) - addr = celleb_epci_get_epci_cfg(hose) + - (((bus->number & 0xff) << 16) - | ((devfn & 0xff) << 8) - | (where & 0xff) - | 0x01000000); - else - addr = celleb_epci_get_epci_cfg(hose) + - (((devfn & 0xff) << 8) | (where & 0xff)); - - pr_debug("EPCI: config_addr = 0x%p\n", addr); - - return addr; -} - -static int celleb_epci_read_config(struct pci_bus *bus, - unsigned int devfn, int where, int size, u32 *val) -{ - PCI_IO_ADDR epci_base; - PCI_IO_ADDR addr; - struct pci_controller *hose = pci_bus_to_host(bus); - - /* allignment check */ - BUG_ON(where % size); - - if (!celleb_epci_get_epci_cfg(hose)) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (bus->number == hose->first_busno && devfn == 0) { - /* EPCI controller self */ - - epci_base = celleb_epci_get_epci_base(hose); - addr = epci_base + where; - - switch (size) { - case 1: - *val = in_8(addr); - break; - case 2: - *val = in_be16(addr); - break; - case 4: - *val = in_be32(addr); - break; - default: - return PCIBIOS_DEVICE_NOT_FOUND; - } - - } else { - - clear_and_disable_master_abort_interrupt(hose); - addr = celleb_epci_make_config_addr(bus, hose, devfn, where); - - switch (size) { - case 1: - *val = in_8(addr); - break; - case 2: - *val = in_le16(addr); - break; - case 4: - *val = in_le32(addr); - break; - default: - return PCIBIOS_DEVICE_NOT_FOUND; - } - } - - pr_debug("EPCI: " - "addr=0x%p, devfn=0x%x, where=0x%x, size=0x%x, val=0x%x\n", - addr, devfn, where, size, *val); - - return celleb_epci_check_abort(hose, NULL); -} - -static int celleb_epci_write_config(struct pci_bus *bus, - unsigned int devfn, int where, int size, u32 val) -{ - PCI_IO_ADDR epci_base; - PCI_IO_ADDR addr; - struct pci_controller *hose = pci_bus_to_host(bus); - - /* allignment check */ - BUG_ON(where % size); - - if (!celleb_epci_get_epci_cfg(hose)) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (bus->number == hose->first_busno && devfn == 0) { - /* EPCI controller self */ - - epci_base = celleb_epci_get_epci_base(hose); - addr = epci_base + where; - - switch (size) { - case 1: - out_8(addr, val); - break; - case 2: - out_be16(addr, val); - break; - case 4: - out_be32(addr, val); - break; - default: - return PCIBIOS_DEVICE_NOT_FOUND; - } - - } else { - - clear_and_disable_master_abort_interrupt(hose); - addr = celleb_epci_make_config_addr(bus, hose, devfn, where); - - switch (size) { - case 1: - out_8(addr, val); - break; - case 2: - out_le16(addr, val); - break; - case 4: - out_le32(addr, val); - break; - default: - return PCIBIOS_DEVICE_NOT_FOUND; - } - } - - return celleb_epci_check_abort(hose, addr); -} - -struct pci_ops celleb_epci_ops = { - .read = celleb_epci_read_config, - .write = celleb_epci_write_config, -}; - -/* to be moved in FW */ -static int __init celleb_epci_init(struct pci_controller *hose) -{ - u32 val; - PCI_IO_ADDR reg; - PCI_IO_ADDR epci_base; - int hwres = 0; - - epci_base = celleb_epci_get_epci_base(hose); - - /* PCI core reset(Internal bus and PCI clock) */ - reg = epci_base + SCC_EPCI_CKCTRL; - val = in_be32(reg); - if (val == 0x00030101) - hwres = 1; - else { - val &= ~(SCC_EPCI_CKCTRL_CRST0 | SCC_EPCI_CKCTRL_CRST1); - out_be32(reg, val); - - /* set PCI core clock */ - val = in_be32(reg); - val |= (SCC_EPCI_CKCTRL_OCLKEN | SCC_EPCI_CKCTRL_LCLKEN); - out_be32(reg, val); - - /* release PCI core reset (internal bus) */ - val = in_be32(reg); - val |= SCC_EPCI_CKCTRL_CRST0; - out_be32(reg, val); - - /* set PCI clock select */ - reg = epci_base + SCC_EPCI_CLKRST; - val = in_be32(reg); - val &= ~SCC_EPCI_CLKRST_CKS_MASK; - val |= SCC_EPCI_CLKRST_CKS_2; - out_be32(reg, val); - - /* set arbiter */ - reg = epci_base + SCC_EPCI_ABTSET; - out_be32(reg, 0x0f1f001f); /* temporary value */ - - /* buffer on */ - reg = epci_base + SCC_EPCI_CLKRST; - val = in_be32(reg); - val |= SCC_EPCI_CLKRST_BC; - out_be32(reg, val); - - /* PCI clock enable */ - val = in_be32(reg); - val |= SCC_EPCI_CLKRST_PCKEN; - out_be32(reg, val); - - /* release PCI core reset (all) */ - reg = epci_base + SCC_EPCI_CKCTRL; - val = in_be32(reg); - val |= (SCC_EPCI_CKCTRL_CRST0 | SCC_EPCI_CKCTRL_CRST1); - out_be32(reg, val); - - /* set base translation registers. (already set by Beat) */ - - /* set base address masks. (already set by Beat) */ - } - - /* release interrupt masks and clear all interrupts */ - reg = epci_base + SCC_EPCI_INTSET; - out_be32(reg, 0x013f011f); /* all interrupts enable */ - reg = epci_base + SCC_EPCI_VIENAB; - val = SCC_EPCI_VIENAB_PMPEE | SCC_EPCI_VIENAB_PMFEE; - out_be32(reg, val); - reg = epci_base + SCC_EPCI_STATUS; - out_be32(reg, 0xffffffff); - reg = epci_base + SCC_EPCI_VISTAT; - out_be32(reg, 0xffffffff); - - /* disable PCI->IB address translation */ - reg = epci_base + SCC_EPCI_VCSR; - val = in_be32(reg); - val &= ~(SCC_EPCI_VCSR_DR | SCC_EPCI_VCSR_AT); - out_be32(reg, val); - - /* set base addresses. (no need to set?) */ - - /* memory space, bus master enable */ - reg = epci_base + PCI_COMMAND; - val = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; - out_be32(reg, val); - - /* endian mode setup */ - reg = epci_base + SCC_EPCI_ECMODE; - val = 0x00550155; - out_be32(reg, val); - - /* set control option */ - reg = epci_base + SCC_EPCI_CNTOPT; - val = in_be32(reg); - val |= SCC_EPCI_CNTOPT_O2PMB; - out_be32(reg, val); - - /* XXX: temporay: set registers for address conversion setup */ - reg = epci_base + SCC_EPCI_CNF10_REG; - out_be32(reg, 0x80000008); - reg = epci_base + SCC_EPCI_CNF14_REG; - out_be32(reg, 0x40000008); - - reg = epci_base + SCC_EPCI_BAM0; - out_be32(reg, 0x80000000); - reg = epci_base + SCC_EPCI_BAM1; - out_be32(reg, 0xe0000000); - - reg = epci_base + SCC_EPCI_PVBAT; - out_be32(reg, 0x80000000); - - if (!hwres) { - /* release external PCI reset */ - reg = epci_base + SCC_EPCI_CLKRST; - val = in_be32(reg); - val |= SCC_EPCI_CLKRST_PCIRST; - out_be32(reg, val); - } - - return 0; -} - -static int __init celleb_setup_epci(struct device_node *node, - struct pci_controller *hose) -{ - struct resource r; - - pr_debug("PCI: celleb_setup_epci()\n"); - - /* - * Note: - * Celleb epci uses cfg_addr and cfg_data member of - * pci_controller structure in irregular way. - * - * cfg_addr is used to map for control registers of - * celleb epci. - * - * cfg_data is used for configuration area of devices - * on Celleb epci buses. - */ - - if (of_address_to_resource(node, 0, &r)) - goto error; - hose->cfg_addr = ioremap(r.start, resource_size(&r)); - if (!hose->cfg_addr) - goto error; - pr_debug("EPCI: cfg_addr map 0x%016llx->0x%016lx + 0x%016llx\n", - r.start, (unsigned long)hose->cfg_addr, resource_size(&r)); - - if (of_address_to_resource(node, 2, &r)) - goto error; - hose->cfg_data = ioremap(r.start, resource_size(&r)); - if (!hose->cfg_data) - goto error; - pr_debug("EPCI: cfg_data map 0x%016llx->0x%016lx + 0x%016llx\n", - r.start, (unsigned long)hose->cfg_data, resource_size(&r)); - - hose->ops = &celleb_epci_ops; - celleb_epci_init(hose); - - return 0; - -error: - if (hose->cfg_addr) - iounmap(hose->cfg_addr); - - if (hose->cfg_data) - iounmap(hose->cfg_data); - return 1; -} - -struct celleb_phb_spec celleb_epci_spec __initdata = { - .setup = celleb_setup_epci, - .ops = &spiderpci_ops, - .iowa_init = &spiderpci_iowa_init, - .iowa_data = (void *)0, -}; diff --git a/arch/powerpc/platforms/cell/celleb_scc_pciex.c b/arch/powerpc/platforms/cell/celleb_scc_pciex.c deleted file mode 100644 index 94170e4f2ce7..000000000000 --- a/arch/powerpc/platforms/cell/celleb_scc_pciex.c +++ /dev/null @@ -1,538 +0,0 @@ -/* - * Support for Celleb PCI-Express. - * - * (C) Copyright 2007-2008 TOSHIBA CORPORATION - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "celleb_scc.h" -#include "celleb_pci.h" - -#define PEX_IN(base, off) in_be32((void __iomem *)(base) + (off)) -#define PEX_OUT(base, off, data) out_be32((void __iomem *)(base) + (off), (data)) - -static void scc_pciex_io_flush(struct iowa_bus *bus) -{ - (void)PEX_IN(bus->phb->cfg_addr, PEXDMRDEN0); -} - -/* - * Memory space access to device on PCIEX - */ -#define PCIEX_MMIO_READ(name, ret) \ -static ret scc_pciex_##name(const PCI_IO_ADDR addr) \ -{ \ - ret val = __do_##name(addr); \ - scc_pciex_io_flush(iowa_mem_find_bus(addr)); \ - return val; \ -} - -#define PCIEX_MMIO_READ_STR(name) \ -static void scc_pciex_##name(const PCI_IO_ADDR addr, void *buf, \ - unsigned long count) \ -{ \ - __do_##name(addr, buf, count); \ - scc_pciex_io_flush(iowa_mem_find_bus(addr)); \ -} - -PCIEX_MMIO_READ(readb, u8) -PCIEX_MMIO_READ(readw, u16) -PCIEX_MMIO_READ(readl, u32) -PCIEX_MMIO_READ(readq, u64) -PCIEX_MMIO_READ(readw_be, u16) -PCIEX_MMIO_READ(readl_be, u32) -PCIEX_MMIO_READ(readq_be, u64) -PCIEX_MMIO_READ_STR(readsb) -PCIEX_MMIO_READ_STR(readsw) -PCIEX_MMIO_READ_STR(readsl) - -static void scc_pciex_memcpy_fromio(void *dest, const PCI_IO_ADDR src, - unsigned long n) -{ - __do_memcpy_fromio(dest, src, n); - scc_pciex_io_flush(iowa_mem_find_bus(src)); -} - -/* - * I/O port access to devices on PCIEX. - */ - -static inline unsigned long get_bus_address(struct pci_controller *phb, - unsigned long port) -{ - return port - ((unsigned long)(phb->io_base_virt) - _IO_BASE); -} - -static u32 scc_pciex_read_port(struct pci_controller *phb, - unsigned long port, int size) -{ - unsigned int byte_enable; - unsigned int cmd, shift; - unsigned long addr; - u32 data, ret; - - BUG_ON(((port & 0x3ul) + size) > 4); - - addr = get_bus_address(phb, port); - shift = addr & 0x3ul; - byte_enable = ((1 << size) - 1) << shift; - cmd = PEXDCMND_IO_READ | (byte_enable << PEXDCMND_BYTE_EN_SHIFT); - PEX_OUT(phb->cfg_addr, PEXDADRS, (addr & ~0x3ul)); - PEX_OUT(phb->cfg_addr, PEXDCMND, cmd); - data = PEX_IN(phb->cfg_addr, PEXDRDATA); - ret = (data >> (shift * 8)) & (0xFFFFFFFF >> ((4 - size) * 8)); - - pr_debug("PCIEX:PIO READ:port=0x%lx, addr=0x%lx, size=%d, be=%x," - " cmd=%x, data=%x, ret=%x\n", port, addr, size, byte_enable, - cmd, data, ret); - - return ret; -} - -static void scc_pciex_write_port(struct pci_controller *phb, - unsigned long port, int size, u32 val) -{ - unsigned int byte_enable; - unsigned int cmd, shift; - unsigned long addr; - u32 data; - - BUG_ON(((port & 0x3ul) + size) > 4); - - addr = get_bus_address(phb, port); - shift = addr & 0x3ul; - byte_enable = ((1 << size) - 1) << shift; - cmd = PEXDCMND_IO_WRITE | (byte_enable << PEXDCMND_BYTE_EN_SHIFT); - data = (val & (0xFFFFFFFF >> (4 - size) * 8)) << (shift * 8); - PEX_OUT(phb->cfg_addr, PEXDADRS, (addr & ~0x3ul)); - PEX_OUT(phb->cfg_addr, PEXDCMND, cmd); - PEX_OUT(phb->cfg_addr, PEXDWDATA, data); - - pr_debug("PCIEX:PIO WRITE:port=0x%lx, addr=%lx, size=%d, val=%x," - " be=%x, cmd=%x, data=%x\n", port, addr, size, val, - byte_enable, cmd, data); -} - -static u8 __scc_pciex_inb(struct pci_controller *phb, unsigned long port) -{ - return (u8)scc_pciex_read_port(phb, port, 1); -} - -static u16 __scc_pciex_inw(struct pci_controller *phb, unsigned long port) -{ - u32 data; - if ((port & 0x3ul) < 3) - data = scc_pciex_read_port(phb, port, 2); - else { - u32 d1 = scc_pciex_read_port(phb, port, 1); - u32 d2 = scc_pciex_read_port(phb, port + 1, 1); - data = d1 | (d2 << 8); - } - return (u16)data; -} - -static u32 __scc_pciex_inl(struct pci_controller *phb, unsigned long port) -{ - unsigned int mod = port & 0x3ul; - u32 data; - if (mod == 0) - data = scc_pciex_read_port(phb, port, 4); - else { - u32 d1 = scc_pciex_read_port(phb, port, 4 - mod); - u32 d2 = scc_pciex_read_port(phb, port + 1, mod); - data = d1 | (d2 << (mod * 8)); - } - return data; -} - -static void __scc_pciex_outb(struct pci_controller *phb, - u8 val, unsigned long port) -{ - scc_pciex_write_port(phb, port, 1, (u32)val); -} - -static void __scc_pciex_outw(struct pci_controller *phb, - u16 val, unsigned long port) -{ - if ((port & 0x3ul) < 3) - scc_pciex_write_port(phb, port, 2, (u32)val); - else { - u32 d1 = val & 0x000000FF; - u32 d2 = (val & 0x0000FF00) >> 8; - scc_pciex_write_port(phb, port, 1, d1); - scc_pciex_write_port(phb, port + 1, 1, d2); - } -} - -static void __scc_pciex_outl(struct pci_controller *phb, - u32 val, unsigned long port) -{ - unsigned int mod = port & 0x3ul; - if (mod == 0) - scc_pciex_write_port(phb, port, 4, val); - else { - u32 d1 = val & (0xFFFFFFFFul >> (mod * 8)); - u32 d2 = val >> ((4 - mod) * 8); - scc_pciex_write_port(phb, port, 4 - mod, d1); - scc_pciex_write_port(phb, port + 1, mod, d2); - } -} - -#define PCIEX_PIO_FUNC(size, name) \ -static u##size scc_pciex_in##name(unsigned long port) \ -{ \ - struct iowa_bus *bus = iowa_pio_find_bus(port); \ - u##size data = __scc_pciex_in##name(bus->phb, port); \ - scc_pciex_io_flush(bus); \ - return data; \ -} \ -static void scc_pciex_ins##name(unsigned long p, void *b, unsigned long c) \ -{ \ - struct iowa_bus *bus = iowa_pio_find_bus(p); \ - __le##size *dst = b; \ - for (; c != 0; c--, dst++) \ - *dst = cpu_to_le##size(__scc_pciex_in##name(bus->phb, p)); \ - scc_pciex_io_flush(bus); \ -} \ -static void scc_pciex_out##name(u##size val, unsigned long port) \ -{ \ - struct iowa_bus *bus = iowa_pio_find_bus(port); \ - __scc_pciex_out##name(bus->phb, val, port); \ -} \ -static void scc_pciex_outs##name(unsigned long p, const void *b, \ - unsigned long c) \ -{ \ - struct iowa_bus *bus = iowa_pio_find_bus(p); \ - const __le##size *src = b; \ - for (; c != 0; c--, src++) \ - __scc_pciex_out##name(bus->phb, le##size##_to_cpu(*src), p); \ -} -#define __le8 u8 -#define cpu_to_le8(x) (x) -#define le8_to_cpu(x) (x) -PCIEX_PIO_FUNC(8, b) -PCIEX_PIO_FUNC(16, w) -PCIEX_PIO_FUNC(32, l) - -static struct ppc_pci_io scc_pciex_ops = { - .readb = scc_pciex_readb, - .readw = scc_pciex_readw, - .readl = scc_pciex_readl, - .readq = scc_pciex_readq, - .readw_be = scc_pciex_readw_be, - .readl_be = scc_pciex_readl_be, - .readq_be = scc_pciex_readq_be, - .readsb = scc_pciex_readsb, - .readsw = scc_pciex_readsw, - .readsl = scc_pciex_readsl, - .memcpy_fromio = scc_pciex_memcpy_fromio, - .inb = scc_pciex_inb, - .inw = scc_pciex_inw, - .inl = scc_pciex_inl, - .outb = scc_pciex_outb, - .outw = scc_pciex_outw, - .outl = scc_pciex_outl, - .insb = scc_pciex_insb, - .insw = scc_pciex_insw, - .insl = scc_pciex_insl, - .outsb = scc_pciex_outsb, - .outsw = scc_pciex_outsw, - .outsl = scc_pciex_outsl, -}; - -static int __init scc_pciex_iowa_init(struct iowa_bus *bus, void *data) -{ - dma_addr_t dummy_page_da; - void *dummy_page_va; - - dummy_page_va = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!dummy_page_va) { - pr_err("PCIEX:Alloc dummy_page_va failed\n"); - return -1; - } - - dummy_page_da = dma_map_single(bus->phb->parent, dummy_page_va, - PAGE_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(bus->phb->parent, dummy_page_da)) { - pr_err("PCIEX:Map dummy page failed.\n"); - kfree(dummy_page_va); - return -1; - } - - PEX_OUT(bus->phb->cfg_addr, PEXDMRDADR0, dummy_page_da); - - return 0; -} - -/* - * config space access - */ -#define MK_PEXDADRS(bus_no, dev_no, func_no, addr) \ - ((uint32_t)(((addr) & ~0x3UL) | \ - ((bus_no) << PEXDADRS_BUSNO_SHIFT) | \ - ((dev_no) << PEXDADRS_DEVNO_SHIFT) | \ - ((func_no) << PEXDADRS_FUNCNO_SHIFT))) - -#define MK_PEXDCMND_BYTE_EN(addr, size) \ - ((((0x1 << (size))-1) << ((addr) & 0x3)) << PEXDCMND_BYTE_EN_SHIFT) -#define MK_PEXDCMND(cmd, addr, size) ((cmd) | MK_PEXDCMND_BYTE_EN(addr, size)) - -static uint32_t config_read_pciex_dev(unsigned int __iomem *base, - uint64_t bus_no, uint64_t dev_no, uint64_t func_no, - uint64_t off, uint64_t size) -{ - uint32_t ret; - uint32_t addr, cmd; - - addr = MK_PEXDADRS(bus_no, dev_no, func_no, off); - cmd = MK_PEXDCMND(PEXDCMND_CONFIG_READ, off, size); - PEX_OUT(base, PEXDADRS, addr); - PEX_OUT(base, PEXDCMND, cmd); - ret = (PEX_IN(base, PEXDRDATA) - >> ((off & (4-size)) * 8)) & ((0x1 << (size * 8)) - 1); - return ret; -} - -static void config_write_pciex_dev(unsigned int __iomem *base, uint64_t bus_no, - uint64_t dev_no, uint64_t func_no, uint64_t off, uint64_t size, - uint32_t data) -{ - uint32_t addr, cmd; - - addr = MK_PEXDADRS(bus_no, dev_no, func_no, off); - cmd = MK_PEXDCMND(PEXDCMND_CONFIG_WRITE, off, size); - PEX_OUT(base, PEXDADRS, addr); - PEX_OUT(base, PEXDCMND, cmd); - PEX_OUT(base, PEXDWDATA, - (data & ((0x1 << (size * 8)) - 1)) << ((off & (4-size)) * 8)); -} - -#define MK_PEXCADRS_BYTE_EN(off, len) \ - ((((0x1 << (len)) - 1) << ((off) & 0x3)) << PEXCADRS_BYTE_EN_SHIFT) -#define MK_PEXCADRS(cmd, addr, size) \ - ((cmd) | MK_PEXCADRS_BYTE_EN(addr, size) | ((addr) & ~0x3)) -static uint32_t config_read_pciex_rc(unsigned int __iomem *base, - uint32_t where, uint32_t size) -{ - PEX_OUT(base, PEXCADRS, MK_PEXCADRS(PEXCADRS_CMD_READ, where, size)); - return (PEX_IN(base, PEXCRDATA) - >> ((where & (4 - size)) * 8)) & ((0x1 << (size * 8)) - 1); -} - -static void config_write_pciex_rc(unsigned int __iomem *base, uint32_t where, - uint32_t size, uint32_t val) -{ - uint32_t data; - - data = (val & ((0x1 << (size * 8)) - 1)) << ((where & (4 - size)) * 8); - PEX_OUT(base, PEXCADRS, MK_PEXCADRS(PEXCADRS_CMD_WRITE, where, size)); - PEX_OUT(base, PEXCWDATA, data); -} - -/* Interfaces */ -/* Note: Work-around - * On SCC PCIEXC, one device is seen on all 32 dev_no. - * As SCC PCIEXC can have only one device on the bus, we look only one dev_no. - * (dev_no = 1) - */ -static int scc_pciex_read_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, unsigned int *val) -{ - struct pci_controller *phb = pci_bus_to_host(bus); - - if (bus->number == phb->first_busno && PCI_SLOT(devfn) != 1) { - *val = ~0; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - if (bus->number == 0 && PCI_SLOT(devfn) == 0) - *val = config_read_pciex_rc(phb->cfg_addr, where, size); - else - *val = config_read_pciex_dev(phb->cfg_addr, bus->number, - PCI_SLOT(devfn), PCI_FUNC(devfn), where, size); - - return PCIBIOS_SUCCESSFUL; -} - -static int scc_pciex_write_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, unsigned int val) -{ - struct pci_controller *phb = pci_bus_to_host(bus); - - if (bus->number == phb->first_busno && PCI_SLOT(devfn) != 1) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (bus->number == 0 && PCI_SLOT(devfn) == 0) - config_write_pciex_rc(phb->cfg_addr, where, size, val); - else - config_write_pciex_dev(phb->cfg_addr, bus->number, - PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, val); - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops scc_pciex_pci_ops = { - .read = scc_pciex_read_config, - .write = scc_pciex_write_config, -}; - -static void pciex_clear_intr_all(unsigned int __iomem *base) -{ - PEX_OUT(base, PEXAERRSTS, 0xffffffff); - PEX_OUT(base, PEXPRERRSTS, 0xffffffff); - PEX_OUT(base, PEXINTSTS, 0xffffffff); -} - -#if 0 -static void pciex_disable_intr_all(unsigned int *base) -{ - PEX_OUT(base, PEXINTMASK, 0x0); - PEX_OUT(base, PEXAERRMASK, 0x0); - PEX_OUT(base, PEXPRERRMASK, 0x0); - PEX_OUT(base, PEXVDMASK, 0x0); -} -#endif - -static void pciex_enable_intr_all(unsigned int __iomem *base) -{ - PEX_OUT(base, PEXINTMASK, 0x0000e7f1); - PEX_OUT(base, PEXAERRMASK, 0x03ff01ff); - PEX_OUT(base, PEXPRERRMASK, 0x0001010f); - PEX_OUT(base, PEXVDMASK, 0x00000001); -} - -static void pciex_check_status(unsigned int __iomem *base) -{ - uint32_t err = 0; - uint32_t intsts, aerr, prerr, rcvcp, lenerr; - uint32_t maea, maec; - - intsts = PEX_IN(base, PEXINTSTS); - aerr = PEX_IN(base, PEXAERRSTS); - prerr = PEX_IN(base, PEXPRERRSTS); - rcvcp = PEX_IN(base, PEXRCVCPLIDA); - lenerr = PEX_IN(base, PEXLENERRIDA); - - if (intsts || aerr || prerr || rcvcp || lenerr) - err = 1; - - pr_info("PCEXC interrupt!!\n"); - pr_info("PEXINTSTS :0x%08x\n", intsts); - pr_info("PEXAERRSTS :0x%08x\n", aerr); - pr_info("PEXPRERRSTS :0x%08x\n", prerr); - pr_info("PEXRCVCPLIDA :0x%08x\n", rcvcp); - pr_info("PEXLENERRIDA :0x%08x\n", lenerr); - - /* print detail of Protection Error */ - if (intsts & 0x00004000) { - uint32_t i, n; - for (i = 0; i < 4; i++) { - n = 1 << i; - if (prerr & n) { - maea = PEX_IN(base, PEXMAEA(i)); - maec = PEX_IN(base, PEXMAEC(i)); - pr_info("PEXMAEC%d :0x%08x\n", i, maec); - pr_info("PEXMAEA%d :0x%08x\n", i, maea); - } - } - } - - if (err) - pciex_clear_intr_all(base); -} - -static irqreturn_t pciex_handle_internal_irq(int irq, void *dev_id) -{ - struct pci_controller *phb = dev_id; - - pr_debug("PCIEX:pciex_handle_internal_irq(irq=%d)\n", irq); - - BUG_ON(phb->cfg_addr == NULL); - - pciex_check_status(phb->cfg_addr); - - return IRQ_HANDLED; -} - -static __init int celleb_setup_pciex(struct device_node *node, - struct pci_controller *phb) -{ - struct resource r; - int virq; - - /* SMMIO registers; used inside this file */ - if (of_address_to_resource(node, 0, &r)) { - pr_err("PCIEXC:Failed to get config resource.\n"); - return 1; - } - phb->cfg_addr = ioremap(r.start, resource_size(&r)); - if (!phb->cfg_addr) { - pr_err("PCIEXC:Failed to remap SMMIO region.\n"); - return 1; - } - - /* Not use cfg_data, cmd and data regs are near address reg */ - phb->cfg_data = NULL; - - /* set pci_ops */ - phb->ops = &scc_pciex_pci_ops; - - /* internal interrupt handler */ - virq = irq_of_parse_and_map(node, 1); - if (!virq) { - pr_err("PCIEXC:Failed to map irq\n"); - goto error; - } - if (request_irq(virq, pciex_handle_internal_irq, - 0, "pciex", (void *)phb)) { - pr_err("PCIEXC:Failed to request irq\n"); - goto error; - } - - /* enable all interrupts */ - pciex_clear_intr_all(phb->cfg_addr); - pciex_enable_intr_all(phb->cfg_addr); - /* MSI: TBD */ - - return 0; - -error: - phb->cfg_data = NULL; - if (phb->cfg_addr) - iounmap(phb->cfg_addr); - phb->cfg_addr = NULL; - return 1; -} - -struct celleb_phb_spec celleb_pciex_spec __initdata = { - .setup = celleb_setup_pciex, - .ops = &scc_pciex_ops, - .iowa_init = &scc_pciex_iowa_init, -}; diff --git a/arch/powerpc/platforms/cell/celleb_scc_sio.c b/arch/powerpc/platforms/cell/celleb_scc_sio.c deleted file mode 100644 index c8eb57193826..000000000000 --- a/arch/powerpc/platforms/cell/celleb_scc_sio.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * setup serial port in SCC - * - * (C) Copyright 2006-2007 TOSHIBA CORPORATION - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include - -#include -#include - -/* sio irq0=0xb00010022 irq0=0xb00010023 irq2=0xb00010024 - mmio=0xfff000-0x1000,0xff2000-0x1000 */ -static int txx9_serial_bitmap __initdata; - -static struct { - uint32_t offset; - uint32_t index; -} txx9_scc_tab[3] __initdata = { - { 0x300, 0 }, /* 0xFFF300 */ - { 0x400, 0 }, /* 0xFFF400 */ - { 0x800, 1 } /* 0xFF2800 */ -}; - -static int __init txx9_serial_init(void) -{ - extern int early_serial_txx9_setup(struct uart_port *port); - struct device_node *node; - int i; - struct uart_port req; - struct of_phandle_args irq; - struct resource res; - - for_each_compatible_node(node, "serial", "toshiba,sio-scc") { - for (i = 0; i < ARRAY_SIZE(txx9_scc_tab); i++) { - if (!(txx9_serial_bitmap & (1< UHC_RESET_WAIT_MAX) { - printk(KERN_ERR "Failed to disable UHC reset %x\n", - in_be32(uhc_clkctrl)); - break; - } - } - - /* Endian Conversion Mode for Master ALL area */ - out_be32(uhc_ecmode, SCC_UHC_ECMODE_BY_BYTE); - - iounmap(uhc_base); -} - -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TOSHIBA_2, - PCI_DEVICE_ID_TOSHIBA_SCC_USB, enable_scc_uhc); diff --git a/arch/powerpc/platforms/cell/celleb_setup.c b/arch/powerpc/platforms/cell/celleb_setup.c deleted file mode 100644 index 90be8ec51686..000000000000 --- a/arch/powerpc/platforms/cell/celleb_setup.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Celleb setup code - * - * (C) Copyright 2006-2007 TOSHIBA CORPORATION - * - * This code is based on arch/powerpc/platforms/cell/setup.c: - * Copyright (C) 1995 Linus Torvalds - * Adapted from 'alpha' version by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) - * Modified by PPC64 Team, IBM Corp - * Modified by Cell Team, IBM Deutschland Entwicklung GmbH - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "beat_interrupt.h" -#include "beat_wrapper.h" -#include "beat.h" -#include "celleb_pci.h" -#include "interrupt.h" -#include "pervasive.h" -#include "ras.h" - -static char celleb_machine_type[128] = "Celleb"; - -static void celleb_show_cpuinfo(struct seq_file *m) -{ - struct device_node *root; - const char *model = ""; - - root = of_find_node_by_path("/"); - if (root) - model = of_get_property(root, "model", NULL); - /* using "CHRP" is to trick anaconda into installing FCx into Celleb */ - seq_printf(m, "machine\t\t: %s %s\n", celleb_machine_type, model); - of_node_put(root); -} - -static int __init celleb_machine_type_hack(char *ptr) -{ - strlcpy(celleb_machine_type, ptr, sizeof(celleb_machine_type)); - return 0; -} - -__setup("celleb_machine_type_hack=", celleb_machine_type_hack); - -static void celleb_progress(char *s, unsigned short hex) -{ - printk("*** %04x : %s\n", hex, s ? s : ""); -} - -static void __init celleb_setup_arch_common(void) -{ - /* init to some ~sane value until calibrate_delay() runs */ - loops_per_jiffy = 50000000; - -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif -} - -static const struct of_device_id celleb_bus_ids[] __initconst = { - { .type = "scc", }, - { .type = "ioif", }, /* old style */ - {}, -}; - -static int __init celleb_publish_devices(void) -{ - /* Publish OF platform devices for southbridge IOs */ - of_platform_bus_probe(NULL, celleb_bus_ids, NULL); - - return 0; -} -machine_device_initcall(celleb_beat, celleb_publish_devices); -machine_device_initcall(celleb_native, celleb_publish_devices); - - -/* - * functions for Celleb-Beat - */ -static void __init celleb_setup_arch_beat(void) -{ -#ifdef CONFIG_SPU_BASE - spu_priv1_ops = &spu_priv1_beat_ops; - spu_management_ops = &spu_management_of_ops; -#endif - - celleb_setup_arch_common(); -} - -static int __init celleb_probe_beat(void) -{ - unsigned long root = of_get_flat_dt_root(); - - if (!of_flat_dt_is_compatible(root, "Beat")) - return 0; - - powerpc_firmware_features |= FW_FEATURE_CELLEB_ALWAYS - | FW_FEATURE_BEAT | FW_FEATURE_LPAR; - hpte_init_beat_v3(); - pm_power_off = beat_power_off; - - return 1; -} - - -/* - * functions for Celleb-native - */ -static void __init celleb_init_IRQ_native(void) -{ - iic_init_IRQ(); - spider_init_IRQ(); -} - -static void __init celleb_setup_arch_native(void) -{ -#ifdef CONFIG_SPU_BASE - spu_priv1_ops = &spu_priv1_mmio_ops; - spu_management_ops = &spu_management_of_ops; -#endif - - cbe_regs_init(); - -#ifdef CONFIG_CBE_RAS - cbe_ras_init(); -#endif - -#ifdef CONFIG_SMP - smp_init_cell(); -#endif - - cbe_pervasive_init(); - - /* XXX: nvram initialization should be added */ - - celleb_setup_arch_common(); -} - -static int __init celleb_probe_native(void) -{ - unsigned long root = of_get_flat_dt_root(); - - if (of_flat_dt_is_compatible(root, "Beat") || - !of_flat_dt_is_compatible(root, "TOSHIBA,Celleb")) - return 0; - - powerpc_firmware_features |= FW_FEATURE_CELLEB_ALWAYS; - hpte_init_native(); - pm_power_off = rtas_power_off; - - return 1; -} - - -/* - * machine definitions - */ -define_machine(celleb_beat) { - .name = "Cell Reference Set (Beat)", - .probe = celleb_probe_beat, - .setup_arch = celleb_setup_arch_beat, - .show_cpuinfo = celleb_show_cpuinfo, - .restart = beat_restart, - .halt = beat_halt, - .get_rtc_time = beat_get_rtc_time, - .set_rtc_time = beat_set_rtc_time, - .calibrate_decr = generic_calibrate_decr, - .progress = celleb_progress, - .power_save = beat_power_save, - .nvram_size = beat_nvram_get_size, - .nvram_read = beat_nvram_read, - .nvram_write = beat_nvram_write, - .set_dabr = beat_set_xdabr, - .init_IRQ = beatic_init_IRQ, - .get_irq = beatic_get_irq, - .pci_probe_mode = celleb_pci_probe_mode, - .pci_setup_phb = celleb_setup_phb, -#ifdef CONFIG_KEXEC - .kexec_cpu_down = beat_kexec_cpu_down, -#endif -}; - -define_machine(celleb_native) { - .name = "Cell Reference Set (native)", - .probe = celleb_probe_native, - .setup_arch = celleb_setup_arch_native, - .show_cpuinfo = celleb_show_cpuinfo, - .restart = rtas_restart, - .halt = rtas_halt, - .get_boot_time = rtas_get_boot_time, - .get_rtc_time = rtas_get_rtc_time, - .set_rtc_time = rtas_set_rtc_time, - .calibrate_decr = generic_calibrate_decr, - .progress = celleb_progress, - .pci_probe_mode = celleb_pci_probe_mode, - .pci_setup_phb = celleb_setup_phb, - .init_IRQ = celleb_init_IRQ_native, -}; diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index c7c8720aa39f..31b1a67daccf 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -1234,5 +1234,3 @@ static int __init cell_iommu_init(void) return 0; } machine_arch_initcall(cell, cell_iommu_init); -machine_arch_initcall(celleb_native, cell_iommu_init); - -- cgit v1.2.3 From f691fa1080c37c48da0cdfeae082c3bef5df2643 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Mon, 30 Mar 2015 14:10:37 +1100 Subject: powerpc: Replace mem_init_done with slab_is_available() We have a powerpc specific global called mem_init_done which is "set on boot once kmalloc can be called". But that's not *quite* true. We set it at the bottom of mem_init(), and rely on the fact that mm_init() calls kmem_cache_init() immediately after that, and nothing is running in parallel. So replace it with the generic and 100% correct slab_is_available(). Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/setup.h | 1 - arch/powerpc/kernel/pci-common.c | 2 +- arch/powerpc/kernel/rtas.c | 4 ++-- arch/powerpc/lib/alloc.c | 2 +- arch/powerpc/mm/mem.c | 3 --- arch/powerpc/mm/pgtable_32.c | 9 ++++----- arch/powerpc/mm/pgtable_64.c | 4 ++-- 7 files changed, 10 insertions(+), 15 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/setup.h b/arch/powerpc/include/asm/setup.h index fbdf18cf954c..e9d384cbd021 100644 --- a/arch/powerpc/include/asm/setup.h +++ b/arch/powerpc/include/asm/setup.h @@ -7,7 +7,6 @@ extern void ppc_printk_progress(char *s, unsigned short hex); extern unsigned int rtas_data; -extern int mem_init_done; /* set on boot once kmalloc can be called */ extern unsigned long long memory_limit; extern unsigned long klimit; extern void *zalloc_maybe_bootmem(size_t size, gfp_t mask); diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 2a525c938158..bcf618bfff1e 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -76,7 +76,7 @@ struct pci_controller *pcibios_alloc_controller(struct device_node *dev) list_add_tail(&phb->list_node, &hose_list); spin_unlock(&hose_spinlock); phb->dn = dev; - phb->is_dynamic = mem_init_done; + phb->is_dynamic = slab_is_available(); #ifdef CONFIG_PPC64 if (dev) { int nid = of_node_to_nid(dev); diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index b9a7b8981ef7..7a488c108410 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -401,7 +401,7 @@ static char *__fetch_rtas_last_error(char *altbuf) buf = altbuf; } else { buf = rtas_err_buf; - if (mem_init_done) + if (slab_is_available()) buf = kmalloc(RTAS_ERROR_LOG_MAX, GFP_ATOMIC); } if (buf) @@ -461,7 +461,7 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...) if (buff_copy) { log_error(buff_copy, ERR_TYPE_RTAS_LOG, 0); - if (mem_init_done) + if (slab_is_available()) kfree(buff_copy); } return ret; diff --git a/arch/powerpc/lib/alloc.c b/arch/powerpc/lib/alloc.c index 4a6c2cf890d9..60b0b3fc8fc1 100644 --- a/arch/powerpc/lib/alloc.c +++ b/arch/powerpc/lib/alloc.c @@ -10,7 +10,7 @@ void * __init_refok zalloc_maybe_bootmem(size_t size, gfp_t mask) { void *p; - if (mem_init_done) + if (slab_is_available()) p = kzalloc(size, mask); else { p = memblock_virt_alloc(size, 0); diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index b7285a5870f8..45fda71feb27 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -61,7 +61,6 @@ #define CPU_FTR_NOEXECUTE 0 #endif -int mem_init_done; unsigned long long memory_limit; #ifdef CONFIG_HIGHMEM @@ -377,8 +376,6 @@ void __init mem_init(void) pr_info(" * 0x%08lx..0x%08lx : vmalloc & ioremap\n", VMALLOC_START, VMALLOC_END); #endif /* CONFIG_PPC32 */ - - mem_init_done = 1; } void free_initmem(void) diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c index 70b4752af54f..7692d1bb1bc6 100644 --- a/arch/powerpc/mm/pgtable_32.c +++ b/arch/powerpc/mm/pgtable_32.c @@ -107,9 +107,8 @@ void pgd_free(struct mm_struct *mm, pgd_t *pgd) __init_refok pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) { pte_t *pte; - extern int mem_init_done; - if (mem_init_done) { + if (slab_is_available()) { pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO); } else { pte = __va(memblock_alloc(PAGE_SIZE, PAGE_SIZE)); @@ -216,7 +215,7 @@ __ioremap_caller(phys_addr_t addr, unsigned long size, unsigned long flags, * Don't allow anybody to remap normal RAM that we're using. * mem_init() sets high_memory so only do the check after that. */ - if (mem_init_done && (p < virt_to_phys(high_memory)) && + if (slab_is_available() && (p < virt_to_phys(high_memory)) && !(__allow_ioremap_reserved && memblock_is_region_reserved(p, size))) { printk("__ioremap(): phys addr 0x%llx is RAM lr %ps\n", (unsigned long long)p, __builtin_return_address(0)); @@ -244,7 +243,7 @@ __ioremap_caller(phys_addr_t addr, unsigned long size, unsigned long flags, if ((v = p_mapped_by_tlbcam(p))) goto out; - if (mem_init_done) { + if (slab_is_available()) { struct vm_struct *area; area = get_vm_area_caller(size, VM_IOREMAP, caller); if (area == 0) @@ -263,7 +262,7 @@ __ioremap_caller(phys_addr_t addr, unsigned long size, unsigned long flags, for (i = 0; i < size && err == 0; i += PAGE_SIZE) err = map_page(v+i, p+i, flags); if (err) { - if (mem_init_done) + if (slab_is_available()) vunmap((void *)v); return NULL; } diff --git a/arch/powerpc/mm/pgtable_64.c b/arch/powerpc/mm/pgtable_64.c index 3ac3a0a1edfb..59daa5eeec25 100644 --- a/arch/powerpc/mm/pgtable_64.c +++ b/arch/powerpc/mm/pgtable_64.c @@ -231,7 +231,7 @@ void __iomem * __ioremap_caller(phys_addr_t addr, unsigned long size, if ((size == 0) || (paligned == 0)) return NULL; - if (mem_init_done) { + if (slab_is_available()) { struct vm_struct *area; area = __get_vm_area_caller(size, VM_IOREMAP, @@ -315,7 +315,7 @@ void __iounmap(volatile void __iomem *token) { void *addr; - if (!mem_init_done) + if (!slab_is_available()) return; addr = (void *) ((unsigned long __force) -- cgit v1.2.3 From 7e862d7e7d118e3becc5b495af10ca076f087180 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Mon, 30 Mar 2015 17:38:09 +1100 Subject: powerpc: Reword the "returning from prom_init" message We get way too many bug reports that say "the kernel is hung in prom_init", which stems from the fact that the last piece of output people see is "returning from prom_init". The kernel is almost never hung in prom_init(), it's just that it's crashed somewhere after prom_init() but prior to the console coming up. The existing message should give a clue to that, ie. "returning from" indicates that prom_init() has finished, but it doesn't seem to work. Let's try something different. This prints: Quiescing Open Firmware ... Booting Linux via __start() ... Which hopefully makes it clear that prom_init() is not the problem, and although __start() probably isn't either, it's at least the right place to begin looking. Signed-off-by: Michael Ellerman Wistfully-Acked-by: Jeremy Kerr --- arch/powerpc/kernel/prom_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 1a85d8f96739..fd1fe4c37599 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -2898,7 +2898,7 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, * Call OF "quiesce" method to shut down pending DMA's from * devices etc... */ - prom_printf("Calling quiesce...\n"); + prom_printf("Quiescing Open Firmware ...\n"); call_prom("quiesce", 0, 0); /* @@ -2910,7 +2910,7 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, /* Don't print anything after quiesce under OPAL, it crashes OFW */ if (of_platform != PLATFORM_OPAL) { - prom_printf("returning from prom_init\n"); + prom_printf("Booting Linux via __start() ...\n"); prom_debug("->dt_header_start=0x%x\n", hdr); } -- cgit v1.2.3 From bdc728a849a7047e55014c6f968aa300cc9f57fa Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 31 Mar 2015 16:00:39 +1100 Subject: powerpc: move find_and_init_phbs() to pSeries specific code Previously, find_and_init_phbs() was used in both PowerNV and pSeries setup. However, since RTAS support has been dropped from PowerNV, we can move it into a platform-specific file. Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/ppc-pci.h | 3 --- arch/powerpc/kernel/rtas_pci.c | 47 ---------------------------------- arch/powerpc/platforms/pseries/setup.c | 47 ++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 50 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h index ade75238ceb5..4122a86d6858 100644 --- a/arch/powerpc/include/asm/ppc-pci.h +++ b/arch/powerpc/include/asm/ppc-pci.h @@ -23,8 +23,6 @@ extern void pci_setup_phb_io_dynamic(struct pci_controller *hose, int primary); extern struct list_head hose_list; -extern void find_and_init_phbs(void); - extern struct pci_dev *isa_bridge_pcidev; /* may be NULL if no ISA bus */ /** Bus Unit ID macros; get low and hi 32-bits of the 64-bit BUID */ @@ -81,7 +79,6 @@ static inline const char *eeh_driver_name(struct pci_dev *pdev) #endif /* CONFIG_EEH */ #else /* CONFIG_PCI */ -static inline void find_and_init_phbs(void) { } static inline void init_pci_config_tokens(void) { } #endif /* !CONFIG_PCI */ diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c index af29df2517f7..73f1934582c2 100644 --- a/arch/powerpc/kernel/rtas_pci.c +++ b/arch/powerpc/kernel/rtas_pci.c @@ -277,50 +277,3 @@ int rtas_setup_phb(struct pci_controller *phb) return 0; } - -void __init find_and_init_phbs(void) -{ - struct device_node *node; - struct pci_controller *phb; - struct device_node *root = of_find_node_by_path("/"); - - for_each_child_of_node(root, node) { - if (node->type == NULL || (strcmp(node->type, "pci") != 0 && - strcmp(node->type, "pciex") != 0)) - continue; - - phb = pcibios_alloc_controller(node); - if (!phb) - continue; - rtas_setup_phb(phb); - pci_process_bridge_OF_ranges(phb, node, 0); - isa_bridge_find_early(phb); - } - - of_node_put(root); - pci_devs_phb_init(); - - /* - * PCI_PROBE_ONLY and PCI_REASSIGN_ALL_BUS can be set via properties - * in chosen. - */ - if (of_chosen) { - const int *prop; - - prop = of_get_property(of_chosen, - "linux,pci-probe-only", NULL); - if (prop) { - if (*prop) - pci_add_flags(PCI_PROBE_ONLY); - else - pci_clear_flags(PCI_PROBE_ONLY); - } - -#ifdef CONFIG_PPC32 /* Will be made generic soon */ - prop = of_get_property(of_chosen, - "linux,pci-assign-all-buses", NULL); - if (prop && *prop) - pci_add_flags(PCI_REASSIGN_ALL_BUS); -#endif /* CONFIG_PPC32 */ - } -} diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 70304070a260..bcc6d24c77aa 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -461,6 +461,53 @@ static long pseries_little_endian_exceptions(void) } #endif +static void __init find_and_init_phbs(void) +{ + struct device_node *node; + struct pci_controller *phb; + struct device_node *root = of_find_node_by_path("/"); + + for_each_child_of_node(root, node) { + if (node->type == NULL || (strcmp(node->type, "pci") != 0 && + strcmp(node->type, "pciex") != 0)) + continue; + + phb = pcibios_alloc_controller(node); + if (!phb) + continue; + rtas_setup_phb(phb); + pci_process_bridge_OF_ranges(phb, node, 0); + isa_bridge_find_early(phb); + } + + of_node_put(root); + pci_devs_phb_init(); + + /* + * PCI_PROBE_ONLY and PCI_REASSIGN_ALL_BUS can be set via properties + * in chosen. + */ + if (of_chosen) { + const int *prop; + + prop = of_get_property(of_chosen, + "linux,pci-probe-only", NULL); + if (prop) { + if (*prop) + pci_add_flags(PCI_PROBE_ONLY); + else + pci_clear_flags(PCI_PROBE_ONLY); + } + +#ifdef CONFIG_PPC32 /* Will be made generic soon */ + prop = of_get_property(of_chosen, + "linux,pci-assign-all-buses", NULL); + if (prop && *prop) + pci_add_flags(PCI_REASSIGN_ALL_BUS); +#endif /* CONFIG_PPC32 */ + } +} + static void __init pSeries_setup_arch(void) { set_arch_panic_timeout(10, ARCH_PANIC_TIMEOUT); -- cgit v1.2.3 From c88c2a188905cb3077c3c38dc498e7e9f8eebeee Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 31 Mar 2015 16:00:41 +1100 Subject: powerpc: pcibios_enable_device_hook: return bool rather than int pcibios_enable_device_hook returned an int. Every implementation returned either -EINVAL or 0. The return value wasn't propagated by the caller: any non-zero return value caused pcibios_enable_device to return -EINVAL itself. Therefore, make the hook return a bool. Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/machdep.h | 4 ++-- arch/powerpc/kernel/pci-common.c | 2 +- arch/powerpc/platforms/powermac/pci.c | 8 ++++---- arch/powerpc/platforms/powermac/pmac.h | 2 +- arch/powerpc/platforms/powernv/pci-ioda.c | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index 098d51e924ea..e29f058c0903 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -237,9 +237,9 @@ struct machdep_calls { /* Called for each PCI bus in the system when it's probed */ void (*pcibios_fixup_bus)(struct pci_bus *); - /* Called when pci_enable_device() is called. Returns 0 to + /* Called when pci_enable_device() is called. Returns true to * allow assignment/enabling of the device. */ - int (*pcibios_enable_device_hook)(struct pci_dev *); + bool (*pcibios_enable_device_hook)(struct pci_dev *); /* Called after scan and before resource survey */ void (*pcibios_fixup_phb)(struct pci_controller *hose); diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index bcf618bfff1e..17827c7345a7 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1451,7 +1451,7 @@ EXPORT_SYMBOL_GPL(pcibios_finish_adding_to_bus); int pcibios_enable_device(struct pci_dev *dev, int mask) { if (ppc_md.pcibios_enable_device_hook) - if (ppc_md.pcibios_enable_device_hook(dev)) + if (!ppc_md.pcibios_enable_device_hook(dev)) return -EINVAL; return pci_enable_resources(dev, mask); diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c index a792f4552442..9c89fd29da60 100644 --- a/arch/powerpc/platforms/powermac/pci.c +++ b/arch/powerpc/platforms/powermac/pci.c @@ -942,7 +942,7 @@ void __init pmac_pci_init(void) } #ifdef CONFIG_PPC32 -int pmac_pci_enable_device_hook(struct pci_dev *dev) +bool pmac_pci_enable_device_hook(struct pci_dev *dev) { struct device_node* node; int updatecfg = 0; @@ -958,11 +958,11 @@ int pmac_pci_enable_device_hook(struct pci_dev *dev) && !node) { printk(KERN_INFO "Apple USB OHCI %s disabled by firmware\n", pci_name(dev)); - return -EINVAL; + return false; } if (!node) - return 0; + return true; uninorth_child = node->parent && of_device_is_compatible(node->parent, "uni-north"); @@ -1003,7 +1003,7 @@ int pmac_pci_enable_device_hook(struct pci_dev *dev) L1_CACHE_BYTES >> 2); } - return 0; + return true; } void pmac_pci_fixup_ohci(struct pci_dev *dev) diff --git a/arch/powerpc/platforms/powermac/pmac.h b/arch/powerpc/platforms/powermac/pmac.h index 46d219345537..b8d572159faa 100644 --- a/arch/powerpc/platforms/powermac/pmac.h +++ b/arch/powerpc/platforms/powermac/pmac.h @@ -25,7 +25,7 @@ extern void pmac_pci_init(void); extern void pmac_nvram_update(void); extern unsigned char pmac_nvram_read_byte(int addr); extern void pmac_nvram_write_byte(int addr, unsigned char val); -extern int pmac_pci_enable_device_hook(struct pci_dev *dev); +extern bool pmac_pci_enable_device_hook(struct pci_dev *dev); extern void pmac_pcibios_after_init(void); extern int of_show_percpuinfo(struct seq_file *m, int i); diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 76b344125cef..f93d6c2bd743 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -1911,7 +1911,7 @@ static resource_size_t pnv_pci_window_alignment(struct pci_bus *bus, /* Prevent enabling devices for which we couldn't properly * assign a PE */ -static int pnv_pci_enable_device_hook(struct pci_dev *dev) +static bool pnv_pci_enable_device_hook(struct pci_dev *dev) { struct pci_controller *hose = pci_bus_to_host(dev->bus); struct pnv_phb *phb = hose->private_data; @@ -1923,13 +1923,13 @@ static int pnv_pci_enable_device_hook(struct pci_dev *dev) * PEs isn't ready. */ if (!phb->initialized) - return 0; + return true; pdn = pci_get_pdn(dev); if (!pdn || pdn->pe_number == IODA_INVALID_PE) - return -EINVAL; + return false; - return 0; + return true; } static u32 pnv_ioda_bdfn_to_pe(struct pnv_phb *phb, struct pci_bus *bus, -- cgit v1.2.3 From e02def5bce12b472e9eb6dcdd9f7af72239e6330 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 31 Mar 2015 16:00:42 +1100 Subject: powerpc: Create pci_controller_ops.dma_dev_setup and shim Introduces the pci_controller_ops structure. Add pci_controller_ops.dma_dev_setup, shadowing ppc_md.pci_dma_dev_setup. Add a shim, and change the callsites to use the shim. Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/pci-bridge.h | 21 +++++++++++++++++++++ arch/powerpc/kernel/pci-common.c | 3 +-- 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 2c6dc2a3d14a..0f441b8e1ea1 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -14,6 +14,13 @@ struct device_node; +/* + * PCI controller operations + */ +struct pci_controller_ops { + void (*dma_dev_setup)(struct pci_dev *dev); +}; + /* * Structure of a PCI controller (host bridge) */ @@ -46,6 +53,7 @@ struct pci_controller { resource_size_t isa_mem_phys; resource_size_t isa_mem_size; + struct pci_controller_ops controller_ops; struct pci_ops *ops; unsigned int __iomem *cfg_addr; void __iomem *cfg_data; @@ -260,5 +268,18 @@ static inline int pcibios_vaddr_is_ioport(void __iomem *address) } #endif /* CONFIG_PCI */ +/* + * Shims to prefer pci_controller version over ppc_md where available. + */ +static inline void pci_dma_dev_setup(struct pci_dev *dev) +{ + struct pci_controller *phb = pci_bus_to_host(dev->bus); + + if (phb->controller_ops.dma_dev_setup) + phb->controller_ops.dma_dev_setup(dev); + else if (ppc_md.pci_dma_dev_setup) + ppc_md.pci_dma_dev_setup(dev); +} + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PCI_BRIDGE_H */ diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 17827c7345a7..7e3757e403d4 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -969,8 +969,7 @@ static void pcibios_setup_device(struct pci_dev *dev) set_dma_offset(&dev->dev, PCI_DRAM_OFFSET); /* Additional platform DMA/iommu setup */ - if (ppc_md.pci_dma_dev_setup) - ppc_md.pci_dma_dev_setup(dev); + pci_dma_dev_setup(dev); /* Read default IRQs and fixup if necessary */ pci_read_irq_line(dev); -- cgit v1.2.3 From b122c95494374ab848f8d9f41d98644c2c318ecc Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 31 Mar 2015 16:00:43 +1100 Subject: powerpc: Create pci_controller_ops.dma_bus_setup and shim Add pci_controller_ops.dma_bus_setup, shadowing ppc_md.pci_dma_bus_setup. Add a shim, and changes the callsites to use the shim. Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/pci-bridge.h | 11 +++++++++++ arch/powerpc/kernel/pci-common.c | 3 +-- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 0f441b8e1ea1..b9732fcb0f5f 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -19,6 +19,7 @@ struct device_node; */ struct pci_controller_ops { void (*dma_dev_setup)(struct pci_dev *dev); + void (*dma_bus_setup)(struct pci_bus *bus); }; /* @@ -281,5 +282,15 @@ static inline void pci_dma_dev_setup(struct pci_dev *dev) ppc_md.pci_dma_dev_setup(dev); } +static inline void pci_dma_bus_setup(struct pci_bus *bus) +{ + struct pci_controller *phb = pci_bus_to_host(bus); + + if (phb->controller_ops.dma_bus_setup) + phb->controller_ops.dma_bus_setup(bus); + else if (ppc_md.pci_dma_bus_setup) + ppc_md.pci_dma_bus_setup(bus); +} + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PCI_BRIDGE_H */ diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 7e3757e403d4..af357cc38ff6 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -953,8 +953,7 @@ void pcibios_setup_bus_self(struct pci_bus *bus) ppc_md.pcibios_fixup_bus(bus); /* Setup bus DMA mappings */ - if (ppc_md.pci_dma_bus_setup) - ppc_md.pci_dma_bus_setup(bus); + pci_dma_bus_setup(bus); } static void pcibios_setup_device(struct pci_dev *dev) -- cgit v1.2.3 From ff9df8c87d6807e90c5c3b0e1fd1649d09fd3bcd Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 31 Mar 2015 16:00:44 +1100 Subject: powerpc: Create pci_controller_ops.probe_mode and shim Add pci_controller_ops.probe_mode, shadowing ppc_md.pci_probe_mode. Add a shim, and changes the callsites to use the shim. We also need to move the probe mode defines to pci-bridge.h from pci.h. They are required by the shim in order to return a sensible default. Previously, the were defined in pci.h, but pci.h includes pci-bridge.h before the relevant #defines. This means the definitions are absent if pci.h is included before pci-bridge.h. This occurs in some drivers. So, move the definitons now, and move them back when we remove the shim. Anything that wants the defines would have had to include pci.h, and since pci.h includes pci-bridge.h, nothing will lose access to the defines. Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/pci-bridge.h | 18 ++++++++++++++++++ arch/powerpc/include/asm/pci.h | 5 ----- arch/powerpc/kernel/pci-common.c | 4 ++-- arch/powerpc/kernel/pci-hotplug.c | 3 +-- arch/powerpc/kernel/pci_of_scan.c | 3 +-- 5 files changed, 22 insertions(+), 11 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index b9732fcb0f5f..278f48978bad 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -12,6 +12,11 @@ #include #include +/* Return values for pci_controller_ops.probe_mode function */ +#define PCI_PROBE_NONE -1 /* Don't look at this bus at all */ +#define PCI_PROBE_NORMAL 0 /* Do normal PCI probing */ +#define PCI_PROBE_DEVTREE 1 /* Instantiate from device tree */ + struct device_node; /* @@ -20,6 +25,8 @@ struct device_node; struct pci_controller_ops { void (*dma_dev_setup)(struct pci_dev *dev); void (*dma_bus_setup)(struct pci_bus *bus); + + int (*probe_mode)(struct pci_bus *); }; /* @@ -292,5 +299,16 @@ static inline void pci_dma_bus_setup(struct pci_bus *bus) ppc_md.pci_dma_bus_setup(bus); } +static inline int pci_probe_mode(struct pci_bus *bus) +{ + struct pci_controller *phb = pci_bus_to_host(bus); + + if (phb->controller_ops.probe_mode) + return phb->controller_ops.probe_mode(bus); + if (ppc_md.pci_probe_mode) + return ppc_md.pci_probe_mode(bus); + return PCI_PROBE_NORMAL; +} + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PCI_BRIDGE_H */ diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 1b0739bc14b5..8745067ac702 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -22,11 +22,6 @@ #include -/* Return values for ppc_md.pci_probe_mode function */ -#define PCI_PROBE_NONE -1 /* Don't look at this bus at all */ -#define PCI_PROBE_NORMAL 0 /* Do normal PCI probing */ -#define PCI_PROBE_DEVTREE 1 /* Instantiate from device tree */ - #define PCIBIOS_MIN_IO 0x1000 #define PCIBIOS_MIN_MEM 0x10000000 diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index af357cc38ff6..b0de23c89ca5 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1622,8 +1622,8 @@ void pcibios_scan_phb(struct pci_controller *hose) /* Get probe mode and perform scan */ mode = PCI_PROBE_NORMAL; - if (node && ppc_md.pci_probe_mode) - mode = ppc_md.pci_probe_mode(bus); + if (node) + mode = pci_probe_mode(bus); pr_debug(" probe mode: %d\n", mode); if (mode == PCI_PROBE_DEVTREE) of_scan_bus(node, bus); diff --git a/arch/powerpc/kernel/pci-hotplug.c b/arch/powerpc/kernel/pci-hotplug.c index 18d9575729a3..27116b1b2d14 100644 --- a/arch/powerpc/kernel/pci-hotplug.c +++ b/arch/powerpc/kernel/pci-hotplug.c @@ -78,8 +78,7 @@ void pcibios_add_pci_devices(struct pci_bus * bus) eeh_add_device_tree_early(PCI_DN(dn)); mode = PCI_PROBE_NORMAL; - if (ppc_md.pci_probe_mode) - mode = ppc_md.pci_probe_mode(bus); + mode = pci_probe_mode(bus); if (mode == PCI_PROBE_DEVTREE) { /* use ofdt-based probe */ diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c index 7122dfece393..4ee63c4f077e 100644 --- a/arch/powerpc/kernel/pci_of_scan.c +++ b/arch/powerpc/kernel/pci_of_scan.c @@ -287,8 +287,7 @@ void of_scan_pci_bridge(struct pci_dev *dev) pr_debug(" bus name: %s\n", bus->name); mode = PCI_PROBE_NORMAL; - if (ppc_md.pci_probe_mode) - mode = ppc_md.pci_probe_mode(bus); + mode = pci_probe_mode(bus); pr_debug(" probe mode: %d\n", mode); if (mode == PCI_PROBE_DEVTREE) -- cgit v1.2.3 From b31e79f8d92ca115a935e37cfd4da74048739689 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 31 Mar 2015 16:00:45 +1100 Subject: powerpc: Create pci_controller_ops.enable_device_hook and shim Add pci_controller_ops.enable_device_hook, shadowing ppc_md.pcibios_enable_device_hook. Add a shim, and changes the callsites to use the shim. Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/pci-bridge.h | 15 +++++++++++++++ arch/powerpc/kernel/pci-common.c | 5 ++--- 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 278f48978bad..9d02ea6255ff 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -27,6 +27,10 @@ struct pci_controller_ops { void (*dma_bus_setup)(struct pci_bus *bus); int (*probe_mode)(struct pci_bus *); + + /* Called when pci_enable_device() is called. Returns true to + * allow assignment/enabling of the device. */ + bool (*enable_device_hook)(struct pci_dev *); }; /* @@ -310,5 +314,16 @@ static inline int pci_probe_mode(struct pci_bus *bus) return PCI_PROBE_NORMAL; } +static inline bool pcibios_enable_device_hook(struct pci_dev *dev) +{ + struct pci_controller *phb = pci_bus_to_host(dev->bus); + + if (phb->controller_ops.enable_device_hook) + return phb->controller_ops.enable_device_hook(dev); + if (ppc_md.pcibios_enable_device_hook) + return ppc_md.pcibios_enable_device_hook(dev); + return true; +} + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PCI_BRIDGE_H */ diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index b0de23c89ca5..8639e9cd65d5 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1448,9 +1448,8 @@ EXPORT_SYMBOL_GPL(pcibios_finish_adding_to_bus); int pcibios_enable_device(struct pci_dev *dev, int mask) { - if (ppc_md.pcibios_enable_device_hook) - if (!ppc_md.pcibios_enable_device_hook(dev)) - return -EINVAL; + if (!pcibios_enable_device_hook(dev)) + return -EINVAL; return pci_enable_resources(dev, mask); } -- cgit v1.2.3 From 542070baf4a0fe9de14cc2c4ca3ff1b43f14f90f Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 31 Mar 2015 16:00:46 +1100 Subject: powerpc: Create pci_controller_ops.window_alignment and shim Add pci_controller_ops.window_alignment, shadowing ppc_md.pcibios_window_alignment. Add a shim, and changes the callsites to use the shim. Here, we use pci_window_alignment, as pcibios_window_alignment is already taken. Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/pci-bridge.h | 21 +++++++++++++++++++++ arch/powerpc/kernel/pci-common.c | 10 +--------- 2 files changed, 22 insertions(+), 9 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 9d02ea6255ff..ed4f8ce803bb 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -31,6 +31,9 @@ struct pci_controller_ops { /* Called when pci_enable_device() is called. Returns true to * allow assignment/enabling of the device. */ bool (*enable_device_hook)(struct pci_dev *); + + /* Called during PCI resource reassignment */ + resource_size_t (*window_alignment)(struct pci_bus *, unsigned long type); }; /* @@ -325,5 +328,23 @@ static inline bool pcibios_enable_device_hook(struct pci_dev *dev) return true; } +static inline resource_size_t pci_window_alignment(struct pci_bus *bus, + unsigned long type) +{ + struct pci_controller *phb = pci_bus_to_host(bus); + + if (phb->controller_ops.window_alignment) + return phb->controller_ops.window_alignment(bus, type); + if (ppc_md.pcibios_window_alignment) + return ppc_md.pcibios_window_alignment(bus, type); + + /* + * PCI core will figure out the default + * alignment: 4KiB for I/O and 1MiB for + * memory window. + */ + return 1; +} + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PCI_BRIDGE_H */ diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 8639e9cd65d5..698e0328d9c3 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -109,15 +109,7 @@ void pcibios_free_controller(struct pci_controller *phb) resource_size_t pcibios_window_alignment(struct pci_bus *bus, unsigned long type) { - if (ppc_md.pcibios_window_alignment) - return ppc_md.pcibios_window_alignment(bus, type); - - /* - * PCI core will figure out the default - * alignment: 4KiB for I/O and 1MiB for - * memory window. - */ - return 1; + return pci_window_alignment(bus, type); } void pcibios_reset_secondary_bus(struct pci_dev *dev) -- cgit v1.2.3 From cd16c7ba0cc21aa1563e4b8430519b6488d0de60 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 31 Mar 2015 16:00:47 +1100 Subject: powerpc: Create pci_controller_ops.reset_secondary_bus and shim Add pci_controller_ops.reset_secondary_bus, shadowing ppc_md.pcibios_reset_secondary_bus. Add a shim, and changes the callsites to use the shim. Use pcibios_reset_secondary_bus_shim, as both pcibios_reset_secondary_bus and pci_reset_secondary_bus are already taken. Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/pci-bridge.h | 17 +++++++++++++++++ arch/powerpc/kernel/pci-common.c | 7 +------ 2 files changed, 18 insertions(+), 6 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index ed4f8ce803bb..bb34b1eebfbe 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -34,6 +34,7 @@ struct pci_controller_ops { /* Called during PCI resource reassignment */ resource_size_t (*window_alignment)(struct pci_bus *, unsigned long type); + void (*reset_secondary_bus)(struct pci_dev *dev); }; /* @@ -346,5 +347,21 @@ static inline resource_size_t pci_window_alignment(struct pci_bus *bus, return 1; } +static inline void pcibios_reset_secondary_bus_shim(struct pci_dev *dev) +{ + struct pci_controller *phb = pci_bus_to_host(dev->bus); + + if (phb->controller_ops.reset_secondary_bus) + phb->controller_ops.reset_secondary_bus(dev); + else if (ppc_md.pcibios_reset_secondary_bus) + ppc_md.pcibios_reset_secondary_bus(dev); + else + /* + * Fallback to the generic function if no + * platform-specific one is provided + */ + pci_reset_secondary_bus(dev); +} + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PCI_BRIDGE_H */ diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 698e0328d9c3..759eb1c87638 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -114,12 +114,7 @@ resource_size_t pcibios_window_alignment(struct pci_bus *bus, void pcibios_reset_secondary_bus(struct pci_dev *dev) { - if (ppc_md.pcibios_reset_secondary_bus) { - ppc_md.pcibios_reset_secondary_bus(dev); - return; - } - - pci_reset_secondary_bus(dev); + pcibios_reset_secondary_bus_shim(dev); } static resource_size_t pcibios_io_size(const struct pci_controller *hose) -- cgit v1.2.3 From 97884e00e29ad6654b8b6939bb805b9d6303427c Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 10 Apr 2015 13:15:47 +1000 Subject: powerpc: fsl_pci, swiotlb: Move controller ops from ppc_md to controller_ops Move the installation of DMA operations out of swiotlb's subsys initcall, and into the generic PCI controller operations struct. These ops are installed conditionally, based on the ppc_swiotlb_enable global. The global can be set in two places: - swiotlb_detect_4g, which is always called at the arch initcall level - setup_pci_atmu, which is called as part of the fsl_add_bridge and fsl_pci_syscore_do_resume. fsl_pci_syscore_do_resume is called late enough that any changes as a result of that call will have no effect. As such, if we test the global and set the operations as part of fsl_add_bridge, after the call to setup_pci_atmu, we can be confident that it will cover all the PCI implementations affected by the changes to dma-swiotlb.c. Signed-off-by: Daniel Axtens Acked-by: Scott Wood Signed-off-by: Michael Ellerman --- arch/powerpc/kernel/dma-swiotlb.c | 11 ++++------- arch/powerpc/sysdev/fsl_pci.c | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/dma-swiotlb.c b/arch/powerpc/kernel/dma-swiotlb.c index 735979764cd4..6e8d764ce47b 100644 --- a/arch/powerpc/kernel/dma-swiotlb.c +++ b/arch/powerpc/kernel/dma-swiotlb.c @@ -116,16 +116,13 @@ void __init swiotlb_detect_4g(void) } } -static int __init swiotlb_late_init(void) +static int __init check_swiotlb_enabled(void) { - if (ppc_swiotlb_enable) { + if (ppc_swiotlb_enable) swiotlb_print_info(); - set_pci_dma_ops(&swiotlb_dma_ops); - ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_swiotlb; - } else { + else swiotlb_free(); - } return 0; } -subsys_initcall(swiotlb_late_init); +subsys_initcall(check_swiotlb_enabled); diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c index 4b74c276e427..9a8fcf0d79d7 100644 --- a/arch/powerpc/sysdev/fsl_pci.c +++ b/arch/powerpc/sysdev/fsl_pci.c @@ -111,6 +111,18 @@ static struct pci_ops fsl_indirect_pcie_ops = #define MAX_PHYS_ADDR_BITS 40 static u64 pci64_dma_offset = 1ull << MAX_PHYS_ADDR_BITS; +#ifdef CONFIG_SWIOTLB +static void setup_swiotlb_ops(struct pci_controller *hose) +{ + if (ppc_swiotlb_enable) { + hose->controller_ops.dma_dev_setup = pci_dma_dev_setup_swiotlb; + set_pci_dma_ops(&swiotlb_dma_ops); + } +} +#else +static inline void setup_swiotlb_ops(struct pci_controller *hose) {} +#endif + static int fsl_pci_dma_set_mask(struct device *dev, u64 dma_mask) { if (!dev->dma_mask || !dma_supported(dev, dma_mask)) @@ -548,6 +560,9 @@ int fsl_add_bridge(struct platform_device *pdev, int is_primary) /* Setup PEX window registers */ setup_pci_atmu(hose); + /* Set up controller operations */ + setup_swiotlb_ops(hose); + return 0; no_bridge: -- cgit v1.2.3 From 467efc2e4fdc44e6cd4be7dd4adf01c14b3d148e Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 31 Mar 2015 16:00:56 +1100 Subject: powerpc: Remove shims for pci_controller_ops operations Remove shims, patch callsites to use pci_controller_ops versions instead. Also move back the probe mode defines, as explained in the patch for pci_probe_mode. Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/machdep.h | 14 ------ arch/powerpc/include/asm/pci-bridge.h | 84 ----------------------------------- arch/powerpc/include/asm/pci.h | 5 +++ arch/powerpc/kernel/pci-common.c | 43 ++++++++++++++---- arch/powerpc/kernel/pci-hotplug.c | 6 ++- arch/powerpc/kernel/pci_of_scan.c | 6 ++- arch/powerpc/sysdev/dart_iommu.c | 5 --- 7 files changed, 50 insertions(+), 113 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index e29f058c0903..5c19ac527a8e 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -103,9 +103,6 @@ struct machdep_calls { #endif #endif /* CONFIG_PPC64 */ - void (*pci_dma_dev_setup)(struct pci_dev *dev); - void (*pci_dma_bus_setup)(struct pci_bus *bus); - /* Platform set_dma_mask and dma_get_required_mask overrides */ int (*dma_set_mask)(struct device *dev, u64 dma_mask); u64 (*dma_get_required_mask)(struct device *dev); @@ -127,7 +124,6 @@ struct machdep_calls { /* PCI stuff */ /* Called after allocating resources */ void (*pcibios_fixup)(void); - int (*pci_probe_mode)(struct pci_bus *); void (*pci_irq_fixup)(struct pci_dev *dev); int (*pcibios_root_bridge_prepare)(struct pci_host_bridge *bridge); @@ -237,19 +233,9 @@ struct machdep_calls { /* Called for each PCI bus in the system when it's probed */ void (*pcibios_fixup_bus)(struct pci_bus *); - /* Called when pci_enable_device() is called. Returns true to - * allow assignment/enabling of the device. */ - bool (*pcibios_enable_device_hook)(struct pci_dev *); - /* Called after scan and before resource survey */ void (*pcibios_fixup_phb)(struct pci_controller *hose); - /* Called during PCI resource reassignment */ - resource_size_t (*pcibios_window_alignment)(struct pci_bus *, unsigned long type); - - /* Reset the secondary bus of bridge */ - void (*pcibios_reset_secondary_bus)(struct pci_dev *dev); - /* Called to shutdown machine specific hardware not already controlled * by other drivers. */ diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index bb34b1eebfbe..7d972bc85638 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -12,11 +12,6 @@ #include #include -/* Return values for pci_controller_ops.probe_mode function */ -#define PCI_PROBE_NONE -1 /* Don't look at this bus at all */ -#define PCI_PROBE_NORMAL 0 /* Do normal PCI probing */ -#define PCI_PROBE_DEVTREE 1 /* Instantiate from device tree */ - struct device_node; /* @@ -284,84 +279,5 @@ static inline int pcibios_vaddr_is_ioport(void __iomem *address) } #endif /* CONFIG_PCI */ -/* - * Shims to prefer pci_controller version over ppc_md where available. - */ -static inline void pci_dma_dev_setup(struct pci_dev *dev) -{ - struct pci_controller *phb = pci_bus_to_host(dev->bus); - - if (phb->controller_ops.dma_dev_setup) - phb->controller_ops.dma_dev_setup(dev); - else if (ppc_md.pci_dma_dev_setup) - ppc_md.pci_dma_dev_setup(dev); -} - -static inline void pci_dma_bus_setup(struct pci_bus *bus) -{ - struct pci_controller *phb = pci_bus_to_host(bus); - - if (phb->controller_ops.dma_bus_setup) - phb->controller_ops.dma_bus_setup(bus); - else if (ppc_md.pci_dma_bus_setup) - ppc_md.pci_dma_bus_setup(bus); -} - -static inline int pci_probe_mode(struct pci_bus *bus) -{ - struct pci_controller *phb = pci_bus_to_host(bus); - - if (phb->controller_ops.probe_mode) - return phb->controller_ops.probe_mode(bus); - if (ppc_md.pci_probe_mode) - return ppc_md.pci_probe_mode(bus); - return PCI_PROBE_NORMAL; -} - -static inline bool pcibios_enable_device_hook(struct pci_dev *dev) -{ - struct pci_controller *phb = pci_bus_to_host(dev->bus); - - if (phb->controller_ops.enable_device_hook) - return phb->controller_ops.enable_device_hook(dev); - if (ppc_md.pcibios_enable_device_hook) - return ppc_md.pcibios_enable_device_hook(dev); - return true; -} - -static inline resource_size_t pci_window_alignment(struct pci_bus *bus, - unsigned long type) -{ - struct pci_controller *phb = pci_bus_to_host(bus); - - if (phb->controller_ops.window_alignment) - return phb->controller_ops.window_alignment(bus, type); - if (ppc_md.pcibios_window_alignment) - return ppc_md.pcibios_window_alignment(bus, type); - - /* - * PCI core will figure out the default - * alignment: 4KiB for I/O and 1MiB for - * memory window. - */ - return 1; -} - -static inline void pcibios_reset_secondary_bus_shim(struct pci_dev *dev) -{ - struct pci_controller *phb = pci_bus_to_host(dev->bus); - - if (phb->controller_ops.reset_secondary_bus) - phb->controller_ops.reset_secondary_bus(dev); - else if (ppc_md.pcibios_reset_secondary_bus) - ppc_md.pcibios_reset_secondary_bus(dev); - else - /* - * Fallback to the generic function if no - * platform-specific one is provided - */ - pci_reset_secondary_bus(dev); -} - #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PCI_BRIDGE_H */ diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 8745067ac702..4aef8d660999 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -22,6 +22,11 @@ #include +/* Return values for pci_controller_ops.probe_mode function */ +#define PCI_PROBE_NONE -1 /* Don't look at this bus at all */ +#define PCI_PROBE_NORMAL 0 /* Do normal PCI probing */ +#define PCI_PROBE_DEVTREE 1 /* Instantiate from device tree */ + #define PCIBIOS_MIN_IO 0x1000 #define PCIBIOS_MIN_MEM 0x10000000 diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 759eb1c87638..9052b4fbc41f 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -109,12 +109,29 @@ void pcibios_free_controller(struct pci_controller *phb) resource_size_t pcibios_window_alignment(struct pci_bus *bus, unsigned long type) { - return pci_window_alignment(bus, type); + struct pci_controller *phb = pci_bus_to_host(bus); + + if (phb->controller_ops.window_alignment) + return phb->controller_ops.window_alignment(bus, type); + + /* + * PCI core will figure out the default + * alignment: 4KiB for I/O and 1MiB for + * memory window. + */ + return 1; } void pcibios_reset_secondary_bus(struct pci_dev *dev) { - pcibios_reset_secondary_bus_shim(dev); + struct pci_controller *phb = pci_bus_to_host(dev->bus); + + if (phb->controller_ops.reset_secondary_bus) { + phb->controller_ops.reset_secondary_bus(dev); + return; + } + + pci_reset_secondary_bus(dev); } static resource_size_t pcibios_io_size(const struct pci_controller *hose) @@ -929,6 +946,8 @@ static void pcibios_fixup_bridge(struct pci_bus *bus) void pcibios_setup_bus_self(struct pci_bus *bus) { + struct pci_controller *phb; + /* Fix up the bus resources for P2P bridges */ if (bus->self != NULL) pcibios_fixup_bridge(bus); @@ -940,11 +959,14 @@ void pcibios_setup_bus_self(struct pci_bus *bus) ppc_md.pcibios_fixup_bus(bus); /* Setup bus DMA mappings */ - pci_dma_bus_setup(bus); + phb = pci_bus_to_host(bus); + if (phb->controller_ops.dma_bus_setup) + phb->controller_ops.dma_bus_setup(bus); } static void pcibios_setup_device(struct pci_dev *dev) { + struct pci_controller *phb; /* Fixup NUMA node as it may not be setup yet by the generic * code and is needed by the DMA init */ @@ -955,7 +977,9 @@ static void pcibios_setup_device(struct pci_dev *dev) set_dma_offset(&dev->dev, PCI_DRAM_OFFSET); /* Additional platform DMA/iommu setup */ - pci_dma_dev_setup(dev); + phb = pci_bus_to_host(dev->bus); + if (phb->controller_ops.dma_dev_setup) + phb->controller_ops.dma_dev_setup(dev); /* Read default IRQs and fixup if necessary */ pci_read_irq_line(dev); @@ -1435,8 +1459,11 @@ EXPORT_SYMBOL_GPL(pcibios_finish_adding_to_bus); int pcibios_enable_device(struct pci_dev *dev, int mask) { - if (!pcibios_enable_device_hook(dev)) - return -EINVAL; + struct pci_controller *phb = pci_bus_to_host(dev->bus); + + if (phb->controller_ops.enable_device_hook) + if (!phb->controller_ops.enable_device_hook(dev)) + return -EINVAL; return pci_enable_resources(dev, mask); } @@ -1608,8 +1635,8 @@ void pcibios_scan_phb(struct pci_controller *hose) /* Get probe mode and perform scan */ mode = PCI_PROBE_NORMAL; - if (node) - mode = pci_probe_mode(bus); + if (node && hose->controller_ops.probe_mode) + mode = hose->controller_ops.probe_mode(bus); pr_debug(" probe mode: %d\n", mode); if (mode == PCI_PROBE_DEVTREE) of_scan_bus(node, bus); diff --git a/arch/powerpc/kernel/pci-hotplug.c b/arch/powerpc/kernel/pci-hotplug.c index 27116b1b2d14..7ed85a69a9c2 100644 --- a/arch/powerpc/kernel/pci-hotplug.c +++ b/arch/powerpc/kernel/pci-hotplug.c @@ -73,12 +73,16 @@ void pcibios_add_pci_devices(struct pci_bus * bus) { int slotno, mode, pass, max; struct pci_dev *dev; + struct pci_controller *phb; struct device_node *dn = pci_bus_to_OF_node(bus); eeh_add_device_tree_early(PCI_DN(dn)); + phb = pci_bus_to_host(bus); + mode = PCI_PROBE_NORMAL; - mode = pci_probe_mode(bus); + if (phb->controller_ops.probe_mode) + mode = phb->controller_ops.probe_mode(bus); if (mode == PCI_PROBE_DEVTREE) { /* use ofdt-based probe */ diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c index 4ee63c4f077e..42e02a2d570b 100644 --- a/arch/powerpc/kernel/pci_of_scan.c +++ b/arch/powerpc/kernel/pci_of_scan.c @@ -207,6 +207,7 @@ void of_scan_pci_bridge(struct pci_dev *dev) { struct device_node *node = dev->dev.of_node; struct pci_bus *bus; + struct pci_controller *phb; const __be32 *busrange, *ranges; int len, i, mode; struct pci_bus_region region; @@ -286,8 +287,11 @@ void of_scan_pci_bridge(struct pci_dev *dev) bus->number); pr_debug(" bus name: %s\n", bus->name); + phb = pci_bus_to_host(bus); + mode = PCI_PROBE_NORMAL; - mode = pci_probe_mode(bus); + if (phb->controller_ops.probe_mode) + mode = phb->controller_ops.probe_mode(bus); pr_debug(" probe mode: %d\n", mode); if (mode == PCI_PROBE_DEVTREE) diff --git a/arch/powerpc/sysdev/dart_iommu.c b/arch/powerpc/sysdev/dart_iommu.c index 120e96a9e2cb..87b80009bc9f 100644 --- a/arch/powerpc/sysdev/dart_iommu.c +++ b/arch/powerpc/sysdev/dart_iommu.c @@ -398,9 +398,6 @@ void __init iommu_init_early_dart(struct pci_controller_ops *controller_ops) if (controller_ops) { controller_ops->dma_dev_setup = pci_dma_dev_setup_dart; controller_ops->dma_bus_setup = pci_dma_bus_setup_dart; - } else { - ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_dart; - ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_dart; } /* Setup pci_dma ops */ set_pci_dma_ops(&dma_iommu_ops); @@ -412,8 +409,6 @@ void __init iommu_init_early_dart(struct pci_controller_ops *controller_ops) controller_ops->dma_dev_setup = NULL; controller_ops->dma_bus_setup = NULL; } - ppc_md.pci_dma_dev_setup = NULL; - ppc_md.pci_dma_bus_setup = NULL; /* Setup pci_dma ops */ set_pci_dma_ops(&dma_direct_ops); -- cgit v1.2.3 From feba40362b11341bee6d8ed58d54b896abbd9f84 Mon Sep 17 00:00:00 2001 From: Sam bobroff Date: Fri, 10 Apr 2015 14:16:47 +1000 Subject: powerpc/tm: Abort syscalls in active transactions This patch changes the syscall handler to doom (tabort) active transactions when a syscall is made and return immediately without performing the syscall. Currently, the system call instruction automatically suspends an active transaction which causes side effects to persist when an active transaction fails. This does change the kernel's behaviour, but in a way that was documented as unsupported. It doesn't reduce functionality because syscalls will still be performed after tsuspend. It also provides a consistent interface and makes the behaviour of user code substantially the same across powerpc and platforms that do not support suspended transactions (e.g. x86 and s390). Performance measurements using http://ozlabs.org/~anton/junkcode/null_syscall.c indicate the cost of a system call increases by about 0.5%. Signed-off-by: Sam Bobroff Acked-By: Michael Neuling Signed-off-by: Michael Ellerman --- Documentation/powerpc/transactional_memory.txt | 32 +++++++++++++------------- arch/powerpc/include/uapi/asm/tm.h | 2 +- arch/powerpc/kernel/entry_64.S | 19 +++++++++++++++ 3 files changed, 36 insertions(+), 17 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/Documentation/powerpc/transactional_memory.txt b/Documentation/powerpc/transactional_memory.txt index 9791e98ab49c..98b39af5254f 100644 --- a/Documentation/powerpc/transactional_memory.txt +++ b/Documentation/powerpc/transactional_memory.txt @@ -74,22 +74,23 @@ Causes of transaction aborts Syscalls ======== -Performing syscalls from within transaction is not recommended, and can lead -to unpredictable results. +Syscalls made from within an active transaction will not be performed and the +transaction will be doomed by the kernel with the failure code TM_CAUSE_SYSCALL +| TM_CAUSE_PERSISTENT. -Syscalls do not by design abort transactions, but beware: The kernel code will -not be running in transactional state. The effect of syscalls will always -remain visible, but depending on the call they may abort your transaction as a -side-effect, read soon-to-be-aborted transactional data that should not remain -invisible, etc. If you constantly retry a transaction that constantly aborts -itself by calling a syscall, you'll have a livelock & make no progress. +Syscalls made from within a suspended transaction are performed as normal and +the transaction is not explicitly doomed by the kernel. However, what the +kernel does to perform the syscall may result in the transaction being doomed +by the hardware. The syscall is performed in suspended mode so any side +effects will be persistent, independent of transaction success or failure. No +guarantees are provided by the kernel about which syscalls will affect +transaction success. -Simple syscalls (e.g. sigprocmask()) "could" be OK. Even things like write() -from, say, printf() should be OK as long as the kernel does not access any -memory that was accessed transactionally. - -Consider any syscalls that happen to work as debug-only -- not recommended for -production use. Best to queue them up till after the transaction is over. +Care must be taken when relying on syscalls to abort during active transactions +if the calls are made via a library. Libraries may cache values (which may +give the appearance of success) or perform operations that cause transaction +failure before entering the kernel (which may produce different failure codes). +Examples are glibc's getpid() and lazy symbol resolution. Signals @@ -176,8 +177,7 @@ kernel aborted a transaction: TM_CAUSE_RESCHED Thread was rescheduled. TM_CAUSE_TLBI Software TLB invalide. TM_CAUSE_FAC_UNAV FP/VEC/VSX unavailable trap. - TM_CAUSE_SYSCALL Currently unused; future syscalls that must abort - transactions for consistency will use this. + TM_CAUSE_SYSCALL Syscall from active transaction. TM_CAUSE_SIGNAL Signal delivered. TM_CAUSE_MISC Currently unused. TM_CAUSE_ALIGNMENT Alignment fault. diff --git a/arch/powerpc/include/uapi/asm/tm.h b/arch/powerpc/include/uapi/asm/tm.h index 5d836b7c1176..5047659815a5 100644 --- a/arch/powerpc/include/uapi/asm/tm.h +++ b/arch/powerpc/include/uapi/asm/tm.h @@ -11,7 +11,7 @@ #define TM_CAUSE_RESCHED 0xde #define TM_CAUSE_TLBI 0xdc #define TM_CAUSE_FAC_UNAV 0xda -#define TM_CAUSE_SYSCALL 0xd8 /* future use */ +#define TM_CAUSE_SYSCALL 0xd8 #define TM_CAUSE_MISC 0xd6 /* future use */ #define TM_CAUSE_SIGNAL 0xd4 #define TM_CAUSE_ALIGNMENT 0xd2 diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index afbc20019c2e..8ca9434c40e6 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -34,6 +34,7 @@ #include #include #include +#include /* * System calls. @@ -145,6 +146,24 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_SPLPAR) andi. r11,r10,_TIF_SYSCALL_DOTRACE bne syscall_dotrace .Lsyscall_dotrace_cont: +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +BEGIN_FTR_SECTION + b 1f +END_FTR_SECTION_IFCLR(CPU_FTR_TM) + extrdi. r11, r12, 1, (63-MSR_TS_T_LG) /* transaction active? */ + beq+ 1f + + /* Doom the transaction and don't perform the syscall: */ + mfmsr r11 + li r12, 1 + rldimi r11, r12, MSR_TM_LG, 63-MSR_TM_LG + mtmsrd r11, 0 + li r11, (TM_CAUSE_SYSCALL|TM_CAUSE_PERSISTENT) + TABORT(R11) + + b .Lsyscall_exit +1: +#endif cmpldi 0,r0,NR_syscalls bge- syscall_enosys -- cgit v1.2.3 From c54b2bf1b5e99760d53ea0376e96a046f93df6ae Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 9 Apr 2015 12:52:56 +1000 Subject: powerpc: Add ppc64 hard lockup detector support The hard lockup detector uses a PMU event as a periodic NMI to detect if we are stuck (where stuck means no timer interrupts have occurred). Ben's rework of the ppc64 soft disable code has made ppc64 PMU exceptions a partial NMI. They can get disabled if an external interrupt comes in, but otherwise PMU interrupts will fire in interrupt disabled regions. We disable the hard lockup detector by default for a few reasons: - It breaks userspace event based branches on POWER8. - It is likely to produce false positives on KVM guests. - Since PMCs can only count to 2^31, counting cycles means we might take multiple PMU exceptions per second per hardware thread even if our hard lockup timeout is 10 seconds. It can be enabled via a boot option, or via procfs. Signed-off-by: Anton Blanchard Signed-off-by: Michael Ellerman --- arch/powerpc/Kconfig | 1 + arch/powerpc/include/asm/nmi.h | 4 ++++ arch/powerpc/kernel/setup_64.c | 20 ++++++++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 arch/powerpc/include/asm/nmi.h (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index c102668b4225..716c9e6a3160 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -152,6 +152,7 @@ config PPC select DCACHE_WORD_ACCESS if PPC64 && CPU_LITTLE_ENDIAN select NO_BOOTMEM select HAVE_GENERIC_RCU_GUP + select HAVE_PERF_EVENTS_NMI if PPC64 config GENERIC_CSUM def_bool CPU_LITTLE_ENDIAN diff --git a/arch/powerpc/include/asm/nmi.h b/arch/powerpc/include/asm/nmi.h new file mode 100644 index 000000000000..ff1ccb375e60 --- /dev/null +++ b/arch/powerpc/include/asm/nmi.h @@ -0,0 +1,4 @@ +#ifndef _ASM_NMI_H +#define _ASM_NMI_H + +#endif /* _ASM_NMI_H */ diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 49f553bbb360..7551e5692597 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -779,3 +780,22 @@ unsigned long memory_block_size_bytes(void) struct ppc_pci_io ppc_pci_io; EXPORT_SYMBOL(ppc_pci_io); #endif + +#ifdef CONFIG_HARDLOCKUP_DETECTOR +u64 hw_nmi_get_sample_period(int watchdog_thresh) +{ + return ppc_proc_freq * watchdog_thresh; +} + +/* + * The hardlockup detector breaks PMU event based branches and is likely + * to get false positives in KVM guests, so disable it by default. + */ +static int __init disable_hardlockup_detector(void) +{ + watchdog_enable_hardlockup_detector(false); + + return 0; +} +early_initcall(disable_hardlockup_detector); +#endif -- cgit v1.2.3 From f7e9e358362557c3aa2c1ec47490f29fe880a09e Mon Sep 17 00:00:00 2001 From: Dave Olson Date: Thu, 2 Apr 2015 21:28:45 -0700 Subject: powerpc: Fix missing L2 cache size in /sys/devices/system/cpu This problem appears to have been introduced in 2.6.29 by commit 93197a36a9c1 "Rewrite sysfs processor cache info code". This caused lscpu to error out on at least e500v2 devices, eg: error: cannot open /sys/devices/system/cpu/cpu0/cache/index2/size: No such file or directory Some embedded powerpc systems use cache-size in DTS for the unified L2 cache size, not d-cache-size, so we need to allow for both DTS names. Added a new CACHE_TYPE_UNIFIED_D cache_type_info structure to handle this. Fixes: 93197a36a9c1 ("powerpc: Rewrite sysfs processor cache info code") Signed-off-by: Dave Olson Signed-off-by: Michael Ellerman --- arch/powerpc/kernel/cacheinfo.c | 44 +++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c index ae77b7e59889..c641983bbdd6 100644 --- a/arch/powerpc/kernel/cacheinfo.c +++ b/arch/powerpc/kernel/cacheinfo.c @@ -61,11 +61,21 @@ struct cache_type_info { }; /* These are used to index the cache_type_info array. */ -#define CACHE_TYPE_UNIFIED 0 -#define CACHE_TYPE_INSTRUCTION 1 -#define CACHE_TYPE_DATA 2 +#define CACHE_TYPE_UNIFIED 0 /* cache-size, cache-block-size, etc. */ +#define CACHE_TYPE_UNIFIED_D 1 /* d-cache-size, d-cache-block-size, etc */ +#define CACHE_TYPE_INSTRUCTION 2 +#define CACHE_TYPE_DATA 3 static const struct cache_type_info cache_type_info[] = { + { + /* Embedded systems that use cache-size, cache-block-size, + * etc. for the Unified (typically L2) cache. */ + .name = "Unified", + .size_prop = "cache-size", + .line_size_props = { "cache-line-size", + "cache-block-size", }, + .nr_sets_prop = "cache-sets", + }, { /* PowerPC Processor binding says the [di]-cache-* * must be equal on unified caches, so just use @@ -293,7 +303,8 @@ static struct cache *cache_find_first_sibling(struct cache *cache) { struct cache *iter; - if (cache->type == CACHE_TYPE_UNIFIED) + if (cache->type == CACHE_TYPE_UNIFIED || + cache->type == CACHE_TYPE_UNIFIED_D) return cache; list_for_each_entry(iter, &cache_list, list) @@ -324,16 +335,29 @@ static bool cache_node_is_unified(const struct device_node *np) return of_get_property(np, "cache-unified", NULL); } -static struct cache *cache_do_one_devnode_unified(struct device_node *node, - int level) +/* + * Unified caches can have two different sets of tags. Most embedded + * use cache-size, etc. for the unified cache size, but open firmware systems + * use d-cache-size, etc. Check on initialization for which type we have, and + * return the appropriate structure type. Assume it's embedded if it isn't + * open firmware. If it's yet a 3rd type, then there will be missing entries + * in /sys/devices/system/cpu/cpu0/cache/index2/, and this code will need + * to be extended further. + */ +static int cache_is_unified_d(const struct device_node *np) { - struct cache *cache; + return of_get_property(np, + cache_type_info[CACHE_TYPE_UNIFIED_D].size_prop, NULL) ? + CACHE_TYPE_UNIFIED_D : CACHE_TYPE_UNIFIED; +} +/* + */ +static struct cache *cache_do_one_devnode_unified(struct device_node *node, int level) +{ pr_debug("creating L%d ucache for %s\n", level, node->full_name); - cache = new_cache(CACHE_TYPE_UNIFIED, level, node); - - return cache; + return new_cache(cache_is_unified_d(node), level, node); } static struct cache *cache_do_one_devnode_split(struct device_node *node, -- cgit v1.2.3 From 89a51df5ab1d38b257300b8ac940bbac3bb0eb9b Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 14 Apr 2015 16:49:05 +1000 Subject: powerpc/eeh: Fix crash in eeh_add_device_early() on Cell The recent change to the EEH probing causes a crash on Cell because eeh_ops is NULL. Check if EEH is enabled and if not bail out. Fixes: ff57b454ddb9 ("powerpc/eeh: Do probe on pci_dn") Signed-off-by: Michael Ellerman --- arch/powerpc/kernel/eeh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 76253eb146be..a4c62eb0ee48 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -1053,7 +1053,7 @@ void eeh_add_device_early(struct pci_dn *pdn) struct pci_controller *phb; struct eeh_dev *edev = pdn_to_eeh_dev(pdn); - if (!edev) + if (!edev || !eeh_enabled()) return; /* USB Bus children of PCI devices will not have BUID's */ -- cgit v1.2.3 From 691e95fd7396905a38d98919e9c150dbc3ea21a3 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Mon, 30 Mar 2015 10:41:03 +0530 Subject: powerpc/mm/thp: Make page table walk safe against thp split/collapse We can disable a THP split or a hugepage collapse by disabling irq. We do send IPI to all the cpus in the early part of split/collapse, and disabling local irq ensure we don't make progress with split/collapse. If the THP is getting split we return NULL from find_linux_pte_or_hugepte(). For all the current callers it should be ok. We need to be careful if we want to use returned pte_t pointer outside the irq disabled region. W.r.t to THP split, the pfn remains the same, but then a hugepage collapse will result in a pfn change. There are few steps we can take to avoid a hugepage collapse.One way is to take page reference inside the irq disable region. Other option is to take mmap_sem so that a parallel collapse will not happen. We can also disable collapse by taking pmd_lock. Another method used by kvm subsystem is to check whether we had a mmu_notifer update in between using mmu_notifier_retry(). Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman --- arch/powerpc/include/asm/pgtable.h | 11 ++++++++++- arch/powerpc/kernel/eeh.c | 6 ++++-- arch/powerpc/kernel/io-workarounds.c | 10 +++++----- arch/powerpc/kvm/book3s_64_mmu_hv.c | 5 +++-- arch/powerpc/kvm/book3s_hv_rm_mmu.c | 30 +++++++++++++++++++++++------- arch/powerpc/kvm/e500_mmu_host.c | 14 ++++++++++++-- arch/powerpc/mm/hash_utils_64.c | 2 +- arch/powerpc/mm/hugetlbpage.c | 22 ++++++++++++++++------ arch/powerpc/perf/callchain.c | 24 ++++++++++++++---------- 9 files changed, 88 insertions(+), 36 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/pgtable.h b/arch/powerpc/include/asm/pgtable.h index 92fe01c355a9..11a38635dd65 100644 --- a/arch/powerpc/include/asm/pgtable.h +++ b/arch/powerpc/include/asm/pgtable.h @@ -247,8 +247,17 @@ extern int gup_hugepte(pte_t *ptep, unsigned long sz, unsigned long addr, #define pmd_large(pmd) 0 #define has_transparent_hugepage() 0 #endif -pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, +pte_t *__find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, unsigned *shift); +static inline pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, + unsigned *shift) +{ + if (!arch_irqs_disabled()) { + pr_info("%s called with irq enabled\n", __func__); + dump_stack(); + } + return __find_linux_pte_or_hugepte(pgdir, ea, shift); +} #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index a4c62eb0ee48..44b480e3a5af 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -334,9 +334,11 @@ static inline unsigned long eeh_token_to_phys(unsigned long token) int hugepage_shift; /* - * We won't find hugepages here, iomem + * We won't find hugepages here(this is iomem). Hence we are not + * worried about _PAGE_SPLITTING/collapse. Also we will not hit + * page table free, because of init_mm. */ - ptep = find_linux_pte_or_hugepte(init_mm.pgd, token, &hugepage_shift); + ptep = __find_linux_pte_or_hugepte(init_mm.pgd, token, &hugepage_shift); if (!ptep) return token; WARN_ON(hugepage_shift); diff --git a/arch/powerpc/kernel/io-workarounds.c b/arch/powerpc/kernel/io-workarounds.c index 24b968f8e4d8..63d9cc4d7366 100644 --- a/arch/powerpc/kernel/io-workarounds.c +++ b/arch/powerpc/kernel/io-workarounds.c @@ -71,15 +71,15 @@ struct iowa_bus *iowa_mem_find_bus(const PCI_IO_ADDR addr) vaddr = (unsigned long)PCI_FIX_ADDR(addr); if (vaddr < PHB_IO_BASE || vaddr >= PHB_IO_END) return NULL; - - ptep = find_linux_pte_or_hugepte(init_mm.pgd, vaddr, + /* + * We won't find huge pages here (iomem). Also can't hit + * a page table free due to init_mm + */ + ptep = __find_linux_pte_or_hugepte(init_mm.pgd, vaddr, &hugepage_shift); if (ptep == NULL) paddr = 0; else { - /* - * we don't have hugepages backing iomem - */ WARN_ON(hugepage_shift); paddr = pte_pfn(*ptep) << PAGE_SHIFT; } diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c index 534acb3c6c3d..26df3864d85a 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c @@ -539,12 +539,13 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, if (!writing && hpte_is_writable(r)) { unsigned int hugepage_shift; pte_t *ptep, pte; + unsigned long flags; /* * We need to protect against page table destruction * while looking up and updating the pte. */ - rcu_read_lock_sched(); + local_irq_save(flags); ptep = find_linux_pte_or_hugepte(current->mm->pgd, hva, &hugepage_shift); if (ptep) { @@ -553,7 +554,7 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, if (pte_write(pte)) write_ok = 1; } - rcu_read_unlock_sched(); + local_irq_restore(flags); } } diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c index 73e083cb9f7e..f559b25de173 100644 --- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c +++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c @@ -26,11 +26,14 @@ static void *real_vmalloc_addr(void *x) { unsigned long addr = (unsigned long) x; pte_t *p; - - p = find_linux_pte_or_hugepte(swapper_pg_dir, addr, NULL); + /* + * assume we don't have huge pages in vmalloc space... + * So don't worry about THP collapse/split. Called + * Only in realmode, hence won't need irq_save/restore. + */ + p = __find_linux_pte_or_hugepte(swapper_pg_dir, addr, NULL); if (!p || !pte_present(*p)) return NULL; - /* assume we don't have huge pages in vmalloc space... */ addr = (pte_pfn(*p) << PAGE_SHIFT) | (addr & ~PAGE_MASK); return __va(addr); } @@ -153,7 +156,7 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags, pte_t *ptep; unsigned int writing; unsigned long mmu_seq; - unsigned long rcbits; + unsigned long rcbits, irq_flags = 0; psize = hpte_page_size(pteh, ptel); if (!psize) @@ -189,7 +192,16 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags, /* Translate to host virtual address */ hva = __gfn_to_hva_memslot(memslot, gfn); - ptep = find_linux_pte_or_hugepte(pgdir, hva, &hpage_shift); + /* + * If we had a page table table change after lookup, we would + * retry via mmu_notifier_retry. + */ + if (realmode) + ptep = __find_linux_pte_or_hugepte(pgdir, hva, &hpage_shift); + else { + local_irq_save(irq_flags); + ptep = find_linux_pte_or_hugepte(pgdir, hva, &hpage_shift); + } if (ptep) { pte_t pte; unsigned int host_pte_size; @@ -202,9 +214,11 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags, * We should always find the guest page size * to <= host page size, if host is using hugepage */ - if (host_pte_size < psize) + if (host_pte_size < psize) { + if (!realmode) + local_irq_restore(flags); return H_PARAMETER; - + } pte = kvmppc_read_update_linux_pte(ptep, writing, hpage_shift); if (pte_present(pte) && !pte_protnone(pte)) { if (writing && !pte_write(pte)) @@ -216,6 +230,8 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags, pa |= gpa & ~PAGE_MASK; } } + if (!realmode) + local_irq_restore(irq_flags); ptel &= ~(HPTE_R_PP0 - psize); ptel |= pa; diff --git a/arch/powerpc/kvm/e500_mmu_host.c b/arch/powerpc/kvm/e500_mmu_host.c index a1f5b0d4b1d6..4d33e199edcc 100644 --- a/arch/powerpc/kvm/e500_mmu_host.c +++ b/arch/powerpc/kvm/e500_mmu_host.c @@ -338,6 +338,7 @@ static inline int kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500, pte_t *ptep; unsigned int wimg = 0; pgd_t *pgdir; + unsigned long flags; /* used to check for invalidations in progress */ mmu_seq = kvm->mmu_notifier_seq; @@ -468,14 +469,23 @@ static inline int kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500, pgdir = vcpu_e500->vcpu.arch.pgdir; + /* + * We are just looking at the wimg bits, so we don't + * care much about the trans splitting bit. + * We are holding kvm->mmu_lock so a notifier invalidate + * can't run hence pfn won't change. + */ + local_irq_save(flags); ptep = find_linux_pte_or_hugepte(pgdir, hva, NULL); if (ptep) { pte_t pte = READ_ONCE(*ptep); - if (pte_present(pte)) + if (pte_present(pte)) { wimg = (pte_val(pte) >> PTE_WIMGE_SHIFT) & MAS2_WIMGE_MASK; - else { + local_irq_restore(flags); + } else { + local_irq_restore(flags); pr_err_ratelimited("%s: pte not present: gfn %lx,pfn %lx\n", __func__, (long)gfn, pfn); ret = -EINVAL; diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index 2c2022d16059..444f7a5b859b 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -1066,7 +1066,7 @@ int hash_page_mm(struct mm_struct *mm, unsigned long ea, #endif /* CONFIG_PPC_64K_PAGES */ /* Get PTE and page size from page tables */ - ptep = find_linux_pte_or_hugepte(pgdir, ea, &hugeshift); + ptep = __find_linux_pte_or_hugepte(pgdir, ea, &hugeshift); if (ptep == NULL || !pte_present(*ptep)) { DBG_LOW(" no PTE !\n"); rc = 1; diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 2cb278a2f658..a9dbb27ca887 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -109,7 +109,7 @@ int pgd_huge(pgd_t pgd) pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) { /* Only called for hugetlbfs pages, hence can ignore THP */ - return find_linux_pte_or_hugepte(mm->pgd, addr, NULL); + return __find_linux_pte_or_hugepte(mm->pgd, addr, NULL); } static int __hugepte_alloc(struct mm_struct *mm, hugepd_t *hpdp, @@ -682,28 +682,35 @@ void hugetlb_free_pgd_range(struct mmu_gather *tlb, } while (addr = next, addr != end); } +/* + * We are holding mmap_sem, so a parallel huge page collapse cannot run. + * To prevent hugepage split, disable irq. + */ struct page * follow_huge_addr(struct mm_struct *mm, unsigned long address, int write) { pte_t *ptep; struct page *page; unsigned shift; - unsigned long mask; + unsigned long mask, flags; /* * Transparent hugepages are handled by generic code. We can skip them * here. */ + local_irq_save(flags); ptep = find_linux_pte_or_hugepte(mm->pgd, address, &shift); /* Verify it is a huge page else bail. */ - if (!ptep || !shift || pmd_trans_huge(*(pmd_t *)ptep)) + if (!ptep || !shift || pmd_trans_huge(*(pmd_t *)ptep)) { + local_irq_restore(flags); return ERR_PTR(-EINVAL); - + } mask = (1UL << shift) - 1; page = pte_page(*ptep); if (page) page += (address & mask) / PAGE_SIZE; + local_irq_restore(flags); return page; } @@ -950,9 +957,12 @@ void flush_dcache_icache_hugepage(struct page *page) * * So long as we atomically load page table pointers we are safe against teardown, * we can follow the address down to the the page and take a ref on it. + * This function need to be called with interrupts disabled. We use this variant + * when we have MSR[EE] = 0 but the paca->soft_enabled = 1 */ -pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, unsigned *shift) +pte_t *__find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, + unsigned *shift) { pgd_t pgd, *pgdp; pud_t pud, *pudp; @@ -1031,7 +1041,7 @@ out: *shift = pdshift; return ret_pte; } -EXPORT_SYMBOL_GPL(find_linux_pte_or_hugepte); +EXPORT_SYMBOL_GPL(__find_linux_pte_or_hugepte); int gup_hugepte(pte_t *ptep, unsigned long sz, unsigned long addr, unsigned long end, int write, struct page **pages, int *nr) diff --git a/arch/powerpc/perf/callchain.c b/arch/powerpc/perf/callchain.c index ead55351b254..ff09cde20cd2 100644 --- a/arch/powerpc/perf/callchain.c +++ b/arch/powerpc/perf/callchain.c @@ -111,41 +111,45 @@ perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs) * interrupt context, so if the access faults, we read the page tables * to find which page (if any) is mapped and access it directly. */ -static int read_user_stack_slow(void __user *ptr, void *ret, int nb) +static int read_user_stack_slow(void __user *ptr, void *buf, int nb) { + int ret = -EFAULT; pgd_t *pgdir; pte_t *ptep, pte; unsigned shift; unsigned long addr = (unsigned long) ptr; unsigned long offset; - unsigned long pfn; + unsigned long pfn, flags; void *kaddr; pgdir = current->mm->pgd; if (!pgdir) return -EFAULT; + local_irq_save(flags); ptep = find_linux_pte_or_hugepte(pgdir, addr, &shift); + if (!ptep) + goto err_out; if (!shift) shift = PAGE_SHIFT; /* align address to page boundary */ offset = addr & ((1UL << shift) - 1); - addr -= offset; - if (ptep == NULL) - return -EFAULT; - pte = *ptep; + pte = READ_ONCE(*ptep); if (!pte_present(pte) || !(pte_val(pte) & _PAGE_USER)) - return -EFAULT; + goto err_out; pfn = pte_pfn(pte); if (!page_is_ram(pfn)) - return -EFAULT; + goto err_out; /* no highmem to worry about here */ kaddr = pfn_to_kaddr(pfn); - memcpy(ret, kaddr + offset, nb); - return 0; + memcpy(buf, kaddr + offset, nb); + ret = 0; +err_out: + local_irq_restore(flags); + return ret; } static int read_user_stack_64(unsigned long __user *ptr, unsigned long *ret) -- cgit v1.2.3 From b6c295df3131c6fa25f8f29625ee0609506150ad Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 28 Mar 2015 14:21:02 +1100 Subject: KVM: PPC: Book3S HV: Accumulate timing information for real-mode code This reads the timebase at various points in the real-mode guest entry/exit code and uses that to accumulate total, minimum and maximum time spent in those parts of the code. Currently these times are accumulated per vcpu in 5 parts of the code: * rm_entry - time taken from the start of kvmppc_hv_entry() until just before entering the guest. * rm_intr - time from when we take a hypervisor interrupt in the guest until we either re-enter the guest or decide to exit to the host. This includes time spent handling hcalls in real mode. * rm_exit - time from when we decide to exit the guest until the return from kvmppc_hv_entry(). * guest - time spend in the guest * cede - time spent napping in real mode due to an H_CEDE hcall while other threads in the same vcore are active. These times are exposed in debugfs in a directory per vcpu that contains a file called "timings". This file contains one line for each of the 5 timings above, with the name followed by a colon and 4 numbers, which are the count (number of times the code has been executed), the total time, the minimum time, and the maximum time, all in nanoseconds. The overhead of the extra code amounts to about 30ns for an hcall that is handled in real mode (e.g. H_SET_DABR), which is about 25%. Since production environments may not wish to incur this overhead, the new code is conditional on a new config symbol, CONFIG_KVM_BOOK3S_HV_EXIT_TIMING. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf --- arch/powerpc/include/asm/kvm_host.h | 21 +++++ arch/powerpc/include/asm/time.h | 3 + arch/powerpc/kernel/asm-offsets.c | 13 +++ arch/powerpc/kernel/time.c | 6 ++ arch/powerpc/kvm/Kconfig | 14 +++ arch/powerpc/kvm/book3s_hv.c | 150 ++++++++++++++++++++++++++++++++ arch/powerpc/kvm/book3s_hv_rmhandlers.S | 141 +++++++++++++++++++++++++++++- 7 files changed, 346 insertions(+), 2 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index f1d0bbc0f079..d2068bba9059 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -369,6 +369,14 @@ struct kvmppc_slb { u8 base_page_size; /* MMU_PAGE_xxx */ }; +/* Struct used to accumulate timing information in HV real mode code */ +struct kvmhv_tb_accumulator { + u64 seqcount; /* used to synchronize access, also count * 2 */ + u64 tb_total; /* total time in timebase ticks */ + u64 tb_min; /* min time */ + u64 tb_max; /* max time */ +}; + # ifdef CONFIG_PPC_FSL_BOOK3E #define KVMPPC_BOOKE_IAC_NUM 2 #define KVMPPC_BOOKE_DAC_NUM 2 @@ -657,6 +665,19 @@ struct kvm_vcpu_arch { u32 emul_inst; #endif + +#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING + struct kvmhv_tb_accumulator *cur_activity; /* What we're timing */ + u64 cur_tb_start; /* when it started */ + struct kvmhv_tb_accumulator rm_entry; /* real-mode entry code */ + struct kvmhv_tb_accumulator rm_intr; /* real-mode intr handling */ + struct kvmhv_tb_accumulator rm_exit; /* real-mode exit code */ + struct kvmhv_tb_accumulator guest_time; /* guest execution */ + struct kvmhv_tb_accumulator cede_time; /* time napping inside guest */ + + struct dentry *debugfs_dir; + struct dentry *debugfs_timings; +#endif /* CONFIG_KVM_BOOK3S_HV_EXIT_TIMING */ }; #define VCPU_FPR(vcpu, i) (vcpu)->arch.fp.fpr[i][TS_FPROFFSET] diff --git a/arch/powerpc/include/asm/time.h b/arch/powerpc/include/asm/time.h index 03cbada59d3a..10fc784a2ad4 100644 --- a/arch/powerpc/include/asm/time.h +++ b/arch/powerpc/include/asm/time.h @@ -211,5 +211,8 @@ extern void secondary_cpu_time_init(void); DECLARE_PER_CPU(u64, decrementers_next_tb); +/* Convert timebase ticks to nanoseconds */ +unsigned long long tb_to_ns(unsigned long long tb_ticks); + #endif /* __KERNEL__ */ #endif /* __POWERPC_TIME_H */ diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 4717859fdd04..3fea721f0da5 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -458,6 +458,19 @@ int main(void) DEFINE(VCPU_SPRG1, offsetof(struct kvm_vcpu, arch.shregs.sprg1)); DEFINE(VCPU_SPRG2, offsetof(struct kvm_vcpu, arch.shregs.sprg2)); DEFINE(VCPU_SPRG3, offsetof(struct kvm_vcpu, arch.shregs.sprg3)); +#endif +#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING + DEFINE(VCPU_TB_RMENTRY, offsetof(struct kvm_vcpu, arch.rm_entry)); + DEFINE(VCPU_TB_RMINTR, offsetof(struct kvm_vcpu, arch.rm_intr)); + DEFINE(VCPU_TB_RMEXIT, offsetof(struct kvm_vcpu, arch.rm_exit)); + DEFINE(VCPU_TB_GUEST, offsetof(struct kvm_vcpu, arch.guest_time)); + DEFINE(VCPU_TB_CEDE, offsetof(struct kvm_vcpu, arch.cede_time)); + DEFINE(VCPU_CUR_ACTIVITY, offsetof(struct kvm_vcpu, arch.cur_activity)); + DEFINE(VCPU_ACTIVITY_START, offsetof(struct kvm_vcpu, arch.cur_tb_start)); + DEFINE(TAS_SEQCOUNT, offsetof(struct kvmhv_tb_accumulator, seqcount)); + DEFINE(TAS_TOTAL, offsetof(struct kvmhv_tb_accumulator, tb_total)); + DEFINE(TAS_MIN, offsetof(struct kvmhv_tb_accumulator, tb_min)); + DEFINE(TAS_MAX, offsetof(struct kvmhv_tb_accumulator, tb_max)); #endif DEFINE(VCPU_SHARED_SPRG3, offsetof(struct kvm_vcpu_arch_shared, sprg3)); DEFINE(VCPU_SHARED_SPRG4, offsetof(struct kvm_vcpu_arch_shared, sprg4)); diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 2d7b33fab953..56f44848b044 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -608,6 +608,12 @@ void arch_suspend_enable_irqs(void) } #endif +unsigned long long tb_to_ns(unsigned long long ticks) +{ + return mulhdu(ticks, tb_to_ns_scale) << tb_to_ns_shift; +} +EXPORT_SYMBOL_GPL(tb_to_ns); + /* * Scheduler clock - returns current time in nanosec units. * diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig index 11850f310fb4..2963e4dd0b80 100644 --- a/arch/powerpc/kvm/Kconfig +++ b/arch/powerpc/kvm/Kconfig @@ -110,6 +110,20 @@ config KVM_BOOK3S_64_PR processor, including emulating 32-bit processors on a 64-bit host. +config KVM_BOOK3S_HV_EXIT_TIMING + bool "Detailed timing for hypervisor real-mode code" + depends on KVM_BOOK3S_HV_POSSIBLE && DEBUG_FS + ---help--- + Calculate time taken for each vcpu in the real-mode guest entry, + exit, and interrupt handling code, plus time spent in the guest + and in nap mode due to idle (cede) while other threads are still + in the guest. The total, minimum and maximum times in nanoseconds + together with the number of executions are reported in debugfs in + kvm/vm#/vcpu#/timings. The overhead is of the order of 30 - 40 + ns per exit on POWER8. + + If unsure, say N. + config KVM_BOOKE_HV bool diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 08f8617f4046..64a02d4c737c 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -1423,6 +1423,154 @@ static struct kvmppc_vcore *kvmppc_vcore_create(struct kvm *kvm, int core) return vcore; } +#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING +static struct debugfs_timings_element { + const char *name; + size_t offset; +} timings[] = { + {"rm_entry", offsetof(struct kvm_vcpu, arch.rm_entry)}, + {"rm_intr", offsetof(struct kvm_vcpu, arch.rm_intr)}, + {"rm_exit", offsetof(struct kvm_vcpu, arch.rm_exit)}, + {"guest", offsetof(struct kvm_vcpu, arch.guest_time)}, + {"cede", offsetof(struct kvm_vcpu, arch.cede_time)}, +}; + +#define N_TIMINGS (sizeof(timings) / sizeof(timings[0])) + +struct debugfs_timings_state { + struct kvm_vcpu *vcpu; + unsigned int buflen; + char buf[N_TIMINGS * 100]; +}; + +static int debugfs_timings_open(struct inode *inode, struct file *file) +{ + struct kvm_vcpu *vcpu = inode->i_private; + struct debugfs_timings_state *p; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + + kvm_get_kvm(vcpu->kvm); + p->vcpu = vcpu; + file->private_data = p; + + return nonseekable_open(inode, file); +} + +static int debugfs_timings_release(struct inode *inode, struct file *file) +{ + struct debugfs_timings_state *p = file->private_data; + + kvm_put_kvm(p->vcpu->kvm); + kfree(p); + return 0; +} + +static ssize_t debugfs_timings_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + struct debugfs_timings_state *p = file->private_data; + struct kvm_vcpu *vcpu = p->vcpu; + char *s, *buf_end; + struct kvmhv_tb_accumulator tb; + u64 count; + loff_t pos; + ssize_t n; + int i, loops; + bool ok; + + if (!p->buflen) { + s = p->buf; + buf_end = s + sizeof(p->buf); + for (i = 0; i < N_TIMINGS; ++i) { + struct kvmhv_tb_accumulator *acc; + + acc = (struct kvmhv_tb_accumulator *) + ((unsigned long)vcpu + timings[i].offset); + ok = false; + for (loops = 0; loops < 1000; ++loops) { + count = acc->seqcount; + if (!(count & 1)) { + smp_rmb(); + tb = *acc; + smp_rmb(); + if (count == acc->seqcount) { + ok = true; + break; + } + } + udelay(1); + } + if (!ok) + snprintf(s, buf_end - s, "%s: stuck\n", + timings[i].name); + else + snprintf(s, buf_end - s, + "%s: %llu %llu %llu %llu\n", + timings[i].name, count / 2, + tb_to_ns(tb.tb_total), + tb_to_ns(tb.tb_min), + tb_to_ns(tb.tb_max)); + s += strlen(s); + } + p->buflen = s - p->buf; + } + + pos = *ppos; + if (pos >= p->buflen) + return 0; + if (len > p->buflen - pos) + len = p->buflen - pos; + n = copy_to_user(buf, p->buf + pos, len); + if (n) { + if (n == len) + return -EFAULT; + len -= n; + } + *ppos = pos + len; + return len; +} + +static ssize_t debugfs_timings_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + return -EACCES; +} + +static const struct file_operations debugfs_timings_ops = { + .owner = THIS_MODULE, + .open = debugfs_timings_open, + .release = debugfs_timings_release, + .read = debugfs_timings_read, + .write = debugfs_timings_write, + .llseek = generic_file_llseek, +}; + +/* Create a debugfs directory for the vcpu */ +static void debugfs_vcpu_init(struct kvm_vcpu *vcpu, unsigned int id) +{ + char buf[16]; + struct kvm *kvm = vcpu->kvm; + + snprintf(buf, sizeof(buf), "vcpu%u", id); + if (IS_ERR_OR_NULL(kvm->arch.debugfs_dir)) + return; + vcpu->arch.debugfs_dir = debugfs_create_dir(buf, kvm->arch.debugfs_dir); + if (IS_ERR_OR_NULL(vcpu->arch.debugfs_dir)) + return; + vcpu->arch.debugfs_timings = + debugfs_create_file("timings", 0444, vcpu->arch.debugfs_dir, + vcpu, &debugfs_timings_ops); +} + +#else /* CONFIG_KVM_BOOK3S_HV_EXIT_TIMING */ +static void debugfs_vcpu_init(struct kvm_vcpu *vcpu, unsigned int id) +{ +} +#endif /* CONFIG_KVM_BOOK3S_HV_EXIT_TIMING */ + static struct kvm_vcpu *kvmppc_core_vcpu_create_hv(struct kvm *kvm, unsigned int id) { @@ -1492,6 +1640,8 @@ static struct kvm_vcpu *kvmppc_core_vcpu_create_hv(struct kvm *kvm, vcpu->arch.cpu_type = KVM_CPU_3S_64; kvmppc_sanity_check(vcpu); + debugfs_vcpu_init(vcpu, id); + return vcpu; free_vcpu: diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index 0814ca122fcd..b06fe53fd509 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -225,7 +225,13 @@ kvm_novcpu_wakeup: /* Got an IPI but other vcpus aren't yet exiting, must be a latecomer */ ld r4, HSTATE_KVM_VCPU(r13) cmpdi r4, 0 - bne kvmppc_got_guest + beq kvmppc_primary_no_guest + +#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING + addi r3, r4, VCPU_TB_RMENTRY + bl kvmhv_start_timing +#endif + b kvmppc_got_guest kvm_novcpu_exit: b hdec_soon @@ -376,6 +382,14 @@ kvmppc_hv_entry: li r6, KVM_GUEST_MODE_HOST_HV stb r6, HSTATE_IN_GUEST(r13) +#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING + /* Store initial timestamp */ + cmpdi r4, 0 + beq 1f + addi r3, r4, VCPU_TB_RMENTRY + bl kvmhv_start_timing +1: +#endif /* Clear out SLB */ li r6,0 slbmte r6,r6 @@ -880,6 +894,12 @@ fast_guest_return: li r9, KVM_GUEST_MODE_GUEST_HV stb r9, HSTATE_IN_GUEST(r13) +#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING + /* Accumulate timing */ + addi r3, r4, VCPU_TB_GUEST + bl kvmhv_accumulate_time +#endif + /* Enter guest */ BEGIN_FTR_SECTION @@ -917,6 +937,23 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR) hrfid b . +#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING +secondary_too_late: + cmpdi r4, 0 + beq 11f + addi r3, r4, VCPU_TB_RMEXIT + bl kvmhv_accumulate_time +11: b kvmhv_switch_to_host + +hdec_soon: + ld r4, HSTATE_KVM_VCPU(r13) + cmpdi r4, 0 + beq 12f + addi r3, r4, VCPU_TB_RMEXIT + bl kvmhv_accumulate_time +12: b kvmhv_do_exit +#endif + /****************************************************************************** * * * Exit code * @@ -1002,6 +1039,16 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR) stw r12,VCPU_TRAP(r9) +#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING + addi r3, r9, VCPU_TB_RMINTR + mr r4, r9 + bl kvmhv_accumulate_time + ld r5, VCPU_GPR(R5)(r9) + ld r6, VCPU_GPR(R6)(r9) + ld r7, VCPU_GPR(R7)(r9) + ld r8, VCPU_GPR(R8)(r9) +#endif + /* Save HEIR (HV emulation assist reg) in emul_inst if this is an HEI (HV emulation interrupt, e40) */ li r3,KVM_INST_FETCH_FAILED @@ -1073,6 +1120,11 @@ guest_exit_cont: /* r9 = vcpu, r12 = trap, r13 = paca */ cmpwi r12, BOOK3S_INTERRUPT_MACHINE_CHECK beq machine_check_realmode mc_cont: +#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING + addi r3, r9, VCPU_TB_RMEXIT + mr r4, r9 + bl kvmhv_accumulate_time +#endif /* Save guest CTRL register, set runlatch to 1 */ 6: mfspr r6,SPRN_CTRLF @@ -1417,7 +1469,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) slbia ptesync -hdec_soon: /* r12 = trap, r13 = paca */ +#ifndef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING +hdec_soon: +#endif +kvmhv_do_exit: /* r12 = trap, r13 = paca */ /* * POWER7/POWER8 guest -> host partition switch code. * We don't have to lock against tlbies but we do @@ -1476,7 +1531,10 @@ hdec_soon: /* r12 = trap, r13 = paca */ addi r6,r6,PACA_SIZE bne 42b +#ifndef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING secondary_too_late: +#endif +kvmhv_switch_to_host: /* Secondary threads wait for primary to do partition switch */ 43: ld r5,HSTATE_KVM_VCORE(r13) ld r4,VCORE_KVM(r5) /* pointer to struct kvm */ @@ -1562,6 +1620,15 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) 1: addi r8,r8,16 .endr +#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING + /* Finish timing, if we have a vcpu */ + ld r4, HSTATE_KVM_VCPU(r13) + cmpdi r4, 0 + li r3, 0 + beq 2f + bl kvmhv_accumulate_time +2: +#endif /* Unset guest mode */ li r0, KVM_GUEST_MODE_NONE stb r0, HSTATE_IN_GUEST(r13) @@ -2069,6 +2136,12 @@ _GLOBAL(kvmppc_h_cede) /* save FP state */ bl kvmppc_save_fp +#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING + ld r4, HSTATE_KVM_VCPU(r13) + addi r3, r4, VCPU_TB_CEDE + bl kvmhv_accumulate_time +#endif + /* * Take a nap until a decrementer or external or doobell interrupt * occurs, with PECE1, PECE0 and PECEDP set in LPCR. Also clear the @@ -2109,6 +2182,11 @@ kvm_end_cede: /* Woken by external or decrementer interrupt */ ld r1, HSTATE_HOST_R1(r13) +#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING + addi r3, r4, VCPU_TB_RMINTR + bl kvmhv_accumulate_time +#endif + /* load up FP state */ bl kvmppc_load_fp @@ -2429,3 +2507,62 @@ kvmppc_fix_pmao: mtspr SPRN_PMC6, r3 isync blr + +#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING +/* + * Start timing an activity + * r3 = pointer to time accumulation struct, r4 = vcpu + */ +kvmhv_start_timing: + ld r5, HSTATE_KVM_VCORE(r13) + lbz r6, VCORE_IN_GUEST(r5) + cmpwi r6, 0 + beq 5f /* if in guest, need to */ + ld r6, VCORE_TB_OFFSET(r5) /* subtract timebase offset */ +5: mftb r5 + subf r5, r6, r5 + std r3, VCPU_CUR_ACTIVITY(r4) + std r5, VCPU_ACTIVITY_START(r4) + blr + +/* + * Accumulate time to one activity and start another. + * r3 = pointer to new time accumulation struct, r4 = vcpu + */ +kvmhv_accumulate_time: + ld r5, HSTATE_KVM_VCORE(r13) + lbz r8, VCORE_IN_GUEST(r5) + cmpwi r8, 0 + beq 4f /* if in guest, need to */ + ld r8, VCORE_TB_OFFSET(r5) /* subtract timebase offset */ +4: ld r5, VCPU_CUR_ACTIVITY(r4) + ld r6, VCPU_ACTIVITY_START(r4) + std r3, VCPU_CUR_ACTIVITY(r4) + mftb r7 + subf r7, r8, r7 + std r7, VCPU_ACTIVITY_START(r4) + cmpdi r5, 0 + beqlr + subf r3, r6, r7 + ld r8, TAS_SEQCOUNT(r5) + cmpdi r8, 0 + addi r8, r8, 1 + std r8, TAS_SEQCOUNT(r5) + lwsync + ld r7, TAS_TOTAL(r5) + add r7, r7, r3 + std r7, TAS_TOTAL(r5) + ld r6, TAS_MIN(r5) + ld r7, TAS_MAX(r5) + beq 3f + cmpd r3, r6 + bge 1f +3: std r3, TAS_MIN(r5) +1: cmpd r3, r7 + ble 2f + std r3, TAS_MAX(r5) +2: lwsync + addi r8, r8, 1 + std r8, TAS_SEQCOUNT(r5) + blr +#endif -- cgit v1.2.3 From 1f09c3ed86287d40fef90611cbbee055313f52cf Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 28 Mar 2015 14:21:04 +1100 Subject: KVM: PPC: Book3S HV: Minor cleanups * Remove unused kvmppc_vcore::n_busy field. * Remove setting of RMOR, since it was only used on PPC970 and the PPC970 KVM support has been removed. * Don't use r1 or r2 in setting the runlatch since they are conventionally reserved for other things; use r0 instead. * Streamline the code a little and remove the ext_interrupt_to_host label. * Add some comments about register usage. * hcall_try_real_mode doesn't need to be global, and can't be called from C code anyway. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf --- arch/powerpc/include/asm/kvm_host.h | 2 -- arch/powerpc/kernel/asm-offsets.c | 1 - arch/powerpc/kvm/book3s_hv_rmhandlers.S | 44 ++++++++++++++------------------- 3 files changed, 19 insertions(+), 28 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 2f339ff9b851..3eecd8868b01 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -227,7 +227,6 @@ struct kvm_arch { unsigned long host_sdr1; int tlbie_lock; unsigned long lpcr; - unsigned long rmor; unsigned long vrma_slb_v; int hpte_setup_done; u32 hpt_order; @@ -271,7 +270,6 @@ struct kvm_arch { */ struct kvmppc_vcore { int n_runnable; - int n_busy; int num_threads; int entry_exit_count; int n_woken; diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 3fea721f0da5..92ec3fcb3415 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -505,7 +505,6 @@ int main(void) DEFINE(KVM_NEED_FLUSH, offsetof(struct kvm, arch.need_tlb_flush.bits)); DEFINE(KVM_ENABLED_HCALLS, offsetof(struct kvm, arch.enabled_hcalls)); DEFINE(KVM_LPCR, offsetof(struct kvm, arch.lpcr)); - DEFINE(KVM_RMOR, offsetof(struct kvm, arch.rmor)); DEFINE(KVM_VRMA_SLB_V, offsetof(struct kvm, arch.vrma_slb_v)); DEFINE(VCPU_DSISR, offsetof(struct kvm_vcpu, arch.shregs.dsisr)); DEFINE(VCPU_DAR, offsetof(struct kvm_vcpu, arch.shregs.dar)); diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index b06fe53fd509..f8267e5b3a50 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -245,9 +245,9 @@ kvm_novcpu_exit: kvm_start_guest: /* Set runlatch bit the minute you wake up from nap */ - mfspr r1, SPRN_CTRLF - ori r1, r1, 1 - mtspr SPRN_CTRLT, r1 + mfspr r0, SPRN_CTRLF + ori r0, r0, 1 + mtspr SPRN_CTRLT, r0 ld r2,PACATOC(r13) @@ -493,11 +493,9 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) cmpwi r0,0 beq 20b - /* Set LPCR and RMOR. */ + /* Set LPCR. */ 10: ld r8,VCORE_LPCR(r5) mtspr SPRN_LPCR,r8 - ld r8,KVM_RMOR(r9) - mtspr SPRN_RMOR,r8 isync /* Check if HDEC expires soon */ @@ -1075,7 +1073,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR) bne 2f mfspr r3,SPRN_HDEC cmpwi r3,0 - bge ignore_hdec + mr r4,r9 + bge fast_guest_return 2: /* See if this is an hcall we can handle in real mode */ cmpwi r12,BOOK3S_INTERRUPT_SYSCALL @@ -1083,26 +1082,21 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR) /* External interrupt ? */ cmpwi r12, BOOK3S_INTERRUPT_EXTERNAL - bne+ ext_interrupt_to_host + bne+ guest_exit_cont /* External interrupt, first check for host_ipi. If this is * set, we know the host wants us out so let's do it now */ bl kvmppc_read_intr cmpdi r3, 0 - bgt ext_interrupt_to_host + bgt guest_exit_cont /* Check if any CPU is heading out to the host, if so head out too */ ld r5, HSTATE_KVM_VCORE(r13) lwz r0, VCORE_ENTRY_EXIT(r5) cmpwi r0, 0x100 - bge ext_interrupt_to_host - - /* Return to guest after delivering any pending interrupt */ mr r4, r9 - b deliver_guest_interrupt - -ext_interrupt_to_host: + blt deliver_guest_interrupt guest_exit_cont: /* r9 = vcpu, r12 = trap, r13 = paca */ /* Save more register state */ @@ -1763,8 +1757,10 @@ kvmppc_hisi: * Returns to the guest if we handle it, or continues on up to * the kernel if we can't (i.e. if we don't have a handler for * it, or if the handler returns H_TOO_HARD). + * + * r5 - r8 contain hcall args, + * r9 = vcpu, r10 = pc, r11 = msr, r12 = trap, r13 = paca */ - .globl hcall_try_real_mode hcall_try_real_mode: ld r3,VCPU_GPR(R3)(r9) andi. r0,r11,MSR_PR @@ -2024,10 +2020,6 @@ hcall_real_table: .globl hcall_real_table_end hcall_real_table_end: -ignore_hdec: - mr r4,r9 - b fast_guest_return - _GLOBAL(kvmppc_h_set_xdabr) andi. r0, r5, DABRX_USER | DABRX_KERNEL beq 6f @@ -2066,7 +2058,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) li r3, 0 blr -_GLOBAL(kvmppc_h_cede) +_GLOBAL(kvmppc_h_cede) /* r3 = vcpu pointer, r11 = msr, r13 = paca */ ori r11,r11,MSR_EE std r11,VCPU_MSR(r3) li r0,1 @@ -2148,9 +2140,9 @@ _GLOBAL(kvmppc_h_cede) * runlatch bit before napping. */ kvm_do_nap: - mfspr r2, SPRN_CTRLF - clrrdi r2, r2, 1 - mtspr SPRN_CTRLT, r2 + mfspr r0, SPRN_CTRLF + clrrdi r0, r0, 1 + mtspr SPRN_CTRLT, r0 li r0,1 stb r0,HSTATE_HWTHREAD_REQ(r13) @@ -2282,13 +2274,14 @@ machine_check_realmode: /* * Check the reason we woke from nap, and take appropriate action. - * Returns: + * Returns (in r3): * 0 if nothing needs to be done * 1 if something happened that needs to be handled by the host * -1 if there was a guest wakeup (IPI) * * Also sets r12 to the interrupt vector for any interrupt that needs * to be handled now by the host (0x500 for external interrupt), or zero. + * Modifies r0, r6, r7, r8. */ kvmppc_check_wake_reason: mfspr r6, SPRN_SRR1 @@ -2324,6 +2317,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) * 0 if no interrupt is pending * 1 if an interrupt is pending that needs to be handled by the host * -1 if there was a guest wakeup IPI (which has now been cleared) + * Modifies r0, r6, r7, r8, returns value in r3. */ kvmppc_read_intr: /* see if a host IPI is pending */ -- cgit v1.2.3 From 5d5b99cd6818bdbea287d23ef055bba1a8a9e648 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 28 Mar 2015 14:21:06 +1100 Subject: KVM: PPC: Book3S HV: Get rid of vcore nap_count and n_woken We can tell when a secondary thread has finished running a guest by the fact that it clears its kvm_hstate.kvm_vcpu pointer, so there is no real need for the nap_count field in the kvmppc_vcore struct. This changes kvmppc_wait_for_nap to poll the kvm_hstate.kvm_vcpu pointers of the secondary threads rather than polling vc->nap_count. Besides reducing the size of the kvmppc_vcore struct by 8 bytes, this also means that we can tell which secondary threads have got stuck and thus print a more informative error message. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf --- arch/powerpc/include/asm/kvm_host.h | 2 -- arch/powerpc/kernel/asm-offsets.c | 1 - arch/powerpc/kvm/book3s_hv.c | 47 +++++++++++++++++++-------------- arch/powerpc/kvm/book3s_hv_rmhandlers.S | 19 +++++-------- 4 files changed, 34 insertions(+), 35 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 83c44257b005..1517faac9b98 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -272,8 +272,6 @@ struct kvmppc_vcore { int n_runnable; int num_threads; int entry_exit_count; - int n_woken; - int nap_count; int napping_threads; int first_vcpuid; u16 pcpu; diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 92ec3fcb3415..8aa824681d76 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -563,7 +563,6 @@ int main(void) DEFINE(VCPU_WORT, offsetof(struct kvm_vcpu, arch.wort)); DEFINE(VCPU_SHADOW_SRR1, offsetof(struct kvm_vcpu, arch.shadow_srr1)); DEFINE(VCORE_ENTRY_EXIT, offsetof(struct kvmppc_vcore, entry_exit_count)); - DEFINE(VCORE_NAP_COUNT, offsetof(struct kvmppc_vcore, nap_count)); DEFINE(VCORE_IN_GUEST, offsetof(struct kvmppc_vcore, in_guest)); DEFINE(VCORE_NAPPING_THREADS, offsetof(struct kvmppc_vcore, napping_threads)); DEFINE(VCORE_KVM, offsetof(struct kvmppc_vcore, kvm)); diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index fb4f16628a51..7c1335dca4a5 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -1729,8 +1729,10 @@ static int kvmppc_grab_hwthread(int cpu) tpaca = &paca[cpu]; /* Ensure the thread won't go into the kernel if it wakes */ - tpaca->kvm_hstate.hwthread_req = 1; tpaca->kvm_hstate.kvm_vcpu = NULL; + tpaca->kvm_hstate.napping = 0; + smp_wmb(); + tpaca->kvm_hstate.hwthread_req = 1; /* * If the thread is already executing in the kernel (e.g. handling @@ -1773,35 +1775,43 @@ static void kvmppc_start_thread(struct kvm_vcpu *vcpu) } cpu = vc->pcpu + vcpu->arch.ptid; tpaca = &paca[cpu]; - tpaca->kvm_hstate.kvm_vcpu = vcpu; tpaca->kvm_hstate.kvm_vcore = vc; tpaca->kvm_hstate.ptid = vcpu->arch.ptid; vcpu->cpu = vc->pcpu; + /* Order stores to hstate.kvm_vcore etc. before store to kvm_vcpu */ smp_wmb(); + tpaca->kvm_hstate.kvm_vcpu = vcpu; #if defined(CONFIG_PPC_ICP_NATIVE) && defined(CONFIG_SMP) - if (cpu != smp_processor_id()) { + if (cpu != smp_processor_id()) xics_wake_cpu(cpu); - if (vcpu->arch.ptid) - ++vc->n_woken; - } #endif } -static void kvmppc_wait_for_nap(struct kvmppc_vcore *vc) +static void kvmppc_wait_for_nap(void) { - int i; + int cpu = smp_processor_id(); + int i, loops; - HMT_low(); - i = 0; - while (vc->nap_count < vc->n_woken) { - if (++i >= 1000000) { - pr_err("kvmppc_wait_for_nap timeout %d %d\n", - vc->nap_count, vc->n_woken); - break; + for (loops = 0; loops < 1000000; ++loops) { + /* + * Check if all threads are finished. + * We set the vcpu pointer when starting a thread + * and the thread clears it when finished, so we look + * for any threads that still have a non-NULL vcpu ptr. + */ + for (i = 1; i < threads_per_subcore; ++i) + if (paca[cpu + i].kvm_hstate.kvm_vcpu) + break; + if (i == threads_per_subcore) { + HMT_medium(); + return; } - cpu_relax(); + HMT_low(); } HMT_medium(); + for (i = 1; i < threads_per_subcore; ++i) + if (paca[cpu + i].kvm_hstate.kvm_vcpu) + pr_err("KVM: CPU %d seems to be stuck\n", cpu + i); } /* @@ -1942,8 +1952,6 @@ static void kvmppc_run_core(struct kvmppc_vcore *vc) /* * Initialize *vc. */ - vc->n_woken = 0; - vc->nap_count = 0; vc->entry_exit_count = 0; vc->preempt_tb = TB_NIL; vc->in_guest = 0; @@ -2002,8 +2010,7 @@ static void kvmppc_run_core(struct kvmppc_vcore *vc) list_for_each_entry(vcpu, &vc->runnable_threads, arch.run_list) vcpu->cpu = -1; /* wait for secondary threads to finish writing their state to memory */ - if (vc->nap_count < vc->n_woken) - kvmppc_wait_for_nap(vc); + kvmppc_wait_for_nap(); for (i = 0; i < threads_per_subcore; ++i) kvmppc_release_hwthread(vc->pcpu + i); /* prevent other vcpu threads from doing kvmppc_start_thread() now */ diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index f8267e5b3a50..6716db3f1a2b 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -292,26 +292,21 @@ kvm_secondary_got_guest: ld r6, PACA_DSCR(r13) std r6, HSTATE_DSCR(r13) + /* Order load of vcore, ptid etc. after load of vcpu */ + lwsync bl kvmppc_hv_entry /* Back from the guest, go back to nap */ /* Clear our vcpu pointer so we don't come back in early */ li r0, 0 - std r0, HSTATE_KVM_VCPU(r13) /* - * Make sure we clear HSTATE_KVM_VCPU(r13) before incrementing - * the nap_count, because once the increment to nap_count is - * visible we could be given another vcpu. + * Once we clear HSTATE_KVM_VCPU(r13), the code in + * kvmppc_run_core() is going to assume that all our vcpu + * state is visible in memory. This lwsync makes sure + * that that is true. */ lwsync - - /* increment the nap count and then go to nap mode */ - ld r4, HSTATE_KVM_VCORE(r13) - addi r4, r4, VCORE_NAP_COUNT -51: lwarx r3, 0, r4 - addi r3, r3, 1 - stwcx. r3, 0, r4 - bne 51b + std r0, HSTATE_KVM_VCPU(r13) /* * At this point we have finished executing in the guest. -- cgit v1.2.3 From 7d6c40da198ac18bd5dd2cd18628d5b4c615d842 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 28 Mar 2015 14:21:09 +1100 Subject: KVM: PPC: Book3S HV: Use bitmap of active threads rather than count Currently, the entry_exit_count field in the kvmppc_vcore struct contains two 8-bit counts, one of the threads that have started entering the guest, and one of the threads that have started exiting the guest. This changes it to an entry_exit_map field which contains two bitmaps of 8 bits each. The advantage of doing this is that it gives us a bitmap of which threads need to be signalled when exiting the guest. That means that we no longer need to use the trick of setting the HDEC to 0 to pull the other threads out of the guest, which led in some cases to a spurious HDEC interrupt on the next guest entry. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf --- arch/powerpc/include/asm/kvm_host.h | 15 ++++---- arch/powerpc/kernel/asm-offsets.c | 2 +- arch/powerpc/kvm/book3s_hv.c | 5 ++- arch/powerpc/kvm/book3s_hv_builtin.c | 10 +++--- arch/powerpc/kvm/book3s_hv_rmhandlers.S | 61 +++++++++++++++------------------ 5 files changed, 44 insertions(+), 49 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 1517faac9b98..d67a83830bd1 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -263,15 +263,15 @@ struct kvm_arch { /* * Struct for a virtual core. - * Note: entry_exit_count combines an entry count in the bottom 8 bits - * and an exit count in the next 8 bits. This is so that we can - * atomically increment the entry count iff the exit count is 0 - * without taking the lock. + * Note: entry_exit_map combines a bitmap of threads that have entered + * in the bottom 8 bits and a bitmap of threads that have exited in the + * next 8 bits. This is so that we can atomically set the entry bit + * iff the exit map is 0 without taking a lock. */ struct kvmppc_vcore { int n_runnable; int num_threads; - int entry_exit_count; + int entry_exit_map; int napping_threads; int first_vcpuid; u16 pcpu; @@ -296,8 +296,9 @@ struct kvmppc_vcore { ulong conferring_threads; }; -#define VCORE_ENTRY_COUNT(vc) ((vc)->entry_exit_count & 0xff) -#define VCORE_EXIT_COUNT(vc) ((vc)->entry_exit_count >> 8) +#define VCORE_ENTRY_MAP(vc) ((vc)->entry_exit_map & 0xff) +#define VCORE_EXIT_MAP(vc) ((vc)->entry_exit_map >> 8) +#define VCORE_IS_EXITING(vc) (VCORE_EXIT_MAP(vc) != 0) /* Values for vcore_state */ #define VCORE_INACTIVE 0 diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 8aa824681d76..0d07efbe3fa7 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -562,7 +562,7 @@ int main(void) DEFINE(VCPU_ACOP, offsetof(struct kvm_vcpu, arch.acop)); DEFINE(VCPU_WORT, offsetof(struct kvm_vcpu, arch.wort)); DEFINE(VCPU_SHADOW_SRR1, offsetof(struct kvm_vcpu, arch.shadow_srr1)); - DEFINE(VCORE_ENTRY_EXIT, offsetof(struct kvmppc_vcore, entry_exit_count)); + DEFINE(VCORE_ENTRY_EXIT, offsetof(struct kvmppc_vcore, entry_exit_map)); DEFINE(VCORE_IN_GUEST, offsetof(struct kvmppc_vcore, in_guest)); DEFINE(VCORE_NAPPING_THREADS, offsetof(struct kvmppc_vcore, napping_threads)); DEFINE(VCORE_KVM, offsetof(struct kvmppc_vcore, kvm)); diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 7c1335dca4a5..ea1600ff52b2 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -1952,7 +1952,7 @@ static void kvmppc_run_core(struct kvmppc_vcore *vc) /* * Initialize *vc. */ - vc->entry_exit_count = 0; + vc->entry_exit_map = 0; vc->preempt_tb = TB_NIL; vc->in_guest = 0; vc->napping_threads = 0; @@ -2119,8 +2119,7 @@ static int kvmppc_run_vcpu(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) * this thread straight away and have it join in. */ if (!signal_pending(current)) { - if (vc->vcore_state == VCORE_RUNNING && - VCORE_EXIT_COUNT(vc) == 0) { + if (vc->vcore_state == VCORE_RUNNING && !VCORE_IS_EXITING(vc)) { kvmppc_create_dtl_entry(vcpu, vc); kvmppc_start_thread(vcpu); trace_kvm_guest_enter(vcpu); diff --git a/arch/powerpc/kvm/book3s_hv_builtin.c b/arch/powerpc/kvm/book3s_hv_builtin.c index 1954a1c4b1f9..275425142bb7 100644 --- a/arch/powerpc/kvm/book3s_hv_builtin.c +++ b/arch/powerpc/kvm/book3s_hv_builtin.c @@ -115,11 +115,11 @@ long int kvmppc_rm_h_confer(struct kvm_vcpu *vcpu, int target, int rv = H_SUCCESS; /* => don't yield */ set_bit(vcpu->arch.ptid, &vc->conferring_threads); - while ((get_tb() < stop) && (VCORE_EXIT_COUNT(vc) == 0)) { - threads_running = VCORE_ENTRY_COUNT(vc); - threads_ceded = hweight32(vc->napping_threads); - threads_conferring = hweight32(vc->conferring_threads); - if (threads_ceded + threads_conferring >= threads_running) { + while ((get_tb() < stop) && !VCORE_IS_EXITING(vc)) { + threads_running = VCORE_ENTRY_MAP(vc); + threads_ceded = vc->napping_threads; + threads_conferring = vc->conferring_threads; + if ((threads_ceded | threads_conferring) == threads_running) { rv = H_TOO_HARD; /* => do yield */ break; } diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index 16719af88bcc..245f5c972030 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -185,7 +185,7 @@ kvmppc_primary_no_guest: or r3, r3, r0 stwcx. r3, 0, r6 bne 1b - /* order napping_threads update vs testing entry_exit_count */ + /* order napping_threads update vs testing entry_exit_map */ isync li r12, 0 lwz r7, VCORE_ENTRY_EXIT(r5) @@ -406,19 +406,21 @@ kvmppc_hv_entry: * We don't have to lock against concurrent tlbies, * but we do have to coordinate across hardware threads. */ - /* Increment entry count iff exit count is zero. */ - ld r5,HSTATE_KVM_VCORE(r13) - addi r9,r5,VCORE_ENTRY_EXIT -21: lwarx r3,0,r9 - cmpwi r3,0x100 /* any threads starting to exit? */ + /* Set bit in entry map iff exit map is zero. */ + ld r5, HSTATE_KVM_VCORE(r13) + li r7, 1 + lbz r6, HSTATE_PTID(r13) + sld r7, r7, r6 + addi r9, r5, VCORE_ENTRY_EXIT +21: lwarx r3, 0, r9 + cmpwi r3, 0x100 /* any threads starting to exit? */ bge secondary_too_late /* if so we're too late to the party */ - addi r3,r3,1 - stwcx. r3,0,r9 + or r3, r3, r7 + stwcx. r3, 0, r9 bne 21b /* Primary thread switches to guest partition. */ ld r9,VCORE_KVM(r5) /* pointer to struct kvm */ - lbz r6,HSTATE_PTID(r13) cmpwi r6,0 bne 20f ld r6,KVM_SDR1(r9) @@ -1477,13 +1479,16 @@ kvmhv_do_exit: /* r12 = trap, r13 = paca */ * We don't have to lock against tlbies but we do * have to coordinate the hardware threads. */ - /* Increment the threads-exiting-guest count in the 0xff00 - bits of vcore->entry_exit_count */ - ld r5,HSTATE_KVM_VCORE(r13) - addi r6,r5,VCORE_ENTRY_EXIT -41: lwarx r3,0,r6 - addi r0,r3,0x100 - stwcx. r0,0,r6 + /* Set our bit in the threads-exiting-guest map in the 0xff00 + bits of vcore->entry_exit_map */ + ld r5, HSTATE_KVM_VCORE(r13) + lbz r4, HSTATE_PTID(r13) + li r7, 0x100 + sld r7, r7, r4 + addi r6, r5, VCORE_ENTRY_EXIT +41: lwarx r3, 0, r6 + or r0, r3, r7 + stwcx. r0, 0, r6 bne 41b isync /* order stwcx. vs. reading napping_threads */ @@ -1492,9 +1497,9 @@ kvmhv_do_exit: /* r12 = trap, r13 = paca */ * up to the kernel or qemu; we can't handle it in real mode. * Thus we have to do a partition switch, so we have to * collect the other threads, if we are the first thread - * to take an interrupt. To do this, we set the HDEC to 0, - * which causes an HDEC interrupt in all threads within 2ns - * because the HDEC register is shared between all 4 threads. + * to take an interrupt. To do this, we send a message or + * IPI to all the threads that have their bit set in the entry + * map in vcore->entry_exit_map (other than ourselves). * However, we don't need to bother if this is an HDEC * interrupt, since the other threads will already be on their * way here in that case. @@ -1503,17 +1508,8 @@ kvmhv_do_exit: /* r12 = trap, r13 = paca */ bge 43f cmpwi r12,BOOK3S_INTERRUPT_HV_DECREMENTER beq 43f - li r0,0 - mtspr SPRN_HDEC,r0 - /* - * Send an IPI to any napping threads, since an HDEC interrupt - * doesn't wake CPUs up from nap. - */ - lwz r3,VCORE_NAPPING_THREADS(r5) - lbz r4,HSTATE_PTID(r13) - li r0,1 - sld r0,r0,r4 + srwi r0,r7,8 andc. r3,r3,r0 /* no sense IPI'ing ourselves */ beq 43f /* Order entry/exit update vs. IPIs */ @@ -2091,12 +2087,11 @@ _GLOBAL(kvmppc_h_cede) /* r3 = vcpu pointer, r11 = msr, r13 = paca */ addi r6,r5,VCORE_NAPPING_THREADS 31: lwarx r4,0,r6 or r4,r4,r0 - PPC_POPCNTW(R7,R4) - cmpw r7,r8 - bge kvm_cede_exit + cmpw r4,r8 + beq kvm_cede_exit stwcx. r4,0,r6 bne 31b - /* order napping_threads update vs testing entry_exit_count */ + /* order napping_threads update vs testing entry_exit_map */ isync li r0,NAPPING_CEDE stb r0,HSTATE_NAPPING(r13) -- cgit v1.2.3 From 66feed61cdf6ee65fd551d3460b1efba6bee55b8 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 28 Mar 2015 14:21:12 +1100 Subject: KVM: PPC: Book3S HV: Use msgsnd for signalling threads on POWER8 This uses msgsnd where possible for signalling other threads within the same core on POWER8 systems, rather than IPIs through the XICS interrupt controller. This includes waking secondary threads to run the guest, the interrupts generated by the virtual XICS, and the interrupts to bring the other threads out of the guest when exiting. Aggregated statistics from debugfs across vcpus for a guest with 32 vcpus, 8 threads/vcore, running on a POWER8, show this before the change: rm_entry: 3387.6ns (228 - 86600, 1008969 samples) rm_exit: 4561.5ns (12 - 3477452, 1009402 samples) rm_intr: 1660.0ns (12 - 553050, 3600051 samples) and this after the change: rm_entry: 3060.1ns (212 - 65138, 953873 samples) rm_exit: 4244.1ns (12 - 9693408, 954331 samples) rm_intr: 1342.3ns (12 - 1104718, 3405326 samples) for a test of booting Fedora 20 big-endian to the login prompt. The time taken for a H_PROD hcall (which is handled in the host kernel) went down from about 35 microseconds to about 16 microseconds with this change. The noinline added to kvmppc_run_core turned out to be necessary for good performance, at least with gcc 4.9.2 as packaged with Fedora 21 and a little-endian POWER8 host. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf --- arch/powerpc/kernel/asm-offsets.c | 3 ++ arch/powerpc/kvm/book3s_hv.c | 51 ++++++++++++++++++++++----------- arch/powerpc/kvm/book3s_hv_builtin.c | 16 +++++++++-- arch/powerpc/kvm/book3s_hv_rmhandlers.S | 22 ++++++++++++-- 4 files changed, 70 insertions(+), 22 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 0d07efbe3fa7..0034b6b3556a 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef CONFIG_PPC64 #include #include @@ -759,5 +760,7 @@ int main(void) offsetof(struct paca_struct, subcore_sibling_mask)); #endif + DEFINE(PPC_DBELL_SERVER, PPC_DBELL_SERVER); + return 0; } diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index ea1600ff52b2..48d3c5d2ecc9 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -84,9 +85,35 @@ static DECLARE_BITMAP(default_enabled_hcalls, MAX_HCALL_OPCODE/4 + 1); static void kvmppc_end_cede(struct kvm_vcpu *vcpu); static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu); +static bool kvmppc_ipi_thread(int cpu) +{ + /* On POWER8 for IPIs to threads in the same core, use msgsnd */ + if (cpu_has_feature(CPU_FTR_ARCH_207S)) { + preempt_disable(); + if (cpu_first_thread_sibling(cpu) == + cpu_first_thread_sibling(smp_processor_id())) { + unsigned long msg = PPC_DBELL_TYPE(PPC_DBELL_SERVER); + msg |= cpu_thread_in_core(cpu); + smp_mb(); + __asm__ __volatile__ (PPC_MSGSND(%0) : : "r" (msg)); + preempt_enable(); + return true; + } + preempt_enable(); + } + +#if defined(CONFIG_PPC_ICP_NATIVE) && defined(CONFIG_SMP) + if (cpu >= 0 && cpu < nr_cpu_ids && paca[cpu].kvm_hstate.xics_phys) { + xics_wake_cpu(cpu); + return true; + } +#endif + + return false; +} + static void kvmppc_fast_vcpu_kick_hv(struct kvm_vcpu *vcpu) { - int me; int cpu = vcpu->cpu; wait_queue_head_t *wqp; @@ -96,20 +123,12 @@ static void kvmppc_fast_vcpu_kick_hv(struct kvm_vcpu *vcpu) ++vcpu->stat.halt_wakeup; } - me = get_cpu(); + if (kvmppc_ipi_thread(cpu + vcpu->arch.ptid)) + return; /* CPU points to the first thread of the core */ - if (cpu != me && cpu >= 0 && cpu < nr_cpu_ids) { -#ifdef CONFIG_PPC_ICP_NATIVE - int real_cpu = cpu + vcpu->arch.ptid; - if (paca[real_cpu].kvm_hstate.xics_phys) - xics_wake_cpu(real_cpu); - else -#endif - if (cpu_online(cpu)) - smp_send_reschedule(cpu); - } - put_cpu(); + if (cpu >= 0 && cpu < nr_cpu_ids && cpu_online(cpu)) + smp_send_reschedule(cpu); } /* @@ -1781,10 +1800,8 @@ static void kvmppc_start_thread(struct kvm_vcpu *vcpu) /* Order stores to hstate.kvm_vcore etc. before store to kvm_vcpu */ smp_wmb(); tpaca->kvm_hstate.kvm_vcpu = vcpu; -#if defined(CONFIG_PPC_ICP_NATIVE) && defined(CONFIG_SMP) if (cpu != smp_processor_id()) - xics_wake_cpu(cpu); -#endif + kvmppc_ipi_thread(cpu); } static void kvmppc_wait_for_nap(void) @@ -1933,7 +1950,7 @@ static void post_guest_process(struct kvmppc_vcore *vc) * Run a set of guest threads on a physical core. * Called with vc->lock held. */ -static void kvmppc_run_core(struct kvmppc_vcore *vc) +static noinline void kvmppc_run_core(struct kvmppc_vcore *vc) { struct kvm_vcpu *vcpu; int i; diff --git a/arch/powerpc/kvm/book3s_hv_builtin.c b/arch/powerpc/kvm/book3s_hv_builtin.c index c42aa55b885f..ed2589d4593f 100644 --- a/arch/powerpc/kvm/book3s_hv_builtin.c +++ b/arch/powerpc/kvm/book3s_hv_builtin.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #define KVM_CMA_CHUNK_ORDER 18 @@ -193,7 +195,7 @@ static inline void rm_writeb(unsigned long paddr, u8 val) } /* - * Send an interrupt to another CPU. + * Send an interrupt or message to another CPU. * This can only be called in real mode. * The caller needs to include any barrier needed to order writes * to memory vs. the IPI/message. @@ -202,7 +204,17 @@ void kvmhv_rm_send_ipi(int cpu) { unsigned long xics_phys; - /* Poke the target */ + /* On POWER8 for IPIs to threads in the same core, use msgsnd */ + if (cpu_has_feature(CPU_FTR_ARCH_207S) && + cpu_first_thread_sibling(cpu) == + cpu_first_thread_sibling(raw_smp_processor_id())) { + unsigned long msg = PPC_DBELL_TYPE(PPC_DBELL_SERVER); + msg |= cpu_thread_in_core(cpu); + __asm__ __volatile__ (PPC_MSGSND(%0) : : "r" (msg)); + return; + } + + /* Else poke the target with an IPI */ xics_phys = paca[cpu].kvm_hstate.xics_phys; rm_writeb(xics_phys + XICS_MFRR, IPI_PRIORITY); } diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index fcf3a617cc8a..4d70df26c402 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -1123,6 +1123,13 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR) cmpwi r12,BOOK3S_INTERRUPT_SYSCALL beq hcall_try_real_mode + /* Hypervisor doorbell - exit only if host IPI flag set */ + cmpwi r12, BOOK3S_INTERRUPT_H_DOORBELL + bne 3f + lbz r0, HSTATE_HOST_IPI(r13) + beq 4f + b guest_exit_cont +3: /* External interrupt ? */ cmpwi r12, BOOK3S_INTERRUPT_EXTERNAL bne+ guest_exit_cont @@ -1135,7 +1142,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR) bgt guest_exit_cont /* Check if any CPU is heading out to the host, if so head out too */ - ld r5, HSTATE_KVM_VCORE(r13) +4: ld r5, HSTATE_KVM_VCORE(r13) lwz r0, VCORE_ENTRY_EXIT(r5) cmpwi r0, 0x100 mr r4, r9 @@ -2148,7 +2155,7 @@ _GLOBAL(kvmppc_h_cede) /* r3 = vcpu pointer, r11 = msr, r13 = paca */ /* * Take a nap until a decrementer or external or doobell interrupt * occurs, with PECE1 and PECE0 set in LPCR. - * On POWER8, if we are ceding, also set PECEDP. + * On POWER8, set PECEDH, and if we are ceding, also set PECEDP. * Also clear the runlatch bit before napping. */ kvm_do_nap: @@ -2161,6 +2168,7 @@ kvm_do_nap: mfspr r5,SPRN_LPCR ori r5,r5,LPCR_PECE0 | LPCR_PECE1 BEGIN_FTR_SECTION + ori r5, r5, LPCR_PECEDH rlwimi r5, r3, 0, LPCR_PECEDP END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) mtspr SPRN_LPCR,r5 @@ -2299,7 +2307,7 @@ machine_check_realmode: * Returns (in r3): * 0 if nothing needs to be done * 1 if something happened that needs to be handled by the host - * -1 if there was a guest wakeup (IPI) + * -1 if there was a guest wakeup (IPI or msgsnd) * * Also sets r12 to the interrupt vector for any interrupt that needs * to be handled now by the host (0x500 for external interrupt), or zero. @@ -2330,7 +2338,15 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) /* hypervisor doorbell */ 3: li r12, BOOK3S_INTERRUPT_H_DOORBELL + /* see if it's a host IPI */ li r3, 1 + lbz r0, HSTATE_HOST_IPI(r13) + cmpwi r0, 0 + bnelr + /* if not, clear it and return -1 */ + lis r6, (PPC_DBELL_SERVER << (63-36))@h + PPC_MSGCLR(6) + li r3, -1 blr /* -- cgit v1.2.3 From 68fc378ce332cc4efd7f314d3e6e15e83f53ebf2 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 30 Apr 2015 15:13:14 +1000 Subject: Revert "powerpc/tm: Abort syscalls in active transactions" This reverts commit feba40362b11341bee6d8ed58d54b896abbd9f84. Although the principle of this change is good, the implementation has a few issues. Firstly we can sometimes fail to abort a syscall because r12 may have been clobbered by C code if we went down the virtual CPU accounting path, or if syscall tracing was enabled. Secondly we have decided that it is safer to abort the syscall even earlier in the syscall entry path, so that we avoid the syscall tracing path when we are transactional. So that we have time to thoroughly test those changes we have decided to revert this for this merge window and will merge the fixed version in the next window. NB. Rather than reverting the selftest we just drop tm-syscall from TEST_PROGS so that it's not run by default. Fixes: feba40362b11 ("powerpc/tm: Abort syscalls in active transactions") Signed-off-by: Michael Ellerman --- Documentation/powerpc/transactional_memory.txt | 32 +++++++++++++------------- arch/powerpc/include/uapi/asm/tm.h | 2 +- arch/powerpc/kernel/entry_64.S | 19 --------------- tools/testing/selftests/powerpc/tm/Makefile | 2 +- 4 files changed, 18 insertions(+), 37 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/Documentation/powerpc/transactional_memory.txt b/Documentation/powerpc/transactional_memory.txt index ba0a2a4a54ba..ded69794a5c0 100644 --- a/Documentation/powerpc/transactional_memory.txt +++ b/Documentation/powerpc/transactional_memory.txt @@ -74,23 +74,22 @@ Causes of transaction aborts Syscalls ======== -Syscalls made from within an active transaction will not be performed and the -transaction will be doomed by the kernel with the failure code TM_CAUSE_SYSCALL -| TM_CAUSE_PERSISTENT. +Performing syscalls from within transaction is not recommended, and can lead +to unpredictable results. -Syscalls made from within a suspended transaction are performed as normal and -the transaction is not explicitly doomed by the kernel. However, what the -kernel does to perform the syscall may result in the transaction being doomed -by the hardware. The syscall is performed in suspended mode so any side -effects will be persistent, independent of transaction success or failure. No -guarantees are provided by the kernel about which syscalls will affect -transaction success. +Syscalls do not by design abort transactions, but beware: The kernel code will +not be running in transactional state. The effect of syscalls will always +remain visible, but depending on the call they may abort your transaction as a +side-effect, read soon-to-be-aborted transactional data that should not remain +invisible, etc. If you constantly retry a transaction that constantly aborts +itself by calling a syscall, you'll have a livelock & make no progress. -Care must be taken when relying on syscalls to abort during active transactions -if the calls are made via a library. Libraries may cache values (which may -give the appearance of success) or perform operations that cause transaction -failure before entering the kernel (which may produce different failure codes). -Examples are glibc's getpid() and lazy symbol resolution. +Simple syscalls (e.g. sigprocmask()) "could" be OK. Even things like write() +from, say, printf() should be OK as long as the kernel does not access any +memory that was accessed transactionally. + +Consider any syscalls that happen to work as debug-only -- not recommended for +production use. Best to queue them up till after the transaction is over. Signals @@ -177,7 +176,8 @@ kernel aborted a transaction: TM_CAUSE_RESCHED Thread was rescheduled. TM_CAUSE_TLBI Software TLB invalid. TM_CAUSE_FAC_UNAV FP/VEC/VSX unavailable trap. - TM_CAUSE_SYSCALL Syscall from active transaction. + TM_CAUSE_SYSCALL Currently unused; future syscalls that must abort + transactions for consistency will use this. TM_CAUSE_SIGNAL Signal delivered. TM_CAUSE_MISC Currently unused. TM_CAUSE_ALIGNMENT Alignment fault. diff --git a/arch/powerpc/include/uapi/asm/tm.h b/arch/powerpc/include/uapi/asm/tm.h index 5047659815a5..5d836b7c1176 100644 --- a/arch/powerpc/include/uapi/asm/tm.h +++ b/arch/powerpc/include/uapi/asm/tm.h @@ -11,7 +11,7 @@ #define TM_CAUSE_RESCHED 0xde #define TM_CAUSE_TLBI 0xdc #define TM_CAUSE_FAC_UNAV 0xda -#define TM_CAUSE_SYSCALL 0xd8 +#define TM_CAUSE_SYSCALL 0xd8 /* future use */ #define TM_CAUSE_MISC 0xd6 /* future use */ #define TM_CAUSE_SIGNAL 0xd4 #define TM_CAUSE_ALIGNMENT 0xd2 diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 8ca9434c40e6..afbc20019c2e 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -34,7 +34,6 @@ #include #include #include -#include /* * System calls. @@ -146,24 +145,6 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_SPLPAR) andi. r11,r10,_TIF_SYSCALL_DOTRACE bne syscall_dotrace .Lsyscall_dotrace_cont: -#ifdef CONFIG_PPC_TRANSACTIONAL_MEM -BEGIN_FTR_SECTION - b 1f -END_FTR_SECTION_IFCLR(CPU_FTR_TM) - extrdi. r11, r12, 1, (63-MSR_TS_T_LG) /* transaction active? */ - beq+ 1f - - /* Doom the transaction and don't perform the syscall: */ - mfmsr r11 - li r12, 1 - rldimi r11, r12, MSR_TM_LG, 63-MSR_TM_LG - mtmsrd r11, 0 - li r11, (TM_CAUSE_SYSCALL|TM_CAUSE_PERSISTENT) - TABORT(R11) - - b .Lsyscall_exit -1: -#endif cmpldi 0,r0,NR_syscalls bge- syscall_enosys diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile index 1b616fa79e93..6bff955e1d55 100644 --- a/tools/testing/selftests/powerpc/tm/Makefile +++ b/tools/testing/selftests/powerpc/tm/Makefile @@ -1,4 +1,4 @@ -TEST_PROGS := tm-resched-dscr tm-syscall +TEST_PROGS := tm-resched-dscr all: $(TEST_PROGS) -- cgit v1.2.3 From 1ae79b78bc52b910a224f3795122538516e07b5f Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Fri, 1 May 2015 09:14:11 +1000 Subject: powerpc/eeh: Fix race condition in pcibios_set_pcie_reset_state() When asserting reset in pcibios_set_pcie_reset_state(), the PE is enforced to (hardware) frozen state in order to drop unexpected PCI transactions (except PCI config read/write) automatically by hardware during reset, which would cause recursive EEH error. However, the (software) frozen state EEH_PE_ISOLATED is missed. When users get 0xFF from PCI config or MMIO read, EEH_PE_ISOLATED is set in PE state retrival backend. Unfortunately, nobody (the reset handler or the EEH recovery functinality in host) will clear EEH_PE_ISOLATED when the PE has been passed through to guest. The patch sets and clears EEH_PE_ISOLATED properly during reset in function pcibios_set_pcie_reset_state() to fix the issue. Fixes: 28158cd ("Enhance pcibios_set_pcie_reset_state()") Reported-by: Carol L. Soto Signed-off-by: Gavin Shan Tested-by: Carol L. Soto Signed-off-by: Michael Ellerman --- arch/powerpc/kernel/eeh.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 44b480e3a5af..fa046ca6d0fa 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -749,21 +749,24 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat eeh_unfreeze_pe(pe, false); eeh_pe_state_clear(pe, EEH_PE_CFG_BLOCKED); eeh_pe_dev_traverse(pe, eeh_restore_dev_state, dev); + eeh_pe_state_clear(pe, EEH_PE_ISOLATED); break; case pcie_hot_reset: + eeh_pe_state_mark(pe, EEH_PE_ISOLATED); eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE); eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev); eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED); eeh_ops->reset(pe, EEH_RESET_HOT); break; case pcie_warm_reset: + eeh_pe_state_mark(pe, EEH_PE_ISOLATED); eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE); eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev); eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED); eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL); break; default: - eeh_pe_state_clear(pe, EEH_PE_CFG_BLOCKED); + eeh_pe_state_clear(pe, EEH_PE_ISOLATED | EEH_PE_CFG_BLOCKED); return -EINVAL; }; -- cgit v1.2.3 From d91dafc02f42e23c1a906202ebde5d7c49ef058d Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Fri, 1 May 2015 09:22:15 +1000 Subject: powerpc/eeh: Delay probing EEH device during hotplug Commit 1c509148b ("powerpc/eeh: Do probe on pci_dn") probes EEH devices in early stage, which is reasonable to pSeries platform. However, it's wrong for PowerNV platform because the PE# isn't determined until the resources (IO and MMIO) are assigned to PE in hotplug case. So we have to delay probing EEH devices for PowerNV platform until the PE# is assigned. Fixes: ff57b454ddb9 ("powerpc/eeh: Do probe on pci_dn") Signed-off-by: Gavin Shan Signed-off-by: Michael Ellerman --- arch/powerpc/kernel/eeh.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index fa046ca6d0fa..9ee61d15653d 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -1061,6 +1061,9 @@ void eeh_add_device_early(struct pci_dn *pdn) if (!edev || !eeh_enabled()) return; + if (!eeh_has_flag(EEH_PROBE_MODE_DEVTREE)) + return; + /* USB Bus children of PCI devices will not have BUID's */ phb = edev->phb; if (NULL == phb || @@ -1115,6 +1118,9 @@ void eeh_add_device_late(struct pci_dev *dev) return; } + if (eeh_has_flag(EEH_PROBE_MODE_DEV)) + eeh_ops->probe(pdn, NULL); + /* * The EEH cache might not be removed correctly because of * unbalanced kref to the device during unplug time, which -- cgit v1.2.3 From 0aab3747091db309b8a484cfd382a41644552aa3 Mon Sep 17 00:00:00 2001 From: Sam Bobroff Date: Fri, 1 May 2015 16:50:34 +1000 Subject: powerpc/powernv: Restore non-volatile CRs after nap Patches 7cba160ad "powernv/cpuidle: Redesign idle states management" and 77b54e9f2 "powernv/powerpc: Add winkle support for offline cpus" use non-volatile condition registers (cr2, cr3 and cr4) early in the system reset interrupt handler (system_reset_pSeries()) before it has been determined if state loss has occurred. If state loss has not occurred, control returns via the power7_wakeup_noloss() path which does not restore those condition registers, leaving them corrupted. Fix this by restoring the condition registers in the power7_wakeup_noloss() case. This is apparent when running a KVM guest on hardware that does not support winkle or sleep and the guest makes use of secondary threads. In practice this means Power7 machines, though some early unreleased Power8 machines may also be susceptible. The secondary CPUs are taken off line before the guest is started and they call pnv_smp_cpu_kill_self(). This checks support for sleep states (in this case there is no support) and power7_nap() is called. When the CPU is woken, power7_nap() returns and because the CPU is still off line, the main while loop executes again. The sleep states support test is executed again, but because the tested values cannot have changed, the compiler has optimized the test away and instead we rely on the result of the first test, which has been left in cr3 and/or cr4. With the result overwritten, the wrong branch is taken and power7_winkle() is called on a CPU that does not support it, leading to it stalling. Fixes: 7cba160ad789 ("powernv/cpuidle: Redesign idle states management") Fixes: 77b54e9f213f ("powernv/powerpc: Add winkle support for offline cpus") [mpe: Massage change log a bit more] Signed-off-by: Sam Bobroff Signed-off-by: Michael Ellerman --- arch/powerpc/kernel/idle_power7.S | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/idle_power7.S b/arch/powerpc/kernel/idle_power7.S index eeaa0d5f69d5..ccde8f084ce4 100644 --- a/arch/powerpc/kernel/idle_power7.S +++ b/arch/powerpc/kernel/idle_power7.S @@ -501,9 +501,11 @@ BEGIN_FTR_SECTION CHECK_HMI_INTERRUPT END_FTR_SECTION_IFSET(CPU_FTR_HVMODE) ld r1,PACAR1(r13) + ld r6,_CCR(r1) ld r4,_MSR(r1) ld r5,_NIP(r1) addi r1,r1,INT_FRAME_SIZE + mtcr r6 mtspr SPRN_SRR1,r4 mtspr SPRN_SRR0,r5 rfid -- cgit v1.2.3 From ffb2d78eca08a1451137583d4e435aecfd6af809 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 12 May 2015 13:23:59 +1000 Subject: powerpc/mce: fix off by one errors in mce event handling Before 69111bac42f5 ("powerpc: Replace __get_cpu_var uses"), in save_mce_event, index got the value of mce_nest_count, and mce_nest_count was incremented *after* index was set. However, that patch changed the behaviour so that mce_nest count was incremented *before* setting index. This causes an off-by-one error, as get_mce_event sets index as mce_nest_count - 1 before reading mce_event. Thus get_mce_event reads bogus data, causing warnings like "Machine Check Exception, Unknown event version 0 !" and breaking MCEs handling. Restore the old behaviour and unbreak MCE handling by subtracting one from the newly incremented value. The same broken change occured in machine_check_queue_event (which set a queue read by machine_check_process_queued_event). Fix that too, unbreaking printing of MCE information. Fixes: 69111bac42f5 ("powerpc: Replace __get_cpu_var uses") CC: stable@vger.kernel.org CC: Mahesh Salgaonkar CC: Christoph Lameter Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- arch/powerpc/kernel/mce.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/mce.c b/arch/powerpc/kernel/mce.c index 15c99b649b04..b2eb4686bd8f 100644 --- a/arch/powerpc/kernel/mce.c +++ b/arch/powerpc/kernel/mce.c @@ -73,7 +73,7 @@ void save_mce_event(struct pt_regs *regs, long handled, uint64_t nip, uint64_t addr) { uint64_t srr1; - int index = __this_cpu_inc_return(mce_nest_count); + int index = __this_cpu_inc_return(mce_nest_count) - 1; struct machine_check_event *mce = this_cpu_ptr(&mce_event[index]); /* @@ -184,7 +184,7 @@ void machine_check_queue_event(void) if (!get_mce_event(&evt, MCE_EVENT_RELEASE)) return; - index = __this_cpu_inc_return(mce_queue_count); + index = __this_cpu_inc_return(mce_queue_count) - 1; /* If queue is full, just return for now. */ if (index >= MAX_MC_EVT) { __this_cpu_dec(mce_queue_count); -- cgit v1.2.3 From 5e95235ccd5442d4a4fe11ec4eb99ba1b7959368 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 14 May 2015 14:45:40 +1000 Subject: powerpc: Align TOC to 256 bytes Recent toolchains force the TOC to be 256 byte aligned. We need to enforce this alignment in our linker script, otherwise pointers to our TOC variables (__toc_start, __prom_init_toc_start) could be incorrect. If they are bad, we die a few hundred instructions into boot. Cc: stable@vger.kernel.org Signed-off-by: Anton Blanchard Signed-off-by: Michael Ellerman --- arch/powerpc/kernel/vmlinux.lds.S | 1 + 1 file changed, 1 insertion(+) (limited to 'arch/powerpc/kernel') diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S index f096e72262f4..1db685104ffc 100644 --- a/arch/powerpc/kernel/vmlinux.lds.S +++ b/arch/powerpc/kernel/vmlinux.lds.S @@ -213,6 +213,7 @@ SECTIONS *(.opd) } + . = ALIGN(256); .got : AT(ADDR(.got) - LOAD_OFFSET) { __toc_start = .; #ifndef CONFIG_RELOCATABLE -- cgit v1.2.3