On 08/02/14 04:38, Huacai Chen wrote:
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@xxxxxxxxxx>
Signed-off-by: Hongliang Tao <taohl@xxxxxxxxxx>
Signed-off-by: Hua Yan <yanh@xxxxxxxxxx>
---
arch/mips/include/asm/mach-loongson/boot_param.h | 159 ++++++++++++++++++++++
arch/mips/include/asm/mach-loongson/loongson.h | 4 +-
arch/mips/loongson/common/env.c | 62 +++++++--
arch/mips/loongson/common/init.c | 9 +-
arch/mips/loongson/common/mem.c | 42 ++++++
arch/mips/loongson/common/pci.c | 6 +-
arch/mips/loongson/common/reset.c | 21 +++
7 files changed, 283 insertions(+), 20 deletions(-)
create mode 100644 arch/mips/include/asm/mach-loongson/boot_param.h
diff --git a/arch/mips/include/asm/mach-loongson/boot_param.h b/arch/mips/include/asm/mach-loongson/boot_param.h
new file mode 100644
index 0000000..a40dd56
--- /dev/null
+++ b/arch/mips/include/asm/mach-loongson/boot_param.h
@@ -0,0 +1,159 @@
+#ifndef __ASM_MACH_LOONGSON_BOOT_PARAM_H_
+#define __ASM_MACH_LOONGSON_BOOT_PARAM_H_
+
+#define SYSTEM_RAM_LOW 1
+#define SYSTEM_RAM_HIGH 2
+#define MEM_RESERVED 3
+#define PCI_IO 4
+#define PCI_MEM 5
+#define LOONGSON_CFG_REG 6
+#define VIDEO_ROM 7
+#define ADAPTER_ROM 8
+#define ACPI_TABLE 9
+#define MAX_MEMORY_TYPE 10
+
+#define LOONGSON3_BOOT_MEM_MAP_MAX 128
+struct efi_memory_map_loongson {
+ u16 vers; /* version of efi_memory_map */
+ u32 nr_map; /* number of memory_maps */
+ u32 mem_freq; /* memory frequence */
+ struct mem_map {
+ u32 node_id; /* node_id which memory attached to */
+ u32 mem_type; /* system memory, pci memory, pci io, etc. */
+ u64 mem_start; /* memory map start address */
+ u32 mem_size; /* each memory_map size, not the total size */
+ } map[LOONGSON3_BOOT_MEM_MAP_MAX];
+} __packed;
+
+enum loongson_cpu_type {
+ Loongson_2E = 0,
+ Loongson_2F = 1,
+ Loongson_3A = 2,
+ Loongson_3B = 3,
+ Loongson_1A = 4,
+ Loongson_1B = 5
+};
+
+/*
+ * Capability and feature descriptor structure for MIPS CPU
+ */
+struct efi_cpuinfo_loongson {
+ u16 vers; /* version of efi_cpuinfo_loongson */
+ u32 processor_id; /* PRID, e.g. 6305, 6306 */
+ u32 cputype; /* Loongson_3A/3B, etc. */
+ u32 total_node; /* num of total numa nodes */
+ u32 cpu_startup_core_id; /* Core id */
+ u32 cpu_clock_freq; /* cpu_clock */
+ u32 nr_cpus;
+} __packed;
+
+struct system_loongson {
+ u16 vers; /* version of system_loongson */
+ u32 ccnuma_smp; /* 0: no numa; 1: has numa */
+ u32 sing_double_channel; /* 1:single; 2:double */
+} __packed;
+
+struct irq_source_routing_table {
+ u16 vers;
+ u16 size;
+ u16 rtr_bus;
+ u16 rtr_devfn;
+ u32 vendor;
+ u32 device;
+ u32 PIC_type; /* conform use HT or PCI to route to CPU-PIC */
+ u64 ht_int_bit; /* 3A: 1<<24; 3B: 1<<16 */
+ u64 ht_enable; /* irqs used in this PIC */
+ u32 node_id; /* node id: 0x0-0; 0x1-1; 0x10-2; 0x11-3 */
+ u64 pci_mem_start_addr;
+ u64 pci_mem_end_addr;
+ u64 pci_io_start_addr;
+ u64 pci_io_end_addr;
+ u64 pci_config_addr;
+} __packed;
+
+struct interface_info {
+ u16 vers; /* version of the specificition */
+ u16 size;
+ u8 flag;
+ char description[64];
+} __packed;
+
+#define MAX_RESOURCE_NUMBER 128
+struct resource_loongson {
+ u64 start; /* resource start address */
+ u64 end; /* resource end address */
+ char name[64];
+ u32 flags;
+};
+
+struct archdev_data {}; /* arch specific additions */
+
+struct board_devices {
+ char name[64]; /* hold the device name */
+ u32 num_resources; /* number of device_resource */
+ /* for each device's resource */
+ struct resource_loongson resource[MAX_RESOURCE_NUMBER];
+ /* arch specific additions */
+ struct archdev_data archdata;
+};
+
+struct loongson_special_attribute {
+ u16 vers; /* version of this special */
+ char special_name[64]; /* special_atribute_name */
+ u32 loongson_special_type; /* type of special device */
+ /* for each device's resource */
+ struct resource_loongson resource[MAX_RESOURCE_NUMBER];
+};
+
+struct loongson_params {
+ u64 memory_offset; /* efi_memory_map_loongson struct offset */
+ u64 cpu_offset; /* efi_cpuinfo_loongson struct offset */
+ u64 system_offset; /* system_loongson struct offset */
+ u64 irq_offset; /* irq_source_routing_table struct offset */
+ u64 interface_offset; /* interface_info struct offset */
+ u64 special_offset; /* loongson_special_attribute struct offset */
+ u64 boarddev_table_offset; /* board_devices offset */
+};
+
+struct smbios_tables {
+ u16 vers; /* version of smbios */
+ u64 vga_bios; /* vga_bios address */
+ struct loongson_params lp;
+};
+
+struct efi_reset_system_t {
+ u64 ResetCold;
+ u64 ResetWarm;
+ u64 ResetType;
+ u64 Shutdown;
+};
+
+struct efi_loongson {
+ u64 mps; /* MPS table */
+ u64 acpi; /* ACPI table (IA64 ext 0.71) */
+ u64 acpi20; /* ACPI table (ACPI 2.0) */
+ struct smbios_tables smbios; /* SM BIOS table */
+ u64 sal_systab; /* SAL system table */
+ u64 boot_info; /* boot info table */
+};
+
+struct boot_params {
+ struct efi_loongson efi;
+ struct efi_reset_system_t reset_system;
+};
+
+struct loongson_system_configuration {
+ u32 nr_cpus;
+ enum loongson_cpu_type cputype;
+ u64 ht_control_base;
+ u64 pci_mem_start_addr;
+ u64 pci_mem_end_addr;
+ u64 pci_io_base;
+ u64 restart_addr;
+ u64 poweroff_addr;
+ u64 vgabios_addr;
+};
+
+extern struct efi_memory_map_loongson *loongson_memmap;
+extern struct loongson_system_configuration loongson_sysconf;
+#endif
diff --git a/arch/mips/include/asm/mach-loongson/loongson.h b/arch/mips/include/asm/mach-loongson/loongson.h
index b286534..5913ea0 100644
--- a/arch/mips/include/asm/mach-loongson/loongson.h
+++ b/arch/mips/include/asm/mach-loongson/loongson.h
@@ -24,8 +24,8 @@ extern void mach_prepare_reboot(void);
extern void mach_prepare_shutdown(void);
/* environment arguments from bootloader */
-extern unsigned long cpu_clock_freq;
-extern unsigned long memsize, highmemsize;
+extern u32 cpu_clock_freq;
+extern u32 memsize, highmemsize;
/* loongson-specific command line, env and memory initialization */
extern void __init prom_init_memory(void);
diff --git a/arch/mips/loongson/common/env.c b/arch/mips/loongson/common/env.c
index 0a18fcf..2fd8716 100644
--- a/arch/mips/loongson/common/env.c
+++ b/arch/mips/loongson/common/env.c
@@ -18,29 +18,30 @@
* option) any later version.
*/
#include <linux/module.h>
-
#include <asm/bootinfo.h>
-
#include <loongson.h>
+#include <boot_param.h>
-unsigned long cpu_clock_freq;
+u32 cpu_clock_freq;
EXPORT_SYMBOL(cpu_clock_freq);
-unsigned long memsize, highmemsize;
+struct efi_memory_map_loongson *loongson_memmap;
+struct loongson_system_configuration loongson_sysconf;
#define parse_even_earlier(res, option, p) \
do { \
unsigned int tmp __maybe_unused; \
\
if (strncmp(option, (char *)p, strlen(option)) == 0) \
- tmp = strict_strtol((char *)p + strlen(option"="), 10, &res); \
+ tmp = kstrtou32((char *)p + strlen(option"="), 10, &res); \
} while (0)
void __init prom_init_env(void)
{
/* pmon passes arguments in 32bit pointers */
- int *_prom_envp;
- unsigned long bus_clock;
unsigned int processor_id;
+
+#ifndef CONFIG_LEFI_FIRMWARE_INTERFACE
+ int *_prom_envp;
long l;
/* firmware arguments are initialized in head.S */
@@ -48,7 +49,6 @@ void __init prom_init_env(void)
l = (long)*_prom_envp;
while (l != 0) {
- parse_even_earlier(bus_clock, "busclock", l);
parse_even_earlier(cpu_clock_freq, "cpuclock", l);
parse_even_earlier(memsize, "memsize", l);
parse_even_earlier(highmemsize, "highmemsize", l);
@@ -57,8 +57,43 @@ void __init prom_init_env(void)
}
if (memsize == 0)
memsize = 256;
- if (bus_clock == 0)
- bus_clock = 66000000;
+ pr_info("memsize=%ld, highmemsize=%ld\n", memsize, highmemsize);
The build breaks here for Loongson 2 due to {,high}memsize now being u32:
arch/mips/loongson/common/env.c: In function 'prom_init_env':
arch/mips/loongson/common/env.c:60:2: error: format '%ld' expects
argument of type 'long int', but argument 2 has type 'u32' [-Werror=format=]
pr_info("memsize=%ld, highmemsize=%ld\n", memsize, highmemsize);
^
arch/mips/loongson/common/env.c:60:2: error: format '%ld' expects
argument of type 'long int', but argument 3 has type 'u32' [-Werror=format=]
cc1: all warnings being treated as errors
Thanks,
Alex
+#else
+ struct boot_params *boot_p;
+ struct loongson_params *loongson_p;
+ struct efi_cpuinfo_loongson *ecpu;
+ struct irq_source_routing_table *eirq_source;
+
+ /* firmware arguments are initialized in head.S */
+ boot_p = (struct boot_params *)fw_arg2;
+ loongson_p = &(boot_p->efi.smbios.lp);
+
+ ecpu = (struct efi_cpuinfo_loongson *)
+ ((u64)loongson_p + loongson_p->cpu_offset);
+ eirq_source = (struct irq_source_routing_table *)
+ ((u64)loongson_p + loongson_p->irq_offset);
+ loongson_memmap = (struct efi_memory_map_loongson *)
+ ((u64)loongson_p + loongson_p->memory_offset);
+
+ cpu_clock_freq = ecpu->cpu_clock_freq;
+ loongson_sysconf.cputype = ecpu->cputype;
+ loongson_sysconf.nr_cpus = ecpu->nr_cpus;
+ if (ecpu->nr_cpus > NR_CPUS || ecpu->nr_cpus == 0)
+ loongson_sysconf.nr_cpus = NR_CPUS;
+
+ loongson_sysconf.pci_mem_start_addr = eirq_source->pci_mem_start_addr;
+ loongson_sysconf.pci_mem_end_addr = eirq_source->pci_mem_end_addr;
+ loongson_sysconf.pci_io_base = eirq_source->pci_io_start_addr;
+
+ loongson_sysconf.restart_addr = boot_p->reset_system.ResetWarm;
+ loongson_sysconf.poweroff_addr = boot_p->reset_system.Shutdown;
+
+ loongson_sysconf.ht_control_base = 0x90000EFDFB000000;
+ loongson_sysconf.vgabios_addr = boot_p->efi.smbios.vga_bios;
+ pr_debug("Shutdown Addr: %llx, Restart Addr: %llx, VBIOS Addr: %llx\n",
+ loongson_sysconf.poweroff_addr, loongson_sysconf.restart_addr,
+ loongson_sysconf.vgabios_addr);
+#endif
if (cpu_clock_freq == 0) {
processor_id = (¤t_cpu_data)->processor_id;
switch (processor_id & PRID_REV_MASK) {
@@ -68,12 +103,13 @@ void __init prom_init_env(void)
case PRID_REV_LOONGSON2F:
cpu_clock_freq = 797000000;
break;
+ case PRID_REV_LOONGSON3A:
+ cpu_clock_freq = 900000000;
+ break;
default:
cpu_clock_freq = 100000000;
break;
}
}
-
- pr_info("busclock=%ld, cpuclock=%ld, memsize=%ld, highmemsize=%ld\n",
- bus_clock, cpu_clock_freq, memsize, highmemsize);
+ pr_info("CpuClock = %u\n", cpu_clock_freq);
}
diff --git a/arch/mips/loongson/common/init.c b/arch/mips/loongson/common/init.c
index ae7af1f..81ba3b4 100644
--- a/arch/mips/loongson/common/init.c
+++ b/arch/mips/loongson/common/init.c
@@ -17,10 +17,6 @@ unsigned long __maybe_unused _loongson_addrwincfg_base;
void __init prom_init(void)
{
- /* init base address of io space */
- set_io_port_base((unsigned long)
- ioremap(LOONGSON_PCIIO_BASE, LOONGSON_PCIIO_SIZE));
-
#ifdef CONFIG_CPU_SUPPORTS_ADDRWINCFG
_loongson_addrwincfg_base = (unsigned long)
ioremap(LOONGSON_ADDRWINCFG_BASE, LOONGSON_ADDRWINCFG_SIZE);
@@ -28,6 +24,11 @@ void __init prom_init(void)
prom_init_cmdline();
prom_init_env();
+
+ /* init base address of io space */
+ set_io_port_base((unsigned long)
+ ioremap(LOONGSON_PCIIO_BASE, LOONGSON_PCIIO_SIZE));
+
prom_init_memory();
/*init the uart base address */
diff --git a/arch/mips/loongson/common/mem.c b/arch/mips/loongson/common/mem.c
index 8626a42..b01d524 100644
--- a/arch/mips/loongson/common/mem.c
+++ b/arch/mips/loongson/common/mem.c
@@ -11,9 +11,14 @@
#include <asm/bootinfo.h>
#include <loongson.h>
+#include <boot_param.h>
#include <mem.h>
#include <pci.h>
+#ifndef CONFIG_LEFI_FIRMWARE_INTERFACE
+
+u32 memsize, highmemsize;
+
void __init prom_init_memory(void)
{
add_memory_region(0x0, (memsize << 20), BOOT_MEM_RAM);
@@ -49,6 +54,43 @@ void __init prom_init_memory(void)
#endif /* !CONFIG_64BIT */
}
+#else /* CONFIG_LEFI_FIRMWARE_INTERFACE */
+
+void __init prom_init_memory(void)
+{
+ int i;
+ u32 node_id;
+ u32 mem_type;
+
+ /* parse memory information */
+ for (i = 0; i < loongson_memmap->nr_map; i++) {
+ node_id = loongson_memmap->map[i].node_id;
+ mem_type = loongson_memmap->map[i].mem_type;
+
+ if (node_id == 0) {
+ switch (mem_type) {
+ case SYSTEM_RAM_LOW:
+ add_memory_region(loongson_memmap->map[i].mem_start,
+ (u64)loongson_memmap->map[i].mem_size << 20,
+ BOOT_MEM_RAM);
+ break;
+ case SYSTEM_RAM_HIGH:
+ add_memory_region(loongson_memmap->map[i].mem_start,
+ (u64)loongson_memmap->map[i].mem_size << 20,
+ BOOT_MEM_RAM);
+ break;
+ case MEM_RESERVED:
+ add_memory_region(loongson_memmap->map[i].mem_start,
+ (u64)loongson_memmap->map[i].mem_size << 20,
+ BOOT_MEM_RESERVED);
+ break;
+ }
+ }
+ }
+}
+
+#endif /* CONFIG_LEFI_FIRMWARE_INTERFACE */
+
/* override of arch/mips/mm/cache.c: __uncached_access */
int __uncached_access(struct file *file, unsigned long addr)
{
diff --git a/arch/mips/loongson/common/pci.c b/arch/mips/loongson/common/pci.c
index fa77844..003ab4e 100644
--- a/arch/mips/loongson/common/pci.c
+++ b/arch/mips/loongson/common/pci.c
@@ -11,6 +11,7 @@
#include <pci.h>
#include <loongson.h>
+#include <boot_param.h>
static struct resource loongson_pci_mem_resource = {
.name = "pci memory space",
@@ -82,7 +83,10 @@ static int __init pcibios_init(void)
setup_pcimap();
loongson_pci_controller.io_map_base = mips_io_port_base;
-
+#ifdef CONFIG_LEFI_FIRMWARE_INTERFACE
+ loongson_pci_mem_resource.start = loongson_sysconf.pci_mem_start_addr;
+ loongson_pci_mem_resource.end = loongson_sysconf.pci_mem_end_addr;
+#endif
register_pci_controller(&loongson_pci_controller);
return 0;
diff --git a/arch/mips/loongson/common/reset.c b/arch/mips/loongson/common/reset.c
index 65bfbb5..a60715e 100644
--- a/arch/mips/loongson/common/reset.c
+++ b/arch/mips/loongson/common/reset.c
@@ -16,6 +16,7 @@
#include <asm/reboot.h>
#include <loongson.h>
+#include <boot_param.h>
static inline void loongson_reboot(void)
{
@@ -37,17 +38,37 @@ static inline void loongson_reboot(void)
static void loongson_restart(char *command)
{
+#ifndef CONFIG_LEFI_FIRMWARE_INTERFACE
/* do preparation for reboot */
mach_prepare_reboot();
/* reboot via jumping to boot base address */
loongson_reboot();
+#else
+ void (*fw_restart)(void) = (void *)loongson_sysconf.restart_addr;
+
+ fw_restart();
+ while (1) {
+ if (cpu_wait)
+ cpu_wait();
+ }
+#endif
}
static void loongson_poweroff(void)
{
+#ifndef CONFIG_LEFI_FIRMWARE_INTERFACE
mach_prepare_shutdown();
unreachable();
+#else
+ void (*fw_poweroff)(void) = (void *)loongson_sysconf.poweroff_addr;
+
+ fw_poweroff();
+ while (1) {
+ if (cpu_wait)
+ cpu_wait();
+ }
+#endif
}
static void loongson_halt(void)