xref: /openbsd-src/sys/arch/loongson/loongson/loongson2_machdep.c (revision 94483548b3741528869e5eee2f096cbf8a5169bb)
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