/* $OpenBSD: generic3a_machdep.c,v 1.12 2023/02/04 19:19:36 cheloha Exp $ */ /* * Copyright (c) 2009, 2010, 2012 Miodrag Vallat. * Copyright (c) 2016, 2017 Visa Hankala. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Generic Loongson 2Gq and 3A code and configuration data. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HPET_FREQ 14318780 #define HPET_MMIO_BASE 0x20000 #define HPET_CONFIGURATION 0x10 #define HPET_MAIN_COUNTER 0xf0 #define HPET_REGVAL32(x) \ REGVAL32(LS3_HT1_MEM_BASE(0) + HPET_MMIO_BASE + (x)) #define IRQ_CASCADE 2 void generic3a_device_register(struct device *, void *); void generic3a_powerdown(void); void generic3a_reset(void); void generic3a_setup(void); #ifdef MULTIPROCESSOR void generic3a_config_secondary_cpus(struct device *, cfprint_t); void generic3a_boot_secondary_cpu(struct cpu_info *); int generic3a_ipi_establish(int (*)(void *), cpuid_t); void generic3a_ipi_set(cpuid_t); void generic3a_ipi_clear(cpuid_t); uint32_t generic3a_ipi_intr(uint32_t, struct trapframe *); paddr_t ls3_ipi_base[MAXCPUS]; int (*ls3_ipi_handler)(void *); #endif /* MULTIPROCESSOR */ void rs780e_pci_attach_hook(pci_chipset_tag_t); void rs780e_setup(void); void rs780sb_setup(pci_chipset_tag_t, int); void rs780e_isa_attach_hook(struct device *, struct device *, struct isabus_attach_args *iba); void *rs780e_isa_intr_establish(void *, int, int, int, int (*)(void *), void *, char *); void rs780e_isa_intr_disestablish(void *, void *); void rs780e_eoi(int); void rs780e_set_imask(uint32_t); void rs780e_irq_mask(int); void rs780e_irq_unmask(int); u_int rs780e_get_timecount(struct timecounter *); struct timecounter rs780e_timecounter = { .tc_get_timecount = rs780e_get_timecount, .tc_counter_mask = 0xffffffffu, /* truncated to 32 bits */ .tc_frequency = HPET_FREQ, .tc_name = "hpet", .tc_quality = 100, .tc_priv = NULL, .tc_user = 0, }; /* Firmware entry points */ void (*generic3a_reboot_entry)(void); void (*generic3a_poweroff_entry)(void); struct mips_isa_chipset rs780e_isa_chipset = { .ic_v = NULL, .ic_attach_hook = rs780e_isa_attach_hook, .ic_intr_establish = rs780e_isa_intr_establish, .ic_intr_disestablish = rs780e_isa_intr_disestablish }; const struct htb_config rs780e_htb_config = { .hc_attach_hook = rs780e_pci_attach_hook }; const struct legacy_io_range rs780e_legacy_ranges[] = { /* isa */ { IO_DMAPG + 4, IO_DMAPG + 4 }, /* mcclock */ { IO_RTC, IO_RTC + 1 }, /* pciide */ { 0x170, 0x170 + 7 }, { 0x1f0, 0x1f0 + 7 }, { 0x376, 0x376 }, { 0x3f6, 0x3f6 }, /* pckbc */ { IO_KBD, IO_KBD }, { IO_KBD + 4, IO_KBD + 4 }, /* SMBus */ { 0x1000, 0x100f }, { 0, 0 } }; const struct platform rs780e_platform = { .system_type = LOONGSON_3A, .vendor = "Loongson", .product = "LS3A with RS780E", .htb_config = &rs780e_htb_config, .isa_chipset = &rs780e_isa_chipset, .legacy_io_ranges = rs780e_legacy_ranges, .setup = rs780e_setup, .device_register = generic3a_device_register, .powerdown = generic3a_powerdown, .reset = generic3a_reset, #ifdef MULTIPROCESSOR .config_secondary_cpus = generic3a_config_secondary_cpus, .boot_secondary_cpu = generic3a_boot_secondary_cpu, .ipi_establish = generic3a_ipi_establish, .ipi_set = generic3a_ipi_set, .ipi_clear = generic3a_ipi_clear, #endif /* MULTIPROCESSOR */ }; const struct pic rs780e_pic = { rs780e_eoi, rs780e_irq_mask, rs780e_irq_unmask }; uint32_t rs780e_imask; /* * Generic 3A routines */ void generic3a_powerdown(void) { if (generic3a_poweroff_entry != NULL) generic3a_poweroff_entry(); } void generic3a_reset(void) { if (generic3a_reboot_entry != NULL) generic3a_reboot_entry(); } void generic3a_setup(void) { const struct pmon_env_reset *resetenv = pmon_get_env_reset(); const struct pmon_env_smbios *smbios = pmon_get_env_smbios(); uint32_t boot_cpuid = loongson3_get_cpuid(); /* Override the mask if it misses the boot CPU. */ if (!ISSET(loongson_cpumask, 1u << boot_cpuid)) { loongson_cpumask = 1u << boot_cpuid; ncpusfound = 1; } nnodes = LS3_NODEID(fls(loongson_cpumask) - 1) + 1; if (resetenv != NULL) { generic3a_reboot_entry = resetenv->warm_boot; generic3a_poweroff_entry = resetenv->poweroff; } if (smbios != NULL) /* pmon_init() has checked that `vga_bios' points to kseg0. */ loongson_videobios = (void *)smbios->vga_bios; loongson3_intr_init(); #ifdef MULTIPROCESSOR ipi_mask = CR_INT_4; set_intr(INTPRI_IPI, CR_INT_4, generic3a_ipi_intr); #endif } void generic3a_device_register(struct device *dev, void *aux) { #if notyet const char *drvrname = dev->dv_cfdata->cf_driver->cd_name; const char *name = dev->dv_xname; if (dev->dv_class != bootdev_class) return; /* * The device numbering must match. There's no way * pmon tells us more info. Depending on the usb slot * and hubs used you may be lucky. Also, assume umass/sd for usb * attached devices. */ switch (bootdev_class) { case DV_DISK: if (strcmp(drvrname, "wd") == 0 && strcmp(name, bootdev) == 0) bootdv = dev; else { /* XXX this really only works safely for usb0... */ if ((strcmp(drvrname, "sd") == 0 || strcmp(drvrname, "cd") == 0) && strncmp(bootdev, "usb", 3) == 0 && strcmp(name + 2, bootdev + 3) == 0) bootdv = dev; } break; case DV_IFNET: /* * This relies on the onboard Ethernet interface being * attached before any other (usb) interface. */ bootdv = dev; break; default: break; } #endif } #ifdef MULTIPROCESSOR void generic3a_config_secondary_cpus(struct device *parent, cfprint_t print) { struct cpu_attach_args caa; struct cpu_hwinfo hw; uint32_t boot_cpu = loongson3_get_cpuid(); uint32_t cpu, unit = 0; ls3_ipi_base[unit++] = LS3_IPI_BASE(0, boot_cpu); memset(&caa, 0, sizeof(caa)); hw = bootcpu_hwinfo; for (cpu = 0; cpu < LOONGSON_MAXCPUS && ncpus < MAXCPUS; cpu++) { if (!ISSET(loongson_cpumask, 1u << cpu)) continue; if (cpu == boot_cpu) continue; ls3_ipi_base[unit++] = LS3_IPI_BASE(LS3_NODEID(cpu), LS3_COREID(cpu)); caa.caa_maa.maa_name = "cpu"; caa.caa_hw = &hw; config_found(parent, &caa, print); } } void generic3a_boot_secondary_cpu(struct cpu_info *ci) { vaddr_t kstack; kstack = alloc_contiguous_pages(USPACE); if (kstack == 0) panic("unable to allocate idle stack"); ci->ci_curprocpaddr = (void *)kstack; /* * The firmware has put the secondary core into a wait loop which * terminates when a non-zero value is written to mailbox0. * After the core exits the loop, the firmware initializes the core's * pc, sp, gp and a1 registers as follows: * * pc = mailbox0 | 0xffffffff00000000u; * sp = mailbox1 | 0x9800000000000000u; * gp = mailbox2 | 0x9800000000000000u; * a1 = mailbox3; */ cpu_spinup_a0 = (uint64_t)ci; cpu_spinup_sp = kstack; mips_sync(); REGVAL64(ls3_ipi_base[ci->ci_cpuid] + LS3_IPI_MBOX0) = (uint64_t)hw_cpu_spinup_trampoline; /* pc */ while (!CPU_IS_RUNNING(ci)) membar_sync(); } int generic3a_ipi_establish(int (*func)(void *), cpuid_t cpuid) { if (cpuid == 0) ls3_ipi_handler = func; /* Clear any pending IPIs. */ REGVAL32(ls3_ipi_base[cpuid] + LS3_IPI_CLEAR) = ~0u; /* Enable the IPI. */ REGVAL32(ls3_ipi_base[cpuid] + LS3_IPI_IMR) = 1u; return 0; } void generic3a_ipi_set(cpuid_t cpuid) { REGVAL32(ls3_ipi_base[cpuid] + LS3_IPI_SET) = 1; } void generic3a_ipi_clear(cpuid_t cpuid) { REGVAL32(ls3_ipi_base[cpuid] + LS3_IPI_CLEAR) = 1; } uint32_t generic3a_ipi_intr(uint32_t hwpend, struct trapframe *frame) { cpuid_t cpuid = cpu_number(); if (ls3_ipi_handler != NULL) ls3_ipi_handler((void *)cpuid); return hwpend; } #endif /* MULTIPROCESSOR */ /* * Routines for RS780E-based systems */ void rs780e_pci_attach_hook(pci_chipset_tag_t pc) { pcireg_t id, tag; int dev, sbdev = -1; for (dev = pci_bus_maxdevs(pc, 0); dev >= 0; dev--) { tag = pci_make_tag(pc, 0, dev, 0); id = pci_conf_read(pc, tag, PCI_ID_REG); if (id == PCI_ID_CODE(PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SBX00_SMB)) { sbdev = dev; break; } } if (sbdev != -1) rs780sb_setup(pc, sbdev); } void rs780e_setup(void) { generic3a_setup(); htb_early_setup(); } void rs780sb_setup(pci_chipset_tag_t pc, int dev) { pcitag_t tag; pcireg_t reg; /* * Set up the PIC in the southbridge. */ /* master */ REGVAL8(HTB_IO_BASE + IO_ICU1 + PIC_ICW1) = ICW1_SELECT | ICW1_IC4; REGVAL8(HTB_IO_BASE + IO_ICU1 + PIC_ICW2) = ICW2_VECTOR(0); REGVAL8(HTB_IO_BASE + IO_ICU1 + PIC_ICW3) = ICW3_CASCADE(IRQ_CASCADE); REGVAL8(HTB_IO_BASE + IO_ICU1 + PIC_ICW4) = ICW4_8086; REGVAL8(HTB_IO_BASE + IO_ICU1 + PIC_OCW1) = 0xff; /* slave */ REGVAL8(HTB_IO_BASE + IO_ICU2 + PIC_ICW1) = ICW1_SELECT | ICW1_IC4; REGVAL8(HTB_IO_BASE + IO_ICU2 + PIC_ICW2) = ICW2_VECTOR(8); REGVAL8(HTB_IO_BASE + IO_ICU2 + PIC_ICW3) = ICW3_SIC(IRQ_CASCADE); REGVAL8(HTB_IO_BASE + IO_ICU2 + PIC_ICW4) = ICW4_8086; REGVAL8(HTB_IO_BASE + IO_ICU2 + PIC_OCW1) = 0xff; loongson3_register_ht_pic(&rs780e_pic); /* * Set up the HPET. * * Unfortunately, PMON does not initialize the MMIO base address or * the tick period, even though it should because it has a complete * view of the system's resources. * Use the same address as in Linux in the hope of avoiding * address space conflicts. */ tag = pci_make_tag(pc, 0, dev, 0); /* Set base address for HPET MMIO. */ pci_conf_write(pc, tag, 0xb4, HPET_MMIO_BASE); /* Enable decoding of HPET MMIO. */ reg = pci_conf_read(pc, tag, 0x40); reg |= 1u << 28; pci_conf_write(pc, tag, 0x40, reg); /* Enable the HPET. */ reg = HPET_REGVAL32(HPET_CONFIGURATION); HPET_REGVAL32(HPET_CONFIGURATION) = reg | 1u; tc_init(&rs780e_timecounter); } void rs780e_isa_attach_hook(struct device *parent, struct device *self, struct isabus_attach_args *iba) { } void * rs780e_isa_intr_establish(void *v, int irq, int type, int level, int (*cb)(void *), void *cbarg, char *name) { return loongson3_ht_intr_establish(irq, level, cb, cbarg, name); } void rs780e_isa_intr_disestablish(void *v, void *ih) { loongson3_ht_intr_disestablish(ih); } void rs780e_eoi(int irq) { KASSERT((unsigned int)irq <= 15); if (irq & 8) { REGVAL8(HTB_IO_BASE + IO_ICU2 + PIC_OCW2) = OCW2_SELECT | OCW2_EOI | OCW2_SL | OCW2_ILS(irq & 7); irq = IRQ_CASCADE; } REGVAL8(HTB_IO_BASE + IO_ICU1 + PIC_OCW2) = OCW2_SELECT | OCW2_EOI | OCW2_SL | OCW2_ILS(irq); } void rs780e_set_imask(uint32_t new_imask) { uint8_t imr1, imr2; imr1 = 0xff & ~new_imask; imr1 &= ~(1u << IRQ_CASCADE); imr2 = 0xff & ~(new_imask >> 8); REGVAL8(HTB_IO_BASE + IO_ICU2 + PIC_OCW1) = imr2; REGVAL8(HTB_IO_BASE + IO_ICU1 + PIC_OCW1) = imr1; rs780e_imask = new_imask; } void rs780e_irq_mask(int irq) { rs780e_set_imask(rs780e_imask & ~(1u << irq)); } void rs780e_irq_unmask(int irq) { rs780e_set_imask(rs780e_imask | (1u << irq)); } u_int rs780e_get_timecount(struct timecounter *arg) { return HPET_REGVAL32(HPET_MAIN_COUNTER); }