1 /* $NetBSD: mmu_sh3.c,v 1.15 2012/02/12 16:34:10 matt Exp $ */
2
3 /*-
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by UCHIYAMA Yasushi.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: mmu_sh3.c,v 1.15 2012/02/12 16:34:10 matt Exp $");
34
35 #include <sys/param.h>
36
37 #include <sh3/pte.h>
38 #include <sh3/mmu.h>
39 #include <sh3/mmu_sh3.h>
40
41
42 void
sh3_mmu_start(void)43 sh3_mmu_start(void)
44 {
45
46 /* Zero clear all TLB entries */
47 sh3_tlb_invalidate_all();
48
49 /* Set current ASID to 0 - kernel */
50 sh_tlb_set_asid(0);
51
52 /* Flush TLB (TF) and enable address translation (AT) */
53 _reg_write_4(SH3_MMUCR, SH3_MMUCR_TF | SH3_MMUCR_AT);
54 }
55
56
57 void
sh3_tlb_invalidate_all(void)58 sh3_tlb_invalidate_all(void)
59 {
60 uint32_t idx, a;
61 int i, way;
62
63 for (i = 0; i < SH3_MMU_ENTRY; ++i) {
64 idx = i << SH3_MMU_VPN_SHIFT;
65
66 for (way = 0; way < SH3_MMU_WAY; ++way) {
67 a = idx | (way << SH3_MMU_WAY_SHIFT);
68
69 _reg_write_4(SH3_MMUAA | a, 0);
70 _reg_write_4(SH3_MMUDA | a, 0);
71 }
72 }
73 }
74
75
76 void
sh3_tlb_invalidate_asid(int asid)77 sh3_tlb_invalidate_asid(int asid)
78 {
79 uint32_t idx, aa;
80 int i, way;
81
82 for (i = 0; i < SH3_MMU_ENTRY; ++i) {
83 idx = i << SH3_MMU_VPN_SHIFT;
84
85 for (way = 0; way < SH3_MMU_WAY; ++way) {
86 aa = SH3_MMUAA | idx | (way << SH3_MMU_WAY_SHIFT);
87
88 if ((_reg_read_4(aa) & SH3_MMUAA_D_ASID_MASK) == asid)
89 _reg_write_4(aa, 0);
90 }
91 }
92 }
93
94
95 void
sh3_tlb_invalidate_addr(int asid,vaddr_t va)96 sh3_tlb_invalidate_addr(int asid, vaddr_t va)
97 {
98 uint32_t match, idx, aa, entry;
99 int way;
100
101 /* What we are looking for in the address array */
102 match = (va & SH3_MMUAA_D_VPN_MASK_4K) | asid;
103
104 /* Where in the address array this VA is located - bits [16:12] */
105 idx = va & SH3_MMU_VPN_MASK;
106
107 /* Check each way - bits [9:8] */
108 for (way = 0; way < SH3_MMU_WAY; ++way) {
109 aa = SH3_MMUAA | idx | (way << SH3_MMU_WAY_SHIFT);
110
111 entry = _reg_read_4(aa)
112 & (SH3_MMUAA_D_VPN_MASK_4K | SH3_MMUAA_D_ASID_MASK);
113
114 if (entry == match) {
115 _reg_write_4(aa, 0);
116 break;
117 }
118 }
119 }
120
121
122 void
sh3_tlb_update(int asid,vaddr_t va,uint32_t pte)123 sh3_tlb_update(int asid, vaddr_t va, uint32_t pte)
124 {
125 static unsigned int rc = 0;
126
127 uint32_t match, idx, a, entry;
128 uint32_t freea, matcha;
129 uint32_t newa, newd;
130 int way;
131 int s;
132
133 KDASSERT(asid < 256 && (pte & ~PGOFSET) != 0 && va != 0);
134
135
136 if ((pte & PG_V) == 0) {
137 sh3_tlb_invalidate_addr(asid, va);
138 return;
139 }
140
141
142 /*
143 * Simple approach is to invalidate + ldtlb, but ldtlb uses
144 * MMUCR.RC to select the way to overwrite, and RC is only
145 * meaningful immediately after TLB exception, so ldtlb here
146 * would update some random way, e.g. a valid way even if
147 * there is an invalid way we could use instead.
148 *
149 * Nano-optimization: as invalidatation needs to loop over
150 * ways anyway, just loop over all of them, noting if there's
151 * either an existing entry for this VA that we can update or
152 * an invalid way we can use.
153 */
154
155 /* What we are looking for in the address array */
156 match = (va & SH3_MMUAA_D_VPN_MASK_4K) | asid;
157
158 /* Where in the address array this VA is located - bits [16:12] */
159 idx = va & SH3_MMU_VPN_MASK;
160
161 newa = match | SH3_MMU_D_VALID;
162 newd = pte & PG_HW_BITS;
163
164 matcha = freea = ~0;
165
166
167 s = splhigh();
168
169 /* Check each way - bits [9:8] */
170 for (way = 0; way < SH3_MMU_WAY; ++way) {
171 a = idx | (way << SH3_MMU_WAY_SHIFT);
172
173 entry = _reg_read_4(SH3_MMUAA | a);
174
175 if ((entry & SH3_MMU_D_VALID) == 0)
176 freea = a;
177
178 entry &= (SH3_MMUAA_D_VPN_MASK_4K | SH3_MMUAA_D_ASID_MASK);
179 if (entry == match) {
180 matcha = a;
181 break;
182 }
183 }
184
185 if ((int)matcha >= 0) /* there's an existing entry, update it */
186 a = matcha;
187 else if ((int)freea >= 0) /* there's an invalid way, overwrite it */
188 a = freea;
189 else { /* no match, all ways are valid */
190 a = idx | (rc << SH3_MMU_WAY_SHIFT);
191 rc = (rc + 1) % SH3_MMU_WAY;
192 }
193
194 _reg_write_4(SH3_MMUAA | a, newa);
195 _reg_write_4(SH3_MMUDA | a, newd);
196
197 splx(s);
198 }
199