162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2009 Lemote Inc. 462306a36Sopenharmony_ci * Author: Wu Zhangjin, wuzhangjin@gmail.com 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/irqchip.h> 862306a36Sopenharmony_ci#include <linux/logic_pio.h> 962306a36Sopenharmony_ci#include <linux/memblock.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/of_address.h> 1262306a36Sopenharmony_ci#include <asm/bootinfo.h> 1362306a36Sopenharmony_ci#include <asm/traps.h> 1462306a36Sopenharmony_ci#include <asm/smp-ops.h> 1562306a36Sopenharmony_ci#include <asm/cacheflush.h> 1662306a36Sopenharmony_ci#include <asm/fw/fw.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <loongson.h> 1962306a36Sopenharmony_ci#include <boot_param.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define NODE_ID_OFFSET_ADDR ((void __iomem *)TO_UNCAC(0x1001041c)) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciu32 node_id_offset; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic void __init mips_nmi_setup(void) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci void *base; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci base = (void *)(CAC_BASE + 0x380); 3062306a36Sopenharmony_ci memcpy(base, except_vec_nmi, 0x80); 3162306a36Sopenharmony_ci flush_icache_range((unsigned long)base, (unsigned long)base + 0x80); 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_civoid ls7a_early_config(void) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci node_id_offset = ((readl(NODE_ID_OFFSET_ADDR) >> 8) & 0x1f) + 36; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_civoid rs780e_early_config(void) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci node_id_offset = 37; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_civoid virtual_early_config(void) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci node_id_offset = 44; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_civoid __init szmem(unsigned int node) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci u32 i, mem_type; 5262306a36Sopenharmony_ci phys_addr_t node_id, mem_start, mem_size; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* Otherwise come from DTB */ 5562306a36Sopenharmony_ci if (loongson_sysconf.fw_interface != LOONGSON_LEFI) 5662306a36Sopenharmony_ci return; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* Parse memory information and activate */ 5962306a36Sopenharmony_ci for (i = 0; i < loongson_memmap->nr_map; i++) { 6062306a36Sopenharmony_ci node_id = loongson_memmap->map[i].node_id; 6162306a36Sopenharmony_ci if (node_id != node) 6262306a36Sopenharmony_ci continue; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci mem_type = loongson_memmap->map[i].mem_type; 6562306a36Sopenharmony_ci mem_size = loongson_memmap->map[i].mem_size; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* Memory size comes in MB if MEM_SIZE_IS_IN_BYTES not set */ 6862306a36Sopenharmony_ci if (mem_size & MEM_SIZE_IS_IN_BYTES) 6962306a36Sopenharmony_ci mem_size &= ~MEM_SIZE_IS_IN_BYTES; 7062306a36Sopenharmony_ci else 7162306a36Sopenharmony_ci mem_size = mem_size << 20; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci mem_start = (node_id << 44) | loongson_memmap->map[i].mem_start; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci switch (mem_type) { 7662306a36Sopenharmony_ci case SYSTEM_RAM_LOW: 7762306a36Sopenharmony_ci case SYSTEM_RAM_HIGH: 7862306a36Sopenharmony_ci case UMA_VIDEO_RAM: 7962306a36Sopenharmony_ci pr_info("Node %d, mem_type:%d\t[%pa], %pa bytes usable\n", 8062306a36Sopenharmony_ci (u32)node_id, mem_type, &mem_start, &mem_size); 8162306a36Sopenharmony_ci memblock_add_node(mem_start, mem_size, node, 8262306a36Sopenharmony_ci MEMBLOCK_NONE); 8362306a36Sopenharmony_ci break; 8462306a36Sopenharmony_ci case SYSTEM_RAM_RESERVED: 8562306a36Sopenharmony_ci case VIDEO_ROM: 8662306a36Sopenharmony_ci case ADAPTER_ROM: 8762306a36Sopenharmony_ci case ACPI_TABLE: 8862306a36Sopenharmony_ci case SMBIOS_TABLE: 8962306a36Sopenharmony_ci pr_info("Node %d, mem_type:%d\t[%pa], %pa bytes reserved\n", 9062306a36Sopenharmony_ci (u32)node_id, mem_type, &mem_start, &mem_size); 9162306a36Sopenharmony_ci memblock_reserve(mem_start, mem_size); 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci /* We should not reserve VUMA_VIDEO_RAM as it overlaps with MMIO */ 9462306a36Sopenharmony_ci case VUMA_VIDEO_RAM: 9562306a36Sopenharmony_ci default: 9662306a36Sopenharmony_ci pr_info("Node %d, mem_type:%d\t[%pa], %pa bytes unhandled\n", 9762306a36Sopenharmony_ci (u32)node_id, mem_type, &mem_start, &mem_size); 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* Reserve vgabios if it comes from firmware */ 10362306a36Sopenharmony_ci if (loongson_sysconf.vgabios_addr) 10462306a36Sopenharmony_ci memblock_reserve(virt_to_phys((void *)loongson_sysconf.vgabios_addr), 10562306a36Sopenharmony_ci SZ_256K); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#ifndef CONFIG_NUMA 10962306a36Sopenharmony_cistatic void __init prom_init_memory(void) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci szmem(0); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci#endif 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_civoid __init prom_init(void) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci fw_init_cmdline(); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (fw_arg2 == 0 || (fdt_magic(fw_arg2) == FDT_MAGIC)) { 12062306a36Sopenharmony_ci loongson_sysconf.fw_interface = LOONGSON_DTB; 12162306a36Sopenharmony_ci prom_dtb_init_env(); 12262306a36Sopenharmony_ci } else { 12362306a36Sopenharmony_ci loongson_sysconf.fw_interface = LOONGSON_LEFI; 12462306a36Sopenharmony_ci prom_lefi_init_env(); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* init base address of io space */ 12862306a36Sopenharmony_ci set_io_port_base(PCI_IOBASE); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (loongson_sysconf.early_config) 13162306a36Sopenharmony_ci loongson_sysconf.early_config(); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci#ifdef CONFIG_NUMA 13462306a36Sopenharmony_ci prom_init_numa_memory(); 13562306a36Sopenharmony_ci#else 13662306a36Sopenharmony_ci prom_init_memory(); 13762306a36Sopenharmony_ci#endif 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Hardcode to CPU UART 0 */ 14062306a36Sopenharmony_ci if ((read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64R) 14162306a36Sopenharmony_ci setup_8250_early_printk_port(TO_UNCAC(LOONGSON_REG_BASE), 0, 1024); 14262306a36Sopenharmony_ci else 14362306a36Sopenharmony_ci setup_8250_early_printk_port(TO_UNCAC(LOONGSON_REG_BASE + 0x1e0), 0, 1024); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci register_smp_ops(&loongson3_smp_ops); 14662306a36Sopenharmony_ci board_nmi_handler_setup = mips_nmi_setup; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int __init add_legacy_isa_io(struct fwnode_handle *fwnode, resource_size_t hw_start, 15062306a36Sopenharmony_ci resource_size_t size) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci int ret = 0; 15362306a36Sopenharmony_ci struct logic_pio_hwaddr *range; 15462306a36Sopenharmony_ci unsigned long vaddr; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci range = kzalloc(sizeof(*range), GFP_ATOMIC); 15762306a36Sopenharmony_ci if (!range) 15862306a36Sopenharmony_ci return -ENOMEM; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci range->fwnode = fwnode; 16162306a36Sopenharmony_ci range->size = size = round_up(size, PAGE_SIZE); 16262306a36Sopenharmony_ci range->hw_start = hw_start; 16362306a36Sopenharmony_ci range->flags = LOGIC_PIO_CPU_MMIO; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci ret = logic_pio_register_range(range); 16662306a36Sopenharmony_ci if (ret) { 16762306a36Sopenharmony_ci kfree(range); 16862306a36Sopenharmony_ci return ret; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Legacy ISA must placed at the start of PCI_IOBASE */ 17262306a36Sopenharmony_ci if (range->io_start != 0) { 17362306a36Sopenharmony_ci logic_pio_unregister_range(range); 17462306a36Sopenharmony_ci kfree(range); 17562306a36Sopenharmony_ci return -EINVAL; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci vaddr = PCI_IOBASE + range->io_start; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci ioremap_page_range(vaddr, vaddr + size, hw_start, pgprot_device(PAGE_KERNEL)); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic __init void reserve_pio_range(void) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct device_node *np; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci for_each_node_by_name(np, "isa") { 19062306a36Sopenharmony_ci struct of_range range; 19162306a36Sopenharmony_ci struct of_range_parser parser; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci pr_info("ISA Bridge: %pOF\n", np); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (of_range_parser_init(&parser, np)) { 19662306a36Sopenharmony_ci pr_info("Failed to parse resources.\n"); 19762306a36Sopenharmony_ci of_node_put(np); 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci for_each_of_range(&parser, &range) { 20262306a36Sopenharmony_ci switch (range.flags & IORESOURCE_TYPE_BITS) { 20362306a36Sopenharmony_ci case IORESOURCE_IO: 20462306a36Sopenharmony_ci pr_info(" IO 0x%016llx..0x%016llx -> 0x%016llx\n", 20562306a36Sopenharmony_ci range.cpu_addr, 20662306a36Sopenharmony_ci range.cpu_addr + range.size - 1, 20762306a36Sopenharmony_ci range.bus_addr); 20862306a36Sopenharmony_ci if (add_legacy_isa_io(&np->fwnode, range.cpu_addr, range.size)) 20962306a36Sopenharmony_ci pr_warn("Failed to reserve legacy IO in Logic PIO\n"); 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci case IORESOURCE_MEM: 21262306a36Sopenharmony_ci pr_info(" MEM 0x%016llx..0x%016llx -> 0x%016llx\n", 21362306a36Sopenharmony_ci range.cpu_addr, 21462306a36Sopenharmony_ci range.cpu_addr + range.size - 1, 21562306a36Sopenharmony_ci range.bus_addr); 21662306a36Sopenharmony_ci break; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_civoid __init arch_init_irq(void) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci reserve_pio_range(); 22562306a36Sopenharmony_ci irqchip_init(); 22662306a36Sopenharmony_ci} 227