Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

MIPS: Loongson: Add UEFI-like firmware interface (LEFI) support

The new UEFI-like firmware interface (LEFI, i.e. Loongson Unified
Firmware Interface) has 3 advantages:

1, Firmware export a physical memory map which is similar to X86's
E820 map, so prom_init_memory() will be more elegant that #ifdef
clauses can be removed.
2, Firmware export a pci irq routing table, we no longer need pci
irq routing fixup in kernel's code.
3, Firmware has a built-in vga bios, and its address is exported,
the linux kernel no longer need an embedded blob.

With the LEFI interface, Loongson-3A/2G and all their successors can use
a unified kernel. All Loongson-based machines support this new interface
except 2E/2F series.

Signed-off-by: Huacai Chen <chenhc@lemote.com>
Signed-off-by: Hongliang Tao <taohl@lemote.com>
Signed-off-by: Hua Yan <yanh@lemote.com>
Tested-by: Alex Smith <alex.smith@imgtec.com>
Reviewed-by: Alex Smith <alex.smith@imgtec.com>
Cc: John Crispin <john@phrozen.org>
Cc: Steven J. Hill <Steven.Hill@imgtec.com>
Cc: Aurelien Jarno <aurelien@aurel32.net>
Cc: linux-mips@linux-mips.org
Cc: Fuxin Zhang <zhangfx@lemote.com>
Cc: Zhangjin Wu <wuzhangjin@gmail.com>
Patchwork: https://patchwork.linux-mips.org/patch/6632
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

authored by

Huacai Chen and committed by
Ralf Baechle
1a08f152 5f6d693e

+292 -20
+163
arch/mips/include/asm/mach-loongson/boot_param.h
··· 1 + #ifndef __ASM_MACH_LOONGSON_BOOT_PARAM_H_ 2 + #define __ASM_MACH_LOONGSON_BOOT_PARAM_H_ 3 + 4 + #define SYSTEM_RAM_LOW 1 5 + #define SYSTEM_RAM_HIGH 2 6 + #define MEM_RESERVED 3 7 + #define PCI_IO 4 8 + #define PCI_MEM 5 9 + #define LOONGSON_CFG_REG 6 10 + #define VIDEO_ROM 7 11 + #define ADAPTER_ROM 8 12 + #define ACPI_TABLE 9 13 + #define MAX_MEMORY_TYPE 10 14 + 15 + #define LOONGSON3_BOOT_MEM_MAP_MAX 128 16 + struct efi_memory_map_loongson { 17 + u16 vers; /* version of efi_memory_map */ 18 + u32 nr_map; /* number of memory_maps */ 19 + u32 mem_freq; /* memory frequence */ 20 + struct mem_map { 21 + u32 node_id; /* node_id which memory attached to */ 22 + u32 mem_type; /* system memory, pci memory, pci io, etc. */ 23 + u64 mem_start; /* memory map start address */ 24 + u32 mem_size; /* each memory_map size, not the total size */ 25 + } map[LOONGSON3_BOOT_MEM_MAP_MAX]; 26 + } __packed; 27 + 28 + enum loongson_cpu_type { 29 + Loongson_2E = 0, 30 + Loongson_2F = 1, 31 + Loongson_3A = 2, 32 + Loongson_3B = 3, 33 + Loongson_1A = 4, 34 + Loongson_1B = 5 35 + }; 36 + 37 + /* 38 + * Capability and feature descriptor structure for MIPS CPU 39 + */ 40 + struct efi_cpuinfo_loongson { 41 + u16 vers; /* version of efi_cpuinfo_loongson */ 42 + u32 processor_id; /* PRID, e.g. 6305, 6306 */ 43 + u32 cputype; /* Loongson_3A/3B, etc. */ 44 + u32 total_node; /* num of total numa nodes */ 45 + u32 cpu_startup_core_id; /* Core id */ 46 + u32 cpu_clock_freq; /* cpu_clock */ 47 + u32 nr_cpus; 48 + } __packed; 49 + 50 + struct system_loongson { 51 + u16 vers; /* version of system_loongson */ 52 + u32 ccnuma_smp; /* 0: no numa; 1: has numa */ 53 + u32 sing_double_channel; /* 1:single; 2:double */ 54 + } __packed; 55 + 56 + struct irq_source_routing_table { 57 + u16 vers; 58 + u16 size; 59 + u16 rtr_bus; 60 + u16 rtr_devfn; 61 + u32 vendor; 62 + u32 device; 63 + u32 PIC_type; /* conform use HT or PCI to route to CPU-PIC */ 64 + u64 ht_int_bit; /* 3A: 1<<24; 3B: 1<<16 */ 65 + u64 ht_enable; /* irqs used in this PIC */ 66 + u32 node_id; /* node id: 0x0-0; 0x1-1; 0x10-2; 0x11-3 */ 67 + u64 pci_mem_start_addr; 68 + u64 pci_mem_end_addr; 69 + u64 pci_io_start_addr; 70 + u64 pci_io_end_addr; 71 + u64 pci_config_addr; 72 + u32 dma_mask_bits; 73 + } __packed; 74 + 75 + struct interface_info { 76 + u16 vers; /* version of the specificition */ 77 + u16 size; 78 + u8 flag; 79 + char description[64]; 80 + } __packed; 81 + 82 + #define MAX_RESOURCE_NUMBER 128 83 + struct resource_loongson { 84 + u64 start; /* resource start address */ 85 + u64 end; /* resource end address */ 86 + char name[64]; 87 + u32 flags; 88 + }; 89 + 90 + struct archdev_data {}; /* arch specific additions */ 91 + 92 + struct board_devices { 93 + char name[64]; /* hold the device name */ 94 + u32 num_resources; /* number of device_resource */ 95 + /* for each device's resource */ 96 + struct resource_loongson resource[MAX_RESOURCE_NUMBER]; 97 + /* arch specific additions */ 98 + struct archdev_data archdata; 99 + }; 100 + 101 + struct loongson_special_attribute { 102 + u16 vers; /* version of this special */ 103 + char special_name[64]; /* special_atribute_name */ 104 + u32 loongson_special_type; /* type of special device */ 105 + /* for each device's resource */ 106 + struct resource_loongson resource[MAX_RESOURCE_NUMBER]; 107 + }; 108 + 109 + struct loongson_params { 110 + u64 memory_offset; /* efi_memory_map_loongson struct offset */ 111 + u64 cpu_offset; /* efi_cpuinfo_loongson struct offset */ 112 + u64 system_offset; /* system_loongson struct offset */ 113 + u64 irq_offset; /* irq_source_routing_table struct offset */ 114 + u64 interface_offset; /* interface_info struct offset */ 115 + u64 special_offset; /* loongson_special_attribute struct offset */ 116 + u64 boarddev_table_offset; /* board_devices offset */ 117 + }; 118 + 119 + struct smbios_tables { 120 + u16 vers; /* version of smbios */ 121 + u64 vga_bios; /* vga_bios address */ 122 + struct loongson_params lp; 123 + }; 124 + 125 + struct efi_reset_system_t { 126 + u64 ResetCold; 127 + u64 ResetWarm; 128 + u64 ResetType; 129 + u64 Shutdown; 130 + u64 DoSuspend; /* NULL if not support */ 131 + }; 132 + 133 + struct efi_loongson { 134 + u64 mps; /* MPS table */ 135 + u64 acpi; /* ACPI table (IA64 ext 0.71) */ 136 + u64 acpi20; /* ACPI table (ACPI 2.0) */ 137 + struct smbios_tables smbios; /* SM BIOS table */ 138 + u64 sal_systab; /* SAL system table */ 139 + u64 boot_info; /* boot info table */ 140 + }; 141 + 142 + struct boot_params { 143 + struct efi_loongson efi; 144 + struct efi_reset_system_t reset_system; 145 + }; 146 + 147 + struct loongson_system_configuration { 148 + u32 nr_cpus; 149 + enum loongson_cpu_type cputype; 150 + u64 ht_control_base; 151 + u64 pci_mem_start_addr; 152 + u64 pci_mem_end_addr; 153 + u64 pci_io_base; 154 + u64 restart_addr; 155 + u64 poweroff_addr; 156 + u64 suspend_addr; 157 + u64 vgabios_addr; 158 + u32 dma_mask_bits; 159 + }; 160 + 161 + extern struct efi_memory_map_loongson *loongson_memmap; 162 + extern struct loongson_system_configuration loongson_sysconf; 163 + #endif
+2 -2
arch/mips/include/asm/mach-loongson/loongson.h
··· 24 24 extern void mach_prepare_shutdown(void); 25 25 26 26 /* environment arguments from bootloader */ 27 - extern unsigned long cpu_clock_freq; 28 - extern unsigned long memsize, highmemsize; 27 + extern u32 cpu_clock_freq; 28 + extern u32 memsize, highmemsize; 29 29 30 30 /* loongson-specific command line, env and memory initialization */ 31 31 extern void __init prom_init_memory(void);
+54 -13
arch/mips/loongson/common/env.c
··· 18 18 * option) any later version. 19 19 */ 20 20 #include <linux/module.h> 21 - 22 21 #include <asm/bootinfo.h> 23 - 24 22 #include <loongson.h> 23 + #include <boot_param.h> 25 24 26 - unsigned long cpu_clock_freq; 25 + u32 cpu_clock_freq; 27 26 EXPORT_SYMBOL(cpu_clock_freq); 28 - unsigned long memsize, highmemsize; 27 + struct efi_memory_map_loongson *loongson_memmap; 28 + struct loongson_system_configuration loongson_sysconf; 29 29 30 30 #define parse_even_earlier(res, option, p) \ 31 31 do { \ 32 32 unsigned int tmp __maybe_unused; \ 33 33 \ 34 34 if (strncmp(option, (char *)p, strlen(option)) == 0) \ 35 - tmp = strict_strtol((char *)p + strlen(option"="), 10, &res); \ 35 + tmp = kstrtou32((char *)p + strlen(option"="), 10, &res); \ 36 36 } while (0) 37 37 38 38 void __init prom_init_env(void) 39 39 { 40 40 /* pmon passes arguments in 32bit pointers */ 41 - int *_prom_envp; 42 - unsigned long bus_clock; 43 41 unsigned int processor_id; 42 + 43 + #ifndef CONFIG_LEFI_FIRMWARE_INTERFACE 44 + int *_prom_envp; 44 45 long l; 45 46 46 47 /* firmware arguments are initialized in head.S */ ··· 49 48 50 49 l = (long)*_prom_envp; 51 50 while (l != 0) { 52 - parse_even_earlier(bus_clock, "busclock", l); 53 51 parse_even_earlier(cpu_clock_freq, "cpuclock", l); 54 52 parse_even_earlier(memsize, "memsize", l); 55 53 parse_even_earlier(highmemsize, "highmemsize", l); ··· 57 57 } 58 58 if (memsize == 0) 59 59 memsize = 256; 60 - if (bus_clock == 0) 61 - bus_clock = 66000000; 60 + pr_info("memsize=%u, highmemsize=%u\n", memsize, highmemsize); 61 + #else 62 + struct boot_params *boot_p; 63 + struct loongson_params *loongson_p; 64 + struct efi_cpuinfo_loongson *ecpu; 65 + struct irq_source_routing_table *eirq_source; 66 + 67 + /* firmware arguments are initialized in head.S */ 68 + boot_p = (struct boot_params *)fw_arg2; 69 + loongson_p = &(boot_p->efi.smbios.lp); 70 + 71 + ecpu = (struct efi_cpuinfo_loongson *) 72 + ((u64)loongson_p + loongson_p->cpu_offset); 73 + eirq_source = (struct irq_source_routing_table *) 74 + ((u64)loongson_p + loongson_p->irq_offset); 75 + loongson_memmap = (struct efi_memory_map_loongson *) 76 + ((u64)loongson_p + loongson_p->memory_offset); 77 + 78 + cpu_clock_freq = ecpu->cpu_clock_freq; 79 + loongson_sysconf.cputype = ecpu->cputype; 80 + loongson_sysconf.nr_cpus = ecpu->nr_cpus; 81 + if (ecpu->nr_cpus > NR_CPUS || ecpu->nr_cpus == 0) 82 + loongson_sysconf.nr_cpus = NR_CPUS; 83 + 84 + loongson_sysconf.pci_mem_start_addr = eirq_source->pci_mem_start_addr; 85 + loongson_sysconf.pci_mem_end_addr = eirq_source->pci_mem_end_addr; 86 + loongson_sysconf.pci_io_base = eirq_source->pci_io_start_addr; 87 + loongson_sysconf.dma_mask_bits = eirq_source->dma_mask_bits; 88 + if (loongson_sysconf.dma_mask_bits < 32 || 89 + loongson_sysconf.dma_mask_bits > 64) 90 + loongson_sysconf.dma_mask_bits = 32; 91 + 92 + loongson_sysconf.restart_addr = boot_p->reset_system.ResetWarm; 93 + loongson_sysconf.poweroff_addr = boot_p->reset_system.Shutdown; 94 + loongson_sysconf.suspend_addr = boot_p->reset_system.DoSuspend; 95 + 96 + loongson_sysconf.ht_control_base = 0x90000EFDFB000000; 97 + loongson_sysconf.vgabios_addr = boot_p->efi.smbios.vga_bios; 98 + pr_debug("Shutdown Addr: %llx, Restart Addr: %llx, VBIOS Addr: %llx\n", 99 + loongson_sysconf.poweroff_addr, loongson_sysconf.restart_addr, 100 + loongson_sysconf.vgabios_addr); 101 + #endif 62 102 if (cpu_clock_freq == 0) { 63 103 processor_id = (&current_cpu_data)->processor_id; 64 104 switch (processor_id & PRID_REV_MASK) { ··· 108 68 case PRID_REV_LOONGSON2F: 109 69 cpu_clock_freq = 797000000; 110 70 break; 71 + case PRID_REV_LOONGSON3A: 72 + cpu_clock_freq = 900000000; 73 + break; 111 74 default: 112 75 cpu_clock_freq = 100000000; 113 76 break; 114 77 } 115 78 } 116 - 117 - pr_info("busclock=%ld, cpuclock=%ld, memsize=%ld, highmemsize=%ld\n", 118 - bus_clock, cpu_clock_freq, memsize, highmemsize); 79 + pr_info("CpuClock = %u\n", cpu_clock_freq); 119 80 }
+5 -4
arch/mips/loongson/common/init.c
··· 17 17 18 18 void __init prom_init(void) 19 19 { 20 - /* init base address of io space */ 21 - set_io_port_base((unsigned long) 22 - ioremap(LOONGSON_PCIIO_BASE, LOONGSON_PCIIO_SIZE)); 23 - 24 20 #ifdef CONFIG_CPU_SUPPORTS_ADDRWINCFG 25 21 _loongson_addrwincfg_base = (unsigned long) 26 22 ioremap(LOONGSON_ADDRWINCFG_BASE, LOONGSON_ADDRWINCFG_SIZE); ··· 24 28 25 29 prom_init_cmdline(); 26 30 prom_init_env(); 31 + 32 + /* init base address of io space */ 33 + set_io_port_base((unsigned long) 34 + ioremap(LOONGSON_PCIIO_BASE, LOONGSON_PCIIO_SIZE)); 35 + 27 36 prom_init_memory(); 28 37 29 38 /*init the uart base address */
+42
arch/mips/loongson/common/mem.c
··· 11 11 #include <asm/bootinfo.h> 12 12 13 13 #include <loongson.h> 14 + #include <boot_param.h> 14 15 #include <mem.h> 15 16 #include <pci.h> 17 + 18 + #ifndef CONFIG_LEFI_FIRMWARE_INTERFACE 19 + 20 + u32 memsize, highmemsize; 16 21 17 22 void __init prom_init_memory(void) 18 23 { ··· 53 48 54 49 #endif /* !CONFIG_64BIT */ 55 50 } 51 + 52 + #else /* CONFIG_LEFI_FIRMWARE_INTERFACE */ 53 + 54 + void __init prom_init_memory(void) 55 + { 56 + int i; 57 + u32 node_id; 58 + u32 mem_type; 59 + 60 + /* parse memory information */ 61 + for (i = 0; i < loongson_memmap->nr_map; i++) { 62 + node_id = loongson_memmap->map[i].node_id; 63 + mem_type = loongson_memmap->map[i].mem_type; 64 + 65 + if (node_id == 0) { 66 + switch (mem_type) { 67 + case SYSTEM_RAM_LOW: 68 + add_memory_region(loongson_memmap->map[i].mem_start, 69 + (u64)loongson_memmap->map[i].mem_size << 20, 70 + BOOT_MEM_RAM); 71 + break; 72 + case SYSTEM_RAM_HIGH: 73 + add_memory_region(loongson_memmap->map[i].mem_start, 74 + (u64)loongson_memmap->map[i].mem_size << 20, 75 + BOOT_MEM_RAM); 76 + break; 77 + case MEM_RESERVED: 78 + add_memory_region(loongson_memmap->map[i].mem_start, 79 + (u64)loongson_memmap->map[i].mem_size << 20, 80 + BOOT_MEM_RESERVED); 81 + break; 82 + } 83 + } 84 + } 85 + } 86 + 87 + #endif /* CONFIG_LEFI_FIRMWARE_INTERFACE */ 56 88 57 89 /* override of arch/mips/mm/cache.c: __uncached_access */ 58 90 int __uncached_access(struct file *file, unsigned long addr)
+5 -1
arch/mips/loongson/common/pci.c
··· 11 11 12 12 #include <pci.h> 13 13 #include <loongson.h> 14 + #include <boot_param.h> 14 15 15 16 static struct resource loongson_pci_mem_resource = { 16 17 .name = "pci memory space", ··· 83 82 setup_pcimap(); 84 83 85 84 loongson_pci_controller.io_map_base = mips_io_port_base; 86 - 85 + #ifdef CONFIG_LEFI_FIRMWARE_INTERFACE 86 + loongson_pci_mem_resource.start = loongson_sysconf.pci_mem_start_addr; 87 + loongson_pci_mem_resource.end = loongson_sysconf.pci_mem_end_addr; 88 + #endif 87 89 register_pci_controller(&loongson_pci_controller); 88 90 89 91 return 0;
+21
arch/mips/loongson/common/reset.c
··· 16 16 #include <asm/reboot.h> 17 17 18 18 #include <loongson.h> 19 + #include <boot_param.h> 19 20 20 21 static inline void loongson_reboot(void) 21 22 { ··· 38 37 39 38 static void loongson_restart(char *command) 40 39 { 40 + #ifndef CONFIG_LEFI_FIRMWARE_INTERFACE 41 41 /* do preparation for reboot */ 42 42 mach_prepare_reboot(); 43 43 44 44 /* reboot via jumping to boot base address */ 45 45 loongson_reboot(); 46 + #else 47 + void (*fw_restart)(void) = (void *)loongson_sysconf.restart_addr; 48 + 49 + fw_restart(); 50 + while (1) { 51 + if (cpu_wait) 52 + cpu_wait(); 53 + } 54 + #endif 46 55 } 47 56 48 57 static void loongson_poweroff(void) 49 58 { 59 + #ifndef CONFIG_LEFI_FIRMWARE_INTERFACE 50 60 mach_prepare_shutdown(); 51 61 unreachable(); 62 + #else 63 + void (*fw_poweroff)(void) = (void *)loongson_sysconf.poweroff_addr; 64 + 65 + fw_poweroff(); 66 + while (1) { 67 + if (cpu_wait) 68 + cpu_wait(); 69 + } 70 + #endif 52 71 } 53 72 54 73 static void loongson_halt(void)