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