xref: /openbsd-src/sys/arch/loongson/loongson/generic3a_machdep.c (revision 24ee467d1e5e812429867003c29e5339cd18bad7)
1 /*	$OpenBSD: generic3a_machdep.c,v 1.12 2023/02/04 19:19:36 cheloha Exp $	*/
2 
3 /*
4  * Copyright (c) 2009, 2010, 2012 Miodrag Vallat.
5  * Copyright (c) 2016, 2017 Visa Hankala.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /*
21  * Generic Loongson 2Gq and 3A code and configuration data.
22  */
23 
24 #include <sys/param.h>
25 #include <sys/systm.h>
26 #include <sys/device.h>
27 #include <sys/proc.h>
28 #include <sys/timetc.h>
29 
30 #include <mips64/archtype.h>
31 #include <mips64/loongson3.h>
32 #include <mips64/mips_cpu.h>
33 
34 #include <machine/autoconf.h>
35 #include <machine/cpu.h>
36 #include <machine/pmon.h>
37 
38 #include <dev/ic/i8259reg.h>
39 #include <dev/isa/isareg.h>
40 #include <dev/isa/isavar.h>
41 
42 #include <dev/pci/pcireg.h>
43 #include <dev/pci/pcivar.h>
44 #include <dev/pci/pcidevs.h>
45 
46 #include <loongson/dev/htbreg.h>
47 #include <loongson/dev/htbvar.h>
48 #include <loongson/dev/leiocvar.h>
49 
50 #define HPET_FREQ		14318780
51 #define HPET_MMIO_BASE		0x20000
52 
53 #define HPET_CONFIGURATION	0x10
54 #define HPET_MAIN_COUNTER	0xf0
55 
56 #define HPET_REGVAL32(x) \
57 	REGVAL32(LS3_HT1_MEM_BASE(0) + HPET_MMIO_BASE + (x))
58 
59 #define IRQ_CASCADE 2
60 
61 void	 generic3a_device_register(struct device *, void *);
62 void	 generic3a_powerdown(void);
63 void	 generic3a_reset(void);
64 void	 generic3a_setup(void);
65 
66 #ifdef MULTIPROCESSOR
67 void	 generic3a_config_secondary_cpus(struct device *, cfprint_t);
68 void	 generic3a_boot_secondary_cpu(struct cpu_info *);
69 int	 generic3a_ipi_establish(int (*)(void *), cpuid_t);
70 void	 generic3a_ipi_set(cpuid_t);
71 void	 generic3a_ipi_clear(cpuid_t);
72 uint32_t generic3a_ipi_intr(uint32_t, struct trapframe *);
73 
74 paddr_t	 ls3_ipi_base[MAXCPUS];
75 int	(*ls3_ipi_handler)(void *);
76 #endif /* MULTIPROCESSOR */
77 
78 void	 rs780e_pci_attach_hook(pci_chipset_tag_t);
79 void	 rs780e_setup(void);
80 void	 rs780sb_setup(pci_chipset_tag_t, int);
81 
82 void	 rs780e_isa_attach_hook(struct device *, struct device *,
83 	    struct isabus_attach_args *iba);
84 void	*rs780e_isa_intr_establish(void *, int, int, int, int (*)(void *),
85 	    void *, char *);
86 void	 rs780e_isa_intr_disestablish(void *, void *);
87 
88 void	 rs780e_eoi(int);
89 void	 rs780e_set_imask(uint32_t);
90 void	 rs780e_irq_mask(int);
91 void	 rs780e_irq_unmask(int);
92 
93 u_int	 rs780e_get_timecount(struct timecounter *);
94 
95 struct timecounter rs780e_timecounter = {
96 	.tc_get_timecount = rs780e_get_timecount,
97 	.tc_counter_mask = 0xffffffffu,	/* truncated to 32 bits */
98 	.tc_frequency = HPET_FREQ,
99 	.tc_name = "hpet",
100 	.tc_quality = 100,
101 	.tc_priv = NULL,
102 	.tc_user = 0,
103 };
104 
105 /* Firmware entry points */
106 void	(*generic3a_reboot_entry)(void);
107 void	(*generic3a_poweroff_entry)(void);
108 
109 struct mips_isa_chipset rs780e_isa_chipset = {
110 	.ic_v = NULL,
111 	.ic_attach_hook = rs780e_isa_attach_hook,
112 	.ic_intr_establish = rs780e_isa_intr_establish,
113 	.ic_intr_disestablish = rs780e_isa_intr_disestablish
114 };
115 
116 const struct htb_config rs780e_htb_config = {
117 	.hc_attach_hook = rs780e_pci_attach_hook
118 };
119 
120 const struct legacy_io_range rs780e_legacy_ranges[] = {
121 	/* isa */
122 	{ IO_DMAPG + 4,	IO_DMAPG + 4 },
123 	/* mcclock */
124 	{ IO_RTC,	IO_RTC + 1 },
125 	/* pciide */
126 	{ 0x170,	0x170 + 7 },
127 	{ 0x1f0,	0x1f0 + 7 },
128 	{ 0x376,	0x376 },
129 	{ 0x3f6,	0x3f6 },
130 	/* pckbc */
131 	{ IO_KBD,	IO_KBD },
132 	{ IO_KBD + 4,	IO_KBD + 4 },
133 	/* SMBus */
134 	{ 0x1000,	0x100f },
135 
136 	{ 0, 0 }
137 };
138 
139 const struct platform rs780e_platform = {
140 	.system_type = LOONGSON_3A,
141 	.vendor = "Loongson",
142 	.product = "LS3A with RS780E",
143 
144 	.htb_config = &rs780e_htb_config,
145 	.isa_chipset = &rs780e_isa_chipset,
146 	.legacy_io_ranges = rs780e_legacy_ranges,
147 
148 	.setup = rs780e_setup,
149 	.device_register = generic3a_device_register,
150 
151 	.powerdown = generic3a_powerdown,
152 	.reset = generic3a_reset,
153 
154 #ifdef MULTIPROCESSOR
155 	.config_secondary_cpus = generic3a_config_secondary_cpus,
156 	.boot_secondary_cpu = generic3a_boot_secondary_cpu,
157 	.ipi_establish = generic3a_ipi_establish,
158 	.ipi_set = generic3a_ipi_set,
159 	.ipi_clear = generic3a_ipi_clear,
160 #endif /* MULTIPROCESSOR */
161 };
162 
163 const struct pic rs780e_pic = {
164 	rs780e_eoi, rs780e_irq_mask, rs780e_irq_unmask
165 };
166 
167 uint32_t rs780e_imask;
168 
169 /*
170  * Generic 3A routines
171  */
172 
173 void
generic3a_powerdown(void)174 generic3a_powerdown(void)
175 {
176 	if (generic3a_poweroff_entry != NULL)
177 		generic3a_poweroff_entry();
178 }
179 
180 void
generic3a_reset(void)181 generic3a_reset(void)
182 {
183 	if (generic3a_reboot_entry != NULL)
184 		generic3a_reboot_entry();
185 }
186 
187 void
generic3a_setup(void)188 generic3a_setup(void)
189 {
190 	const struct pmon_env_reset *resetenv = pmon_get_env_reset();
191 	const struct pmon_env_smbios *smbios = pmon_get_env_smbios();
192 	uint32_t boot_cpuid = loongson3_get_cpuid();
193 
194 	/* Override the mask if it misses the boot CPU. */
195 	if (!ISSET(loongson_cpumask, 1u << boot_cpuid)) {
196 		loongson_cpumask = 1u << boot_cpuid;
197 		ncpusfound = 1;
198 	}
199 
200 	nnodes = LS3_NODEID(fls(loongson_cpumask) - 1) + 1;
201 
202 	if (resetenv != NULL) {
203 		generic3a_reboot_entry = resetenv->warm_boot;
204 		generic3a_poweroff_entry = resetenv->poweroff;
205 	}
206 
207 	if (smbios != NULL)
208 		/* pmon_init() has checked that `vga_bios' points to kseg0. */
209 		loongson_videobios = (void *)smbios->vga_bios;
210 
211 	loongson3_intr_init();
212 
213 #ifdef MULTIPROCESSOR
214 	ipi_mask = CR_INT_4;
215 	set_intr(INTPRI_IPI, CR_INT_4, generic3a_ipi_intr);
216 #endif
217 }
218 
219 void
generic3a_device_register(struct device * dev,void * aux)220 generic3a_device_register(struct device *dev, void *aux)
221 {
222 #if notyet
223 	const char *drvrname = dev->dv_cfdata->cf_driver->cd_name;
224 	const char *name = dev->dv_xname;
225 
226 	if (dev->dv_class != bootdev_class)
227 		return;
228 
229 	/*
230 	 * The device numbering must match. There's no way
231 	 * pmon tells us more info. Depending on the usb slot
232 	 * and hubs used you may be lucky. Also, assume umass/sd for usb
233 	 * attached devices.
234 	 */
235 	switch (bootdev_class) {
236 	case DV_DISK:
237 		if (strcmp(drvrname, "wd") == 0 && strcmp(name, bootdev) == 0)
238 			bootdv = dev;
239 		else {
240 			/* XXX this really only works safely for usb0... */
241 			if ((strcmp(drvrname, "sd") == 0 ||
242 			    strcmp(drvrname, "cd") == 0) &&
243 			    strncmp(bootdev, "usb", 3) == 0 &&
244 			    strcmp(name + 2, bootdev + 3) == 0)
245 				bootdv = dev;
246 		}
247 		break;
248 	case DV_IFNET:
249 		/*
250 		 * This relies on the onboard Ethernet interface being
251 		 * attached before any other (usb) interface.
252 		 */
253 		bootdv = dev;
254 		break;
255 	default:
256 		break;
257 	}
258 #endif
259 }
260 
261 #ifdef MULTIPROCESSOR
262 
263 void
generic3a_config_secondary_cpus(struct device * parent,cfprint_t print)264 generic3a_config_secondary_cpus(struct device *parent, cfprint_t print)
265 {
266 	struct cpu_attach_args caa;
267 	struct cpu_hwinfo hw;
268 	uint32_t boot_cpu = loongson3_get_cpuid();
269 	uint32_t cpu, unit = 0;
270 
271 	ls3_ipi_base[unit++] = LS3_IPI_BASE(0, boot_cpu);
272 
273 	memset(&caa, 0, sizeof(caa));
274 	hw = bootcpu_hwinfo;
275 	for (cpu = 0; cpu < LOONGSON_MAXCPUS && ncpus < MAXCPUS; cpu++) {
276 		if (!ISSET(loongson_cpumask, 1u << cpu))
277 			continue;
278 		if (cpu == boot_cpu)
279 			continue;
280 
281 		ls3_ipi_base[unit++] = LS3_IPI_BASE(LS3_NODEID(cpu),
282 		    LS3_COREID(cpu));
283 
284 		caa.caa_maa.maa_name = "cpu";
285 		caa.caa_hw = &hw;
286 		config_found(parent, &caa, print);
287 	}
288 }
289 
290 void
generic3a_boot_secondary_cpu(struct cpu_info * ci)291 generic3a_boot_secondary_cpu(struct cpu_info *ci)
292 {
293 	vaddr_t kstack;
294 
295 	kstack = alloc_contiguous_pages(USPACE);
296 	if (kstack == 0)
297 		panic("unable to allocate idle stack");
298 	ci->ci_curprocpaddr = (void *)kstack;
299 
300 	/*
301 	 * The firmware has put the secondary core into a wait loop which
302 	 * terminates when a non-zero value is written to mailbox0.
303 	 * After the core exits the loop, the firmware initializes the core's
304 	 * pc, sp, gp and a1 registers as follows:
305 	 *
306 	 * pc = mailbox0 | 0xffffffff00000000u;
307 	 * sp = mailbox1 | 0x9800000000000000u;
308 	 * gp = mailbox2 | 0x9800000000000000u;
309 	 * a1 = mailbox3;
310 	 */
311 
312 	cpu_spinup_a0 = (uint64_t)ci;
313 	cpu_spinup_sp = kstack;
314 	mips_sync();
315 
316 	REGVAL64(ls3_ipi_base[ci->ci_cpuid] + LS3_IPI_MBOX0) =
317 	    (uint64_t)hw_cpu_spinup_trampoline;  /* pc */
318 
319 	while (!CPU_IS_RUNNING(ci))
320 		membar_sync();
321 }
322 
323 int
generic3a_ipi_establish(int (* func)(void *),cpuid_t cpuid)324 generic3a_ipi_establish(int (*func)(void *), cpuid_t cpuid)
325 {
326 	if (cpuid == 0)
327 		ls3_ipi_handler = func;
328 
329 	/* Clear any pending IPIs. */
330 	REGVAL32(ls3_ipi_base[cpuid] + LS3_IPI_CLEAR) = ~0u;
331 
332 	/* Enable the IPI. */
333 	REGVAL32(ls3_ipi_base[cpuid] + LS3_IPI_IMR) = 1u;
334 
335 	return 0;
336 }
337 
338 void
generic3a_ipi_set(cpuid_t cpuid)339 generic3a_ipi_set(cpuid_t cpuid)
340 {
341 	REGVAL32(ls3_ipi_base[cpuid] + LS3_IPI_SET) = 1;
342 }
343 
344 void
generic3a_ipi_clear(cpuid_t cpuid)345 generic3a_ipi_clear(cpuid_t cpuid)
346 {
347 	REGVAL32(ls3_ipi_base[cpuid] + LS3_IPI_CLEAR) = 1;
348 }
349 
350 uint32_t
generic3a_ipi_intr(uint32_t hwpend,struct trapframe * frame)351 generic3a_ipi_intr(uint32_t hwpend, struct trapframe *frame)
352 {
353 	cpuid_t cpuid = cpu_number();
354 
355 	if (ls3_ipi_handler != NULL)
356 		ls3_ipi_handler((void *)cpuid);
357 
358 	return hwpend;
359 }
360 
361 #endif /* MULTIPROCESSOR */
362 
363 /*
364  * Routines for RS780E-based systems
365  */
366 
367 void
rs780e_pci_attach_hook(pci_chipset_tag_t pc)368 rs780e_pci_attach_hook(pci_chipset_tag_t pc)
369 {
370 	pcireg_t id, tag;
371 	int dev, sbdev = -1;
372 
373 	for (dev = pci_bus_maxdevs(pc, 0); dev >= 0; dev--) {
374 		tag = pci_make_tag(pc, 0, dev, 0);
375 		id = pci_conf_read(pc, tag, PCI_ID_REG);
376 		if (id == PCI_ID_CODE(PCI_VENDOR_ATI,
377 		    PCI_PRODUCT_ATI_SBX00_SMB)) {
378 			sbdev = dev;
379 			break;
380 		}
381 	}
382 
383 	if (sbdev != -1)
384 		rs780sb_setup(pc, sbdev);
385 }
386 
387 void
rs780e_setup(void)388 rs780e_setup(void)
389 {
390 	generic3a_setup();
391 
392 	htb_early_setup();
393 }
394 
395 void
rs780sb_setup(pci_chipset_tag_t pc,int dev)396 rs780sb_setup(pci_chipset_tag_t pc, int dev)
397 {
398 	pcitag_t tag;
399 	pcireg_t reg;
400 
401 	/*
402 	 * Set up the PIC in the southbridge.
403 	 */
404 
405 	/* master */
406 	REGVAL8(HTB_IO_BASE + IO_ICU1 + PIC_ICW1) = ICW1_SELECT | ICW1_IC4;
407 	REGVAL8(HTB_IO_BASE + IO_ICU1 + PIC_ICW2) = ICW2_VECTOR(0);
408 	REGVAL8(HTB_IO_BASE + IO_ICU1 + PIC_ICW3) = ICW3_CASCADE(IRQ_CASCADE);
409 	REGVAL8(HTB_IO_BASE + IO_ICU1 + PIC_ICW4) = ICW4_8086;
410 	REGVAL8(HTB_IO_BASE + IO_ICU1 + PIC_OCW1) = 0xff;
411 
412 	/* slave */
413 	REGVAL8(HTB_IO_BASE + IO_ICU2 + PIC_ICW1) = ICW1_SELECT | ICW1_IC4;
414 	REGVAL8(HTB_IO_BASE + IO_ICU2 + PIC_ICW2) = ICW2_VECTOR(8);
415 	REGVAL8(HTB_IO_BASE + IO_ICU2 + PIC_ICW3) = ICW3_SIC(IRQ_CASCADE);
416 	REGVAL8(HTB_IO_BASE + IO_ICU2 + PIC_ICW4) = ICW4_8086;
417 	REGVAL8(HTB_IO_BASE + IO_ICU2 + PIC_OCW1) = 0xff;
418 
419 	loongson3_register_ht_pic(&rs780e_pic);
420 
421 	/*
422 	 * Set up the HPET.
423 	 *
424 	 * Unfortunately, PMON does not initialize the MMIO base address or
425 	 * the tick period, even though it should because it has a complete
426 	 * view of the system's resources.
427 	 * Use the same address as in Linux in the hope of avoiding
428 	 * address space conflicts.
429 	 */
430 
431 	tag = pci_make_tag(pc, 0, dev, 0);
432 
433 	/* Set base address for HPET MMIO. */
434 	pci_conf_write(pc, tag, 0xb4, HPET_MMIO_BASE);
435 
436 	/* Enable decoding of HPET MMIO. */
437 	reg = pci_conf_read(pc, tag, 0x40);
438 	reg |= 1u << 28;
439 	pci_conf_write(pc, tag, 0x40, reg);
440 
441 	/* Enable the HPET. */
442 	reg = HPET_REGVAL32(HPET_CONFIGURATION);
443 	HPET_REGVAL32(HPET_CONFIGURATION) = reg | 1u;
444 
445 	tc_init(&rs780e_timecounter);
446 }
447 
448 void
rs780e_isa_attach_hook(struct device * parent,struct device * self,struct isabus_attach_args * iba)449 rs780e_isa_attach_hook(struct device *parent, struct device *self,
450     struct isabus_attach_args *iba)
451 {
452 }
453 
454 void *
rs780e_isa_intr_establish(void * v,int irq,int type,int level,int (* cb)(void *),void * cbarg,char * name)455 rs780e_isa_intr_establish(void *v, int irq, int type, int level,
456     int (*cb)(void *), void *cbarg, char *name)
457 {
458 	return loongson3_ht_intr_establish(irq, level, cb, cbarg, name);
459 }
460 
461 void
rs780e_isa_intr_disestablish(void * v,void * ih)462 rs780e_isa_intr_disestablish(void *v, void *ih)
463 {
464 	loongson3_ht_intr_disestablish(ih);
465 }
466 
467 void
rs780e_eoi(int irq)468 rs780e_eoi(int irq)
469 {
470 	KASSERT((unsigned int)irq <= 15);
471 
472 	if (irq & 8) {
473 		REGVAL8(HTB_IO_BASE + IO_ICU2 + PIC_OCW2) =
474 		    OCW2_SELECT | OCW2_EOI | OCW2_SL | OCW2_ILS(irq & 7);
475 		irq = IRQ_CASCADE;
476 	}
477 	REGVAL8(HTB_IO_BASE + IO_ICU1 + PIC_OCW2) =
478 	    OCW2_SELECT | OCW2_EOI | OCW2_SL | OCW2_ILS(irq);
479 }
480 
481 void
rs780e_set_imask(uint32_t new_imask)482 rs780e_set_imask(uint32_t new_imask)
483 {
484 	uint8_t imr1, imr2;
485 
486 	imr1 = 0xff & ~new_imask;
487 	imr1 &= ~(1u << IRQ_CASCADE);
488 	imr2 = 0xff & ~(new_imask >> 8);
489 
490 	REGVAL8(HTB_IO_BASE + IO_ICU2 + PIC_OCW1) = imr2;
491 	REGVAL8(HTB_IO_BASE + IO_ICU1 + PIC_OCW1) = imr1;
492 
493 	rs780e_imask = new_imask;
494 }
495 
496 void
rs780e_irq_mask(int irq)497 rs780e_irq_mask(int irq)
498 {
499 	rs780e_set_imask(rs780e_imask & ~(1u << irq));
500 }
501 
502 void
rs780e_irq_unmask(int irq)503 rs780e_irq_unmask(int irq)
504 {
505 	rs780e_set_imask(rs780e_imask | (1u << irq));
506 }
507 
508 u_int
rs780e_get_timecount(struct timecounter * arg)509 rs780e_get_timecount(struct timecounter *arg)
510 {
511 	return HPET_REGVAL32(HPET_MAIN_COUNTER);
512 }
513