1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2009 Lemote Inc. |
4 | * Author: Wu Zhangjin, wuzhangjin@gmail.com |
5 | */ |
6 | |
7 | #include <linux/irqchip.h> |
8 | #include <linux/logic_pio.h> |
9 | #include <linux/memblock.h> |
10 | #include <linux/of.h> |
11 | #include <linux/of_address.h> |
12 | #include <asm/bootinfo.h> |
13 | #include <asm/traps.h> |
14 | #include <asm/smp-ops.h> |
15 | #include <asm/cacheflush.h> |
16 | #include <asm/fw/fw.h> |
17 | |
18 | #include <loongson.h> |
19 | #include <boot_param.h> |
20 | |
21 | #define NODE_ID_OFFSET_ADDR ((void __iomem *)TO_UNCAC(0x1001041c)) |
22 | |
23 | u32 node_id_offset; |
24 | |
25 | static void __init mips_nmi_setup(void) |
26 | { |
27 | void *base; |
28 | |
29 | base = (void *)(CAC_BASE + 0x380); |
30 | memcpy(base, except_vec_nmi, 0x80); |
31 | flush_icache_range(start: (unsigned long)base, end: (unsigned long)base + 0x80); |
32 | } |
33 | |
34 | void ls7a_early_config(void) |
35 | { |
36 | node_id_offset = ((readl(NODE_ID_OFFSET_ADDR) >> 8) & 0x1f) + 36; |
37 | } |
38 | |
39 | void rs780e_early_config(void) |
40 | { |
41 | node_id_offset = 37; |
42 | } |
43 | |
44 | void virtual_early_config(void) |
45 | { |
46 | node_id_offset = 44; |
47 | } |
48 | |
49 | void __init szmem(unsigned int node) |
50 | { |
51 | u32 i, mem_type; |
52 | phys_addr_t node_id, mem_start, mem_size; |
53 | |
54 | /* Otherwise come from DTB */ |
55 | if (loongson_sysconf.fw_interface != LOONGSON_LEFI) |
56 | return; |
57 | |
58 | /* Parse memory information and activate */ |
59 | for (i = 0; i < loongson_memmap->nr_map; i++) { |
60 | node_id = loongson_memmap->map[i].node_id; |
61 | if (node_id != node) |
62 | continue; |
63 | |
64 | mem_type = loongson_memmap->map[i].mem_type; |
65 | mem_size = loongson_memmap->map[i].mem_size; |
66 | |
67 | /* Memory size comes in MB if MEM_SIZE_IS_IN_BYTES not set */ |
68 | if (mem_size & MEM_SIZE_IS_IN_BYTES) |
69 | mem_size &= ~MEM_SIZE_IS_IN_BYTES; |
70 | else |
71 | mem_size = mem_size << 20; |
72 | |
73 | mem_start = (node_id << 44) | loongson_memmap->map[i].mem_start; |
74 | |
75 | switch (mem_type) { |
76 | case SYSTEM_RAM_LOW: |
77 | case SYSTEM_RAM_HIGH: |
78 | case UMA_VIDEO_RAM: |
79 | pr_info("Node %d, mem_type:%d\t[%pa], %pa bytes usable\n" , |
80 | (u32)node_id, mem_type, &mem_start, &mem_size); |
81 | memblock_add_node(base: mem_start, size: mem_size, nid: node, |
82 | flags: MEMBLOCK_NONE); |
83 | break; |
84 | case SYSTEM_RAM_RESERVED: |
85 | case VIDEO_ROM: |
86 | case ADAPTER_ROM: |
87 | case ACPI_TABLE: |
88 | case SMBIOS_TABLE: |
89 | pr_info("Node %d, mem_type:%d\t[%pa], %pa bytes reserved\n" , |
90 | (u32)node_id, mem_type, &mem_start, &mem_size); |
91 | memblock_reserve(base: mem_start, size: mem_size); |
92 | break; |
93 | /* We should not reserve VUMA_VIDEO_RAM as it overlaps with MMIO */ |
94 | case VUMA_VIDEO_RAM: |
95 | default: |
96 | pr_info("Node %d, mem_type:%d\t[%pa], %pa bytes unhandled\n" , |
97 | (u32)node_id, mem_type, &mem_start, &mem_size); |
98 | break; |
99 | } |
100 | } |
101 | |
102 | /* Reserve vgabios if it comes from firmware */ |
103 | if (loongson_sysconf.vgabios_addr) |
104 | memblock_reserve(virt_to_phys((void *)loongson_sysconf.vgabios_addr), |
105 | SZ_256K); |
106 | /* set nid for reserved memory */ |
107 | memblock_set_node(base: (u64)node << 44, size: (u64)(node + 1) << 44, |
108 | type: &memblock.reserved, nid: node); |
109 | } |
110 | |
111 | #ifndef CONFIG_NUMA |
112 | static void __init prom_init_memory(void) |
113 | { |
114 | szmem(0); |
115 | } |
116 | #endif |
117 | |
118 | void __init prom_init(void) |
119 | { |
120 | fw_init_cmdline(); |
121 | |
122 | if (fw_arg2 == 0 || (fdt_magic(fw_arg2) == FDT_MAGIC)) { |
123 | loongson_sysconf.fw_interface = LOONGSON_DTB; |
124 | prom_dtb_init_env(); |
125 | } else { |
126 | loongson_sysconf.fw_interface = LOONGSON_LEFI; |
127 | prom_lefi_init_env(); |
128 | } |
129 | |
130 | /* init base address of io space */ |
131 | set_io_port_base(PCI_IOBASE); |
132 | |
133 | if (loongson_sysconf.early_config) |
134 | loongson_sysconf.early_config(); |
135 | |
136 | #ifdef CONFIG_NUMA |
137 | prom_init_numa_memory(); |
138 | #else |
139 | prom_init_memory(); |
140 | #endif |
141 | |
142 | /* Hardcode to CPU UART 0 */ |
143 | if ((read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64R) |
144 | setup_8250_early_printk_port(TO_UNCAC(LOONGSON_REG_BASE), 0, 1024); |
145 | else |
146 | setup_8250_early_printk_port(TO_UNCAC(LOONGSON_REG_BASE + 0x1e0), 0, 1024); |
147 | |
148 | register_smp_ops(&loongson3_smp_ops); |
149 | board_nmi_handler_setup = mips_nmi_setup; |
150 | } |
151 | |
152 | static int __init add_legacy_isa_io(struct fwnode_handle *fwnode, resource_size_t hw_start, |
153 | resource_size_t size) |
154 | { |
155 | int ret = 0; |
156 | struct logic_pio_hwaddr *range; |
157 | unsigned long vaddr; |
158 | |
159 | range = kzalloc(size: sizeof(*range), GFP_ATOMIC); |
160 | if (!range) |
161 | return -ENOMEM; |
162 | |
163 | range->fwnode = fwnode; |
164 | range->size = size = round_up(size, PAGE_SIZE); |
165 | range->hw_start = hw_start; |
166 | range->flags = LOGIC_PIO_CPU_MMIO; |
167 | |
168 | ret = logic_pio_register_range(newrange: range); |
169 | if (ret) { |
170 | kfree(objp: range); |
171 | return ret; |
172 | } |
173 | |
174 | /* Legacy ISA must placed at the start of PCI_IOBASE */ |
175 | if (range->io_start != 0) { |
176 | logic_pio_unregister_range(range); |
177 | kfree(objp: range); |
178 | return -EINVAL; |
179 | } |
180 | |
181 | vaddr = PCI_IOBASE + range->io_start; |
182 | |
183 | vmap_page_range(addr: vaddr, end: vaddr + size, phys_addr: hw_start, pgprot_device(PAGE_KERNEL)); |
184 | |
185 | return 0; |
186 | } |
187 | |
188 | static __init void reserve_pio_range(void) |
189 | { |
190 | struct device_node *np; |
191 | |
192 | for_each_node_by_name(np, "isa" ) { |
193 | struct of_range range; |
194 | struct of_range_parser parser; |
195 | |
196 | pr_info("ISA Bridge: %pOF\n" , np); |
197 | |
198 | if (of_range_parser_init(parser: &parser, node: np)) { |
199 | pr_info("Failed to parse resources.\n" ); |
200 | of_node_put(node: np); |
201 | break; |
202 | } |
203 | |
204 | for_each_of_range(&parser, &range) { |
205 | switch (range.flags & IORESOURCE_TYPE_BITS) { |
206 | case IORESOURCE_IO: |
207 | pr_info(" IO 0x%016llx..0x%016llx -> 0x%016llx\n" , |
208 | range.cpu_addr, |
209 | range.cpu_addr + range.size - 1, |
210 | range.bus_addr); |
211 | if (add_legacy_isa_io(fwnode: &np->fwnode, hw_start: range.cpu_addr, size: range.size)) |
212 | pr_warn("Failed to reserve legacy IO in Logic PIO\n" ); |
213 | break; |
214 | case IORESOURCE_MEM: |
215 | pr_info(" MEM 0x%016llx..0x%016llx -> 0x%016llx\n" , |
216 | range.cpu_addr, |
217 | range.cpu_addr + range.size - 1, |
218 | range.bus_addr); |
219 | break; |
220 | } |
221 | } |
222 | } |
223 | } |
224 | |
225 | void __init arch_init_irq(void) |
226 | { |
227 | reserve_pio_range(); |
228 | irqchip_init(); |
229 | } |
230 | |