1 /* $OpenBSD: loongson2_machdep.c,v 1.17 2019/12/20 13:34:41 visa Exp $ */
2
3 /*
4 * Copyright (c) 2009, 2010 Miodrag Vallat.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/kernel.h>
22 #include <sys/proc.h>
23 #include <sys/sysctl.h>
24
25 #include <uvm/uvm_extern.h>
26
27 #include <machine/autoconf.h>
28 #include <machine/cpu.h>
29 #include <machine/loongson2.h>
30 #include <machine/memconf.h>
31 #include <machine/pmon.h>
32
33 #ifdef HIBERNATE
34 #include <machine/hibernate_var.h>
35 #endif /* HIBERNATE */
36
37 #include <loongson/dev/bonitoreg.h>
38
39 extern struct phys_mem_desc mem_layout[MAXMEMSEGS];
40 extern paddr_t loongson_memlo_alias;
41
42 int is_memory_range(paddr_t, psize_t, psize_t);
43 void loongson2e_setup(u_long, u_long);
44 void loongson2f_setup(u_long, u_long);
45 void loongson2f_setup_window(uint, uint, uint64_t, uint64_t, uint64_t, uint);
46
47 /* PCI view of CPU memory */
48 paddr_t loongson_dma_base = 0;
49
50 /*
51 * Canonical crossbow assignments on Loongson 2F based designs.
52 * Might need to move to a per-design header file in the future.
53 */
54
55 #define MASTER_CPU 0
56 #define MASTER_PCI 1
57
58 #define WINDOW_CPU_LOW 0
59 #define WINDOW_CPU_PCILO 1
60 #define WINDOW_CPU_PCIHI 2
61 #define WINDOW_CPU_DDR 3
62
63 #define WINDOW_PCI_DDR 0
64
65 #define DDR_PHYSICAL_BASE 0x0000000000000000UL /* memory starts at 0 */
66 #define DDR_PHYSICAL_SIZE 0x0000000080000000UL /* up to 2GB */
67 #define DDR_WINDOW_BASE 0x0000000080000000UL /* mapped at 2GB */
68
69 #define PCI_RESOURCE_BASE 0x0000000000000000UL
70 #define PCI_RESOURCE_SIZE 0x0000000080000000UL
71
72 #define PCI_DDR_BASE 0x0000000080000000UL /* PCI->DDR at 2GB */
73
74 /*
75 * Setup memory mappings for Loongson 2E processors.
76 */
77
78 void
loongson2e_setup(u_long memlo,u_long memhi)79 loongson2e_setup(u_long memlo, u_long memhi)
80 {
81 if (memhi > ((DDR_PHYSICAL_SIZE - BONITO_PCIHI_BASE) >> 20)) {
82 pmon_printf("WARNING! %d MB of memory will not be used",
83 memhi - ((DDR_PHYSICAL_SIZE - BONITO_PCIHI_BASE) >> 20));
84 memhi = (DDR_PHYSICAL_SIZE - BONITO_PCIHI_BASE) >> 20;
85 }
86
87 memlo = atop(memlo << 20);
88 memhi = atop(memhi << 20);
89 physmem = memlo + memhi;
90
91 /* do NOT stomp on exception area */
92 mem_layout[0].mem_first_page = atop(DDR_PHYSICAL_BASE) + 1;
93 mem_layout[0].mem_last_page = atop(DDR_PHYSICAL_BASE) + memlo;
94
95 if (memhi != 0) {
96 mem_layout[1].mem_first_page = atop(BONITO_PCIHI_BASE);
97 mem_layout[1].mem_last_page = atop(BONITO_PCIHI_BASE) +
98 memhi;
99 }
100
101 loongson_dma_base = PCI_DDR_BASE ^ DDR_PHYSICAL_BASE;
102 }
103
104 /*
105 * Setup memory mappings for Loongson 2F processors.
106 */
107
108 void
loongson2f_setup(u_long memlo,u_long memhi)109 loongson2f_setup(u_long memlo, u_long memhi)
110 {
111 /*
112 * Because we'll only set up a 2GB window for the PCI bus to
113 * access local memory, we'll limit ourselves to 2GB of usable
114 * memory as well.
115 *
116 * Note that this is a bad justification for this; it should be
117 * possible to setup a 1GB PCI space / 3GB memory access window,
118 * and use bounce buffers if physmem > 3GB; but at the moment
119 * there is no need to solve this problem until Loongson 2F-based
120 * hardware with more than 2GB of memory is commonly encountered.
121 *
122 * Also note that, despite the crossbar window registers being
123 * 64-bit wide, the upper 32-bit always read back as zeroes, so
124 * it is dubious whether it is possible to use more than a 4GB
125 * address space... and thus more than 2GB of physical memory.
126 */
127
128 physmem = memlo + memhi; /* in MB so far */
129 if (physmem > (DDR_PHYSICAL_SIZE >> 20)) {
130 pmon_printf("WARNING! %d MB of memory will not be used",
131 physmem - (DDR_PHYSICAL_SIZE >> 20));
132 memhi = (DDR_PHYSICAL_SIZE >> 20) - 256;
133 }
134
135 memlo = atop(memlo << 20);
136 memhi = atop(memhi << 20);
137 physmem = memlo + memhi;
138
139 /*
140 * PMON configures the system with only the low 256MB of memory
141 * accessible.
142 *
143 * We need to reprogram the address windows in order to be able to
144 * access the whole memory, both by the local processor and by the
145 * PCI bus.
146 *
147 * To make our life easier, we'll setup the memory as a contiguous
148 * range starting at 2GB, and take into account the fact that the
149 * first 256MB are also aliased at address zero (which is where the
150 * kernel is loaded, really).
151 */
152
153 if (memhi != 0) {
154 /* do NOT stomp on exception area */
155 mem_layout[0].mem_first_page = atop(DDR_WINDOW_BASE) + 1;
156 mem_layout[0].mem_last_page = atop(DDR_WINDOW_BASE) +
157 memlo + memhi;
158 loongson_dma_base = PCI_DDR_BASE ^ DDR_WINDOW_BASE;
159 loongson_memlo_alias = DDR_WINDOW_BASE;
160 } else {
161 /* do NOT stomp on exception area */
162 mem_layout[0].mem_first_page = atop(DDR_PHYSICAL_BASE) + 1;
163 mem_layout[0].mem_last_page = atop(DDR_PHYSICAL_BASE) +
164 memlo /* + memhi */;
165 loongson_dma_base = PCI_DDR_BASE ^ DDR_PHYSICAL_BASE;
166 }
167 #ifdef HIBERNATE
168 mem_layout[0].mem_first_page += HIBERNATE_RESERVED_PAGES;
169 #endif
170
171 /*
172 * Allow access to memory beyond 256MB, by programming the
173 * Loongson 2F address window registers.
174 * This also makes sure PCI->DDR accesses can use a contiguous
175 * area regardless of the actual memory size.
176 */
177
178 /*
179 * Master #0 (cpu) window #0 allows access to the low 256MB
180 * of memory at address zero onwards.
181 * This window is inherited from PMON; we set it up just in case.
182 */
183 loongson2f_setup_window(MASTER_CPU, WINDOW_CPU_LOW, DDR_PHYSICAL_BASE,
184 ~(0x0fffffffUL), DDR_PHYSICAL_BASE, MASTER_CPU);
185
186 /*
187 * Master #0 (cpu) window #1 allows access to the ``low'' PCI
188 * space (from 0x10000000 to 0x1fffffff).
189 * This window is inherited from PMON; we set it up just in case.
190 */
191 loongson2f_setup_window(MASTER_CPU, WINDOW_CPU_PCILO, BONITO_PCILO_BASE,
192 ~(0x0fffffffUL), BONITO_PCILO_BASE, MASTER_PCI);
193
194 /*
195 * Master #1 (PCI) window #0 allows access to the memory space
196 * by PCI devices at addresses 0x80000000 onwards.
197 * This window is inherited from PMON, but its mask might be too
198 * restrictive (256MB) so we make sure it matches our needs.
199 */
200 loongson2f_setup_window(MASTER_PCI, WINDOW_PCI_DDR, PCI_DDR_BASE,
201 ~(DDR_PHYSICAL_SIZE - 1), DDR_PHYSICAL_BASE, MASTER_CPU);
202
203 /*
204 * Master #0 (CPU) window #2 allows access to a subset of the ``high''
205 * PCI space (from 0x40000000 to 0x7fffffff only).
206 */
207 loongson2f_setup_window(MASTER_CPU, WINDOW_CPU_PCIHI, LS2F_PCIHI_BASE,
208 ~((uint64_t)LS2F_PCIHI_SIZE - 1), LS2F_PCIHI_BASE, MASTER_PCI);
209
210 /*
211 * Master #0 (CPU) window #3 allows access to the whole memory space
212 * at addresses 0x80000000 onwards.
213 */
214 loongson2f_setup_window(MASTER_CPU, WINDOW_CPU_DDR, DDR_WINDOW_BASE,
215 ~(DDR_PHYSICAL_SIZE - 1), DDR_PHYSICAL_BASE, MASTER_CPU);
216
217 cpu_cpuspeed = loongson2f_cpuspeed;
218 }
219
220 /*
221 * Setup a window in the Loongson2F crossbar.
222 */
223
224 void
loongson2f_setup_window(uint master,uint window,uint64_t base,uint64_t mask,uint64_t mmap,uint slave)225 loongson2f_setup_window(uint master, uint window, uint64_t base, uint64_t mask,
226 uint64_t mmap, uint slave)
227 {
228 volatile uint64_t *awrreg;
229
230 awrreg = (volatile uint64_t *)PHYS_TO_XKPHYS(
231 LOONGSON_AWR_BASE(master, window), CCA_NC);
232 *awrreg = base;
233 (void)*awrreg;
234
235 awrreg = (volatile uint64_t *)PHYS_TO_XKPHYS(
236 LOONGSON_AWR_SIZE(master, window), CCA_NC);
237 *awrreg = mask;
238 (void)*awrreg;
239
240 awrreg = (volatile uint64_t *)PHYS_TO_XKPHYS(
241 LOONGSON_AWR_MMAP(master, window), CCA_NC);
242 *awrreg = mmap | slave;
243 (void)*awrreg;
244 }
245
246 /*
247 * Return whether a given physical address points to managed memory.
248 * (used by /dev/mem)
249 */
250
251 int
is_memory_range(paddr_t pa,psize_t len,psize_t limit)252 is_memory_range(paddr_t pa, psize_t len, psize_t limit)
253 {
254 struct phys_mem_desc *seg;
255 uint64_t fp, lp;
256 int i;
257
258 fp = atop(pa);
259 lp = atop(round_page(pa + len));
260
261 if (limit != 0 && lp > atop(limit))
262 return 0;
263
264 /*
265 * Allow access to the low 256MB aliased region on 2F systems,
266 * if we are accessing memory at 2GB onwards.
267 */
268 if (pa < 0x10000000 && loongson_ver >= 0x2f) {
269 fp += mem_layout[0].mem_first_page - 1;
270 lp += mem_layout[0].mem_first_page - 1;
271 }
272
273 for (i = 0, seg = mem_layout; i < MAXMEMSEGS; i++, seg++)
274 if (fp >= seg->mem_first_page && lp <= seg->mem_last_page)
275 return 1;
276
277 return 0;
278 }
279
280 int
loongson2f_cpuspeed(int * freq)281 loongson2f_cpuspeed(int *freq)
282 {
283 uint32_t step, val;
284
285 val = REGVAL(LOONGSON_CHIP_CONFIG0);
286 step = (val & 0x7) + 1;
287 *freq = ((bootcpu_hwinfo.clock / 8) * step) / 1000000;
288
289 return 0;
290 }
291
292 void
loongson2f_setperf(int percent)293 loongson2f_setperf(int percent)
294 {
295 uint32_t step, val;
296
297 step = percent * 8 / 100;
298 if (step < 2)
299 step = 2;
300
301 /*
302 * Set clock step.
303 */
304 val = REGVAL(LOONGSON_CHIP_CONFIG0);
305 val = (val & ~0x7) | (step - 1);
306 REGVAL(LOONGSON_CHIP_CONFIG0) = val;
307 (void)REGVAL(LOONGSON_CHIP_CONFIG0);
308 }
309