aboutsummaryrefslogtreecommitdiff
path: root/arch/sparc64/kernel
diff options
context:
space:
mode:
authorJeff Garzik2005-10-18 18:30:58 -0400
committerJeff Garzik2005-10-18 18:30:58 -0400
commit77501f3cb648e18733509a951ed31eddd7ef2c0b (patch)
tree82624caa354394df2422562bf6dda56a2c088704 /arch/sparc64/kernel
parent422fa08e538b649a9b80258950d2f8a202f45f19 (diff)
parent59a10b172fccaea793352c00fd9065f0a5b4ef70 (diff)
Merge branch 'upstream'
Diffstat (limited to 'arch/sparc64/kernel')
-rw-r--r--arch/sparc64/kernel/dtlb_base.S14
-rw-r--r--arch/sparc64/kernel/dtlb_prot.S12
-rw-r--r--arch/sparc64/kernel/entry.S39
-rw-r--r--arch/sparc64/kernel/head.S166
-rw-r--r--arch/sparc64/kernel/irq.c1
-rw-r--r--arch/sparc64/kernel/itlb_base.S26
-rw-r--r--arch/sparc64/kernel/ktlb.S92
-rw-r--r--arch/sparc64/kernel/pci_iommu.c363
-rw-r--r--arch/sparc64/kernel/pci_psycho.c44
-rw-r--r--arch/sparc64/kernel/pci_sabre.c39
-rw-r--r--arch/sparc64/kernel/pci_schizo.c57
-rw-r--r--arch/sparc64/kernel/power.c64
-rw-r--r--arch/sparc64/kernel/rtrap.S7
-rw-r--r--arch/sparc64/kernel/smp.c7
14 files changed, 422 insertions, 509 deletions
diff --git a/arch/sparc64/kernel/dtlb_base.S b/arch/sparc64/kernel/dtlb_base.S
index 702d349c1e88..6528786840c0 100644
--- a/arch/sparc64/kernel/dtlb_base.S
+++ b/arch/sparc64/kernel/dtlb_base.S
@@ -53,19 +53,18 @@
* be guaranteed to be 0 ... mmu_context.h does guarantee this
* by only using 10 bits in the hwcontext value.
*/
-#define CREATE_VPTE_OFFSET1(r1, r2)
+#define CREATE_VPTE_OFFSET1(r1, r2) nop
#define CREATE_VPTE_OFFSET2(r1, r2) \
srax r1, 10, r2
-#define CREATE_VPTE_NOP nop
#else
#define CREATE_VPTE_OFFSET1(r1, r2) \
srax r1, PAGE_SHIFT, r2
#define CREATE_VPTE_OFFSET2(r1, r2) \
sllx r2, 3, r2
-#define CREATE_VPTE_NOP
#endif
/* DTLB ** ICACHE line 1: Quick user TLB misses */
+ mov TLB_SFSR, %g1
ldxa [%g1 + %g1] ASI_DMMU, %g4 ! Get TAG_ACCESS
andcc %g4, TAG_CONTEXT_BITS, %g0 ! From Nucleus?
from_tl1_trap:
@@ -74,18 +73,16 @@ from_tl1_trap:
be,pn %xcc, kvmap ! Yep, special processing
CREATE_VPTE_OFFSET2(%g4, %g6) ! Create VPTE offset
cmp %g5, 4 ! Last trap level?
- be,pn %xcc, longpath ! Yep, cannot risk VPTE miss
- nop ! delay slot
/* DTLB ** ICACHE line 2: User finish + quick kernel TLB misses */
+ be,pn %xcc, longpath ! Yep, cannot risk VPTE miss
+ nop ! delay slot
ldxa [%g3 + %g6] ASI_S, %g5 ! Load VPTE
1: brgez,pn %g5, longpath ! Invalid, branch out
nop ! Delay-slot
9: stxa %g5, [%g0] ASI_DTLB_DATA_IN ! Reload TLB
retry ! Trap return
nop
- nop
- nop
/* DTLB ** ICACHE line 3: winfixups+real_faults */
longpath:
@@ -106,8 +103,7 @@ longpath:
nop
nop
nop
- CREATE_VPTE_NOP
+ nop
#undef CREATE_VPTE_OFFSET1
#undef CREATE_VPTE_OFFSET2
-#undef CREATE_VPTE_NOP
diff --git a/arch/sparc64/kernel/dtlb_prot.S b/arch/sparc64/kernel/dtlb_prot.S
index d848bb7374bb..e0a920162604 100644
--- a/arch/sparc64/kernel/dtlb_prot.S
+++ b/arch/sparc64/kernel/dtlb_prot.S
@@ -14,14 +14,14 @@
*/
/* PROT ** ICACHE line 1: User DTLB protection trap */
- stxa %g0, [%g1] ASI_DMMU ! Clear SFSR FaultValid bit
- membar #Sync ! Synchronize ASI stores
- rdpr %pstate, %g5 ! Move into alternate globals
+ mov TLB_SFSR, %g1
+ stxa %g0, [%g1] ASI_DMMU ! Clear FaultValid bit
+ membar #Sync ! Synchronize stores
+ rdpr %pstate, %g5 ! Move into alt-globals
wrpr %g5, PSTATE_AG|PSTATE_MG, %pstate
- rdpr %tl, %g1 ! Need to do a winfixup?
+ rdpr %tl, %g1 ! Need a winfixup?
cmp %g1, 1 ! Trap level >1?
- mov TLB_TAG_ACCESS, %g4 ! Prepare reload of vaddr
- nop
+ mov TLB_TAG_ACCESS, %g4 ! For reload of vaddr
/* PROT ** ICACHE line 2: More real fault processing */
bgu,pn %xcc, winfix_trampoline ! Yes, perform winfixup
diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S
index f685035dbdb8..11a848402fb1 100644
--- a/arch/sparc64/kernel/entry.S
+++ b/arch/sparc64/kernel/entry.S
@@ -33,7 +33,7 @@
/* This is trivial with the new code... */
.globl do_fpdis
do_fpdis:
- sethi %hi(TSTATE_PEF), %g4 ! IEU0
+ sethi %hi(TSTATE_PEF), %g4
rdpr %tstate, %g5
andcc %g5, %g4, %g0
be,pt %xcc, 1f
@@ -50,18 +50,18 @@ do_fpdis:
add %g0, %g0, %g0
ba,a,pt %xcc, rtrap_clr_l6
-1: ldub [%g6 + TI_FPSAVED], %g5 ! Load Group
- wr %g0, FPRS_FEF, %fprs ! LSU Group+4bubbles
- andcc %g5, FPRS_FEF, %g0 ! IEU1 Group
- be,a,pt %icc, 1f ! CTI
- clr %g7 ! IEU0
- ldx [%g6 + TI_GSR], %g7 ! Load Group
-1: andcc %g5, FPRS_DL, %g0 ! IEU1
- bne,pn %icc, 2f ! CTI
- fzero %f0 ! FPA
- andcc %g5, FPRS_DU, %g0 ! IEU1 Group
- bne,pn %icc, 1f ! CTI
- fzero %f2 ! FPA
+1: ldub [%g6 + TI_FPSAVED], %g5
+ wr %g0, FPRS_FEF, %fprs
+ andcc %g5, FPRS_FEF, %g0
+ be,a,pt %icc, 1f
+ clr %g7
+ ldx [%g6 + TI_GSR], %g7
+1: andcc %g5, FPRS_DL, %g0
+ bne,pn %icc, 2f
+ fzero %f0
+ andcc %g5, FPRS_DU, %g0
+ bne,pn %icc, 1f
+ fzero %f2
faddd %f0, %f2, %f4
fmuld %f0, %f2, %f6
faddd %f0, %f2, %f8
@@ -104,8 +104,10 @@ do_fpdis:
add %g6, TI_FPREGS + 0xc0, %g2
faddd %f0, %f2, %f8
fmuld %f0, %f2, %f10
- ldda [%g1] ASI_BLK_S, %f32 ! grrr, where is ASI_BLK_NUCLEUS 8-(
+ membar #Sync
+ ldda [%g1] ASI_BLK_S, %f32
ldda [%g2] ASI_BLK_S, %f48
+ membar #Sync
faddd %f0, %f2, %f12
fmuld %f0, %f2, %f14
faddd %f0, %f2, %f16
@@ -116,7 +118,6 @@ do_fpdis:
fmuld %f0, %f2, %f26
faddd %f0, %f2, %f28
fmuld %f0, %f2, %f30
- membar #Sync
b,pt %xcc, fpdis_exit
nop
2: andcc %g5, FPRS_DU, %g0
@@ -133,8 +134,10 @@ do_fpdis:
add %g6, TI_FPREGS + 0x40, %g2
faddd %f32, %f34, %f36
fmuld %f32, %f34, %f38
- ldda [%g1] ASI_BLK_S, %f0 ! grrr, where is ASI_BLK_NUCLEUS 8-(
+ membar #Sync
+ ldda [%g1] ASI_BLK_S, %f0
ldda [%g2] ASI_BLK_S, %f16
+ membar #Sync
faddd %f32, %f34, %f40
fmuld %f32, %f34, %f42
faddd %f32, %f34, %f44
@@ -147,7 +150,6 @@ do_fpdis:
fmuld %f32, %f34, %f58
faddd %f32, %f34, %f60
fmuld %f32, %f34, %f62
- membar #Sync
ba,pt %xcc, fpdis_exit
nop
3: mov SECONDARY_CONTEXT, %g3
@@ -158,7 +160,8 @@ do_fpdis:
stxa %g2, [%g3] ASI_DMMU
membar #Sync
mov 0x40, %g2
- ldda [%g1] ASI_BLK_S, %f0 ! grrr, where is ASI_BLK_NUCLEUS 8-(
+ membar #Sync
+ ldda [%g1] ASI_BLK_S, %f0
ldda [%g1 + %g2] ASI_BLK_S, %f16
add %g1, 0x80, %g1
ldda [%g1] ASI_BLK_S, %f32
diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S
index 24340496cdd3..b49dcd4504b0 100644
--- a/arch/sparc64/kernel/head.S
+++ b/arch/sparc64/kernel/head.S
@@ -28,19 +28,14 @@
#include <asm/mmu.h>
/* This section from from _start to sparc64_boot_end should fit into
- * 0x0000.0000.0040.4000 to 0x0000.0000.0040.8000 and will be sharing space
- * with bootup_user_stack, which is from 0x0000.0000.0040.4000 to
- * 0x0000.0000.0040.6000 and empty_bad_page, which is from
- * 0x0000.0000.0040.6000 to 0x0000.0000.0040.8000.
+ * 0x0000000000404000 to 0x0000000000408000.
*/
-
.text
.globl start, _start, stext, _stext
_start:
start:
_stext:
stext:
-bootup_user_stack:
! 0x0000000000404000
b sparc64_boot
flushw /* Flush register file. */
@@ -191,8 +186,9 @@ prom_boot_mapping_phys_low:
stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 5
stx %l2, [%sp + 2047 + 128 + 0x18] ! arg1: "translate"
stx %l5, [%sp + 2047 + 128 + 0x20] ! arg2: prom_mmu_ihandle_cache
- srlx %l0, 22, %l3
- sllx %l3, 22, %l3
+ /* PAGE align */
+ srlx %l0, 13, %l3
+ sllx %l3, 13, %l3
stx %l3, [%sp + 2047 + 128 + 0x28] ! arg3: vaddr, our PC
stx %g0, [%sp + 2047 + 128 + 0x30] ! res1
stx %g0, [%sp + 2047 + 128 + 0x38] ! res2
@@ -211,6 +207,9 @@ prom_boot_mapping_phys_low:
ldx [%sp + 2047 + 128 + 0x48], %l2 ! physaddr high
stx %l2, [%l4 + 0x0]
ldx [%sp + 2047 + 128 + 0x50], %l3 ! physaddr low
+ /* 4MB align */
+ srlx %l3, 22, %l3
+ sllx %l3, 22, %l3
stx %l3, [%l4 + 0x8]
/* Leave service as-is, "call-method" */
@@ -382,32 +381,78 @@ tlb_fixup_done:
nop
/* Not reached... */
-/* IMPORTANT NOTE: Whenever making changes here, check
- * trampoline.S as well. -jj */
- .globl setup_tba
-setup_tba: /* i0 = is_starfire */
- save %sp, -160, %sp
+ /* This is meant to allow the sharing of this code between
+ * boot processor invocation (via setup_tba() below) and
+ * secondary processor startup (via trampoline.S). The
+ * former does use this code, the latter does not yet due
+ * to some complexities. That should be fixed up at some
+ * point.
+ *
+ * There used to be enormous complexity wrt. transferring
+ * over from the firwmare's trap table to the Linux kernel's.
+ * For example, there was a chicken & egg problem wrt. building
+ * the OBP page tables, yet needing to be on the Linux kernel
+ * trap table (to translate PAGE_OFFSET addresses) in order to
+ * do that.
+ *
+ * We now handle OBP tlb misses differently, via linear lookups
+ * into the prom_trans[] array. So that specific problem no
+ * longer exists. Yet, unfortunately there are still some issues
+ * preventing trampoline.S from using this code... ho hum.
+ */
+ .globl setup_trap_table
+setup_trap_table:
+ save %sp, -192, %sp
- rdpr %tba, %g7
- sethi %hi(prom_tba), %o1
- or %o1, %lo(prom_tba), %o1
- stx %g7, [%o1]
+ /* Force interrupts to be disabled. */
+ rdpr %pstate, %o1
+ andn %o1, PSTATE_IE, %o1
+ wrpr %o1, 0x0, %pstate
+ wrpr %g0, 15, %pil
+
+ /* Make the firmware call to jump over to the Linux trap table. */
+ call prom_set_trap_table
+ sethi %hi(sparc64_ttable_tl0), %o0
+
+ /* Start using proper page size encodings in ctx register. */
+ sethi %hi(sparc64_kern_pri_context), %g3
+ ldx [%g3 + %lo(sparc64_kern_pri_context)], %g2
+ mov PRIMARY_CONTEXT, %g1
+ stxa %g2, [%g1] ASI_DMMU
+ membar #Sync
+
+ /* The Linux trap handlers expect various trap global registers
+ * to be setup with some fixed values. So here we set these
+ * up very carefully. These globals are:
+ *
+ * Alternate Globals (PSTATE_AG):
+ *
+ * %g6 --> current_thread_info()
+ *
+ * MMU Globals (PSTATE_MG):
+ *
+ * %g1 --> TLB_SFSR
+ * %g2 --> ((_PAGE_VALID | _PAGE_SZ4MB |
+ * _PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W)
+ * ^ 0xfffff80000000000)
+ * (this %g2 value is used for computing the PAGE_OFFSET kernel
+ * TLB entries quickly, the virtual address of the fault XOR'd
+ * with this %g2 value is the PTE to load into the TLB)
+ * %g3 --> VPTE_BASE_CHEETAH or VPTE_BASE_SPITFIRE
+ *
+ * Interrupt Globals (PSTATE_IG, setup by init_irqwork_curcpu()):
+ *
+ * %g6 --> __irq_work[smp_processor_id()]
+ */
- /* Setup "Linux" globals 8-) */
rdpr %pstate, %o1
mov %g6, %o2
- wrpr %o1, (PSTATE_AG|PSTATE_IE), %pstate
- sethi %hi(sparc64_ttable_tl0), %g1
- wrpr %g1, %tba
+ wrpr %o1, PSTATE_AG, %pstate
mov %o2, %g6
- /* Set up MMU globals */
- wrpr %o1, (PSTATE_MG|PSTATE_IE), %pstate
-
- /* Set fixed globals used by dTLB miss handler. */
#define KERN_HIGHBITS ((_PAGE_VALID|_PAGE_SZ4MB)^0xfffff80000000000)
#define KERN_LOWBITS (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W)
-
+ wrpr %o1, PSTATE_MG, %pstate
mov TSB_REG, %g1
stxa %g0, [%g1] ASI_DMMU
membar #Sync
@@ -419,17 +464,17 @@ setup_tba: /* i0 = is_starfire */
sllx %g2, 32, %g2
or %g2, KERN_LOWBITS, %g2
- BRANCH_IF_ANY_CHEETAH(g3,g7,cheetah_vpte_base)
- ba,pt %xcc, spitfire_vpte_base
+ BRANCH_IF_ANY_CHEETAH(g3,g7,8f)
+ ba,pt %xcc, 9f
nop
-cheetah_vpte_base:
+8:
sethi %uhi(VPTE_BASE_CHEETAH), %g3
or %g3, %ulo(VPTE_BASE_CHEETAH), %g3
ba,pt %xcc, 2f
sllx %g3, 32, %g3
-spitfire_vpte_base:
+9:
sethi %uhi(VPTE_BASE_SPITFIRE), %g3
or %g3, %ulo(VPTE_BASE_SPITFIRE), %g3
sllx %g3, 32, %g3
@@ -455,41 +500,55 @@ spitfire_vpte_base:
sllx %o2, 32, %o2
wr %o2, %asr25
- /* Ok, we're done setting up all the state our trap mechanims needs,
- * now get back into normal globals and let the PROM know what is up.
- */
2:
wrpr %g0, %g0, %wstate
- wrpr %o1, PSTATE_IE, %pstate
+ wrpr %o1, 0x0, %pstate
call init_irqwork_curcpu
nop
- call prom_set_trap_table
- sethi %hi(sparc64_ttable_tl0), %o0
-
- /* Start using proper page size encodings in ctx register. */
- sethi %hi(sparc64_kern_pri_context), %g3
- ldx [%g3 + %lo(sparc64_kern_pri_context)], %g2
- mov PRIMARY_CONTEXT, %g1
- stxa %g2, [%g1] ASI_DMMU
- membar #Sync
-
+ /* Now we can turn interrupts back on. */
rdpr %pstate, %o1
or %o1, PSTATE_IE, %o1
wrpr %o1, 0, %pstate
+ wrpr %g0, 0x0, %pil
ret
restore
+ .globl setup_tba
+setup_tba: /* i0 = is_starfire */
+ save %sp, -192, %sp
+
+ /* The boot processor is the only cpu which invokes this
+ * routine, the other cpus set things up via trampoline.S.
+ * So save the OBP trap table address here.
+ */
+ rdpr %tba, %g7
+ sethi %hi(prom_tba), %o1
+ or %o1, %lo(prom_tba), %o1
+ stx %g7, [%o1]
+
+ call setup_trap_table
+ nop
+
+ ret
+ restore
+sparc64_boot_end:
+
+#include "systbls.S"
+#include "ktlb.S"
+#include "etrap.S"
+#include "rtrap.S"
+#include "winfixup.S"
+#include "entry.S"
+
/*
- * The following skips make sure the trap table in ttable.S is aligned
+ * The following skip makes sure the trap table in ttable.S is aligned
* on a 32K boundary as required by the v9 specs for TBA register.
*/
-sparc64_boot_end:
- .skip 0x2000 + _start - sparc64_boot_end
-bootup_user_stack_end:
- .skip 0x2000
+1:
+ .skip 0x4000 + _start - 1b
#ifdef CONFIG_SBUS
/* This is just a hack to fool make depend config.h discovering
@@ -501,15 +560,6 @@ bootup_user_stack_end:
! 0x0000000000408000
#include "ttable.S"
-#include "systbls.S"
-#include "ktlb.S"
-#include "etrap.S"
-#include "rtrap.S"
-#include "winfixup.S"
-#include "entry.S"
-
- /* This is just anal retentiveness on my part... */
- .align 16384
.data
.align 8
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c
index c9b69167632a..233526ba3abe 100644
--- a/arch/sparc64/kernel/irq.c
+++ b/arch/sparc64/kernel/irq.c
@@ -27,6 +27,7 @@
#include <asm/atomic.h>
#include <asm/system.h>
#include <asm/irq.h>
+#include <asm/io.h>
#include <asm/sbus.h>
#include <asm/iommu.h>
#include <asm/upa.h>
diff --git a/arch/sparc64/kernel/itlb_base.S b/arch/sparc64/kernel/itlb_base.S
index b5e32dfa4fbc..4951ff8f6877 100644
--- a/arch/sparc64/kernel/itlb_base.S
+++ b/arch/sparc64/kernel/itlb_base.S
@@ -15,14 +15,12 @@
*/
#define CREATE_VPTE_OFFSET1(r1, r2) \
srax r1, 10, r2
-#define CREATE_VPTE_OFFSET2(r1, r2)
-#define CREATE_VPTE_NOP nop
+#define CREATE_VPTE_OFFSET2(r1, r2) nop
#else /* PAGE_SHIFT */
#define CREATE_VPTE_OFFSET1(r1, r2) \
srax r1, PAGE_SHIFT, r2
#define CREATE_VPTE_OFFSET2(r1, r2) \
sllx r2, 3, r2
-#define CREATE_VPTE_NOP
#endif /* PAGE_SHIFT */
@@ -36,6 +34,7 @@
*/
/* ITLB ** ICACHE line 1: Quick user TLB misses */
+ mov TLB_SFSR, %g1
ldxa [%g1 + %g1] ASI_IMMU, %g4 ! Get TAG_ACCESS
CREATE_VPTE_OFFSET1(%g4, %g6) ! Create VPTE offset
CREATE_VPTE_OFFSET2(%g4, %g6) ! Create VPTE offset
@@ -43,41 +42,38 @@
1: brgez,pn %g5, 3f ! Not valid, branch out
sethi %hi(_PAGE_EXEC), %g4 ! Delay-slot
andcc %g5, %g4, %g0 ! Executable?
+
+/* ITLB ** ICACHE line 2: Real faults */
be,pn %xcc, 3f ! Nope, branch.
nop ! Delay-slot
2: stxa %g5, [%g0] ASI_ITLB_DATA_IN ! Load PTE into TLB
retry ! Trap return
-3: rdpr %pstate, %g4 ! Move into alternate globals
-
-/* ITLB ** ICACHE line 2: Real faults */
+3: rdpr %pstate, %g4 ! Move into alt-globals
wrpr %g4, PSTATE_AG|PSTATE_MG, %pstate
rdpr %tpc, %g5 ! And load faulting VA
mov FAULT_CODE_ITLB, %g4 ! It was read from ITLB
-sparc64_realfault_common: ! Called by TL0 dtlb_miss too
+
+/* ITLB ** ICACHE line 3: Finish faults */
+sparc64_realfault_common: ! Called by dtlb_miss
stb %g4, [%g6 + TI_FAULT_CODE]
stx %g5, [%g6 + TI_FAULT_ADDR]
ba,pt %xcc, etrap ! Save state
1: rd %pc, %g7 ! ...
- nop
-
-/* ITLB ** ICACHE line 3: Finish faults + window fixups */
call do_sparc64_fault ! Call fault handler
add %sp, PTREGS_OFF, %o0! Compute pt_regs arg
ba,pt %xcc, rtrap_clr_l6 ! Restore cpu state
nop
+
+/* ITLB ** ICACHE line 4: Window fixups */
winfix_trampoline:
rdpr %tpc, %g3 ! Prepare winfixup TNPC
- or %g3, 0x7c, %g3 ! Compute offset to branch
+ or %g3, 0x7c, %g3 ! Compute branch offset
wrpr %g3, %tnpc ! Write it into TNPC
done ! Do it to it
-
-/* ITLB ** ICACHE line 4: Unused... */
nop
nop
nop
nop
- CREATE_VPTE_NOP
#undef CREATE_VPTE_OFFSET1
#undef CREATE_VPTE_OFFSET2
-#undef CREATE_VPTE_NOP
diff --git a/arch/sparc64/kernel/ktlb.S b/arch/sparc64/kernel/ktlb.S
index 7796b37f478c..d9244d3c9f73 100644
--- a/arch/sparc64/kernel/ktlb.S
+++ b/arch/sparc64/kernel/ktlb.S
@@ -58,9 +58,6 @@ vpte_noent:
done
vpte_insn_obp:
- sethi %hi(prom_pmd_phys), %g5
- ldx [%g5 + %lo(prom_pmd_phys)], %g5
-
/* Behave as if we are at TL0. */
wrpr %g0, 1, %tl
rdpr %tpc, %g4 /* Find original faulting iaddr */
@@ -71,58 +68,57 @@ vpte_insn_obp:
mov TLB_SFSR, %g1
stxa %g4, [%g1 + %g1] ASI_IMMU
- /* Get PMD offset. */
- srlx %g4, 23, %g6
- and %g6, 0x7ff, %g6
- sllx %g6, 2, %g6
-
- /* Load PMD, is it valid? */
- lduwa [%g5 + %g6] ASI_PHYS_USE_EC, %g5
- brz,pn %g5, longpath
- sllx %g5, 11, %g5
-
- /* Get PTE offset. */
- srlx %g4, 13, %g6
- and %g6, 0x3ff, %g6
- sllx %g6, 3, %g6
-
- /* Load PTE. */
- ldxa [%g5 + %g6] ASI_PHYS_USE_EC, %g5
- brgez,pn %g5, longpath
- nop
-
- /* TLB load and return from trap. */
+ sethi %hi(prom_trans), %g5
+ or %g5, %lo(prom_trans), %g5
+
+1: ldx [%g5 + 0x00], %g6 ! base
+ brz,a,pn %g6, longpath ! no more entries, fail
+ mov TLB_SFSR, %g1 ! and restore %g1
+ ldx [%g5 + 0x08], %g1 ! len
+ add %g6, %g1, %g1 ! end
+ cmp %g6, %g4
+ bgu,pt %xcc, 2f
+ cmp %g4, %g1
+ bgeu,pt %xcc, 2f
+ ldx [%g5 + 0x10], %g1 ! PTE
+
+ /* TLB load, restore %g1, and return from trap. */
+ sub %g4, %g6, %g6
+ add %g1, %g6, %g5
+ mov TLB_SFSR, %g1
stxa %g5, [%g0] ASI_ITLB_DATA_IN
retry
-kvmap_do_obp:
- sethi %hi(prom_pmd_phys), %g5
- ldx [%g5 + %lo(prom_pmd_phys)], %g5
-
- /* Get PMD offset. */
- srlx %g4, 23, %g6
- and %g6, 0x7ff, %g6
- sllx %g6, 2, %g6
-
- /* Load PMD, is it valid? */
- lduwa [%g5 + %g6] ASI_PHYS_USE_EC, %g5
- brz,pn %g5, longpath
- sllx %g5, 11, %g5
-
- /* Get PTE offset. */
- srlx %g4, 13, %g6
- and %g6, 0x3ff, %g6
- sllx %g6, 3, %g6
-
- /* Load PTE. */
- ldxa [%g5 + %g6] ASI_PHYS_USE_EC, %g5
- brgez,pn %g5, longpath
- nop
+2: ba,pt %xcc, 1b
+ add %g5, (3 * 8), %g5 ! next entry
- /* TLB load and return from trap. */
+kvmap_do_obp:
+ sethi %hi(prom_trans), %g5
+ or %g5, %lo(prom_trans), %g5
+ srlx %g4, 13, %g4
+ sllx %g4, 13, %g4
+
+1: ldx [%g5 + 0x00], %g6 ! base
+ brz,a,pn %g6, longpath ! no more entries, fail
+ mov TLB_SFSR, %g1 ! and restore %g1
+ ldx [%g5 + 0x08], %g1 ! len
+ add %g6, %g1, %g1 ! end
+ cmp %g6, %g4
+ bgu,pt %xcc, 2f
+ cmp %g4, %g1
+ bgeu,pt %xcc, 2f
+ ldx [%g5 + 0x10], %g1 ! PTE
+
+ /* TLB load, restore %g1, and return from trap. */
+ sub %g4, %g6, %g6
+ add %g1, %g6, %g5
+ mov TLB_SFSR, %g1
stxa %g5, [%g0] ASI_DTLB_DATA_IN
retry
+2: ba,pt %xcc, 1b
+ add %g5, (3 * 8), %g5 ! next entry
+
/*
* On a first level data miss, check whether this is to the OBP range (note
* that such accesses can be made by prom, as well as by kernel using
diff --git a/arch/sparc64/kernel/pci_iommu.c b/arch/sparc64/kernel/pci_iommu.c
index 425c60cfea19..a11910be1013 100644
--- a/arch/sparc64/kernel/pci_iommu.c
+++ b/arch/sparc64/kernel/pci_iommu.c
@@ -49,12 +49,6 @@ static void __iommu_flushall(struct pci_iommu *iommu)
/* Ensure completion of previous PIO writes. */
(void) pci_iommu_read(iommu->write_complete_reg);
-
- /* Now update everyone's flush point. */
- for (entry = 0; entry < PBM_NCLUSTERS; entry++) {
- iommu->alloc_info[entry].flush =
- iommu->alloc_info[entry].next;
- }
}
#define IOPTE_CONSISTENT(CTX) \
@@ -80,120 +74,117 @@ static void inline iopte_make_dummy(struct pci_iommu *iommu, iopte_t *iopte)
iopte_val(*iopte) = val;
}
-void pci_iommu_table_init(struct pci_iommu *iommu, int tsbsize)
+/* Based largely upon the ppc64 iommu allocator. */
+static long pci_arena_alloc(struct pci_iommu *iommu, unsigned long npages)
{
- int i;
-
- tsbsize /= sizeof(iopte_t);
-
- for (i = 0; i < tsbsize; i++)
- iopte_make_dummy(iommu, &iommu->page_table[i]);
-}
-
-static iopte_t *alloc_streaming_cluster(struct pci_iommu *iommu, unsigned long npages)
-{
- iopte_t *iopte, *limit, *first;
- unsigned long cnum, ent, flush_point;
-
- cnum = 0;
- while ((1UL << cnum) < npages)
- cnum++;
- iopte = (iommu->page_table +
- (cnum << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS)));
-
- if (cnum == 0)
- limit = (iommu->page_table +
- iommu->lowest_consistent_map);
- else
- limit = (iopte +
- (1 << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS)));
-
- iopte += ((ent = iommu->alloc_info[cnum].next) << cnum);
- flush_point = iommu->alloc_info[cnum].flush;
-
- first = iopte;
- for (;;) {
- if (IOPTE_IS_DUMMY(iommu, iopte)) {
- if ((iopte + (1 << cnum)) >= limit)
- ent = 0;
- else
- ent = ent + 1;
- iommu->alloc_info[cnum].next = ent;
- if (ent == flush_point)
- __iommu_flushall(iommu);
- break;
+ struct pci_iommu_arena *arena = &iommu->arena;
+ unsigned long n, i, start, end, limit;
+ int pass;
+
+ limit = arena->limit;
+ start = arena->hint;
+ pass = 0;
+
+again:
+ n = find_next_zero_bit(arena->map, limit, start);
+ end = n + npages;
+ if (unlikely(end >= limit)) {
+ if (likely(pass < 1)) {
+ limit = start;
+ start = 0;
+ __iommu_flushall(iommu);
+ pass++;
+ goto again;
+ } else {
+ /* Scanned the whole thing, give up. */
+ return -1;
}
- iopte += (1 << cnum);
- ent++;
- if (iopte >= limit) {
- iopte = (iommu->page_table +
- (cnum <<
- (iommu->page_table_sz_bits - PBM_LOGCLUSTERS)));
- ent = 0;
+ }
+
+ for (i = n; i < end; i++) {
+ if (test_bit(i, arena->map)) {
+ start = i + 1;
+ goto again;
}
- if (ent == flush_point)
- __iommu_flushall(iommu);
- if (iopte == first)
- goto bad;
}
- /* I've got your streaming cluster right here buddy boy... */
- return iopte;
+ for (i = n; i < end; i++)
+ __set_bit(i, arena->map);
-bad:
- printk(KERN_EMERG "pci_iommu: alloc_streaming_cluster of npages(%ld) failed!\n",
- npages);
- return NULL;
+ arena->hint = end;
+
+ return n;
}
-static void free_streaming_cluster(struct pci_iommu *iommu, dma_addr_t base,
- unsigned long npages, unsigned long ctx)
+static void pci_arena_free(struct pci_iommu_arena *arena, unsigned long base, unsigned long npages)
{
- unsigned long cnum, ent;
+ unsigned long i;
- cnum = 0;
- while ((1UL << cnum) < npages)
- cnum++;
+ for (i = base; i < (base + npages); i++)
+ __clear_bit(i, arena->map);
+}
- ent = (base << (32 - IO_PAGE_SHIFT + PBM_LOGCLUSTERS - iommu->page_table_sz_bits))
- >> (32 + PBM_LOGCLUSTERS + cnum - iommu->page_table_sz_bits);
+void pci_iommu_table_init(struct pci_iommu *iommu, int tsbsize, u32 dma_offset, u32 dma_addr_mask)
+{
+ unsigned long i, tsbbase, order, sz, num_tsb_entries;
+
+ num_tsb_entries = tsbsize / sizeof(iopte_t);
+
+ /* Setup initial software IOMMU state. */
+ spin_lock_init(&iommu->lock);
+ iommu->ctx_lowest_free = 1;
+ iommu->page_table_map_base = dma_offset;
+ iommu->dma_addr_mask = dma_addr_mask;
+
+ /* Allocate and initialize the free area map. */
+ sz = num_tsb_entries / 8;
+ sz = (sz + 7UL) & ~7UL;
+ iommu->arena.map = kmalloc(sz, GFP_KERNEL);
+ if (!iommu->arena.map) {
+ prom_printf("PCI_IOMMU: Error, kmalloc(arena.map) failed.\n");
+ prom_halt();
+ }
+ memset(iommu->arena.map, 0, sz);
+ iommu->arena.limit = num_tsb_entries;
- /* If the global flush might not have caught this entry,
- * adjust the flush point such that we will flush before
- * ever trying to reuse it.
+ /* Allocate and initialize the dummy page which we
+ * set inactive IO PTEs to point to.
*/
-#define between(X,Y,Z) (((Z) - (Y)) >= ((X) - (Y)))
- if (between(ent, iommu->alloc_info[cnum].next, iommu->alloc_info[cnum].flush))
- iommu->alloc_info[cnum].flush = ent;
-#undef between
+ iommu->dummy_page = __get_free_pages(GFP_KERNEL, 0);
+ if (!iommu->dummy_page) {
+ prom_printf("PCI_IOMMU: Error, gfp(dummy_page) failed.\n");
+ prom_halt();
+ }
+ memset((void *)iommu->dummy_page, 0, PAGE_SIZE);
+ iommu->dummy_page_pa = (unsigned long) __pa(iommu->dummy_page);
+
+ /* Now allocate and setup the IOMMU page table itself. */
+ order = get_order(tsbsize);
+ tsbbase = __get_free_pages(GFP_KERNEL, order);
+ if (!tsbbase) {
+ prom_printf("PCI_IOMMU: Error, gfp(tsb) failed.\n");
+ prom_halt();
+ }
+ iommu->page_table = (iopte_t *)tsbbase;
+
+ for (i = 0; i < num_tsb_entries; i++)
+ iopte_make_dummy(iommu, &iommu->page_table[i]);
}
-/* We allocate consistent mappings from the end of cluster zero. */
-static iopte_t *alloc_consistent_cluster(struct pci_iommu *iommu, unsigned long npages)
+static inline iopte_t *alloc_npages(struct pci_iommu *iommu, unsigned long npages)
{
- iopte_t *iopte;
+ long entry;
- iopte = iommu->page_table + (1 << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS));
- while (iopte > iommu->page_table) {
- iopte--;
- if (IOPTE_IS_DUMMY(iommu, iopte)) {
- unsigned long tmp = npages;
+ entry = pci_arena_alloc(iommu, npages);
+ if (unlikely(entry < 0))
+ return NULL;
- while (--tmp) {
- iopte--;
- if (!IOPTE_IS_DUMMY(iommu, iopte))
- break;
- }
- if (tmp == 0) {
- u32 entry = (iopte - iommu->page_table);
+ return iommu->page_table + entry;
+}
- if (entry < iommu->lowest_consistent_map)
- iommu->lowest_consistent_map = entry;
- return iopte;
- }
- }
- }
- return NULL;
+static inline void free_npages(struct pci_iommu *iommu, dma_addr_t base, unsigned long npages)
+{
+ pci_arena_free(&iommu->arena, base >> IO_PAGE_SHIFT, npages);
}
static int iommu_alloc_ctx(struct pci_iommu *iommu)
@@ -233,7 +224,7 @@ void *pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_ad
struct pcidev_cookie *pcp;
struct pci_iommu *iommu;
iopte_t *iopte;
- unsigned long flags, order, first_page, ctx;
+ unsigned long flags, order, first_page;
void *ret;
int npages;
@@ -251,9 +242,10 @@ void *pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_ad
iommu = pcp->pbm->iommu;
spin_lock_irqsave(&iommu->lock, flags);
- iopte = alloc_consistent_cluster(iommu, size >> IO_PAGE_SHIFT);
- if (iopte == NULL) {
- spin_unlock_irqrestore(&iommu->lock, flags);
+ iopte = alloc_npages(iommu, size >> IO_PAGE_SHIFT);
+ spin_unlock_irqrestore(&iommu->lock, flags);
+
+ if (unlikely(iopte == NULL)) {
free_pages(first_page, order);
return NULL;
}
@@ -262,31 +254,15 @@ void *pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_ad
((iopte - iommu->page_table) << IO_PAGE_SHIFT));
ret = (void *) first_page;
npages = size >> IO_PAGE_SHIFT;
- ctx = 0;
- if (iommu->iommu_ctxflush)
- ctx = iommu_alloc_ctx(iommu);
first_page = __pa(first_page);
while (npages--) {
- iopte_val(*iopte) = (IOPTE_CONSISTENT(ctx) |
+ iopte_val(*iopte) = (IOPTE_CONSISTENT(0UL) |
IOPTE_WRITE |
(first_page & IOPTE_PAGE));
iopte++;
first_page += IO_PAGE_SIZE;
}
- {
- int i;
- u32 daddr = *dma_addrp;
-
- npages = size >> IO_PAGE_SHIFT;
- for (i = 0; i < npages; i++) {
- pci_iommu_write(iommu->iommu_flush, daddr);
- daddr += IO_PAGE_SIZE;
- }
- }
-
- spin_unlock_irqrestore(&iommu->lock, flags);
-
return ret;
}
@@ -296,7 +272,7 @@ void pci_free_consistent(struct pci_dev *pdev, size_t size, void *cpu, dma_addr_
struct pcidev_cookie *pcp;
struct pci_iommu *iommu;
iopte_t *iopte;
- unsigned long flags, order, npages, i, ctx;
+ unsigned long flags, order, npages;
npages = IO_PAGE_ALIGN(size) >> IO_PAGE_SHIFT;
pcp = pdev->sysdata;
@@ -306,46 +282,7 @@ void pci_free_consistent(struct pci_dev *pdev, size_t size, void *cpu, dma_addr_
spin_lock_irqsave(&iommu->lock, flags);
- if ((iopte - iommu->page_table) ==
- iommu->lowest_consistent_map) {
- iopte_t *walk = iopte + npages;
- iopte_t *limit;
-
- limit = (iommu->page_table +
- (1 << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS)));
- while (walk < limit) {
- if (!IOPTE_IS_DUMMY(iommu, walk))
- break;
- walk++;
- }
- iommu->lowest_consistent_map =
- (walk - iommu->page_table);
- }
-
- /* Data for consistent mappings cannot enter the streaming
- * buffers, so we only need to update the TSB. We flush
- * the IOMMU here as well to prevent conflicts with the
- * streaming mapping deferred tlb flush scheme.
- */
-
- ctx = 0;
- if (iommu->iommu_ctxflush)
- ctx = (iopte_val(*iopte) & IOPTE_CONTEXT) >> 47UL;
-
- for (i = 0; i < npages; i++, iopte++)
- iopte_make_dummy(iommu, iopte);
-
- if (iommu->iommu_ctxflush) {
- pci_iommu_write(iommu->iommu_ctxflush, ctx);
- } else {
- for (i = 0; i < npages; i++) {
- u32 daddr = dvma + (i << IO_PAGE_SHIFT);
-
- pci_iommu_write(iommu->iommu_flush, daddr);
- }
- }
-
- iommu_free_ctx(iommu, ctx);
+ free_npages(iommu, dvma, npages);
spin_unlock_irqrestore(&iommu->lock, flags);
@@ -372,25 +309,27 @@ dma_addr_t pci_map_single(struct pci_dev *pdev, void *ptr, size_t sz, int direct
iommu = pcp->pbm->iommu;
strbuf = &pcp->pbm->stc;
- if (direction == PCI_DMA_NONE)
- BUG();
+ if (unlikely(direction == PCI_DMA_NONE))
+ goto bad_no_ctx;
oaddr = (unsigned long)ptr;
npages = IO_PAGE_ALIGN(oaddr + sz) - (oaddr & IO_PAGE_MASK);
npages >>= IO_PAGE_SHIFT;
spin_lock_irqsave(&iommu->lock, flags);
+ base = alloc_npages(iommu, npages);
+ ctx = 0;
+ if (iommu->iommu_ctxflush)
+ ctx = iommu_alloc_ctx(iommu);
+ spin_unlock_irqrestore(&iommu->lock, flags);
- base = alloc_streaming_cluster(iommu, npages);
- if (base == NULL)
+ if (unlikely(!base))
goto bad;
+
bus_addr = (iommu->page_table_map_base +
((base - iommu->page_table) << IO_PAGE_SHIFT));
ret = bus_addr | (oaddr & ~IO_PAGE_MASK);
base_paddr = __pa(oaddr & IO_PAGE_MASK);
- ctx = 0;
- if (iommu->iommu_ctxflush)
- ctx = iommu_alloc_ctx(iommu);
if (strbuf->strbuf_enabled)
iopte_protection = IOPTE_STREAMING(ctx);
else
@@ -401,12 +340,13 @@ dma_addr_t pci_map_single(struct pci_dev *pdev, void *ptr, size_t sz, int direct
for (i = 0; i < npages; i++, base++, base_paddr += IO_PAGE_SIZE)
iopte_val(*base) = iopte_protection | base_paddr;
- spin_unlock_irqrestore(&iommu->lock, flags);
-
return ret;
bad:
- spin_unlock_irqrestore(&iommu->lock, flags);
+ iommu_free_ctx(iommu, ctx);
+bad_no_ctx:
+ if (printk_ratelimit())
+ WARN_ON(1);
return PCI_DMA_ERROR_CODE;
}
@@ -481,10 +421,13 @@ void pci_unmap_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int
struct pci_iommu *iommu;
struct pci_strbuf *strbuf;
iopte_t *base;
- unsigned long flags, npages, ctx;
+ unsigned long flags, npages, ctx, i;
- if (direction == PCI_DMA_NONE)
- BUG();
+ if (unlikely(direction == PCI_DMA_NONE)) {
+ if (printk_ratelimit())
+ WARN_ON(1);
+ return;
+ }
pcp = pdev->sysdata;
iommu = pcp->pbm->iommu;
@@ -510,13 +453,14 @@ void pci_unmap_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int
/* Step 1: Kick data out of streaming buffers if necessary. */
if (strbuf->strbuf_enabled)
- pci_strbuf_flush(strbuf, iommu, bus_addr, ctx, npages, direction);
+ pci_strbuf_flush(strbuf, iommu, bus_addr, ctx,
+ npages, direction);
- /* Step 2: Clear out first TSB entry. */
- iopte_make_dummy(iommu, base);
+ /* Step 2: Clear out TSB entries. */
+ for (i = 0; i < npages; i++)
+ iopte_make_dummy(iommu, base + i);
- free_streaming_cluster(iommu, bus_addr - iommu->page_table_map_base,
- npages, ctx);
+ free_npages(iommu, bus_addr - iommu->page_table_map_base, npages);
iommu_free_ctx(iommu, ctx);
@@ -621,6 +565,8 @@ int pci_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int
pci_map_single(pdev,
(page_address(sglist->page) + sglist->offset),
sglist->length, direction);
+ if (unlikely(sglist->dma_address == PCI_DMA_ERROR_CODE))
+ return 0;
sglist->dma_length = sglist->length;
return 1;
}
@@ -629,21 +575,29 @@ int pci_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int
iommu = pcp->pbm->iommu;
strbuf = &pcp->pbm->stc;
- if (direction == PCI_DMA_NONE)
- BUG();
+ if (unlikely(direction == PCI_DMA_NONE))
+ goto bad_no_ctx;
/* Step 1: Prepare scatter list. */
npages = prepare_sg(sglist, nelems);
- /* Step 2: Allocate a cluster. */
+ /* Step 2: Allocate a cluster and context, if necessary. */
spin_lock_irqsave(&iommu->lock, flags);
- base = alloc_streaming_cluster(iommu, npages);
+ base = alloc_npages(iommu, npages);
+ ctx = 0;
+ if (iommu->iommu_ctxflush)
+ ctx = iommu_alloc_ctx(iommu);
+
+ spin_unlock_irqrestore(&iommu->lock, flags);
+
if (base == NULL)
goto bad;
- dma_base = iommu->page_table_map_base + ((base - iommu->page_table) << IO_PAGE_SHIFT);
+
+ dma_base = iommu->page_table_map_base +
+ ((base - iommu->page_table) << IO_PAGE_SHIFT);
/* Step 3: Normalize DMA addresses. */
used = nelems;
@@ -656,30 +610,28 @@ int pci_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int
}
used = nelems - used;
- /* Step 4: Choose a context if necessary. */
- ctx = 0;
- if (iommu->iommu_ctxflush)
- ctx = iommu_alloc_ctx(iommu);
-
- /* Step 5: Create the mappings. */
+ /* Step 4: Create the mappings. */
if (strbuf->strbuf_enabled)
iopte_protection = IOPTE_STREAMING(ctx);
else
iopte_protection = IOPTE_CONSISTENT(ctx);
if (direction != PCI_DMA_TODEVICE)
iopte_protection |= IOPTE_WRITE;
- fill_sg (base, sglist, used, nelems, iopte_protection);
+
+ fill_sg(base, sglist, used, nelems, iopte_protection);
+
#ifdef VERIFY_SG
verify_sglist(sglist, nelems, base, npages);
#endif
- spin_unlock_irqrestore(&iommu->lock, flags);
-
return used;
bad:
- spin_unlock_irqrestore(&iommu->lock, flags);
- return PCI_DMA_ERROR_CODE;
+ iommu_free_ctx(iommu, ctx);
+bad_no_ctx:
+ if (printk_ratelimit())
+ WARN_ON(1);
+ return 0;
}
/* Unmap a set of streaming mode DMA translations. */
@@ -692,8 +644,10 @@ void pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems,
unsigned long flags, ctx, i, npages;
u32 bus_addr;
- if (direction == PCI_DMA_NONE)
- BUG();
+ if (unlikely(direction == PCI_DMA_NONE)) {
+ if (printk_ratelimit())
+ WARN_ON(1);
+ }
pcp = pdev->sysdata;
iommu = pcp->pbm->iommu;
@@ -705,7 +659,8 @@ void pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems,
if (sglist[i].dma_length == 0)
break;
i--;
- npages = (IO_PAGE_ALIGN(sglist[i].dma_address + sglist[i].dma_length) - bus_addr) >> IO_PAGE_SHIFT;
+ npages = (IO_PAGE_ALIGN(sglist[i].dma_address + sglist[i].dma_length) -
+ bus_addr) >> IO_PAGE_SHIFT;
base = iommu->page_table +
((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT);
@@ -726,11 +681,11 @@ void pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems,
if (strbuf->strbuf_enabled)
pci_strbuf_flush(strbuf, iommu, bus_addr, ctx, npages, direction);
- /* Step 2: Clear out first TSB entry. */
- iopte_make_dummy(iommu, base);
+ /* Step 2: Clear out the TSB entries. */
+ for (i = 0; i < npages; i++)
+ iopte_make_dummy(iommu, base + i);
- free_streaming_cluster(iommu, bus_addr - iommu->page_table_map_base,
- npages, ctx);
+ free_npages(iommu, bus_addr - iommu->page_table_map_base, npages);
iommu_free_ctx(iommu, ctx);
diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c
index 6ed1ef25e0ac..c03ed5f49d31 100644
--- a/arch/sparc64/kernel/pci_psycho.c
+++ b/arch/sparc64/kernel/pci_psycho.c
@@ -1207,13 +1207,9 @@ static void psycho_scan_bus(struct pci_controller_info *p)
static void psycho_iommu_init(struct pci_controller_info *p)
{
struct pci_iommu *iommu = p->pbm_A.iommu;
- unsigned long tsbbase, i;
+ unsigned long i;
u64 control;
- /* Setup initial software IOMMU state. */
- spin_lock_init(&iommu->lock);
- iommu->ctx_lowest_free = 1;
-
/* Register addresses. */
iommu->iommu_control = p->pbm_A.controller_regs + PSYCHO_IOMMU_CONTROL;
iommu->iommu_tsbbase = p->pbm_A.controller_regs + PSYCHO_IOMMU_TSBBASE;
@@ -1240,40 +1236,10 @@ static void psycho_iommu_init(struct pci_controller_info *p)
/* Leave diag mode enabled for full-flushing done
* in pci_iommu.c
*/
+ pci_iommu_table_init(iommu, IO_TSB_SIZE, 0xc0000000, 0xffffffff);
- iommu->dummy_page = __get_free_pages(GFP_KERNEL, 0);
- if (!iommu->dummy_page) {
- prom_printf("PSYCHO_IOMMU: Error, gfp(dummy_page) failed.\n");
- prom_halt();
- }
- memset((void *)iommu->dummy_page, 0, PAGE_SIZE);
- iommu->dummy_page_pa = (unsigned long) __pa(iommu->dummy_page);
-
- /* Using assumed page size 8K with 128K entries we need 1MB iommu page
- * table (128K ioptes * 8 bytes per iopte). This is
- * page order 7 on UltraSparc.
- */
- tsbbase = __get_free_pages(GFP_KERNEL, get_order(IO_TSB_SIZE));
- if (!tsbbase) {
- prom_printf("PSYCHO_IOMMU: Error, gfp(tsb) failed.\n");
- prom_halt();
- }
- iommu->page_table = (iopte_t *)tsbbase;
- iommu->page_table_sz_bits = 17;
- iommu->page_table_map_base = 0xc0000000;
- iommu->dma_addr_mask = 0xffffffff;
- pci_iommu_table_init(iommu, IO_TSB_SIZE);
-
- /* We start with no consistent mappings. */
- iommu->lowest_consistent_map =
- 1 << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS);
-
- for (i = 0; i < PBM_NCLUSTERS; i++) {
- iommu->alloc_info[i].flush = 0;
- iommu->alloc_info[i].next = 0;
- }
-
- psycho_write(p->pbm_A.controller_regs + PSYCHO_IOMMU_TSBBASE, __pa(tsbbase));
+ psycho_write(p->pbm_A.controller_regs + PSYCHO_IOMMU_TSBBASE,
+ __pa(iommu->page_table));
control = psycho_read(p->pbm_A.controller_regs + PSYCHO_IOMMU_CONTROL);
control &= ~(PSYCHO_IOMMU_CTRL_TSBSZ | PSYCHO_IOMMU_CTRL_TBWSZ);
@@ -1281,7 +1247,7 @@ static void psycho_iommu_init(struct pci_controller_info *p)
psycho_write(p->pbm_A.controller_regs + PSYCHO_IOMMU_CONTROL, control);
/* If necessary, hook us up for starfire IRQ translations. */
- if(this_is_starfire)
+ if (this_is_starfire)
p->starfire_cookie = starfire_hookup(p->pbm_A.portid);
else
p->starfire_cookie = NULL;
diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c
index 0ee6bd5b9ac6..da8e1364194f 100644
--- a/arch/sparc64/kernel/pci_sabre.c
+++ b/arch/sparc64/kernel/pci_sabre.c
@@ -1267,13 +1267,9 @@ static void sabre_iommu_init(struct pci_controller_info *p,
u32 dma_mask)
{
struct pci_iommu *iommu = p->pbm_A.iommu;
- unsigned long tsbbase, i, order;
+ unsigned long i;
u64 control;
- /* Setup initial software IOMMU state. */
- spin_lock_init(&iommu->lock);
- iommu->ctx_lowest_free = 1;
-
/* Register addresses. */
iommu->iommu_control = p->pbm_A.controller_regs + SABRE_IOMMU_CONTROL;
iommu->iommu_tsbbase = p->pbm_A.controller_regs + SABRE_IOMMU_TSBBASE;
@@ -1295,26 +1291,10 @@ static void sabre_iommu_init(struct pci_controller_info *p,
/* Leave diag mode enabled for full-flushing done
* in pci_iommu.c
*/
+ pci_iommu_table_init(iommu, tsbsize * 1024 * 8, dvma_offset, dma_mask);
- iommu->dummy_page = __get_free_pages(GFP_KERNEL, 0);
- if (!iommu->dummy_page) {
- prom_printf("PSYCHO_IOMMU: Error, gfp(dummy_page) failed.\n");
- prom_halt();
- }
- memset((void *)iommu->dummy_page, 0, PAGE_SIZE);
- iommu->dummy_page_pa = (unsigned long) __pa(iommu->dummy_page);
-
- tsbbase = __get_free_pages(GFP_KERNEL, order = get_order(tsbsize * 1024 * 8));
- if (!tsbbase) {
- prom_printf("SABRE_IOMMU: Error, gfp(tsb) failed.\n");
- prom_halt();
- }
- iommu->page_table = (iopte_t *)tsbbase;
- iommu->page_table_map_base = dvma_offset;
- iommu->dma_addr_mask = dma_mask;
- pci_iommu_table_init(iommu, PAGE_SIZE << order);
-
- sabre_write(p->pbm_A.controller_regs + SABRE_IOMMU_TSBBASE, __pa(tsbbase));
+ sabre_write(p->pbm_A.controller_regs + SABRE_IOMMU_TSBBASE,
+ __pa(iommu->page_table));
control = sabre_read(p->pbm_A.controller_regs + SABRE_IOMMU_CONTROL);
control &= ~(SABRE_IOMMUCTRL_TSBSZ | SABRE_IOMMUCTRL_TBWSZ);
@@ -1322,11 +1302,9 @@ static void sabre_iommu_init(struct pci_controller_info *p,
switch(tsbsize) {
case 64:
control |= SABRE_IOMMU_TSBSZ_64K;
- iommu->page_table_sz_bits = 16;
break;
case 128:
control |= SABRE_IOMMU_TSBSZ_128K;
- iommu->page_table_sz_bits = 17;
break;
default:
prom_printf("iommu_init: Illegal TSB size %d\n", tsbsize);
@@ -1334,15 +1312,6 @@ static void sabre_iommu_init(struct pci_controller_info *p,
break;
}
sabre_write(p->pbm_A.controller_regs + SABRE_IOMMU_CONTROL, control);
-
- /* We start with no consistent mappings. */
- iommu->lowest_consistent_map =
- 1 << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS);
-
- for (i = 0; i < PBM_NCLUSTERS; i++) {
- iommu->alloc_info[i].flush = 0;
- iommu->alloc_info[i].next = 0;
- }
}
static void pbm_register_toplevel_resources(struct pci_controller_info *p,
diff --git a/arch/sparc64/kernel/pci_schizo.c b/arch/sparc64/kernel/pci_schizo.c
index cae5b61fe2f0..d8c4e0919b4e 100644
--- a/arch/sparc64/kernel/pci_schizo.c
+++ b/arch/sparc64/kernel/pci_schizo.c
@@ -1765,7 +1765,7 @@ static void schizo_pbm_strbuf_init(struct pci_pbm_info *pbm)
static void schizo_pbm_iommu_init(struct pci_pbm_info *pbm)
{
struct pci_iommu *iommu = pbm->iommu;
- unsigned long tsbbase, i, tagbase, database, order;
+ unsigned long i, tagbase, database;
u32 vdma[2], dma_mask;
u64 control;
int err, tsbsize;
@@ -1800,10 +1800,6 @@ static void schizo_pbm_iommu_init(struct pci_pbm_info *pbm)
prom_halt();
};
- /* Setup initial software IOMMU state. */
- spin_lock_init(&iommu->lock);
- iommu->ctx_lowest_free = 1;
-
/* Register addresses, SCHIZO has iommu ctx flushing. */
iommu->iommu_control = pbm->pbm_regs + SCHIZO_IOMMU_CONTROL;
iommu->iommu_tsbbase = pbm->pbm_regs + SCHIZO_IOMMU_TSBBASE;
@@ -1832,56 +1828,9 @@ static void schizo_pbm_iommu_init(struct pci_pbm_info *pbm)
/* Leave diag mode enabled for full-flushing done
* in pci_iommu.c
*/
+ pci_iommu_table_init(iommu, tsbsize * 8 * 1024, vdma[0], dma_mask);
- iommu->dummy_page = __get_free_pages(GFP_KERNEL, 0);
- if (!iommu->dummy_page) {
- prom_printf("PSYCHO_IOMMU: Error, gfp(dummy_page) failed.\n");
- prom_halt();
- }
- memset((void *)iommu->dummy_page, 0, PAGE_SIZE);
- iommu->dummy_page_pa = (unsigned long) __pa(iommu->dummy_page);
-
- /* Using assumed page size 8K with 128K entries we need 1MB iommu page
- * table (128K ioptes * 8 bytes per iopte). This is
- * page order 7 on UltraSparc.
- */
- order = get_order(tsbsize * 8 * 1024);
- tsbbase = __get_free_pages(GFP_KERNEL, order);
- if (!tsbbase) {
- prom_printf("%s: Error, gfp(tsb) failed.\n", pbm->name);
- prom_halt();
- }
-
- iommu->page_table = (iopte_t *)tsbbase;
- iommu->page_table_map_base = vdma[0];
- iommu->dma_addr_mask = dma_mask;
- pci_iommu_table_init(iommu, PAGE_SIZE << order);
-
- switch (tsbsize) {
- case 64:
- iommu->page_table_sz_bits = 16;
- break;
-
- case 128:
- iommu->page_table_sz_bits = 17;
- break;
-
- default:
- prom_printf("iommu_init: Illegal TSB size %d\n", tsbsize);
- prom_halt();
- break;
- };
-
- /* We start with no consistent mappings. */
- iommu->lowest_consistent_map =
- 1 << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS);
-
- for (i = 0; i < PBM_NCLUSTERS; i++) {
- iommu->alloc_info[i].flush = 0;
- iommu->alloc_info[i].next = 0;
- }
-
- schizo_write(iommu->iommu_tsbbase, __pa(tsbbase));
+ schizo_write(iommu->iommu_tsbbase, __pa(iommu->page_table));
control = schizo_read(iommu->iommu_control);
control &= ~(SCHIZO_IOMMU_CTRL_TSBSZ | SCHIZO_IOMMU_CTRL_TBWSZ);
diff --git a/arch/sparc64/kernel/power.c b/arch/sparc64/kernel/power.c
index 946cee0257ea..9e8362ea3104 100644
--- a/arch/sparc64/kernel/power.c
+++ b/arch/sparc64/kernel/power.c
@@ -17,6 +17,7 @@
#include <asm/system.h>
#include <asm/ebus.h>
+#include <asm/isa.h>
#include <asm/auxio.h>
#include <linux/unistd.h>
@@ -100,46 +101,83 @@ again:
return 0;
}
-static int __init has_button_interrupt(struct linux_ebus_device *edev)
+static int __init has_button_interrupt(unsigned int irq, int prom_node)
{
- if (edev->irqs[0] == PCI_IRQ_NONE)
+ if (irq == PCI_IRQ_NONE)
return 0;
- if (!prom_node_has_property(edev->prom_node, "button"))
+ if (!prom_node_has_property(prom_node, "button"))
return 0;
return 1;
}
-void __init power_init(void)
+static int __init power_probe_ebus(struct resource **resp, unsigned int *irq_p, int *prom_node_p)
{
struct linux_ebus *ebus;
struct linux_ebus_device *edev;
+
+ for_each_ebus(ebus) {
+ for_each_ebusdev(edev, ebus) {
+ if (!strcmp(edev->prom_name, "power")) {
+ *resp = &edev->resource[0];
+ *irq_p = edev->irqs[0];
+ *prom_node_p = edev->prom_node;
+ return 0;
+ }
+ }
+ }
+ return -ENODEV;
+}
+
+static int __init power_probe_isa(struct resource **resp, unsigned int *irq_p, int *prom_node_p)
+{
+ struct sparc_isa_bridge *isa_bus;
+ struct sparc_isa_device *isa_dev;
+
+ for_each_isa(isa_bus) {
+ for_each_isadev(isa_dev, isa_bus) {
+ if (!strcmp(isa_dev->prom_name, "power")) {
+ *resp = &isa_dev->resource;
+ *irq_p = isa_dev->irq;
+ *prom_node_p = isa_dev->prom_node;
+ return 0;
+ }
+ }
+ }
+ return -ENODEV;
+}
+
+void __init power_init(void)
+{
+ struct resource *res = NULL;
+ unsigned int irq;
+ int prom_node;
static int invoked;
if (invoked)
return;
invoked = 1;
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->prom_name, "power"))
- goto found;
- }
- }
+ if (!power_probe_ebus(&res, &irq, &prom_node))
+ goto found;
+
+ if (!power_probe_isa(&res, &irq, &prom_node))
+ goto found;
+
return;
found:
- power_reg = ioremap(edev->resource[0].start, 0x4);
+ power_reg = ioremap(res->start, 0x4);
printk("power: Control reg at %p ... ", power_reg);
poweroff_method = machine_halt; /* able to use the standard halt */
- if (has_button_interrupt(edev)) {
+ if (has_button_interrupt(irq, prom_node)) {
if (kernel_thread(powerd, NULL, CLONE_FS) < 0) {
printk("Failed to start power daemon.\n");
return;
}
printk("powerd running.\n");
- if (request_irq(edev->irqs[0],
+ if (request_irq(irq,
power_handler, SA_SHIRQ, "power", NULL) < 0)
printk("power: Error, cannot register IRQ handler.\n");
} else {
diff --git a/arch/sparc64/kernel/rtrap.S b/arch/sparc64/kernel/rtrap.S
index ecfb42a69a44..090dcca00d2a 100644
--- a/arch/sparc64/kernel/rtrap.S
+++ b/arch/sparc64/kernel/rtrap.S
@@ -312,32 +312,33 @@ kern_fpucheck: ldub [%g6 + TI_FPDEPTH], %l5
wr %g1, FPRS_FEF, %fprs
ldx [%o1 + %o5], %g1
add %g6, TI_XFSR, %o1
- membar #StoreLoad | #LoadLoad
sll %o0, 8, %o2
add %g6, TI_FPREGS, %o3
brz,pn %l6, 1f
add %g6, TI_FPREGS+0x40, %o4
+ membar #Sync
ldda [%o3 + %o2] ASI_BLK_P, %f0
ldda [%o4 + %o2] ASI_BLK_P, %f16
+ membar #Sync
1: andcc %l2, FPRS_DU, %g0
be,pn %icc, 1f
wr %g1, 0, %gsr
add %o2, 0x80, %o2
+ membar #Sync
ldda [%o3 + %o2] ASI_BLK_P, %f32
ldda [%o4 + %o2] ASI_BLK_P, %f48
-
1: membar #Sync
ldx [%o1 + %o5], %fsr
2: stb %l5, [%g6 + TI_FPDEPTH]
ba,pt %xcc, rt_continue
nop
5: wr %g0, FPRS_FEF, %fprs
- membar #StoreLoad | #LoadLoad
sll %o0, 8, %o2
add %g6, TI_FPREGS+0x80, %o3
add %g6, TI_FPREGS+0xc0, %o4
+ membar #Sync
ldda [%o3 + %o2] ASI_BLK_P, %f32
ldda [%o4 + %o2] ASI_BLK_P, %f48
membar #Sync
diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c
index 590df5a16f5a..b137fd63f5e1 100644
--- a/arch/sparc64/kernel/smp.c
+++ b/arch/sparc64/kernel/smp.c
@@ -1001,13 +1001,6 @@ void smp_penguin_jailcell(int irq, struct pt_regs *regs)
preempt_enable();
}
-extern unsigned long xcall_promstop;
-
-void smp_promstop_others(void)
-{
- smp_cross_call(&xcall_promstop, 0, 0, 0);
-}
-
#define prof_multiplier(__cpu) cpu_data(__cpu).multiplier
#define prof_counter(__cpu) cpu_data(__cpu).counter