1 | /* |
2 | * arch/sh/mm/tlb-urb.c |
3 | * |
4 | * TLB entry wiring helpers for URB-equipped parts. |
5 | * |
6 | * Copyright (C) 2010 Matt Fleming |
7 | * |
8 | * This file is subject to the terms and conditions of the GNU General Public |
9 | * License. See the file "COPYING" in the main directory of this archive |
10 | * for more details. |
11 | */ |
12 | #include <linux/mm.h> |
13 | #include <linux/io.h> |
14 | #include <asm/tlb.h> |
15 | #include <asm/mmu_context.h> |
16 | |
17 | /* |
18 | * Load the entry for 'addr' into the TLB and wire the entry. |
19 | */ |
20 | void tlb_wire_entry(struct vm_area_struct *vma, unsigned long addr, pte_t pte) |
21 | { |
22 | unsigned long status, flags; |
23 | int urb; |
24 | |
25 | local_irq_save(flags); |
26 | |
27 | status = __raw_readl(addr: MMUCR); |
28 | urb = (status & MMUCR_URB) >> MMUCR_URB_SHIFT; |
29 | status &= ~MMUCR_URC; |
30 | |
31 | /* |
32 | * Make sure we're not trying to wire the last TLB entry slot. |
33 | */ |
34 | BUG_ON(!--urb); |
35 | |
36 | urb = urb % MMUCR_URB_NENTRIES; |
37 | |
38 | /* |
39 | * Insert this entry into the highest non-wired TLB slot (via |
40 | * the URC field). |
41 | */ |
42 | status |= (urb << MMUCR_URC_SHIFT); |
43 | __raw_writel(val: status, addr: MMUCR); |
44 | ctrl_barrier(); |
45 | |
46 | /* Load the entry into the TLB */ |
47 | __update_tlb(vma, addr, pte); |
48 | |
49 | /* ... and wire it up. */ |
50 | status = __raw_readl(addr: MMUCR); |
51 | |
52 | status &= ~MMUCR_URB; |
53 | status |= (urb << MMUCR_URB_SHIFT); |
54 | |
55 | __raw_writel(val: status, addr: MMUCR); |
56 | ctrl_barrier(); |
57 | |
58 | local_irq_restore(flags); |
59 | } |
60 | |
61 | /* |
62 | * Unwire the last wired TLB entry. |
63 | * |
64 | * It should also be noted that it is not possible to wire and unwire |
65 | * TLB entries in an arbitrary order. If you wire TLB entry N, followed |
66 | * by entry N+1, you must unwire entry N+1 first, then entry N. In this |
67 | * respect, it works like a stack or LIFO queue. |
68 | */ |
69 | void tlb_unwire_entry(void) |
70 | { |
71 | unsigned long status, flags; |
72 | int urb; |
73 | |
74 | local_irq_save(flags); |
75 | |
76 | status = __raw_readl(addr: MMUCR); |
77 | urb = (status & MMUCR_URB) >> MMUCR_URB_SHIFT; |
78 | status &= ~MMUCR_URB; |
79 | |
80 | /* |
81 | * Make sure we're not trying to unwire a TLB entry when none |
82 | * have been wired. |
83 | */ |
84 | BUG_ON(urb++ == MMUCR_URB_NENTRIES); |
85 | |
86 | urb = urb % MMUCR_URB_NENTRIES; |
87 | |
88 | status |= (urb << MMUCR_URB_SHIFT); |
89 | __raw_writel(val: status, addr: MMUCR); |
90 | ctrl_barrier(); |
91 | |
92 | local_irq_restore(flags); |
93 | } |
94 | |