* [PATCH V4 09/22] LoongArch: Add boot and setup routines [not found] <20210927064300.624279-1-chenhuacai@loongson.cn> @ 2021-09-27 6:42 ` Huacai Chen 2021-09-27 6:52 ` Ard Biesheuvel 0 siblings, 1 reply; 3+ messages in thread From: Huacai Chen @ 2021-09-27 6:42 UTC (permalink / raw) To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton, David Airlie, Jonathan Corbet, Linus Torvalds Cc: linux-arch, linux-doc, linux-kernel, Xuefeng Li, Yanteng Si, Huacai Chen, Jiaxun Yang, Huacai Chen, Ard Biesheuvel, linux-efi This patch adds basic boot, setup and reset routines for LoongArch. LoongArch uses UEFI-based firmware and uses ACPI as the boot protocol. Now the boot information passed to kernel is like this: 1, kernel get a0, a1 and a2 from bootloader. 2, a0 is "argc", a1 is "argc", so "cmdline" comes from a0/a1. 3, a2 is "envrion", which is a pointer to a "struct bootparamsinterface" 4, "struct bootparamsinterface" include a "systemtable" pointer, whose type is "efi_system_table_t". Most information, include ACPI tables, come from here. Cc: Ard Biesheuvel <ardb@kernel.org> Cc: linux-efi@vger.kernel.org Signed-off-by: Huacai Chen <chenhuacai@loongson.cn> --- arch/loongarch/include/asm/acenv.h | 17 + arch/loongarch/include/asm/acpi.h | 37 ++ arch/loongarch/include/asm/boot_param.h | 91 +++++ arch/loongarch/include/asm/bootinfo.h | 33 ++ arch/loongarch/include/asm/dmi.h | 24 ++ arch/loongarch/include/asm/efi.h | 32 ++ arch/loongarch/include/asm/fw.h | 18 + arch/loongarch/include/asm/reboot.h | 10 + arch/loongarch/include/asm/setup.h | 21 ++ arch/loongarch/kernel/acpi.c | 324 ++++++++++++++++ arch/loongarch/kernel/cacheinfo.c | 122 ++++++ arch/loongarch/kernel/cmdline.c | 28 ++ arch/loongarch/kernel/cpu-probe.c | 301 +++++++++++++++ arch/loongarch/kernel/efi.c | 95 +++++ arch/loongarch/kernel/env.c | 170 +++++++++ arch/loongarch/kernel/head.S | 72 ++++ arch/loongarch/kernel/mem.c | 83 +++++ arch/loongarch/kernel/reset.c | 90 +++++ arch/loongarch/kernel/setup.c | 469 ++++++++++++++++++++++++ arch/loongarch/kernel/time.c | 237 ++++++++++++ arch/loongarch/kernel/topology.c | 13 + 21 files changed, 2287 insertions(+) create mode 100644 arch/loongarch/include/asm/acenv.h create mode 100644 arch/loongarch/include/asm/acpi.h create mode 100644 arch/loongarch/include/asm/boot_param.h create mode 100644 arch/loongarch/include/asm/bootinfo.h create mode 100644 arch/loongarch/include/asm/dmi.h create mode 100644 arch/loongarch/include/asm/efi.h create mode 100644 arch/loongarch/include/asm/fw.h create mode 100644 arch/loongarch/include/asm/reboot.h create mode 100644 arch/loongarch/include/asm/setup.h create mode 100644 arch/loongarch/kernel/acpi.c create mode 100644 arch/loongarch/kernel/cacheinfo.c create mode 100644 arch/loongarch/kernel/cmdline.c create mode 100644 arch/loongarch/kernel/cpu-probe.c create mode 100644 arch/loongarch/kernel/efi.c create mode 100644 arch/loongarch/kernel/env.c create mode 100644 arch/loongarch/kernel/head.S create mode 100644 arch/loongarch/kernel/mem.c create mode 100644 arch/loongarch/kernel/reset.c create mode 100644 arch/loongarch/kernel/setup.c create mode 100644 arch/loongarch/kernel/time.c create mode 100644 arch/loongarch/kernel/topology.c diff --git a/arch/loongarch/include/asm/acenv.h b/arch/loongarch/include/asm/acenv.h new file mode 100644 index 000000000000..d1f41fbc9070 --- /dev/null +++ b/arch/loongarch/include/asm/acenv.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * LoongArch specific ACPICA environments and implementation + * + * Author: Jianmin Lv <lvjianmin@loongson.cn> + * Huacai Chen <chenhuacai@loongson.cn> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ + +#ifndef _ASM_LOONGARCH_ACENV_H +#define _ASM_LOONGARCH_ACENV_H + +/* The head file is required by ACPI core, but we have nothing to fill + * it now, update it later when needed. + */ + +#endif /* _ASM_LOONGARCH_ACENV_H */ diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h new file mode 100644 index 000000000000..29e2214d5901 --- /dev/null +++ b/arch/loongarch/include/asm/acpi.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Author: Jianmin Lv <lvjianmin@loongson.cn> + * Huacai Chen <chenhuacai@loongson.cn> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ + +#ifndef _ASM_LOONGARCH_ACPI_H +#define _ASM_LOONGARCH_ACPI_H + +#ifdef CONFIG_ACPI +extern int acpi_strict; +extern int acpi_disabled; +extern int acpi_pci_disabled; +extern int acpi_noirq; + +#define acpi_os_ioremap ioremap + +static inline void disable_acpi(void) +{ + acpi_disabled = 1; + acpi_pci_disabled = 1; + acpi_noirq = 1; +} + +static inline bool acpi_has_cpu_in_madt(void) +{ + return true; +} + +extern struct list_head acpi_wakeup_device_list; + +#endif /* !CONFIG_ACPI */ + +#define ACPI_TABLE_UPGRADE_MAX_PHYS (max_low_pfn << PAGE_SHIFT) + +#endif /* _ASM_LOONGARCH_ACPI_H */ diff --git a/arch/loongarch/include/asm/boot_param.h b/arch/loongarch/include/asm/boot_param.h new file mode 100644 index 000000000000..f515431d838b --- /dev/null +++ b/arch/loongarch/include/asm/boot_param.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_BOOT_PARAM_H_ +#define __ASM_BOOT_PARAM_H_ + +#ifdef CONFIG_VT +#include <linux/screen_info.h> +#endif + +#define ADDRESS_TYPE_SYSRAM 1 +#define ADDRESS_TYPE_RESERVED 2 +#define ADDRESS_TYPE_ACPI 3 +#define ADDRESS_TYPE_NVS 4 +#define ADDRESS_TYPE_PMEM 5 + +#define LOONGSON3_BOOT_MEM_MAP_MAX 128 + +#define LOONGSON_EFIBOOT_SIGNATURE "BPI" +#define LOONGSON_MEM_LINKLIST "MEM" +#define LOONGSON_VBIOS_LINKLIST "VBIOS" +#define LOONGSON_SCREENINFO_LINKLIST "SINFO" + +/* Values for Version BPI */ +enum bpi_version { + BPI_VERSION_V1 = 1000, /* Signature="BPI01000" */ + BPI_VERSION_V2 = 1001, /* Signature="BPI01001" */ +}; + +/* Flags in bootparamsinterface */ +#define BPI_FLAGS_UEFI_SUPPORTED BIT(0) + +struct _extention_list_hdr { + u64 signature; + u32 length; + u8 revision; + u8 checksum; + struct _extention_list_hdr *next; +} __packed; + +struct bootparamsinterface { + u64 signature; /* {"B", "P", "I", "0", "1", ... } */ + void *systemtable; + struct _extention_list_hdr *extlist; + u64 flags; +} __packed; + +struct loongsonlist_mem_map { + struct _extention_list_hdr header; /* {"M", "E", "M"} */ + u8 map_count; + struct loongson_mem_map { + u32 mem_type; + u64 mem_start; + u64 mem_size; + } __packed map[LOONGSON3_BOOT_MEM_MAP_MAX]; +} __packed; + +struct loongsonlist_vbios { + struct _extention_list_hdr header; /* {"V", "B", "I", "O", "S"} */ + u64 vbios_addr; +} __packed; + +struct loongsonlist_screeninfo { + struct _extention_list_hdr header; /* {"S", "I", "N", "F", "O"} */ + struct screen_info si; +} __packed; + +struct loongson_board_info { + int bios_size; + char *bios_vendor; + char *bios_version; + char *bios_release_date; + char *board_name; + char *board_vendor; +}; + +struct loongson_system_configuration { + int bpi_ver; + int nr_cpus; + int nr_nodes; + int nr_io_pics; + int boot_cpu_id; + int cores_per_node; + int cores_per_package; + char *cpuname; + u64 vgabios_addr; +}; + +extern struct loongson_board_info b_info; +extern struct bootparamsinterface *efi_bp; +extern struct loongsonlist_mem_map *loongson_mem_map; +extern struct loongson_system_configuration loongson_sysconf; +#endif diff --git a/arch/loongarch/include/asm/bootinfo.h b/arch/loongarch/include/asm/bootinfo.h new file mode 100644 index 000000000000..41838633d1aa --- /dev/null +++ b/arch/loongarch/include/asm/bootinfo.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ +#ifndef _ASM_BOOTINFO_H +#define _ASM_BOOTINFO_H + +#include <linux/types.h> +#include <asm/setup.h> + +const char *get_system_type(void); + +extern void early_memblock_init(void); +extern void detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_addr_t sz_max); + +extern void early_init(void); +extern void platform_init(void); + +extern void free_init_pages(const char *what, unsigned long begin, unsigned long end); + +/* + * Initial kernel command line, usually setup by fw_init_cmdline() + */ +extern char arcs_cmdline[COMMAND_LINE_SIZE]; + +/* + * Registers a0, a1, a3 and a4 as passed to the kernel entry by firmware + */ +extern unsigned long fw_arg0, fw_arg1, fw_arg2, fw_arg3; + +extern unsigned long initrd_start, initrd_end; + +#endif /* _ASM_BOOTINFO_H */ diff --git a/arch/loongarch/include/asm/dmi.h b/arch/loongarch/include/asm/dmi.h new file mode 100644 index 000000000000..fbe98f0e56e0 --- /dev/null +++ b/arch/loongarch/include/asm/dmi.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ +#ifndef _ASM_DMI_H +#define _ASM_DMI_H + +#include <linux/io.h> +#include <linux/memblock.h> + +#define dmi_early_remap(x, l) dmi_remap(x, l) +#define dmi_early_unmap(x, l) dmi_unmap(x) +#define dmi_alloc(l) memblock_alloc_low(l, PAGE_SIZE) + +static inline void *dmi_remap(u64 phys_addr, unsigned long size) +{ + return ((void *)TO_CAC(phys_addr)); +} + +static inline void dmi_unmap(void *addr) +{ +} + +#endif /* _ASM_DMI_H */ diff --git a/arch/loongarch/include/asm/efi.h b/arch/loongarch/include/asm/efi.h new file mode 100644 index 000000000000..90e8e35d7f4b --- /dev/null +++ b/arch/loongarch/include/asm/efi.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ +#ifndef _ASM_LOONGARCH_EFI_H +#define _ASM_LOONGARCH_EFI_H + +#include <linux/efi.h> + +extern void __init efi_init(void); +extern void __init efi_runtime_init(void); +extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt); + +#define ARCH_EFI_IRQ_FLAGS_MASK 0x00000001 /*bit0: CP0 Status.IE*/ + +#define arch_efi_call_virt_setup() \ +({ \ +}) + +#define arch_efi_call_virt(p, f, args...) \ +({ \ + efi_##f##_t * __f; \ + __f = p->f; \ + __f(args); \ +}) + +#define arch_efi_call_virt_teardown() \ +({ \ +}) + + +#endif /* _ASM_LOONGARCH_EFI_H */ diff --git a/arch/loongarch/include/asm/fw.h b/arch/loongarch/include/asm/fw.h new file mode 100644 index 000000000000..8a0e8e7c625f --- /dev/null +++ b/arch/loongarch/include/asm/fw.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ +#ifndef __ASM_FW_H_ +#define __ASM_FW_H_ + +#include <asm/bootinfo.h> + +extern int fw_argc; +extern long *_fw_argv, *_fw_envp; + +#define fw_argv(index) ((char *)(long)_fw_argv[(index)]) +#define fw_envp(index) ((char *)(long)_fw_envp[(index)]) + +extern void fw_init_cmdline(void); + +#endif /* __ASM_FW_H_ */ diff --git a/arch/loongarch/include/asm/reboot.h b/arch/loongarch/include/asm/reboot.h new file mode 100644 index 000000000000..0e96b0f42993 --- /dev/null +++ b/arch/loongarch/include/asm/reboot.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ +#ifndef _ASM_REBOOT_H +#define _ASM_REBOOT_H + +extern void (*pm_restart)(void); + +#endif /* _ASM_REBOOT_H */ diff --git a/arch/loongarch/include/asm/setup.h b/arch/loongarch/include/asm/setup.h new file mode 100644 index 000000000000..fc1fb741b4da --- /dev/null +++ b/arch/loongarch/include/asm/setup.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ + +#ifndef _LOONGARCH_SETUP_H +#define _LOONGARCH_SETUP_H + +#include <linux/types.h> +#include <uapi/asm/setup.h> + +#define VECSIZE 0x200 + +extern unsigned long eentry; +extern unsigned long tlbrentry; +extern void cpu_cache_init(void); +extern void per_cpu_trap_init(int cpu); +extern void set_handler(unsigned long offset, void *addr, unsigned long len); +extern void set_merr_handler(unsigned long offset, void *addr, unsigned long len); + +#endif /* __SETUP_H */ diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c new file mode 100644 index 000000000000..3f77c937e9ad --- /dev/null +++ b/arch/loongarch/kernel/acpi.c @@ -0,0 +1,324 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * acpi.c - Architecture-Specific Low-Level ACPI Boot Support + * + * Author: Jianmin Lv <lvjianmin@loongson.cn> + * Huacai Chen <chenhuacai@loongson.cn> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ + +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/memblock.h> +#include <linux/serial_core.h> +#include <asm/io.h> +#include <asm/loongson.h> + +int acpi_disabled; +EXPORT_SYMBOL(acpi_disabled); +int acpi_noirq; +int acpi_pci_disabled; +EXPORT_SYMBOL(acpi_pci_disabled); +int acpi_strict = 1; /* We have no workarounds on LoongArch */ +int num_processors; +int disabled_cpus; +enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM; + +u64 acpi_saved_sp; + +#define MAX_CORE_PIC 256 + +#define PREFIX "ACPI: " + +/* + * Following __acpi_xx functions should be implemented for sepecific cpu. + */ +int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp) +{ + if (irqp != NULL) + *irqp = acpi_register_gsi(NULL, gsi, -1, -1); + return 0; +} +EXPORT_SYMBOL_GPL(acpi_gsi_to_irq); + +int acpi_isa_irq_to_gsi(unsigned int isa_irq, u32 *gsi) +{ + if (gsi) + *gsi = isa_irq; + return 0; +} + +/* + * success: return IRQ number (>=0) + * failure: return < 0 + */ +int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity) +{ + int id, hwirq; + struct irq_domain *domain; + struct irq_fwspec fwspec; + + switch (gsi) { + case GSI_MIN_CPU_IRQ ... GSI_MAX_CPU_IRQ: + hwirq = gsi - GSI_MIN_CPU_IRQ; + return irq_create_mapping(liointc_domain, hwirq); + + case GSI_MIN_PCH_IRQ ... GSI_MAX_PCH_IRQ: + id = find_pch_pic(gsi); + if (id < 0) + return -1; + + domain = pch_pic_domain[id]; + if (!domain) + return gsi; + + hwirq = gsi - acpi_pchpic[id]->gsi_base; + fwspec.param_count = 2; + fwspec.param[0] = hwirq; + fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; + + return irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, &fwspec); + } + + return -1; +} +EXPORT_SYMBOL_GPL(acpi_register_gsi); + +void acpi_unregister_gsi(u32 gsi) +{ + +} +EXPORT_SYMBOL_GPL(acpi_unregister_gsi); + +void __iomem *__init __acpi_map_table(unsigned long phys, unsigned long size) +{ + + if (!phys || !size) + return NULL; + + return early_memremap(phys, size); +} +void __init __acpi_unmap_table(void __iomem *map, unsigned long size) +{ + if (!map || !size) + return; + + early_memunmap(map, size); +} + +void __init acpi_boot_table_init(void) +{ + /* + * If acpi_disabled, bail out + */ + if (acpi_disabled) + return; + + /* + * Initialize the ACPI boot-time table parser. + */ + if (acpi_table_init()) { + disable_acpi(); + return; + } +} + +static int __init +acpi_parse_cpuintc(union acpi_subtable_headers *header, const unsigned long end) +{ + struct acpi_madt_core_pic *processor = NULL; + + processor = (struct acpi_madt_core_pic *)header; + if (BAD_MADT_ENTRY(processor, end)) + return -EINVAL; + + acpi_table_print_madt_entry(&header->common); + + return 0; +} + +static int __init +acpi_parse_liointc(union acpi_subtable_headers *header, const unsigned long end) +{ + struct acpi_madt_lio_pic *liointc = NULL; + + liointc = (struct acpi_madt_lio_pic *)header; + + if (BAD_MADT_ENTRY(liointc, end)) + return -EINVAL; + + acpi_liointc = liointc; + + return 0; +} + +static int __init +acpi_parse_eiointc(union acpi_subtable_headers *header, const unsigned long end) +{ + static int id = 0; + struct acpi_madt_eio_pic *eiointc = NULL; + + eiointc = (struct acpi_madt_eio_pic *)header; + + if (BAD_MADT_ENTRY(eiointc, end)) + return -EINVAL; + + acpi_eiointc[id++] = eiointc; + loongson_sysconf.nr_io_pics = id; + + return 0; +} + +static int __init +acpi_parse_htintc(union acpi_subtable_headers *header, const unsigned long end) +{ + struct acpi_madt_ht_pic *htintc = NULL; + + htintc = (struct acpi_madt_ht_pic *)header; + + if (BAD_MADT_ENTRY(htintc, end)) + return -EINVAL; + + acpi_htintc = htintc; + loongson_sysconf.nr_io_pics = 1; + + return 0; +} + +static int __init +acpi_parse_pch_pic(union acpi_subtable_headers *header, const unsigned long end) +{ + static int id = 0; + struct acpi_madt_bio_pic *pchpic = NULL; + + pchpic = (struct acpi_madt_bio_pic *)header; + + if (BAD_MADT_ENTRY(pchpic, end)) + return -EINVAL; + + acpi_pchpic[id++] = pchpic; + + return 0; +} + +static int __init +acpi_parse_pch_msi(union acpi_subtable_headers *header, const unsigned long end) +{ + static int id = 0; + struct acpi_madt_msi_pic *pchmsi = NULL; + + pchmsi = (struct acpi_madt_msi_pic *)header; + + if (BAD_MADT_ENTRY(pchmsi, end)) + return -EINVAL; + + acpi_pchmsi[id++] = pchmsi; + + return 0; +} + +static int __init +acpi_parse_pch_lpc(union acpi_subtable_headers *header, const unsigned long end) +{ + struct acpi_madt_lpc_pic *pchlpc = NULL; + + pchlpc = (struct acpi_madt_lpc_pic *)header; + + if (BAD_MADT_ENTRY(pchlpc, end)) + return -EINVAL; + + acpi_pchlpc = pchlpc; + + return 0; +} + +static void __init acpi_process_madt(void) +{ + int error; + + /* Parse MADT CPUINTC entries */ + error = acpi_table_parse_madt(ACPI_MADT_TYPE_CORE_PIC, acpi_parse_cpuintc, MAX_CORE_PIC); + if (error < 0) { + disable_acpi(); + pr_err(PREFIX "Invalid BIOS MADT (CPUINTC entries), ACPI disabled\n"); + return; + } + + loongson_sysconf.nr_cpus = num_processors; + + /* Parse MADT LIOINTC entries */ + error = acpi_table_parse_madt(ACPI_MADT_TYPE_LIO_PIC, acpi_parse_liointc, 1); + if (error < 0) { + disable_acpi(); + pr_err(PREFIX "Invalid BIOS MADT (LIOINTC entries), ACPI disabled\n"); + return; + } + + /* Parse MADT EIOINTC entries */ + error = acpi_table_parse_madt(ACPI_MADT_TYPE_EIO_PIC, acpi_parse_eiointc, MAX_IO_PICS); + if (error < 0) { + disable_acpi(); + pr_err(PREFIX "Invalid BIOS MADT (EIOINTC entries), ACPI disabled\n"); + return; + } + + /* Parse MADT HTVEC entries */ + error = acpi_table_parse_madt(ACPI_MADT_TYPE_HT_PIC, acpi_parse_htintc, 1); + if (error < 0) { + disable_acpi(); + pr_err(PREFIX "Invalid BIOS MADT (HTVEC entries), ACPI disabled\n"); + return; + } + + /* Parse MADT PCHPIC entries */ + error = acpi_table_parse_madt(ACPI_MADT_TYPE_BIO_PIC, acpi_parse_pch_pic, MAX_IO_PICS); + if (error < 0) { + disable_acpi(); + pr_err(PREFIX "Invalid BIOS MADT (PCHPIC entries), ACPI disabled\n"); + return; + } + + /* Parse MADT PCHMSI entries */ + error = acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, acpi_parse_pch_msi, MAX_IO_PICS); + if (error < 0) { + disable_acpi(); + pr_err(PREFIX "Invalid BIOS MADT (PCHMSI entries), ACPI disabled\n"); + return; + } + + /* Parse MADT PCHLPC entries */ + error = acpi_table_parse_madt(ACPI_MADT_TYPE_LPC_PIC, acpi_parse_pch_lpc, 1); + if (error < 0) { + disable_acpi(); + pr_err(PREFIX "Invalid BIOS MADT (PCHLPC entries), ACPI disabled\n"); + return; + } +} + +int __init acpi_boot_init(void) +{ + /* + * If acpi_disabled, bail out + */ + if (acpi_disabled) + return -1; + + loongson_sysconf.boot_cpu_id = read_csr_cpuid(); + + /* + * Process the Multiple APIC Description Table (MADT), if present + */ + acpi_process_madt(); + + /* Do not enable ACPI SPCR console by default */ + acpi_parse_spcr(earlycon_acpi_spcr_enable, false); + + return 0; +} + +void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size) +{ + memblock_mark_nomap(addr, size); +} diff --git a/arch/loongarch/kernel/cacheinfo.c b/arch/loongarch/kernel/cacheinfo.c new file mode 100644 index 000000000000..bedeb3eb1157 --- /dev/null +++ b/arch/loongarch/kernel/cacheinfo.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * LoongArch cacheinfo support + * + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ +#include <linux/cacheinfo.h> + +/* Populates leaf and increments to next leaf */ +#define populate_cache(cache, leaf, c_level, c_type) \ +do { \ + leaf->type = c_type; \ + leaf->level = c_level; \ + leaf->coherency_line_size = c->cache.linesz; \ + leaf->number_of_sets = c->cache.sets; \ + leaf->ways_of_associativity = c->cache.ways; \ + leaf->size = c->cache.linesz * c->cache.sets * \ + c->cache.ways; \ + leaf++; \ +} while (0) + +int init_cache_level(unsigned int cpu) +{ + struct cpuinfo_loongarch *c = ¤t_cpu_data; + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); + int levels = 0, leaves = 0; + + /* + * If Dcache is not set, we assume the cache structures + * are not properly initialized. + */ + if (c->dcache.waysize) + levels += 1; + else + return -ENOENT; + + + leaves += (c->icache.waysize) ? 2 : 1; + + if (c->vcache.waysize) { + levels++; + leaves++; + } + + if (c->scache.waysize) { + levels++; + leaves++; + } + + if (c->tcache.waysize) { + levels++; + leaves++; + } + + this_cpu_ci->num_levels = levels; + this_cpu_ci->num_leaves = leaves; + return 0; +} + +static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, + struct cacheinfo *sib_leaf) +{ + return !((this_leaf->level == 1) || (this_leaf->level == 2)); +} + +static void cache_cpumap_setup(unsigned int cpu) +{ + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); + struct cacheinfo *this_leaf, *sib_leaf; + unsigned int index; + + for (index = 0; index < this_cpu_ci->num_leaves; index++) { + unsigned int i; + + this_leaf = this_cpu_ci->info_list + index; + /* skip if shared_cpu_map is already populated */ + if (!cpumask_empty(&this_leaf->shared_cpu_map)) + continue; + + cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map); + for_each_online_cpu(i) { + struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i); + + if (i == cpu || !sib_cpu_ci->info_list) + continue;/* skip if itself or no cacheinfo */ + sib_leaf = sib_cpu_ci->info_list + index; + if (cache_leaves_are_shared(this_leaf, sib_leaf)) { + cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map); + cpumask_set_cpu(i, &this_leaf->shared_cpu_map); + } + } + } +} + +int populate_cache_leaves(unsigned int cpu) +{ + int level = 1; + struct cpuinfo_loongarch *c = ¤t_cpu_data; + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); + struct cacheinfo *this_leaf = this_cpu_ci->info_list; + + if (c->icache.waysize) { + populate_cache(dcache, this_leaf, level, CACHE_TYPE_DATA); + populate_cache(icache, this_leaf, level++, CACHE_TYPE_INST); + } else { + populate_cache(dcache, this_leaf, level++, CACHE_TYPE_UNIFIED); + } + + if (c->vcache.waysize) + populate_cache(vcache, this_leaf, level++, CACHE_TYPE_UNIFIED); + + if (c->scache.waysize) + populate_cache(scache, this_leaf, level++, CACHE_TYPE_UNIFIED); + + if (c->tcache.waysize) + populate_cache(tcache, this_leaf, level++, CACHE_TYPE_UNIFIED); + + cache_cpumap_setup(cpu); + this_cpu_ci->cpu_map_populated = true; + + return 0; +} diff --git a/arch/loongarch/kernel/cmdline.c b/arch/loongarch/kernel/cmdline.c new file mode 100644 index 000000000000..46bf4486b54c --- /dev/null +++ b/arch/loongarch/kernel/cmdline.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/string.h> + +#include <asm/fw.h> + +int fw_argc; +long *_fw_argv, *_fw_envp; + +void __init fw_init_cmdline(void) +{ + int i; + + fw_argc = fw_arg0; + _fw_argv = (long *)fw_arg1; + _fw_envp = (long *)fw_arg2; + + arcs_cmdline[0] = '\0'; + for (i = 1; i < fw_argc; i++) { + strlcat(arcs_cmdline, fw_argv(i), COMMAND_LINE_SIZE); + if (i < (fw_argc - 1)) + strlcat(arcs_cmdline, " ", COMMAND_LINE_SIZE); + } +} diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c new file mode 100644 index 000000000000..acf7eb19f75d --- /dev/null +++ b/arch/loongarch/kernel/cpu-probe.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Processor capabilities determination functions. + * + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ptrace.h> +#include <linux/smp.h> +#include <linux/stddef.h> +#include <linux/export.h> +#include <linux/printk.h> +#include <linux/uaccess.h> + +#include <asm/cpu-features.h> +#include <asm/elf.h> +#include <asm/fpu.h> +#include <asm/loongarch.h> +#include <asm/pgtable-bits.h> +#include <asm/setup.h> + +/* Hardware capabilities */ +unsigned int elf_hwcap __read_mostly; +EXPORT_SYMBOL_GPL(elf_hwcap); + +/* + * Determine the FCSR mask for FPU hardware. + */ +static inline void cpu_set_fpu_fcsr_mask(struct cpuinfo_loongarch *c) +{ + unsigned long sr, mask, fcsr, fcsr0, fcsr1; + + fcsr = c->fpu_csr0; + mask = FPU_CSR_ALL_X | FPU_CSR_ALL_E | FPU_CSR_ALL_S | FPU_CSR_RM; + + sr = read_csr_euen(); + enable_fpu(); + + fcsr0 = fcsr & mask; + write_fcsr(LOONGARCH_FCSR0, fcsr0); + fcsr0 = read_fcsr(LOONGARCH_FCSR0); + + fcsr1 = fcsr | ~mask; + write_fcsr(LOONGARCH_FCSR0, fcsr1); + fcsr1 = read_fcsr(LOONGARCH_FCSR0); + + write_fcsr(LOONGARCH_FCSR0, fcsr); + + write_csr_euen(sr); + + c->fpu_mask = ~(fcsr0 ^ fcsr1) & ~mask; +} + +static inline void set_elf_platform(int cpu, const char *plat) +{ + if (cpu == 0) + __elf_platform = plat; +} + +/* MAP BASE */ +unsigned long vm_map_base; +EXPORT_SYMBOL_GPL(vm_map_base); + +static void cpu_probe_addrbits(struct cpuinfo_loongarch *c) +{ +#ifdef __NEED_ADDRBITS_PROBE + c->pabits = (read_cpucfg(LOONGARCH_CPUCFG1) & CPUCFG1_PABITS) >> 4; + c->vabits = (read_cpucfg(LOONGARCH_CPUCFG1) & CPUCFG1_VABITS) >> 12; + vm_map_base = 0UL - (1UL << c->vabits); +#endif +} + +static void set_isa(struct cpuinfo_loongarch *c, unsigned int isa) +{ + switch (isa) { + case LOONGARCH_CPU_ISA_LA64: + c->isa_level |= LOONGARCH_CPU_ISA_LA64; + fallthrough; + case LOONGARCH_CPU_ISA_LA32S: + c->isa_level |= LOONGARCH_CPU_ISA_LA32S; + fallthrough; + case LOONGARCH_CPU_ISA_LA32R: + c->isa_level |= LOONGARCH_CPU_ISA_LA32R; + break; + } +} + +static void cpu_probe_common(struct cpuinfo_loongarch *c) +{ + unsigned int config; + unsigned long asid_mask; + + c->options = LOONGARCH_CPU_CPUCFG | LOONGARCH_CPU_CSR | + LOONGARCH_CPU_TLB | LOONGARCH_CPU_VINT | LOONGARCH_CPU_WATCH; + + elf_hwcap |= HWCAP_LOONGARCH_CRC32; + + config = read_cpucfg(LOONGARCH_CPUCFG1); + if (config & CPUCFG1_UAL) { + c->options |= LOONGARCH_CPU_UAL; + elf_hwcap |= HWCAP_LOONGARCH_UAL; + } + + config = read_cpucfg(LOONGARCH_CPUCFG2); + if (config & CPUCFG2_LAM) { + c->options |= LOONGARCH_CPU_LAM; + elf_hwcap |= HWCAP_LOONGARCH_LAM; + } + if (config & CPUCFG2_FP) { + c->options |= LOONGARCH_CPU_FPU; + elf_hwcap |= HWCAP_LOONGARCH_FPU; + } + if (config & CPUCFG2_COMPLEX) { + c->options |= LOONGARCH_CPU_COMPLEX; + elf_hwcap |= HWCAP_LOONGARCH_COMPLEX; + } + if (config & CPUCFG2_CRYPTO) { + c->options |= LOONGARCH_CPU_CRYPTO; + elf_hwcap |= HWCAP_LOONGARCH_CRYPTO; + } + if (config & CPUCFG2_LVZP) { + c->options |= LOONGARCH_CPU_LVZ; + elf_hwcap |= HWCAP_LOONGARCH_LVZ; + } + + config = read_cpucfg(LOONGARCH_CPUCFG6); + if (config & CPUCFG6_PMP) + c->options |= LOONGARCH_CPU_PMP; + + config = iocsr_readl(LOONGARCH_IOCSR_FEATURES); + if (config & IOCSRF_CSRIPI) + c->options |= LOONGARCH_CPU_CSRIPI; + if (config & IOCSRF_EXTIOI) + c->options |= LOONGARCH_CPU_EXTIOI; + if (config & IOCSRF_FREQSCALE) + c->options |= LOONGARCH_CPU_SCALEFREQ; + if (config & IOCSRF_VM) + c->options |= LOONGARCH_CPU_HYPERVISOR; + + config = csr_readl(LOONGARCH_CSR_ASID); + config = (config & CSR_ASID_BIT) >> CSR_ASID_BIT_SHIFT; + asid_mask = GENMASK(config - 1, 0); + set_cpu_asid_mask(c, asid_mask); + + config = read_csr_prcfg1(); + c->kscratch_mask = GENMASK((config & CSR_CONF1_KSNUM) - 1, 0); + c->kscratch_mask &= ~(EXC_KSCRATCH_MASK | PERCPU_KSCRATCH_MASK | KVM_KSCRATCH_MASK); + + config = read_csr_prcfg3(); + switch (config & CSR_CONF3_TLBTYPE) { + case 0: + c->tlbsizemtlb = 0; + c->tlbsizestlbsets = 0; + c->tlbsizestlbways = 0; + c->tlbsize = 0; + break; + case 1: + c->tlbsizemtlb = ((config & CSR_CONF3_MTLBSIZE) >> CSR_CONF3_MTLBSIZE_SHIFT) + 1; + c->tlbsizestlbsets = 0; + c->tlbsizestlbways = 0; + c->tlbsize = c->tlbsizemtlb + c->tlbsizestlbsets * c->tlbsizestlbways; + break; + case 2: + c->tlbsizemtlb = ((config & CSR_CONF3_MTLBSIZE) >> CSR_CONF3_MTLBSIZE_SHIFT) + 1; + c->tlbsizestlbsets = 1 << ((config & CSR_CONF3_STLBIDX) >> CSR_CONF3_STLBIDX_SHIFT); + c->tlbsizestlbways = ((config & CSR_CONF3_STLBWAYS) >> CSR_CONF3_STLBWAYS_SHIFT) + 1; + c->tlbsize = c->tlbsizemtlb + c->tlbsizestlbsets * c->tlbsizestlbways; + break; + default: + pr_warn("Warning: unimplemented tlb type\n"); + } +} + +#define MAX_NAME_LEN 32 +#define VENDOR_OFFSET 0 +#define CPUNAME_OFFSET 9 + +static char cpu_full_name[MAX_NAME_LEN] = " - "; + +static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int cpu) +{ + uint64_t *vendor = (void *)(&cpu_full_name[VENDOR_OFFSET]); + uint64_t *cpuname = (void *)(&cpu_full_name[CPUNAME_OFFSET]); + + __cpu_full_name[cpu] = cpu_full_name; + *vendor = iocsr_readq(LOONGARCH_IOCSR_VENDOR); + *cpuname = iocsr_readq(LOONGARCH_IOCSR_CPUNAME); + + switch (c->processor_id & PRID_IMP_MASK) { + case PRID_IMP_LOONGSON_32: + c->cputype = CPU_LOONGSON32; + set_isa(c, LOONGARCH_CPU_ISA_LA32S); + __cpu_family[cpu] = "Loongson-32bit"; + pr_info("Standard 32-bit Loongson Processor probed\n"); + break; + case PRID_IMP_LOONGSON_64R: + c->cputype = CPU_LOONGSON64; + set_isa(c, LOONGARCH_CPU_ISA_LA64); + __cpu_family[cpu] = "Loongson-64bit"; + pr_info("Reduced 64-bit Loongson Processor probed\n"); + break; + case PRID_IMP_LOONGSON_64C: + c->cputype = CPU_LOONGSON64; + set_isa(c, LOONGARCH_CPU_ISA_LA64); + __cpu_family[cpu] = "Loongson-64bit"; + pr_info("Classic 64-bit Loongson Processor probed\n"); + break; + case PRID_IMP_LOONGSON_64G: + c->cputype = CPU_LOONGSON64; + set_isa(c, LOONGARCH_CPU_ISA_LA64); + __cpu_family[cpu] = "Loongson-64bit"; + pr_info("Generic 64-bit Loongson Processor probed\n"); + break; + default: /* Default to 64 bit */ + c->cputype = CPU_LOONGSON64; + set_isa(c, LOONGARCH_CPU_ISA_LA64); + __cpu_family[cpu] = "Loongson-64bit"; + pr_info("Unknown 64-bit Loongson Processor probed\n"); + } +} + +#ifdef CONFIG_64BIT +/* For use by uaccess.h */ +u64 __ua_limit; +EXPORT_SYMBOL(__ua_limit); +#endif + +const char *__cpu_family[NR_CPUS]; +const char *__cpu_full_name[NR_CPUS]; +const char *__elf_platform; + +static void cpu_report(void) +{ + struct cpuinfo_loongarch *c = ¤t_cpu_data; + + pr_info("CPU%d revision is: %08x (%s)\n", + smp_processor_id(), c->processor_id, cpu_family_string()); + if (c->options & LOONGARCH_CPU_FPU) + pr_info("FPU%d revision is: %08x\n", smp_processor_id(), c->fpu_vers); +} + +void cpu_probe(void) +{ + unsigned int cpu = smp_processor_id(); + struct cpuinfo_loongarch *c = ¤t_cpu_data; + + /* + * Set a default elf platform, cpu probe may later + * overwrite it with a more precise value + */ + set_elf_platform(cpu, "loongarch"); + + c->cputype = CPU_UNKNOWN; + c->processor_id = read_cpucfg(LOONGARCH_CPUCFG0); + c->fpu_vers = (read_cpucfg(LOONGARCH_CPUCFG2) >> 3) & 0x3; + + c->fpu_csr0 = FPU_CSR_RN; + c->fpu_mask = FPU_CSR_RSVD; + + cpu_probe_common(c); + + per_cpu_trap_init(cpu); + + switch (c->processor_id & PRID_COMP_MASK) { + case PRID_COMP_LOONGSON: + cpu_probe_loongson(c, cpu); + break; + } + + BUG_ON(!__cpu_family[cpu]); + BUG_ON(c->cputype == CPU_UNKNOWN); + + cpu_probe_addrbits(c); + +#ifdef CONFIG_64BIT + if (cpu == 0) + __ua_limit = ~((1ull << cpu_vabits) - 1); +#endif + + cpu_report(); +} + +void cpu_set_cluster(struct cpuinfo_loongarch *cpuinfo, unsigned int cluster) +{ + /* Ensure the core number fits in the field */ + WARN_ON(cluster > (LOONGARCH_GLOBALNUMBER_CLUSTER >> + LOONGARCH_GLOBALNUMBER_CLUSTER_SHF)); + + cpuinfo->globalnumber &= ~LOONGARCH_GLOBALNUMBER_CLUSTER; + cpuinfo->globalnumber |= cluster << LOONGARCH_GLOBALNUMBER_CLUSTER_SHF; +} + +void cpu_set_core(struct cpuinfo_loongarch *cpuinfo, unsigned int core) +{ + /* Ensure the core number fits in the field */ + WARN_ON(core > (LOONGARCH_GLOBALNUMBER_CORE >> LOONGARCH_GLOBALNUMBER_CORE_SHF)); + + cpuinfo->globalnumber &= ~LOONGARCH_GLOBALNUMBER_CORE; + cpuinfo->globalnumber |= core << LOONGARCH_GLOBALNUMBER_CORE_SHF; +} diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c new file mode 100644 index 000000000000..c05abd143e44 --- /dev/null +++ b/arch/loongarch/kernel/efi.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * EFI initialization + * + * Author: Jianmin Lv <lvjianmin@loongson.cn> + * Huacai Chen <chenhuacai@loongson.cn> + * + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ + +#include <linux/acpi.h> +#include <linux/efi.h> +#include <linux/efi-bgrt.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/kobject.h> +#include <linux/memblock.h> +#include <linux/reboot.h> +#include <linux/uaccess.h> + +#include <asm/efi.h> +#include <asm/boot_param.h> + +static efi_config_table_type_t arch_tables[] __initdata = {{},}; + +void __init efi_runtime_init(void) +{ + if (!efi_enabled(EFI_BOOT)) + return; + + if (!efi.runtime) + return; + + if (efi_runtime_disabled()) { + pr_info("EFI runtime services will be disabled.\n"); + return; + } + + efi_native_runtime_setup(); + set_bit(EFI_RUNTIME_SERVICES, &efi.flags); +} + +void __init efi_init(void) +{ + unsigned long efi_config_table; + efi_system_table_t *efi_systab; + + if (!efi_bp) + return; + + efi_systab = (efi_system_table_t *)efi_bp->systemtable; + if (!efi_systab) { + pr_err("Can't find EFI system table.\n"); + return; + } + + set_bit(EFI_64BIT, &efi.flags); + efi_config_table = (unsigned long)efi_systab->tables; + efi.runtime = (efi_runtime_services_t *)efi_systab->runtime; + efi.runtime_version = efi.runtime ? (unsigned int)efi.runtime->hdr.revision : 0; + + efi_config_parse_tables((void *)efi_systab->tables, efi_systab->nr_tables, arch_tables); +} + +static ssize_t boardinfo_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, + "BIOS Information\n" + "Vendor\t\t\t: %s\n" + "Version\t\t\t: %s\n" + "ROM Size\t\t: %d KB\n" + "Release Date\t\t: %s\n\n" + "Board Information\n" + "Manufacturer\t\t: %s\n" + "Board Name\t\t: %s\n" + "Family\t\t\t: LOONGSON64\n\n", + b_info.bios_vendor, b_info.bios_version, + b_info.bios_size, b_info.bios_release_date, + b_info.board_vendor, b_info.board_name); +} + +static struct kobj_attribute boardinfo_attr = __ATTR(boardinfo, 0444, + boardinfo_show, NULL); + +static int __init boardinfo_init(void) +{ + if (!efi_kobj) + return -EINVAL; + + return sysfs_create_file(efi_kobj, &boardinfo_attr.attr); +} +late_initcall(boardinfo_init); diff --git a/arch/loongarch/kernel/env.c b/arch/loongarch/kernel/env.c new file mode 100644 index 000000000000..234fe282a98c --- /dev/null +++ b/arch/loongarch/kernel/env.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Author: Huacai Chen <chenhuacai@loongson.cn> + * + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ +#include <linux/export.h> +#include <linux/acpi.h> +#include <linux/efi.h> +#include <asm/fw.h> +#include <asm/time.h> +#include <asm/bootinfo.h> +#include <asm/loongson.h> + +struct bootparamsinterface *efi_bp; +struct loongsonlist_mem_map *loongson_mem_map; +struct loongsonlist_vbios *pvbios; +struct loongson_system_configuration loongson_sysconf; +EXPORT_SYMBOL(loongson_sysconf); + +u64 loongson_chipcfg[MAX_PACKAGES]; +u64 loongson_chiptemp[MAX_PACKAGES]; +u64 loongson_freqctrl[MAX_PACKAGES]; +unsigned long long smp_group[MAX_PACKAGES]; + +static void __init register_addrs_set(u64 *registers, const u64 addr, int num) +{ + u64 i; + + for (i = 0; i < num; i++) { + *registers = (i << 44) | addr; + registers++; + } +} + +static u8 ext_listhdr_checksum(u8 *buffer, u32 length) +{ + u8 sum = 0; + u8 *end = buffer + length; + + while (buffer < end) { + sum = (u8)(sum + *(buffer++)); + } + + return (sum); +} + +static int parse_mem(struct _extention_list_hdr *head) +{ + loongson_mem_map = (struct loongsonlist_mem_map *)head; + if (ext_listhdr_checksum((u8 *)loongson_mem_map, head->length)) { + pr_warn("mem checksum error\n"); + return -EPERM; + } + + return 0; +} + +static int parse_vbios(struct _extention_list_hdr *head) +{ + pvbios = (struct loongsonlist_vbios *)head; + + if (ext_listhdr_checksum((u8 *)pvbios, head->length)) { + pr_warn("vbios_addr checksum error\n"); + return -EPERM; + } + + loongson_sysconf.vgabios_addr = pvbios->vbios_addr; + + return 0; +} + +static int parse_screeninfo(struct _extention_list_hdr *head) +{ + struct loongsonlist_screeninfo *pscreeninfo; + + pscreeninfo = (struct loongsonlist_screeninfo *)head; + if (ext_listhdr_checksum((u8 *)pscreeninfo, head->length)) { + pr_warn("screeninfo_addr checksum error\n"); + return -EPERM; + } + + memcpy(&screen_info, &pscreeninfo->si, sizeof(screen_info)); + + return 0; +} + +static int list_find(struct _extention_list_hdr *head) +{ + struct _extention_list_hdr *fhead = head; + + if (fhead == NULL) { + pr_warn("the link is empty!\n"); + return -1; + } + + while (fhead != NULL) { + if (memcmp(&(fhead->signature), LOONGSON_MEM_LINKLIST, 3) == 0) { + if (parse_mem(fhead) != 0) { + pr_warn("parse mem failed\n"); + return -EPERM; + } + } else if (memcmp(&(fhead->signature), LOONGSON_VBIOS_LINKLIST, 5) == 0) { + if (parse_vbios(fhead) != 0) { + pr_warn("parse vbios failed\n"); + return -EPERM; + } + } else if (memcmp(&(fhead->signature), LOONGSON_SCREENINFO_LINKLIST, 5) == 0) { + if (parse_screeninfo(fhead) != 0) { + pr_warn("parse screeninfo failed\n"); + return -EPERM; + } + } + fhead = fhead->next; + } + + return 0; +} + +static void __init parse_bpi_flags(void) +{ + if (efi_bp->flags & BPI_FLAGS_UEFI_SUPPORTED) + set_bit(EFI_BOOT, &efi.flags); + else + clear_bit(EFI_BOOT, &efi.flags); +} + +static int get_bpi_version(void *signature) +{ + char data[8]; + int r, version = 0; + + memset(data, 0, 8); + memcpy(data, signature + 4, 4); + r = kstrtoint(data, 10, &version); + + if (r < 0 || version < BPI_VERSION_V1) + panic("Fatal error, invalid BPI version: %d\n", version); + + if (version >= BPI_VERSION_V2) + parse_bpi_flags(); + + return version; +} + +void __init fw_init_environ(void) +{ + efi_bp = (struct bootparamsinterface *)_fw_envp; + loongson_sysconf.bpi_ver = get_bpi_version(&efi_bp->signature); + + register_addrs_set(smp_group, TO_UNCAC(0x1fe01000), 16); + register_addrs_set(loongson_chipcfg, TO_UNCAC(0x1fe00180), 16); + register_addrs_set(loongson_chiptemp, TO_UNCAC(0x1fe0019c), 16); + register_addrs_set(loongson_freqctrl, TO_UNCAC(0x1fe001d0), 16); + + if (list_find(efi_bp->extlist)) + pr_warn("Scan bootparam failed\n"); +} + +static int __init init_cpu_fullname(void) +{ + int cpu; + + if (loongson_sysconf.cpuname && !strncmp(loongson_sysconf.cpuname, "Loongson", 8)) { + for (cpu = 0; cpu < NR_CPUS; cpu++) + __cpu_full_name[cpu] = loongson_sysconf.cpuname; + } + return 0; +} +arch_initcall(init_cpu_fullname); diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S new file mode 100644 index 000000000000..53e02fb0559d --- /dev/null +++ b/arch/loongarch/kernel/head.S @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ +#include <linux/init.h> +#include <linux/threads.h> + +#include <asm/addrspace.h> +#include <asm/asm.h> +#include <asm/asmmacro.h> +#include <asm/regdef.h> +#include <asm/loongarch.h> +#include <asm/stackframe.h> + +SYM_ENTRY(_stext, SYM_L_GLOBAL, SYM_A_NONE) + + __REF + +SYM_CODE_START(kernel_entry) # kernel entry point + + /* We might not get launched at the address the kernel is linked to, + so we jump there. */ + la.abs t0, 0f + jirl zero, t0, 0 +0: + la t0, __bss_start # clear .bss + st.d zero, t0, 0 + la t1, __bss_stop - LONGSIZE +1: + addi.d t0, t0, LONGSIZE + st.d zero, t0, 0 + bne t0, t1, 1b + + la t0, fw_arg0 + st.d a0, t0, 0 # firmware arguments + la t0, fw_arg1 + st.d a1, t0, 0 + la t0, fw_arg2 + st.d a2, t0, 0 + la t0, fw_arg3 + st.d a3, t0, 0 + + /* Config direct window and set PG */ + li.d t0, CSR_DMW0_INIT # UC, PLV0, 0x8000 xxxx xxxx xxxx + csrwr t0, LOONGARCH_CSR_DMWIN0 + li.d t0, CSR_DMW1_INIT # CA, PLV0, 0x9000 xxxx xxxx xxxx + csrwr t0, LOONGARCH_CSR_DMWIN1 + /* Enable PG */ + li.w t0, 0xb0 # PLV=0, IE=0, PG=1 + csrwr t0, LOONGARCH_CSR_CRMD + li.w t0, 0x04 # PLV=0, PIE=1, PWE=0 + csrwr t0, LOONGARCH_CSR_PRMD + li.w t0, 0x00 # FPE=0, SXE=0, ASXE=0, BTE=0 + csrwr t0, LOONGARCH_CSR_EUEN + + /* KScratch3 used for percpu base, initialized as 0 */ + csrwr zero, PERCPU_BASE_KS + /* GPR21 used for percpu base (runtime), initialized as 0 */ + or x0, zero, zero + + la tp, init_thread_union + /* Set the SP after an empty pt_regs. */ + PTR_LI sp, (_THREAD_SIZE - 32 - PT_SIZE) + PTR_ADDU sp, sp, tp + set_saved_sp sp, t0, t1 + PTR_ADDIU sp, sp, -4 * SZREG # init stack pointer + + bl start_kernel + +SYM_CODE_END(kernel_entry) + +SYM_ENTRY(kernel_entry_end, SYM_L_GLOBAL, SYM_A_NONE) diff --git a/arch/loongarch/kernel/mem.c b/arch/loongarch/kernel/mem.c new file mode 100644 index 000000000000..c1ac71034448 --- /dev/null +++ b/arch/loongarch/kernel/mem.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/memblock.h> + +#include <asm/bootinfo.h> +#include <asm/loongson.h> +#include <asm/sections.h> + +void __init early_memblock_init(void) +{ + int i; + u32 mem_type; + u64 mem_start, mem_end, mem_size; + + /* parse memory information */ + for (i = 0; i < loongson_mem_map->map_count; i++) { + mem_type = loongson_mem_map->map[i].mem_type; + mem_start = loongson_mem_map->map[i].mem_start; + mem_size = loongson_mem_map->map[i].mem_size; + mem_end = mem_start + mem_size; + + switch (mem_type) { + case ADDRESS_TYPE_SYSRAM: + memblock_add(mem_start, mem_size); + if (max_low_pfn < (mem_end >> PAGE_SHIFT)) + max_low_pfn = mem_end >> PAGE_SHIFT; + break; + } + } + memblock_set_current_limit(PFN_PHYS(max_low_pfn)); +} + +void __init fw_init_memory(void) +{ + int i; + u32 mem_type; + u64 mem_start, mem_end, mem_size; + unsigned long start_pfn, end_pfn; + static unsigned long num_physpages; + + /* parse memory information */ + for (i = 0; i < loongson_mem_map->map_count; i++) { + mem_type = loongson_mem_map->map[i].mem_type; + mem_start = loongson_mem_map->map[i].mem_start; + mem_size = loongson_mem_map->map[i].mem_size; + mem_end = mem_start + mem_size; + + switch (mem_type) { + case ADDRESS_TYPE_SYSRAM: + mem_start = PFN_ALIGN(mem_start); + mem_end = PFN_ALIGN(mem_end - PAGE_SIZE + 1); + num_physpages += (mem_size >> PAGE_SHIFT); + memblock_set_node(mem_start, mem_size, &memblock.memory, 0); + break; + case ADDRESS_TYPE_ACPI: + case ADDRESS_TYPE_RESERVED: + memblock_reserve(loongson_mem_map->map[i].mem_start, + loongson_mem_map->map[i].mem_size); + break; + } + } + + get_pfn_range_for_nid(0, &start_pfn, &end_pfn); + pr_info("start_pfn=0x%lx, end_pfn=0x%lx, num_physpages:0x%lx\n", + start_pfn, end_pfn, num_physpages); + + NODE_DATA(0)->node_start_pfn = start_pfn; + NODE_DATA(0)->node_spanned_pages = end_pfn - start_pfn; + + /* used by finalize_initrd() */ + max_low_pfn = end_pfn; + + /* Reserve the first 2MB */ + memblock_reserve(PHYS_OFFSET, 0x200000); + + /* Reserve the kernel text/data/bss */ + memblock_reserve(__pa_symbol(&_text), + __pa_symbol(&_end) - __pa_symbol(&_text)); +} diff --git a/arch/loongarch/kernel/reset.c b/arch/loongarch/kernel/reset.c new file mode 100644 index 000000000000..b140c2aa2c5a --- /dev/null +++ b/arch/loongarch/kernel/reset.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ +#include <linux/kernel.h> +#include <linux/acpi.h> +#include <linux/efi.h> +#include <linux/export.h> +#include <linux/pm.h> +#include <linux/types.h> +#include <linux/reboot.h> +#include <linux/delay.h> +#include <linux/console.h> + +#include <acpi/reboot.h> +#include <asm/compiler.h> +#include <asm/idle.h> +#include <asm/loongarch.h> +#include <asm/reboot.h> + +static void default_halt(void) +{ + local_irq_disable(); + clear_csr_ecfg(ECFG0_IM); + + pr_notice("\n\n** You can safely turn off the power now **\n\n"); + console_flush_on_panic(CONSOLE_FLUSH_PENDING); + + while (true) { + __arch_cpu_idle(); + } +} + +static void default_poweroff(void) +{ +#ifdef CONFIG_EFI + efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL); +#endif + while (true) { + __arch_cpu_idle(); + } +} + +static void default_restart(void) +{ +#ifdef CONFIG_EFI + if (efi_capsule_pending(NULL)) + efi_reboot(REBOOT_WARM, NULL); + else + efi_reboot(REBOOT_COLD, NULL); +#endif + if (!acpi_disabled) + acpi_reboot(); + + while (true) { + __arch_cpu_idle(); + } +} + +void (*pm_restart)(void); +EXPORT_SYMBOL(pm_restart); + +void (*pm_power_off)(void); +EXPORT_SYMBOL(pm_power_off); + +void machine_halt(void) +{ + default_halt(); +} + +void machine_power_off(void) +{ + pm_power_off(); +} + +void machine_restart(char *command) +{ + do_kernel_restart(command); + pm_restart(); +} + +static int __init loongarch_reboot_setup(void) +{ + pm_restart = default_restart; + pm_power_off = default_poweroff; + + return 0; +} + +arch_initcall(loongarch_reboot_setup); diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c new file mode 100644 index 000000000000..7ec32959b36c --- /dev/null +++ b/arch/loongarch/kernel/setup.c @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + * + * Derived from MIPS: + * Copyright (C) 1995 Linus Torvalds + * Copyright (C) 1995 Waldorf Electronics + * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 01, 02, 03 Ralf Baechle + * Copyright (C) 1996 Stoned Elipot + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) 2000, 2001, 2002, 2007 Maciej W. Rozycki + */ +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/dmi.h> +#include <linux/efi.h> +#include <linux/export.h> +#include <linux/screen_info.h> +#include <linux/memblock.h> +#include <linux/initrd.h> +#include <linux/ioport.h> +#include <linux/root_dev.h> +#include <linux/console.h> +#include <linux/pfn.h> +#include <linux/platform_device.h> +#include <linux/sizes.h> +#include <linux/device.h> +#include <linux/dma-map-ops.h> +#include <linux/swiotlb.h> + +#include <asm/addrspace.h> +#include <asm/bootinfo.h> +#include <asm/cache.h> +#include <asm/cpu.h> +#include <asm/dma.h> +#include <asm/efi.h> +#include <asm/fw.h> +#include <asm/loongson.h> +#include <asm/pgalloc.h> +#include <asm/sections.h> +#include <asm/setup.h> +#include <asm/time.h> + +#define SMBIOS_BIOSSIZE_OFFSET 0x09 +#define SMBIOS_BIOSEXTERN_OFFSET 0x13 +#define SMBIOS_FREQLOW_OFFSET 0x16 +#define SMBIOS_FREQHIGH_OFFSET 0x17 +#define SMBIOS_FREQLOW_MASK 0xFF +#define SMBIOS_CORE_PACKAGE_OFFSET 0x23 +#define LOONGSON_EFI_ENABLE (1 << 3) + +#ifdef CONFIG_VT +struct screen_info screen_info; +#endif + +DEFINE_PER_CPU(unsigned long, kernelsp); +unsigned long fw_arg0, fw_arg1, fw_arg2, fw_arg3; +struct cpuinfo_loongarch cpu_data[NR_CPUS] __read_mostly; + +EXPORT_SYMBOL(cpu_data); + +struct loongson_board_info b_info; +static const char dmi_empty_string[] = " "; + +/* + * Setup information + * + * These are initialized so they are in the .data section + */ + +char __initdata arcs_cmdline[COMMAND_LINE_SIZE]; +static char __initdata command_line[COMMAND_LINE_SIZE]; + +static struct resource code_resource = { .name = "Kernel code", }; +static struct resource data_resource = { .name = "Kernel data", }; +static struct resource bss_resource = { .name = "Kernel bss", }; + +const char *get_system_type(void) +{ + return "generic-loongson-machine"; +} + +static const char *dmi_string_parse(const struct dmi_header *dm, u8 s) +{ + const u8 *bp = ((u8 *) dm) + dm->length; + + if (s) { + s--; + while (s > 0 && *bp) { + bp += strlen(bp) + 1; + s--; + } + + if (*bp != 0) { + size_t len = strlen(bp)+1; + size_t cmp_len = len > 8 ? 8 : len; + + if (!memcmp(bp, dmi_empty_string, cmp_len)) + return dmi_empty_string; + + return bp; + } + } + + return ""; +} + +static void __init parse_cpu_table(const struct dmi_header *dm) +{ + long freq_temp = 0; + char *dmi_data = (char *)dm; + + freq_temp = ((*(dmi_data + SMBIOS_FREQHIGH_OFFSET) << 8) + + ((*(dmi_data + SMBIOS_FREQLOW_OFFSET)) & SMBIOS_FREQLOW_MASK)); + cpu_clock_freq = freq_temp * 1000000; + + loongson_sysconf.cpuname = (void *)dmi_string_parse(dm, dmi_data[16]); + loongson_sysconf.cores_per_package = *(dmi_data + SMBIOS_CORE_PACKAGE_OFFSET); + + pr_info("CpuClock = %llu\n", cpu_clock_freq); +} + +static void __init parse_bios_table(const struct dmi_header *dm) +{ + int bios_extern; + char *dmi_data = (char *)dm; + + bios_extern = *(dmi_data + SMBIOS_BIOSEXTERN_OFFSET); + b_info.bios_size = *(dmi_data + SMBIOS_BIOSSIZE_OFFSET); + + if (bios_extern & LOONGSON_EFI_ENABLE) + set_bit(EFI_BOOT, &efi.flags); + else + clear_bit(EFI_BOOT, &efi.flags); +} + +static void __init find_tokens(const struct dmi_header *dm, void *dummy) +{ + switch (dm->type) { + case 0x0: /* Extern BIOS */ + parse_bios_table(dm); + break; + case 0x4: /* Calling interface */ + parse_cpu_table(dm); + break; + } +} +static void __init smbios_parse(void) +{ + b_info.bios_vendor = (void *)dmi_get_system_info(DMI_BIOS_VENDOR); + b_info.bios_version = (void *)dmi_get_system_info(DMI_BIOS_VERSION); + b_info.bios_release_date = (void *)dmi_get_system_info(DMI_BIOS_DATE); + b_info.board_vendor = (void *)dmi_get_system_info(DMI_BOARD_VENDOR); + b_info.board_name = (void *)dmi_get_system_info(DMI_BOARD_NAME); + dmi_walk(find_tokens, NULL); +} + +/* + * Manage initrd + */ +#ifdef CONFIG_BLK_DEV_INITRD + +static unsigned long __init init_initrd(void) +{ + if (!phys_initrd_start || !phys_initrd_size) + goto disable; + + initrd_start = (unsigned long)phys_to_virt(phys_initrd_start); + initrd_end = (unsigned long)phys_to_virt(phys_initrd_start + phys_initrd_size); + + if (!initrd_start || initrd_end <= initrd_start) + goto disable; + + if (initrd_start & ~PAGE_MASK) { + pr_err("initrd start must be page aligned\n"); + goto disable; + } + if (initrd_start < PAGE_OFFSET) { + pr_err("initrd start < PAGE_OFFSET\n"); + goto disable; + } + + ROOT_DEV = Root_RAM0; + + return 0; +disable: + initrd_start = 0; + initrd_end = 0; + return 0; +} + +static void __init finalize_initrd(void) +{ + unsigned long size = initrd_end - initrd_start; + + if (size == 0) { + pr_info("Initrd not found or empty"); + goto disable; + } + if (__pa(initrd_end) > PFN_PHYS(max_low_pfn)) { + pr_err("Initrd extends beyond end of memory"); + goto disable; + } + + + memblock_reserve(__pa(initrd_start), size); + initrd_below_start_ok = 1; + + pr_info("Initial ramdisk at: 0x%lx (%lu bytes)\n", + initrd_start, size); + return; +disable: + pr_cont(" - disabling initrd\n"); + initrd_start = 0; + initrd_end = 0; +} + +#else /* !CONFIG_BLK_DEV_INITRD */ + +static unsigned long __init init_initrd(void) +{ + return 0; +} + +#define finalize_initrd() do {} while (0) + +#endif + +static int usermem __initdata; + +static int __init early_parse_mem(char *p) +{ + phys_addr_t start, size; + + /* + * If a user specifies memory size, we + * blow away any automatically generated + * size. + */ + if (usermem == 0) { + usermem = 1; + memblock_remove(memblock_start_of_DRAM(), + memblock_end_of_DRAM() - memblock_start_of_DRAM()); + } + start = 0; + size = memparse(p, &p); + if (*p == '@') + start = memparse(p + 1, &p); + + memblock_add(start, size); + + return 0; +} +early_param("mem", early_parse_mem); + +static int __init early_parse_memmap(char *p) +{ + char *oldp; + u64 start_at, mem_size; + + if (!p) + return -EINVAL; + + if (!strncmp(p, "exactmap", 8)) { + pr_err("\"memmap=exactmap\" invalid on LoongArch\n"); + return 0; + } + + oldp = p; + mem_size = memparse(p, &p); + if (p == oldp) + return -EINVAL; + + if (*p == '@') { + start_at = memparse(p+1, &p); + memblock_add(start_at, mem_size); + } else if (*p == '#') { + pr_err("\"memmap=nn#ss\" (force ACPI data) invalid on LoongArch\n"); + return -EINVAL; + } else if (*p == '$') { + start_at = memparse(p+1, &p); + memblock_add(start_at, mem_size); + memblock_reserve(start_at, mem_size); + } else { + pr_err("\"memmap\" invalid format!\n"); + return -EINVAL; + } + + if (*p == '\0') { + usermem = 1; + return 0; + } else + return -EINVAL; +} +early_param("memmap", early_parse_memmap); + +static void __init check_kernel_sections_mem(void) +{ + phys_addr_t start = __pa_symbol(&_text); + phys_addr_t size = __pa_symbol(&_end) - start; + + if (!memblock_is_region_memory(start, size)) { + pr_info("Kernel sections are not in the memory maps\n"); + memblock_add(start, size); + } +} + +static void __init bootcmdline_append(const char *s, size_t max) +{ + if (!s[0] || !max) + return; + + if (boot_command_line[0]) + strlcat(boot_command_line, " ", COMMAND_LINE_SIZE); + + strlcat(boot_command_line, s, max); +} + +static void __init bootcmdline_init(char **cmdline_p) +{ + boot_command_line[0] = 0; + + /* + * Take arguments from the bootloader at first. Early code should have + * filled arcs_cmdline with arguments from the bootloader. + */ + bootcmdline_append(arcs_cmdline, COMMAND_LINE_SIZE); + + strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE); + *cmdline_p = command_line; + + parse_early_param(); +} + +/* + * arch_mem_init - initialize memory management subsystem + */ +static void __init arch_mem_init(char **cmdline_p) +{ + if (usermem) + pr_info("User-defined physical RAM map overwrite\n"); + + check_kernel_sections_mem(); + + memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0); + pagetable_init(); + + /* + * Prevent memblock from allocating high memory. + * This cannot be done before max_low_pfn is detected, so up + * to this point is possible to only reserve physical memory + * with memblock_reserve; memblock_alloc* can be used + * only after this point + */ + memblock_set_current_limit(PFN_PHYS(max_low_pfn)); + + /* + * In order to reduce the possibility of kernel panic when failed to + * get IO TLB memory under CONFIG_SWIOTLB, it is better to allocate + * low memory as small as possible before plat_swiotlb_setup(), so + * make sparse_init() using top-down allocation. + */ + memblock_set_bottom_up(false); + sparse_init(); + memblock_set_bottom_up(true); + + swiotlb_init(1); + + dma_contiguous_reserve(PFN_PHYS(max_low_pfn)); + + memblock_dump_all(); + + early_memtest(PFN_PHYS(ARCH_PFN_OFFSET), PFN_PHYS(max_low_pfn)); +} + +static void __init resource_init(void) +{ + u64 i; + phys_addr_t start, end; + + code_resource.start = __pa_symbol(&_text); + code_resource.end = __pa_symbol(&_etext) - 1; + data_resource.start = __pa_symbol(&_etext); + data_resource.end = __pa_symbol(&_edata) - 1; + bss_resource.start = __pa_symbol(&__bss_start); + bss_resource.end = __pa_symbol(&__bss_stop) - 1; + + for_each_mem_range(i, &start, &end) { + struct resource *res; + + res = memblock_alloc(sizeof(struct resource), SMP_CACHE_BYTES); + if (!res) + panic("%s: Failed to allocate %zu bytes\n", __func__, + sizeof(struct resource)); + + res->start = start; + /* + * In memblock, end points to the first byte after the + * range while in resourses, end points to the last byte in + * the range. + */ + res->end = end - 1; + res->flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY; + res->name = "System RAM"; + + request_resource(&iomem_resource, res); + + /* + * We don't know which RAM region contains kernel data, + * so we try it repeatedly and let the resource manager + * test it. + */ + request_resource(res, &code_resource); + request_resource(res, &data_resource); + request_resource(res, &bss_resource); + } +} + +void __init platform_init(void) +{ + efi_init(); +#ifdef CONFIG_ACPI_TABLE_UPGRADE + acpi_table_upgrade(); +#endif +#ifdef CONFIG_ACPI + acpi_gbl_use_default_register_widths = false; + acpi_boot_table_init(); + acpi_boot_init(); +#endif + + fw_init_memory(); + dmi_setup(); + smbios_parse(); + pr_info("The BIOS Version: %s\n", b_info.bios_version); + + efi_runtime_init(); +} + +void __init setup_arch(char **cmdline_p) +{ + cpu_probe(); + + fw_init_cmdline(); + fw_init_environ(); + early_memblock_init(); + bootcmdline_init(cmdline_p); + + init_initrd(); + platform_init(); + finalize_initrd(); + + arch_mem_init(cmdline_p); + + resource_init(); + + paging_init(); +} + +static int __init register_gop_device(void) +{ + void *pd; + + if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) + return 0; + pd = platform_device_register_data(NULL, "efi-framebuffer", 0, + &screen_info, sizeof(screen_info)); + return PTR_ERR_OR_ZERO(pd); +} +subsys_initcall(register_gop_device); diff --git a/arch/loongarch/kernel/time.c b/arch/loongarch/kernel/time.c new file mode 100644 index 000000000000..2e00802a3742 --- /dev/null +++ b/arch/loongarch/kernel/time.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Common time service routines for LoongArch machines. + * + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + */ +#include <linux/clockchips.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/sched_clock.h> +#include <linux/spinlock.h> + +#include <asm/cpu-features.h> +#include <asm/loongarch.h> +#include <asm/time.h> + +u64 cpu_clock_freq; +EXPORT_SYMBOL(cpu_clock_freq); +u64 const_clock_freq; +EXPORT_SYMBOL(const_clock_freq); + +static DEFINE_RAW_SPINLOCK(state_lock); +static DEFINE_PER_CPU(struct clock_event_device, constant_clockevent_device); + +static void constant_event_handler(struct clock_event_device *dev) +{ +} + +irqreturn_t constant_timer_interrupt(int irq, void *data) +{ + int cpu = smp_processor_id(); + struct clock_event_device *cd; + + /* Clear Timer Interrupt */ + write_csr_tintclear(CSR_TINTCLR_TI); + cd = &per_cpu(constant_clockevent_device, cpu); + cd->event_handler(cd); + + return IRQ_HANDLED; +} + +static int constant_set_state_oneshot(struct clock_event_device *evt) +{ + unsigned long timer_config; + + raw_spin_lock(&state_lock); + + timer_config = csr_readq(LOONGARCH_CSR_TCFG); + timer_config |= CSR_TCFG_EN; + timer_config &= ~CSR_TCFG_PERIOD; + csr_writeq(timer_config, LOONGARCH_CSR_TCFG); + + raw_spin_unlock(&state_lock); + + return 0; +} + +static int constant_set_state_oneshot_stopped(struct clock_event_device *evt) +{ + unsigned long timer_config; + + raw_spin_lock(&state_lock); + + timer_config = csr_readq(LOONGARCH_CSR_TCFG); + timer_config &= ~CSR_TCFG_EN; + csr_writeq(timer_config, LOONGARCH_CSR_TCFG); + + raw_spin_unlock(&state_lock); + + return 0; +} + +static int constant_set_state_periodic(struct clock_event_device *evt) +{ + unsigned long period; + unsigned long timer_config; + + raw_spin_lock(&state_lock); + + period = const_clock_freq / HZ; + timer_config = period & CSR_TCFG_VAL; + timer_config |= (CSR_TCFG_PERIOD | CSR_TCFG_EN); + csr_writeq(timer_config, LOONGARCH_CSR_TCFG); + + raw_spin_unlock(&state_lock); + + return 0; +} + +static int constant_set_state_shutdown(struct clock_event_device *evt) +{ + return 0; +} + +static int constant_timer_next_event(unsigned long delta, struct clock_event_device *evt) +{ + unsigned long timer_config; + + delta &= CSR_TCFG_VAL; + timer_config = delta | CSR_TCFG_EN; + csr_writeq(timer_config, LOONGARCH_CSR_TCFG); + + return 0; +} + +static unsigned long __init get_loops_per_jiffy(void) +{ + unsigned long lpj = (unsigned long)const_clock_freq; + + do_div(lpj, HZ); + + return lpj; +} + +static long init_timeval; + +void sync_counter(void) +{ + /* Ensure counter begin at 0 */ + csr_writeq(-init_timeval, LOONGARCH_CSR_CNTC); +} + +int constant_clockevent_init(void) +{ + unsigned int irq; + unsigned int cpu = smp_processor_id(); + unsigned long min_delta = 0x600; + unsigned long max_delta = (1UL << 48) - 1; + struct clock_event_device *cd; + static int timer_irq_installed = 0; + + irq = get_timer_irq(); + + cd = &per_cpu(constant_clockevent_device, cpu); + + cd->name = "Constant"; + cd->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_PERCPU; + + cd->irq = irq; + cd->rating = 320; + cd->cpumask = cpumask_of(cpu); + cd->set_state_oneshot = constant_set_state_oneshot; + cd->set_state_oneshot_stopped = constant_set_state_oneshot_stopped; + cd->set_state_periodic = constant_set_state_periodic; + cd->set_state_shutdown = constant_set_state_shutdown; + cd->set_next_event = constant_timer_next_event; + cd->event_handler = constant_event_handler; + + clockevents_config_and_register(cd, const_clock_freq, min_delta, max_delta); + + if (timer_irq_installed) + return 0; + + timer_irq_installed = 1; + + sync_counter(); + + if (request_irq(irq, constant_timer_interrupt, IRQF_PERCPU | IRQF_TIMER, "timer", NULL)) + pr_err("Failed to request irq %d (timer)\n", irq); + + lpj_fine = get_loops_per_jiffy(); + pr_info("Constant clock event device register\n"); + + return 0; +} + +static u64 read_const_counter(struct clocksource *clk) +{ + return drdtime(); +} + +static struct clocksource clocksource_const = { + .name = "Constant", + .rating = 400, + .read = read_const_counter, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .mult = 0, + .shift = 10, +}; + +unsigned long long notrace sched_clock(void) +{ + /* 64-bit arithmetic can overflow, so use 128-bit. */ + u64 t1, t2, t3; + unsigned long long rv; + u64 mult = clocksource_const.mult; + u64 shift = clocksource_const.shift; + u64 cnt = read_const_counter(NULL); + + __asm__ ( + "nor %[t1], $r0, %[shift] \n\t" + "mulh.du %[t2], %[cnt], %[mult] \n\t" + "mul.d %[t3], %[cnt], %[mult] \n\t" + "slli.d %[t2], %[t2], 1 \n\t" + "srl.d %[rv], %[t3], %[shift] \n\t" + "sll.d %[t1], %[t2], %[t1] \n\t" + "or %[rv], %[t1], %[rv] \n\t" + : [rv] "=&r" (rv), [t1] "=&r" (t1), [t2] "=&r" (t2), [t3] "=&r" (t3) + : [cnt] "r" (cnt), [mult] "r" (mult), [shift] "r" (shift) + : ); + + return rv; +} + +int __init constant_clocksource_init(void) +{ + int res; + unsigned long freq; + + freq = const_clock_freq; + + clocksource_const.mult = + clocksource_hz2mult(freq, clocksource_const.shift); + + res = clocksource_register_hz(&clocksource_const, freq); + + pr_info("Constant clock source device register\n"); + + return res; +} + +void __init time_init(void) +{ + if (!cpu_has_cpucfg) + const_clock_freq = cpu_clock_freq; + else + const_clock_freq = calc_const_freq(); + + init_timeval = drdtime() - csr_readq(LOONGARCH_CSR_CNTC); + + constant_clockevent_init(); + constant_clocksource_init(); +} diff --git a/arch/loongarch/kernel/topology.c b/arch/loongarch/kernel/topology.c new file mode 100644 index 000000000000..3b2cbb95875b --- /dev/null +++ b/arch/loongarch/kernel/topology.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/cpu.h> +#include <linux/init.h> +#include <linux/percpu.h> + +static struct cpu cpu_device; + +static int __init topology_init(void) +{ + return register_cpu(&cpu_device, 0); +} + +subsys_initcall(topology_init); -- 2.27.0 ^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH V4 09/22] LoongArch: Add boot and setup routines 2021-09-27 6:42 ` [PATCH V4 09/22] LoongArch: Add boot and setup routines Huacai Chen @ 2021-09-27 6:52 ` Ard Biesheuvel 2021-09-29 3:24 ` Huacai Chen 0 siblings, 1 reply; 3+ messages in thread From: Ard Biesheuvel @ 2021-09-27 6:52 UTC (permalink / raw) To: Huacai Chen Cc: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton, David Airlie, Jonathan Corbet, Linus Torvalds, linux-arch, Linux Doc Mailing List, Linux Kernel Mailing List, Xuefeng Li, Yanteng Si, Huacai Chen, Jiaxun Yang, linux-efi On Mon, 27 Sept 2021 at 08:49, Huacai Chen <chenhuacai@loongson.cn> wrote: > > This patch adds basic boot, setup and reset routines for LoongArch. > LoongArch uses UEFI-based firmware and uses ACPI as the boot protocol. > > Now the boot information passed to kernel is like this: > 1, kernel get a0, a1 and a2 from bootloader. > 2, a0 is "argc", a1 is "argc", so "cmdline" comes from a0/a1. > 3, a2 is "envrion", which is a pointer to a "struct bootparamsinterface" > 4, "struct bootparamsinterface" include a "systemtable" pointer, whose > type is "efi_system_table_t". Most information, include ACPI tables, > come from here. > Could you please quote the relevant sections of the UEFI and ACPI specifications where this boot protocol is described? > Cc: Ard Biesheuvel <ardb@kernel.org> > Cc: linux-efi@vger.kernel.org > Signed-off-by: Huacai Chen <chenhuacai@loongson.cn> > --- > arch/loongarch/include/asm/acenv.h | 17 + > arch/loongarch/include/asm/acpi.h | 37 ++ > arch/loongarch/include/asm/boot_param.h | 91 +++++ > arch/loongarch/include/asm/bootinfo.h | 33 ++ > arch/loongarch/include/asm/dmi.h | 24 ++ > arch/loongarch/include/asm/efi.h | 32 ++ > arch/loongarch/include/asm/fw.h | 18 + > arch/loongarch/include/asm/reboot.h | 10 + > arch/loongarch/include/asm/setup.h | 21 ++ > arch/loongarch/kernel/acpi.c | 324 ++++++++++++++++ > arch/loongarch/kernel/cacheinfo.c | 122 ++++++ > arch/loongarch/kernel/cmdline.c | 28 ++ > arch/loongarch/kernel/cpu-probe.c | 301 +++++++++++++++ > arch/loongarch/kernel/efi.c | 95 +++++ > arch/loongarch/kernel/env.c | 170 +++++++++ > arch/loongarch/kernel/head.S | 72 ++++ > arch/loongarch/kernel/mem.c | 83 +++++ > arch/loongarch/kernel/reset.c | 90 +++++ > arch/loongarch/kernel/setup.c | 469 ++++++++++++++++++++++++ > arch/loongarch/kernel/time.c | 237 ++++++++++++ > arch/loongarch/kernel/topology.c | 13 + > 21 files changed, 2287 insertions(+) > create mode 100644 arch/loongarch/include/asm/acenv.h > create mode 100644 arch/loongarch/include/asm/acpi.h > create mode 100644 arch/loongarch/include/asm/boot_param.h > create mode 100644 arch/loongarch/include/asm/bootinfo.h > create mode 100644 arch/loongarch/include/asm/dmi.h > create mode 100644 arch/loongarch/include/asm/efi.h > create mode 100644 arch/loongarch/include/asm/fw.h > create mode 100644 arch/loongarch/include/asm/reboot.h > create mode 100644 arch/loongarch/include/asm/setup.h > create mode 100644 arch/loongarch/kernel/acpi.c > create mode 100644 arch/loongarch/kernel/cacheinfo.c > create mode 100644 arch/loongarch/kernel/cmdline.c > create mode 100644 arch/loongarch/kernel/cpu-probe.c > create mode 100644 arch/loongarch/kernel/efi.c > create mode 100644 arch/loongarch/kernel/env.c > create mode 100644 arch/loongarch/kernel/head.S > create mode 100644 arch/loongarch/kernel/mem.c > create mode 100644 arch/loongarch/kernel/reset.c > create mode 100644 arch/loongarch/kernel/setup.c > create mode 100644 arch/loongarch/kernel/time.c > create mode 100644 arch/loongarch/kernel/topology.c > > diff --git a/arch/loongarch/include/asm/acenv.h b/arch/loongarch/include/asm/acenv.h > new file mode 100644 > index 000000000000..d1f41fbc9070 > --- /dev/null > +++ b/arch/loongarch/include/asm/acenv.h > @@ -0,0 +1,17 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * LoongArch specific ACPICA environments and implementation > + * > + * Author: Jianmin Lv <lvjianmin@loongson.cn> > + * Huacai Chen <chenhuacai@loongson.cn> > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > + > +#ifndef _ASM_LOONGARCH_ACENV_H > +#define _ASM_LOONGARCH_ACENV_H > + > +/* The head file is required by ACPI core, but we have nothing to fill > + * it now, update it later when needed. > + */ > + > +#endif /* _ASM_LOONGARCH_ACENV_H */ > diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h > new file mode 100644 > index 000000000000..29e2214d5901 > --- /dev/null > +++ b/arch/loongarch/include/asm/acpi.h > @@ -0,0 +1,37 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Author: Jianmin Lv <lvjianmin@loongson.cn> > + * Huacai Chen <chenhuacai@loongson.cn> > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > + > +#ifndef _ASM_LOONGARCH_ACPI_H > +#define _ASM_LOONGARCH_ACPI_H > + > +#ifdef CONFIG_ACPI > +extern int acpi_strict; > +extern int acpi_disabled; > +extern int acpi_pci_disabled; > +extern int acpi_noirq; > + > +#define acpi_os_ioremap ioremap > + > +static inline void disable_acpi(void) > +{ > + acpi_disabled = 1; > + acpi_pci_disabled = 1; > + acpi_noirq = 1; > +} > + > +static inline bool acpi_has_cpu_in_madt(void) > +{ > + return true; > +} > + > +extern struct list_head acpi_wakeup_device_list; > + > +#endif /* !CONFIG_ACPI */ > + > +#define ACPI_TABLE_UPGRADE_MAX_PHYS (max_low_pfn << PAGE_SHIFT) > + > +#endif /* _ASM_LOONGARCH_ACPI_H */ > diff --git a/arch/loongarch/include/asm/boot_param.h b/arch/loongarch/include/asm/boot_param.h > new file mode 100644 > index 000000000000..f515431d838b > --- /dev/null > +++ b/arch/loongarch/include/asm/boot_param.h > @@ -0,0 +1,91 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#ifndef __ASM_BOOT_PARAM_H_ > +#define __ASM_BOOT_PARAM_H_ > + > +#ifdef CONFIG_VT > +#include <linux/screen_info.h> > +#endif > + > +#define ADDRESS_TYPE_SYSRAM 1 > +#define ADDRESS_TYPE_RESERVED 2 > +#define ADDRESS_TYPE_ACPI 3 > +#define ADDRESS_TYPE_NVS 4 > +#define ADDRESS_TYPE_PMEM 5 > + > +#define LOONGSON3_BOOT_MEM_MAP_MAX 128 > + > +#define LOONGSON_EFIBOOT_SIGNATURE "BPI" > +#define LOONGSON_MEM_LINKLIST "MEM" > +#define LOONGSON_VBIOS_LINKLIST "VBIOS" > +#define LOONGSON_SCREENINFO_LINKLIST "SINFO" > + > +/* Values for Version BPI */ > +enum bpi_version { > + BPI_VERSION_V1 = 1000, /* Signature="BPI01000" */ > + BPI_VERSION_V2 = 1001, /* Signature="BPI01001" */ > +}; > + > +/* Flags in bootparamsinterface */ > +#define BPI_FLAGS_UEFI_SUPPORTED BIT(0) > + > +struct _extention_list_hdr { > + u64 signature; > + u32 length; > + u8 revision; > + u8 checksum; > + struct _extention_list_hdr *next; > +} __packed; > + > +struct bootparamsinterface { > + u64 signature; /* {"B", "P", "I", "0", "1", ... } */ > + void *systemtable; > + struct _extention_list_hdr *extlist; > + u64 flags; > +} __packed; > + > +struct loongsonlist_mem_map { > + struct _extention_list_hdr header; /* {"M", "E", "M"} */ > + u8 map_count; > + struct loongson_mem_map { > + u32 mem_type; > + u64 mem_start; > + u64 mem_size; > + } __packed map[LOONGSON3_BOOT_MEM_MAP_MAX]; > +} __packed; > + > +struct loongsonlist_vbios { > + struct _extention_list_hdr header; /* {"V", "B", "I", "O", "S"} */ > + u64 vbios_addr; > +} __packed; > + > +struct loongsonlist_screeninfo { > + struct _extention_list_hdr header; /* {"S", "I", "N", "F", "O"} */ > + struct screen_info si; > +} __packed; > + > +struct loongson_board_info { > + int bios_size; > + char *bios_vendor; > + char *bios_version; > + char *bios_release_date; > + char *board_name; > + char *board_vendor; > +}; > + > +struct loongson_system_configuration { > + int bpi_ver; > + int nr_cpus; > + int nr_nodes; > + int nr_io_pics; > + int boot_cpu_id; > + int cores_per_node; > + int cores_per_package; > + char *cpuname; > + u64 vgabios_addr; > +}; > + > +extern struct loongson_board_info b_info; > +extern struct bootparamsinterface *efi_bp; > +extern struct loongsonlist_mem_map *loongson_mem_map; > +extern struct loongson_system_configuration loongson_sysconf; > +#endif > diff --git a/arch/loongarch/include/asm/bootinfo.h b/arch/loongarch/include/asm/bootinfo.h > new file mode 100644 > index 000000000000..41838633d1aa > --- /dev/null > +++ b/arch/loongarch/include/asm/bootinfo.h > @@ -0,0 +1,33 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > +#ifndef _ASM_BOOTINFO_H > +#define _ASM_BOOTINFO_H > + > +#include <linux/types.h> > +#include <asm/setup.h> > + > +const char *get_system_type(void); > + > +extern void early_memblock_init(void); > +extern void detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_addr_t sz_max); > + > +extern void early_init(void); > +extern void platform_init(void); > + > +extern void free_init_pages(const char *what, unsigned long begin, unsigned long end); > + > +/* > + * Initial kernel command line, usually setup by fw_init_cmdline() > + */ > +extern char arcs_cmdline[COMMAND_LINE_SIZE]; > + > +/* > + * Registers a0, a1, a3 and a4 as passed to the kernel entry by firmware > + */ > +extern unsigned long fw_arg0, fw_arg1, fw_arg2, fw_arg3; > + > +extern unsigned long initrd_start, initrd_end; > + > +#endif /* _ASM_BOOTINFO_H */ > diff --git a/arch/loongarch/include/asm/dmi.h b/arch/loongarch/include/asm/dmi.h > new file mode 100644 > index 000000000000..fbe98f0e56e0 > --- /dev/null > +++ b/arch/loongarch/include/asm/dmi.h > @@ -0,0 +1,24 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > +#ifndef _ASM_DMI_H > +#define _ASM_DMI_H > + > +#include <linux/io.h> > +#include <linux/memblock.h> > + > +#define dmi_early_remap(x, l) dmi_remap(x, l) > +#define dmi_early_unmap(x, l) dmi_unmap(x) > +#define dmi_alloc(l) memblock_alloc_low(l, PAGE_SIZE) > + > +static inline void *dmi_remap(u64 phys_addr, unsigned long size) > +{ > + return ((void *)TO_CAC(phys_addr)); > +} > + > +static inline void dmi_unmap(void *addr) > +{ > +} > + > +#endif /* _ASM_DMI_H */ > diff --git a/arch/loongarch/include/asm/efi.h b/arch/loongarch/include/asm/efi.h > new file mode 100644 > index 000000000000..90e8e35d7f4b > --- /dev/null > +++ b/arch/loongarch/include/asm/efi.h > @@ -0,0 +1,32 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > +#ifndef _ASM_LOONGARCH_EFI_H > +#define _ASM_LOONGARCH_EFI_H > + > +#include <linux/efi.h> > + > +extern void __init efi_init(void); > +extern void __init efi_runtime_init(void); > +extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt); > + > +#define ARCH_EFI_IRQ_FLAGS_MASK 0x00000001 /*bit0: CP0 Status.IE*/ > + > +#define arch_efi_call_virt_setup() \ > +({ \ > +}) > + > +#define arch_efi_call_virt(p, f, args...) \ > +({ \ > + efi_##f##_t * __f; \ > + __f = p->f; \ > + __f(args); \ > +}) > + > +#define arch_efi_call_virt_teardown() \ > +({ \ > +}) > + > + > +#endif /* _ASM_LOONGARCH_EFI_H */ > diff --git a/arch/loongarch/include/asm/fw.h b/arch/loongarch/include/asm/fw.h > new file mode 100644 > index 000000000000..8a0e8e7c625f > --- /dev/null > +++ b/arch/loongarch/include/asm/fw.h > @@ -0,0 +1,18 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > +#ifndef __ASM_FW_H_ > +#define __ASM_FW_H_ > + > +#include <asm/bootinfo.h> > + > +extern int fw_argc; > +extern long *_fw_argv, *_fw_envp; > + > +#define fw_argv(index) ((char *)(long)_fw_argv[(index)]) > +#define fw_envp(index) ((char *)(long)_fw_envp[(index)]) > + > +extern void fw_init_cmdline(void); > + > +#endif /* __ASM_FW_H_ */ > diff --git a/arch/loongarch/include/asm/reboot.h b/arch/loongarch/include/asm/reboot.h > new file mode 100644 > index 000000000000..0e96b0f42993 > --- /dev/null > +++ b/arch/loongarch/include/asm/reboot.h > @@ -0,0 +1,10 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > +#ifndef _ASM_REBOOT_H > +#define _ASM_REBOOT_H > + > +extern void (*pm_restart)(void); > + > +#endif /* _ASM_REBOOT_H */ > diff --git a/arch/loongarch/include/asm/setup.h b/arch/loongarch/include/asm/setup.h > new file mode 100644 > index 000000000000..fc1fb741b4da > --- /dev/null > +++ b/arch/loongarch/include/asm/setup.h > @@ -0,0 +1,21 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > + > +#ifndef _LOONGARCH_SETUP_H > +#define _LOONGARCH_SETUP_H > + > +#include <linux/types.h> > +#include <uapi/asm/setup.h> > + > +#define VECSIZE 0x200 > + > +extern unsigned long eentry; > +extern unsigned long tlbrentry; > +extern void cpu_cache_init(void); > +extern void per_cpu_trap_init(int cpu); > +extern void set_handler(unsigned long offset, void *addr, unsigned long len); > +extern void set_merr_handler(unsigned long offset, void *addr, unsigned long len); > + > +#endif /* __SETUP_H */ > diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c > new file mode 100644 > index 000000000000..3f77c937e9ad > --- /dev/null > +++ b/arch/loongarch/kernel/acpi.c > @@ -0,0 +1,324 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * acpi.c - Architecture-Specific Low-Level ACPI Boot Support > + * > + * Author: Jianmin Lv <lvjianmin@loongson.cn> > + * Huacai Chen <chenhuacai@loongson.cn> > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > + > +#include <linux/init.h> > +#include <linux/acpi.h> > +#include <linux/irq.h> > +#include <linux/irqdomain.h> > +#include <linux/memblock.h> > +#include <linux/serial_core.h> > +#include <asm/io.h> > +#include <asm/loongson.h> > + > +int acpi_disabled; > +EXPORT_SYMBOL(acpi_disabled); > +int acpi_noirq; > +int acpi_pci_disabled; > +EXPORT_SYMBOL(acpi_pci_disabled); > +int acpi_strict = 1; /* We have no workarounds on LoongArch */ > +int num_processors; > +int disabled_cpus; > +enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM; > + > +u64 acpi_saved_sp; > + > +#define MAX_CORE_PIC 256 > + > +#define PREFIX "ACPI: " > + > +/* > + * Following __acpi_xx functions should be implemented for sepecific cpu. > + */ > +int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp) > +{ > + if (irqp != NULL) > + *irqp = acpi_register_gsi(NULL, gsi, -1, -1); > + return 0; > +} > +EXPORT_SYMBOL_GPL(acpi_gsi_to_irq); > + > +int acpi_isa_irq_to_gsi(unsigned int isa_irq, u32 *gsi) > +{ > + if (gsi) > + *gsi = isa_irq; > + return 0; > +} > + > +/* > + * success: return IRQ number (>=0) > + * failure: return < 0 > + */ > +int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity) > +{ > + int id, hwirq; > + struct irq_domain *domain; > + struct irq_fwspec fwspec; > + > + switch (gsi) { > + case GSI_MIN_CPU_IRQ ... GSI_MAX_CPU_IRQ: > + hwirq = gsi - GSI_MIN_CPU_IRQ; > + return irq_create_mapping(liointc_domain, hwirq); > + > + case GSI_MIN_PCH_IRQ ... GSI_MAX_PCH_IRQ: > + id = find_pch_pic(gsi); > + if (id < 0) > + return -1; > + > + domain = pch_pic_domain[id]; > + if (!domain) > + return gsi; > + > + hwirq = gsi - acpi_pchpic[id]->gsi_base; > + fwspec.param_count = 2; > + fwspec.param[0] = hwirq; > + fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; > + > + return irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, &fwspec); > + } > + > + return -1; > +} > +EXPORT_SYMBOL_GPL(acpi_register_gsi); > + > +void acpi_unregister_gsi(u32 gsi) > +{ > + > +} > +EXPORT_SYMBOL_GPL(acpi_unregister_gsi); > + > +void __iomem *__init __acpi_map_table(unsigned long phys, unsigned long size) > +{ > + > + if (!phys || !size) > + return NULL; > + > + return early_memremap(phys, size); > +} > +void __init __acpi_unmap_table(void __iomem *map, unsigned long size) > +{ > + if (!map || !size) > + return; > + > + early_memunmap(map, size); > +} > + > +void __init acpi_boot_table_init(void) > +{ > + /* > + * If acpi_disabled, bail out > + */ > + if (acpi_disabled) > + return; > + > + /* > + * Initialize the ACPI boot-time table parser. > + */ > + if (acpi_table_init()) { > + disable_acpi(); > + return; > + } > +} > + > +static int __init > +acpi_parse_cpuintc(union acpi_subtable_headers *header, const unsigned long end) > +{ > + struct acpi_madt_core_pic *processor = NULL; > + > + processor = (struct acpi_madt_core_pic *)header; > + if (BAD_MADT_ENTRY(processor, end)) > + return -EINVAL; > + > + acpi_table_print_madt_entry(&header->common); > + > + return 0; > +} > + > +static int __init > +acpi_parse_liointc(union acpi_subtable_headers *header, const unsigned long end) > +{ > + struct acpi_madt_lio_pic *liointc = NULL; > + > + liointc = (struct acpi_madt_lio_pic *)header; > + > + if (BAD_MADT_ENTRY(liointc, end)) > + return -EINVAL; > + > + acpi_liointc = liointc; > + > + return 0; > +} > + > +static int __init > +acpi_parse_eiointc(union acpi_subtable_headers *header, const unsigned long end) > +{ > + static int id = 0; > + struct acpi_madt_eio_pic *eiointc = NULL; > + > + eiointc = (struct acpi_madt_eio_pic *)header; > + > + if (BAD_MADT_ENTRY(eiointc, end)) > + return -EINVAL; > + > + acpi_eiointc[id++] = eiointc; > + loongson_sysconf.nr_io_pics = id; > + > + return 0; > +} > + > +static int __init > +acpi_parse_htintc(union acpi_subtable_headers *header, const unsigned long end) > +{ > + struct acpi_madt_ht_pic *htintc = NULL; > + > + htintc = (struct acpi_madt_ht_pic *)header; > + > + if (BAD_MADT_ENTRY(htintc, end)) > + return -EINVAL; > + > + acpi_htintc = htintc; > + loongson_sysconf.nr_io_pics = 1; > + > + return 0; > +} > + > +static int __init > +acpi_parse_pch_pic(union acpi_subtable_headers *header, const unsigned long end) > +{ > + static int id = 0; > + struct acpi_madt_bio_pic *pchpic = NULL; > + > + pchpic = (struct acpi_madt_bio_pic *)header; > + > + if (BAD_MADT_ENTRY(pchpic, end)) > + return -EINVAL; > + > + acpi_pchpic[id++] = pchpic; > + > + return 0; > +} > + > +static int __init > +acpi_parse_pch_msi(union acpi_subtable_headers *header, const unsigned long end) > +{ > + static int id = 0; > + struct acpi_madt_msi_pic *pchmsi = NULL; > + > + pchmsi = (struct acpi_madt_msi_pic *)header; > + > + if (BAD_MADT_ENTRY(pchmsi, end)) > + return -EINVAL; > + > + acpi_pchmsi[id++] = pchmsi; > + > + return 0; > +} > + > +static int __init > +acpi_parse_pch_lpc(union acpi_subtable_headers *header, const unsigned long end) > +{ > + struct acpi_madt_lpc_pic *pchlpc = NULL; > + > + pchlpc = (struct acpi_madt_lpc_pic *)header; > + > + if (BAD_MADT_ENTRY(pchlpc, end)) > + return -EINVAL; > + > + acpi_pchlpc = pchlpc; > + > + return 0; > +} > + > +static void __init acpi_process_madt(void) > +{ > + int error; > + > + /* Parse MADT CPUINTC entries */ > + error = acpi_table_parse_madt(ACPI_MADT_TYPE_CORE_PIC, acpi_parse_cpuintc, MAX_CORE_PIC); > + if (error < 0) { > + disable_acpi(); > + pr_err(PREFIX "Invalid BIOS MADT (CPUINTC entries), ACPI disabled\n"); > + return; > + } > + > + loongson_sysconf.nr_cpus = num_processors; > + > + /* Parse MADT LIOINTC entries */ > + error = acpi_table_parse_madt(ACPI_MADT_TYPE_LIO_PIC, acpi_parse_liointc, 1); > + if (error < 0) { > + disable_acpi(); > + pr_err(PREFIX "Invalid BIOS MADT (LIOINTC entries), ACPI disabled\n"); > + return; > + } > + > + /* Parse MADT EIOINTC entries */ > + error = acpi_table_parse_madt(ACPI_MADT_TYPE_EIO_PIC, acpi_parse_eiointc, MAX_IO_PICS); > + if (error < 0) { > + disable_acpi(); > + pr_err(PREFIX "Invalid BIOS MADT (EIOINTC entries), ACPI disabled\n"); > + return; > + } > + > + /* Parse MADT HTVEC entries */ > + error = acpi_table_parse_madt(ACPI_MADT_TYPE_HT_PIC, acpi_parse_htintc, 1); > + if (error < 0) { > + disable_acpi(); > + pr_err(PREFIX "Invalid BIOS MADT (HTVEC entries), ACPI disabled\n"); > + return; > + } > + > + /* Parse MADT PCHPIC entries */ > + error = acpi_table_parse_madt(ACPI_MADT_TYPE_BIO_PIC, acpi_parse_pch_pic, MAX_IO_PICS); > + if (error < 0) { > + disable_acpi(); > + pr_err(PREFIX "Invalid BIOS MADT (PCHPIC entries), ACPI disabled\n"); > + return; > + } > + > + /* Parse MADT PCHMSI entries */ > + error = acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, acpi_parse_pch_msi, MAX_IO_PICS); > + if (error < 0) { > + disable_acpi(); > + pr_err(PREFIX "Invalid BIOS MADT (PCHMSI entries), ACPI disabled\n"); > + return; > + } > + > + /* Parse MADT PCHLPC entries */ > + error = acpi_table_parse_madt(ACPI_MADT_TYPE_LPC_PIC, acpi_parse_pch_lpc, 1); > + if (error < 0) { > + disable_acpi(); > + pr_err(PREFIX "Invalid BIOS MADT (PCHLPC entries), ACPI disabled\n"); > + return; > + } > +} > + > +int __init acpi_boot_init(void) > +{ > + /* > + * If acpi_disabled, bail out > + */ > + if (acpi_disabled) > + return -1; > + > + loongson_sysconf.boot_cpu_id = read_csr_cpuid(); > + > + /* > + * Process the Multiple APIC Description Table (MADT), if present > + */ > + acpi_process_madt(); > + > + /* Do not enable ACPI SPCR console by default */ > + acpi_parse_spcr(earlycon_acpi_spcr_enable, false); > + > + return 0; > +} > + > +void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size) > +{ > + memblock_mark_nomap(addr, size); > +} > diff --git a/arch/loongarch/kernel/cacheinfo.c b/arch/loongarch/kernel/cacheinfo.c > new file mode 100644 > index 000000000000..bedeb3eb1157 > --- /dev/null > +++ b/arch/loongarch/kernel/cacheinfo.c > @@ -0,0 +1,122 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * LoongArch cacheinfo support > + * > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > +#include <linux/cacheinfo.h> > + > +/* Populates leaf and increments to next leaf */ > +#define populate_cache(cache, leaf, c_level, c_type) \ > +do { \ > + leaf->type = c_type; \ > + leaf->level = c_level; \ > + leaf->coherency_line_size = c->cache.linesz; \ > + leaf->number_of_sets = c->cache.sets; \ > + leaf->ways_of_associativity = c->cache.ways; \ > + leaf->size = c->cache.linesz * c->cache.sets * \ > + c->cache.ways; \ > + leaf++; \ > +} while (0) > + > +int init_cache_level(unsigned int cpu) > +{ > + struct cpuinfo_loongarch *c = ¤t_cpu_data; > + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); > + int levels = 0, leaves = 0; > + > + /* > + * If Dcache is not set, we assume the cache structures > + * are not properly initialized. > + */ > + if (c->dcache.waysize) > + levels += 1; > + else > + return -ENOENT; > + > + > + leaves += (c->icache.waysize) ? 2 : 1; > + > + if (c->vcache.waysize) { > + levels++; > + leaves++; > + } > + > + if (c->scache.waysize) { > + levels++; > + leaves++; > + } > + > + if (c->tcache.waysize) { > + levels++; > + leaves++; > + } > + > + this_cpu_ci->num_levels = levels; > + this_cpu_ci->num_leaves = leaves; > + return 0; > +} > + > +static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, > + struct cacheinfo *sib_leaf) > +{ > + return !((this_leaf->level == 1) || (this_leaf->level == 2)); > +} > + > +static void cache_cpumap_setup(unsigned int cpu) > +{ > + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); > + struct cacheinfo *this_leaf, *sib_leaf; > + unsigned int index; > + > + for (index = 0; index < this_cpu_ci->num_leaves; index++) { > + unsigned int i; > + > + this_leaf = this_cpu_ci->info_list + index; > + /* skip if shared_cpu_map is already populated */ > + if (!cpumask_empty(&this_leaf->shared_cpu_map)) > + continue; > + > + cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map); > + for_each_online_cpu(i) { > + struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i); > + > + if (i == cpu || !sib_cpu_ci->info_list) > + continue;/* skip if itself or no cacheinfo */ > + sib_leaf = sib_cpu_ci->info_list + index; > + if (cache_leaves_are_shared(this_leaf, sib_leaf)) { > + cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map); > + cpumask_set_cpu(i, &this_leaf->shared_cpu_map); > + } > + } > + } > +} > + > +int populate_cache_leaves(unsigned int cpu) > +{ > + int level = 1; > + struct cpuinfo_loongarch *c = ¤t_cpu_data; > + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); > + struct cacheinfo *this_leaf = this_cpu_ci->info_list; > + > + if (c->icache.waysize) { > + populate_cache(dcache, this_leaf, level, CACHE_TYPE_DATA); > + populate_cache(icache, this_leaf, level++, CACHE_TYPE_INST); > + } else { > + populate_cache(dcache, this_leaf, level++, CACHE_TYPE_UNIFIED); > + } > + > + if (c->vcache.waysize) > + populate_cache(vcache, this_leaf, level++, CACHE_TYPE_UNIFIED); > + > + if (c->scache.waysize) > + populate_cache(scache, this_leaf, level++, CACHE_TYPE_UNIFIED); > + > + if (c->tcache.waysize) > + populate_cache(tcache, this_leaf, level++, CACHE_TYPE_UNIFIED); > + > + cache_cpumap_setup(cpu); > + this_cpu_ci->cpu_map_populated = true; > + > + return 0; > +} > diff --git a/arch/loongarch/kernel/cmdline.c b/arch/loongarch/kernel/cmdline.c > new file mode 100644 > index 000000000000..46bf4486b54c > --- /dev/null > +++ b/arch/loongarch/kernel/cmdline.c > @@ -0,0 +1,28 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > +#include <linux/init.h> > +#include <linux/kernel.h> > +#include <linux/string.h> > + > +#include <asm/fw.h> > + > +int fw_argc; > +long *_fw_argv, *_fw_envp; > + > +void __init fw_init_cmdline(void) > +{ > + int i; > + > + fw_argc = fw_arg0; > + _fw_argv = (long *)fw_arg1; > + _fw_envp = (long *)fw_arg2; > + > + arcs_cmdline[0] = '\0'; > + for (i = 1; i < fw_argc; i++) { > + strlcat(arcs_cmdline, fw_argv(i), COMMAND_LINE_SIZE); > + if (i < (fw_argc - 1)) > + strlcat(arcs_cmdline, " ", COMMAND_LINE_SIZE); > + } > +} > diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c > new file mode 100644 > index 000000000000..acf7eb19f75d > --- /dev/null > +++ b/arch/loongarch/kernel/cpu-probe.c > @@ -0,0 +1,301 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Processor capabilities determination functions. > + * > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > +#include <linux/init.h> > +#include <linux/kernel.h> > +#include <linux/ptrace.h> > +#include <linux/smp.h> > +#include <linux/stddef.h> > +#include <linux/export.h> > +#include <linux/printk.h> > +#include <linux/uaccess.h> > + > +#include <asm/cpu-features.h> > +#include <asm/elf.h> > +#include <asm/fpu.h> > +#include <asm/loongarch.h> > +#include <asm/pgtable-bits.h> > +#include <asm/setup.h> > + > +/* Hardware capabilities */ > +unsigned int elf_hwcap __read_mostly; > +EXPORT_SYMBOL_GPL(elf_hwcap); > + > +/* > + * Determine the FCSR mask for FPU hardware. > + */ > +static inline void cpu_set_fpu_fcsr_mask(struct cpuinfo_loongarch *c) > +{ > + unsigned long sr, mask, fcsr, fcsr0, fcsr1; > + > + fcsr = c->fpu_csr0; > + mask = FPU_CSR_ALL_X | FPU_CSR_ALL_E | FPU_CSR_ALL_S | FPU_CSR_RM; > + > + sr = read_csr_euen(); > + enable_fpu(); > + > + fcsr0 = fcsr & mask; > + write_fcsr(LOONGARCH_FCSR0, fcsr0); > + fcsr0 = read_fcsr(LOONGARCH_FCSR0); > + > + fcsr1 = fcsr | ~mask; > + write_fcsr(LOONGARCH_FCSR0, fcsr1); > + fcsr1 = read_fcsr(LOONGARCH_FCSR0); > + > + write_fcsr(LOONGARCH_FCSR0, fcsr); > + > + write_csr_euen(sr); > + > + c->fpu_mask = ~(fcsr0 ^ fcsr1) & ~mask; > +} > + > +static inline void set_elf_platform(int cpu, const char *plat) > +{ > + if (cpu == 0) > + __elf_platform = plat; > +} > + > +/* MAP BASE */ > +unsigned long vm_map_base; > +EXPORT_SYMBOL_GPL(vm_map_base); > + > +static void cpu_probe_addrbits(struct cpuinfo_loongarch *c) > +{ > +#ifdef __NEED_ADDRBITS_PROBE > + c->pabits = (read_cpucfg(LOONGARCH_CPUCFG1) & CPUCFG1_PABITS) >> 4; > + c->vabits = (read_cpucfg(LOONGARCH_CPUCFG1) & CPUCFG1_VABITS) >> 12; > + vm_map_base = 0UL - (1UL << c->vabits); > +#endif > +} > + > +static void set_isa(struct cpuinfo_loongarch *c, unsigned int isa) > +{ > + switch (isa) { > + case LOONGARCH_CPU_ISA_LA64: > + c->isa_level |= LOONGARCH_CPU_ISA_LA64; > + fallthrough; > + case LOONGARCH_CPU_ISA_LA32S: > + c->isa_level |= LOONGARCH_CPU_ISA_LA32S; > + fallthrough; > + case LOONGARCH_CPU_ISA_LA32R: > + c->isa_level |= LOONGARCH_CPU_ISA_LA32R; > + break; > + } > +} > + > +static void cpu_probe_common(struct cpuinfo_loongarch *c) > +{ > + unsigned int config; > + unsigned long asid_mask; > + > + c->options = LOONGARCH_CPU_CPUCFG | LOONGARCH_CPU_CSR | > + LOONGARCH_CPU_TLB | LOONGARCH_CPU_VINT | LOONGARCH_CPU_WATCH; > + > + elf_hwcap |= HWCAP_LOONGARCH_CRC32; > + > + config = read_cpucfg(LOONGARCH_CPUCFG1); > + if (config & CPUCFG1_UAL) { > + c->options |= LOONGARCH_CPU_UAL; > + elf_hwcap |= HWCAP_LOONGARCH_UAL; > + } > + > + config = read_cpucfg(LOONGARCH_CPUCFG2); > + if (config & CPUCFG2_LAM) { > + c->options |= LOONGARCH_CPU_LAM; > + elf_hwcap |= HWCAP_LOONGARCH_LAM; > + } > + if (config & CPUCFG2_FP) { > + c->options |= LOONGARCH_CPU_FPU; > + elf_hwcap |= HWCAP_LOONGARCH_FPU; > + } > + if (config & CPUCFG2_COMPLEX) { > + c->options |= LOONGARCH_CPU_COMPLEX; > + elf_hwcap |= HWCAP_LOONGARCH_COMPLEX; > + } > + if (config & CPUCFG2_CRYPTO) { > + c->options |= LOONGARCH_CPU_CRYPTO; > + elf_hwcap |= HWCAP_LOONGARCH_CRYPTO; > + } > + if (config & CPUCFG2_LVZP) { > + c->options |= LOONGARCH_CPU_LVZ; > + elf_hwcap |= HWCAP_LOONGARCH_LVZ; > + } > + > + config = read_cpucfg(LOONGARCH_CPUCFG6); > + if (config & CPUCFG6_PMP) > + c->options |= LOONGARCH_CPU_PMP; > + > + config = iocsr_readl(LOONGARCH_IOCSR_FEATURES); > + if (config & IOCSRF_CSRIPI) > + c->options |= LOONGARCH_CPU_CSRIPI; > + if (config & IOCSRF_EXTIOI) > + c->options |= LOONGARCH_CPU_EXTIOI; > + if (config & IOCSRF_FREQSCALE) > + c->options |= LOONGARCH_CPU_SCALEFREQ; > + if (config & IOCSRF_VM) > + c->options |= LOONGARCH_CPU_HYPERVISOR; > + > + config = csr_readl(LOONGARCH_CSR_ASID); > + config = (config & CSR_ASID_BIT) >> CSR_ASID_BIT_SHIFT; > + asid_mask = GENMASK(config - 1, 0); > + set_cpu_asid_mask(c, asid_mask); > + > + config = read_csr_prcfg1(); > + c->kscratch_mask = GENMASK((config & CSR_CONF1_KSNUM) - 1, 0); > + c->kscratch_mask &= ~(EXC_KSCRATCH_MASK | PERCPU_KSCRATCH_MASK | KVM_KSCRATCH_MASK); > + > + config = read_csr_prcfg3(); > + switch (config & CSR_CONF3_TLBTYPE) { > + case 0: > + c->tlbsizemtlb = 0; > + c->tlbsizestlbsets = 0; > + c->tlbsizestlbways = 0; > + c->tlbsize = 0; > + break; > + case 1: > + c->tlbsizemtlb = ((config & CSR_CONF3_MTLBSIZE) >> CSR_CONF3_MTLBSIZE_SHIFT) + 1; > + c->tlbsizestlbsets = 0; > + c->tlbsizestlbways = 0; > + c->tlbsize = c->tlbsizemtlb + c->tlbsizestlbsets * c->tlbsizestlbways; > + break; > + case 2: > + c->tlbsizemtlb = ((config & CSR_CONF3_MTLBSIZE) >> CSR_CONF3_MTLBSIZE_SHIFT) + 1; > + c->tlbsizestlbsets = 1 << ((config & CSR_CONF3_STLBIDX) >> CSR_CONF3_STLBIDX_SHIFT); > + c->tlbsizestlbways = ((config & CSR_CONF3_STLBWAYS) >> CSR_CONF3_STLBWAYS_SHIFT) + 1; > + c->tlbsize = c->tlbsizemtlb + c->tlbsizestlbsets * c->tlbsizestlbways; > + break; > + default: > + pr_warn("Warning: unimplemented tlb type\n"); > + } > +} > + > +#define MAX_NAME_LEN 32 > +#define VENDOR_OFFSET 0 > +#define CPUNAME_OFFSET 9 > + > +static char cpu_full_name[MAX_NAME_LEN] = " - "; > + > +static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int cpu) > +{ > + uint64_t *vendor = (void *)(&cpu_full_name[VENDOR_OFFSET]); > + uint64_t *cpuname = (void *)(&cpu_full_name[CPUNAME_OFFSET]); > + > + __cpu_full_name[cpu] = cpu_full_name; > + *vendor = iocsr_readq(LOONGARCH_IOCSR_VENDOR); > + *cpuname = iocsr_readq(LOONGARCH_IOCSR_CPUNAME); > + > + switch (c->processor_id & PRID_IMP_MASK) { > + case PRID_IMP_LOONGSON_32: > + c->cputype = CPU_LOONGSON32; > + set_isa(c, LOONGARCH_CPU_ISA_LA32S); > + __cpu_family[cpu] = "Loongson-32bit"; > + pr_info("Standard 32-bit Loongson Processor probed\n"); > + break; > + case PRID_IMP_LOONGSON_64R: > + c->cputype = CPU_LOONGSON64; > + set_isa(c, LOONGARCH_CPU_ISA_LA64); > + __cpu_family[cpu] = "Loongson-64bit"; > + pr_info("Reduced 64-bit Loongson Processor probed\n"); > + break; > + case PRID_IMP_LOONGSON_64C: > + c->cputype = CPU_LOONGSON64; > + set_isa(c, LOONGARCH_CPU_ISA_LA64); > + __cpu_family[cpu] = "Loongson-64bit"; > + pr_info("Classic 64-bit Loongson Processor probed\n"); > + break; > + case PRID_IMP_LOONGSON_64G: > + c->cputype = CPU_LOONGSON64; > + set_isa(c, LOONGARCH_CPU_ISA_LA64); > + __cpu_family[cpu] = "Loongson-64bit"; > + pr_info("Generic 64-bit Loongson Processor probed\n"); > + break; > + default: /* Default to 64 bit */ > + c->cputype = CPU_LOONGSON64; > + set_isa(c, LOONGARCH_CPU_ISA_LA64); > + __cpu_family[cpu] = "Loongson-64bit"; > + pr_info("Unknown 64-bit Loongson Processor probed\n"); > + } > +} > + > +#ifdef CONFIG_64BIT > +/* For use by uaccess.h */ > +u64 __ua_limit; > +EXPORT_SYMBOL(__ua_limit); > +#endif > + > +const char *__cpu_family[NR_CPUS]; > +const char *__cpu_full_name[NR_CPUS]; > +const char *__elf_platform; > + > +static void cpu_report(void) > +{ > + struct cpuinfo_loongarch *c = ¤t_cpu_data; > + > + pr_info("CPU%d revision is: %08x (%s)\n", > + smp_processor_id(), c->processor_id, cpu_family_string()); > + if (c->options & LOONGARCH_CPU_FPU) > + pr_info("FPU%d revision is: %08x\n", smp_processor_id(), c->fpu_vers); > +} > + > +void cpu_probe(void) > +{ > + unsigned int cpu = smp_processor_id(); > + struct cpuinfo_loongarch *c = ¤t_cpu_data; > + > + /* > + * Set a default elf platform, cpu probe may later > + * overwrite it with a more precise value > + */ > + set_elf_platform(cpu, "loongarch"); > + > + c->cputype = CPU_UNKNOWN; > + c->processor_id = read_cpucfg(LOONGARCH_CPUCFG0); > + c->fpu_vers = (read_cpucfg(LOONGARCH_CPUCFG2) >> 3) & 0x3; > + > + c->fpu_csr0 = FPU_CSR_RN; > + c->fpu_mask = FPU_CSR_RSVD; > + > + cpu_probe_common(c); > + > + per_cpu_trap_init(cpu); > + > + switch (c->processor_id & PRID_COMP_MASK) { > + case PRID_COMP_LOONGSON: > + cpu_probe_loongson(c, cpu); > + break; > + } > + > + BUG_ON(!__cpu_family[cpu]); > + BUG_ON(c->cputype == CPU_UNKNOWN); > + > + cpu_probe_addrbits(c); > + > +#ifdef CONFIG_64BIT > + if (cpu == 0) > + __ua_limit = ~((1ull << cpu_vabits) - 1); > +#endif > + > + cpu_report(); > +} > + > +void cpu_set_cluster(struct cpuinfo_loongarch *cpuinfo, unsigned int cluster) > +{ > + /* Ensure the core number fits in the field */ > + WARN_ON(cluster > (LOONGARCH_GLOBALNUMBER_CLUSTER >> > + LOONGARCH_GLOBALNUMBER_CLUSTER_SHF)); > + > + cpuinfo->globalnumber &= ~LOONGARCH_GLOBALNUMBER_CLUSTER; > + cpuinfo->globalnumber |= cluster << LOONGARCH_GLOBALNUMBER_CLUSTER_SHF; > +} > + > +void cpu_set_core(struct cpuinfo_loongarch *cpuinfo, unsigned int core) > +{ > + /* Ensure the core number fits in the field */ > + WARN_ON(core > (LOONGARCH_GLOBALNUMBER_CORE >> LOONGARCH_GLOBALNUMBER_CORE_SHF)); > + > + cpuinfo->globalnumber &= ~LOONGARCH_GLOBALNUMBER_CORE; > + cpuinfo->globalnumber |= core << LOONGARCH_GLOBALNUMBER_CORE_SHF; > +} > diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c > new file mode 100644 > index 000000000000..c05abd143e44 > --- /dev/null > +++ b/arch/loongarch/kernel/efi.c > @@ -0,0 +1,95 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * EFI initialization > + * > + * Author: Jianmin Lv <lvjianmin@loongson.cn> > + * Huacai Chen <chenhuacai@loongson.cn> > + * > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > + > +#include <linux/acpi.h> > +#include <linux/efi.h> > +#include <linux/efi-bgrt.h> > +#include <linux/init.h> > +#include <linux/kernel.h> > +#include <linux/export.h> > +#include <linux/io.h> > +#include <linux/kobject.h> > +#include <linux/memblock.h> > +#include <linux/reboot.h> > +#include <linux/uaccess.h> > + > +#include <asm/efi.h> > +#include <asm/boot_param.h> > + > +static efi_config_table_type_t arch_tables[] __initdata = {{},}; > + > +void __init efi_runtime_init(void) > +{ > + if (!efi_enabled(EFI_BOOT)) > + return; > + > + if (!efi.runtime) > + return; > + > + if (efi_runtime_disabled()) { > + pr_info("EFI runtime services will be disabled.\n"); > + return; > + } > + > + efi_native_runtime_setup(); > + set_bit(EFI_RUNTIME_SERVICES, &efi.flags); > +} > + > +void __init efi_init(void) > +{ > + unsigned long efi_config_table; > + efi_system_table_t *efi_systab; > + > + if (!efi_bp) > + return; > + > + efi_systab = (efi_system_table_t *)efi_bp->systemtable; > + if (!efi_systab) { > + pr_err("Can't find EFI system table.\n"); > + return; > + } > + > + set_bit(EFI_64BIT, &efi.flags); > + efi_config_table = (unsigned long)efi_systab->tables; > + efi.runtime = (efi_runtime_services_t *)efi_systab->runtime; > + efi.runtime_version = efi.runtime ? (unsigned int)efi.runtime->hdr.revision : 0; > + > + efi_config_parse_tables((void *)efi_systab->tables, efi_systab->nr_tables, arch_tables); > +} > + > +static ssize_t boardinfo_show(struct kobject *kobj, > + struct kobj_attribute *attr, char *buf) > +{ > + return sprintf(buf, > + "BIOS Information\n" > + "Vendor\t\t\t: %s\n" > + "Version\t\t\t: %s\n" > + "ROM Size\t\t: %d KB\n" > + "Release Date\t\t: %s\n\n" > + "Board Information\n" > + "Manufacturer\t\t: %s\n" > + "Board Name\t\t: %s\n" > + "Family\t\t\t: LOONGSON64\n\n", > + b_info.bios_vendor, b_info.bios_version, > + b_info.bios_size, b_info.bios_release_date, > + b_info.board_vendor, b_info.board_name); > +} > + > +static struct kobj_attribute boardinfo_attr = __ATTR(boardinfo, 0444, > + boardinfo_show, NULL); > + > +static int __init boardinfo_init(void) > +{ > + if (!efi_kobj) > + return -EINVAL; > + > + return sysfs_create_file(efi_kobj, &boardinfo_attr.attr); > +} > +late_initcall(boardinfo_init); > diff --git a/arch/loongarch/kernel/env.c b/arch/loongarch/kernel/env.c > new file mode 100644 > index 000000000000..234fe282a98c > --- /dev/null > +++ b/arch/loongarch/kernel/env.c > @@ -0,0 +1,170 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Author: Huacai Chen <chenhuacai@loongson.cn> > + * > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > +#include <linux/export.h> > +#include <linux/acpi.h> > +#include <linux/efi.h> > +#include <asm/fw.h> > +#include <asm/time.h> > +#include <asm/bootinfo.h> > +#include <asm/loongson.h> > + > +struct bootparamsinterface *efi_bp; > +struct loongsonlist_mem_map *loongson_mem_map; > +struct loongsonlist_vbios *pvbios; > +struct loongson_system_configuration loongson_sysconf; > +EXPORT_SYMBOL(loongson_sysconf); > + > +u64 loongson_chipcfg[MAX_PACKAGES]; > +u64 loongson_chiptemp[MAX_PACKAGES]; > +u64 loongson_freqctrl[MAX_PACKAGES]; > +unsigned long long smp_group[MAX_PACKAGES]; > + > +static void __init register_addrs_set(u64 *registers, const u64 addr, int num) > +{ > + u64 i; > + > + for (i = 0; i < num; i++) { > + *registers = (i << 44) | addr; > + registers++; > + } > +} > + > +static u8 ext_listhdr_checksum(u8 *buffer, u32 length) > +{ > + u8 sum = 0; > + u8 *end = buffer + length; > + > + while (buffer < end) { > + sum = (u8)(sum + *(buffer++)); > + } > + > + return (sum); > +} > + > +static int parse_mem(struct _extention_list_hdr *head) > +{ > + loongson_mem_map = (struct loongsonlist_mem_map *)head; > + if (ext_listhdr_checksum((u8 *)loongson_mem_map, head->length)) { > + pr_warn("mem checksum error\n"); > + return -EPERM; > + } > + > + return 0; > +} > + > +static int parse_vbios(struct _extention_list_hdr *head) > +{ > + pvbios = (struct loongsonlist_vbios *)head; > + > + if (ext_listhdr_checksum((u8 *)pvbios, head->length)) { > + pr_warn("vbios_addr checksum error\n"); > + return -EPERM; > + } > + > + loongson_sysconf.vgabios_addr = pvbios->vbios_addr; > + > + return 0; > +} > + > +static int parse_screeninfo(struct _extention_list_hdr *head) > +{ > + struct loongsonlist_screeninfo *pscreeninfo; > + > + pscreeninfo = (struct loongsonlist_screeninfo *)head; > + if (ext_listhdr_checksum((u8 *)pscreeninfo, head->length)) { > + pr_warn("screeninfo_addr checksum error\n"); > + return -EPERM; > + } > + > + memcpy(&screen_info, &pscreeninfo->si, sizeof(screen_info)); > + > + return 0; > +} > + > +static int list_find(struct _extention_list_hdr *head) > +{ > + struct _extention_list_hdr *fhead = head; > + > + if (fhead == NULL) { > + pr_warn("the link is empty!\n"); > + return -1; > + } > + > + while (fhead != NULL) { > + if (memcmp(&(fhead->signature), LOONGSON_MEM_LINKLIST, 3) == 0) { > + if (parse_mem(fhead) != 0) { > + pr_warn("parse mem failed\n"); > + return -EPERM; > + } > + } else if (memcmp(&(fhead->signature), LOONGSON_VBIOS_LINKLIST, 5) == 0) { > + if (parse_vbios(fhead) != 0) { > + pr_warn("parse vbios failed\n"); > + return -EPERM; > + } > + } else if (memcmp(&(fhead->signature), LOONGSON_SCREENINFO_LINKLIST, 5) == 0) { > + if (parse_screeninfo(fhead) != 0) { > + pr_warn("parse screeninfo failed\n"); > + return -EPERM; > + } > + } > + fhead = fhead->next; > + } > + > + return 0; > +} > + > +static void __init parse_bpi_flags(void) > +{ > + if (efi_bp->flags & BPI_FLAGS_UEFI_SUPPORTED) > + set_bit(EFI_BOOT, &efi.flags); > + else > + clear_bit(EFI_BOOT, &efi.flags); > +} > + > +static int get_bpi_version(void *signature) > +{ > + char data[8]; > + int r, version = 0; > + > + memset(data, 0, 8); > + memcpy(data, signature + 4, 4); > + r = kstrtoint(data, 10, &version); > + > + if (r < 0 || version < BPI_VERSION_V1) > + panic("Fatal error, invalid BPI version: %d\n", version); > + > + if (version >= BPI_VERSION_V2) > + parse_bpi_flags(); > + > + return version; > +} > + > +void __init fw_init_environ(void) > +{ > + efi_bp = (struct bootparamsinterface *)_fw_envp; > + loongson_sysconf.bpi_ver = get_bpi_version(&efi_bp->signature); > + > + register_addrs_set(smp_group, TO_UNCAC(0x1fe01000), 16); > + register_addrs_set(loongson_chipcfg, TO_UNCAC(0x1fe00180), 16); > + register_addrs_set(loongson_chiptemp, TO_UNCAC(0x1fe0019c), 16); > + register_addrs_set(loongson_freqctrl, TO_UNCAC(0x1fe001d0), 16); > + > + if (list_find(efi_bp->extlist)) > + pr_warn("Scan bootparam failed\n"); > +} > + > +static int __init init_cpu_fullname(void) > +{ > + int cpu; > + > + if (loongson_sysconf.cpuname && !strncmp(loongson_sysconf.cpuname, "Loongson", 8)) { > + for (cpu = 0; cpu < NR_CPUS; cpu++) > + __cpu_full_name[cpu] = loongson_sysconf.cpuname; > + } > + return 0; > +} > +arch_initcall(init_cpu_fullname); > diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S > new file mode 100644 > index 000000000000..53e02fb0559d > --- /dev/null > +++ b/arch/loongarch/kernel/head.S > @@ -0,0 +1,72 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > +#include <linux/init.h> > +#include <linux/threads.h> > + > +#include <asm/addrspace.h> > +#include <asm/asm.h> > +#include <asm/asmmacro.h> > +#include <asm/regdef.h> > +#include <asm/loongarch.h> > +#include <asm/stackframe.h> > + > +SYM_ENTRY(_stext, SYM_L_GLOBAL, SYM_A_NONE) > + > + __REF > + > +SYM_CODE_START(kernel_entry) # kernel entry point > + > + /* We might not get launched at the address the kernel is linked to, > + so we jump there. */ > + la.abs t0, 0f > + jirl zero, t0, 0 > +0: > + la t0, __bss_start # clear .bss > + st.d zero, t0, 0 > + la t1, __bss_stop - LONGSIZE > +1: > + addi.d t0, t0, LONGSIZE > + st.d zero, t0, 0 > + bne t0, t1, 1b > + > + la t0, fw_arg0 > + st.d a0, t0, 0 # firmware arguments > + la t0, fw_arg1 > + st.d a1, t0, 0 > + la t0, fw_arg2 > + st.d a2, t0, 0 > + la t0, fw_arg3 > + st.d a3, t0, 0 > + > + /* Config direct window and set PG */ > + li.d t0, CSR_DMW0_INIT # UC, PLV0, 0x8000 xxxx xxxx xxxx > + csrwr t0, LOONGARCH_CSR_DMWIN0 > + li.d t0, CSR_DMW1_INIT # CA, PLV0, 0x9000 xxxx xxxx xxxx > + csrwr t0, LOONGARCH_CSR_DMWIN1 > + /* Enable PG */ > + li.w t0, 0xb0 # PLV=0, IE=0, PG=1 > + csrwr t0, LOONGARCH_CSR_CRMD > + li.w t0, 0x04 # PLV=0, PIE=1, PWE=0 > + csrwr t0, LOONGARCH_CSR_PRMD > + li.w t0, 0x00 # FPE=0, SXE=0, ASXE=0, BTE=0 > + csrwr t0, LOONGARCH_CSR_EUEN > + > + /* KScratch3 used for percpu base, initialized as 0 */ > + csrwr zero, PERCPU_BASE_KS > + /* GPR21 used for percpu base (runtime), initialized as 0 */ > + or x0, zero, zero > + > + la tp, init_thread_union > + /* Set the SP after an empty pt_regs. */ > + PTR_LI sp, (_THREAD_SIZE - 32 - PT_SIZE) > + PTR_ADDU sp, sp, tp > + set_saved_sp sp, t0, t1 > + PTR_ADDIU sp, sp, -4 * SZREG # init stack pointer > + > + bl start_kernel > + > +SYM_CODE_END(kernel_entry) > + > +SYM_ENTRY(kernel_entry_end, SYM_L_GLOBAL, SYM_A_NONE) > diff --git a/arch/loongarch/kernel/mem.c b/arch/loongarch/kernel/mem.c > new file mode 100644 > index 000000000000..c1ac71034448 > --- /dev/null > +++ b/arch/loongarch/kernel/mem.c > @@ -0,0 +1,83 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > +#include <linux/fs.h> > +#include <linux/mm.h> > +#include <linux/memblock.h> > + > +#include <asm/bootinfo.h> > +#include <asm/loongson.h> > +#include <asm/sections.h> > + > +void __init early_memblock_init(void) > +{ > + int i; > + u32 mem_type; > + u64 mem_start, mem_end, mem_size; > + > + /* parse memory information */ > + for (i = 0; i < loongson_mem_map->map_count; i++) { > + mem_type = loongson_mem_map->map[i].mem_type; > + mem_start = loongson_mem_map->map[i].mem_start; > + mem_size = loongson_mem_map->map[i].mem_size; > + mem_end = mem_start + mem_size; > + > + switch (mem_type) { > + case ADDRESS_TYPE_SYSRAM: > + memblock_add(mem_start, mem_size); > + if (max_low_pfn < (mem_end >> PAGE_SHIFT)) > + max_low_pfn = mem_end >> PAGE_SHIFT; > + break; > + } > + } > + memblock_set_current_limit(PFN_PHYS(max_low_pfn)); > +} > + > +void __init fw_init_memory(void) > +{ > + int i; > + u32 mem_type; > + u64 mem_start, mem_end, mem_size; > + unsigned long start_pfn, end_pfn; > + static unsigned long num_physpages; > + > + /* parse memory information */ > + for (i = 0; i < loongson_mem_map->map_count; i++) { > + mem_type = loongson_mem_map->map[i].mem_type; > + mem_start = loongson_mem_map->map[i].mem_start; > + mem_size = loongson_mem_map->map[i].mem_size; > + mem_end = mem_start + mem_size; > + > + switch (mem_type) { > + case ADDRESS_TYPE_SYSRAM: > + mem_start = PFN_ALIGN(mem_start); > + mem_end = PFN_ALIGN(mem_end - PAGE_SIZE + 1); > + num_physpages += (mem_size >> PAGE_SHIFT); > + memblock_set_node(mem_start, mem_size, &memblock.memory, 0); > + break; > + case ADDRESS_TYPE_ACPI: > + case ADDRESS_TYPE_RESERVED: > + memblock_reserve(loongson_mem_map->map[i].mem_start, > + loongson_mem_map->map[i].mem_size); > + break; > + } > + } > + > + get_pfn_range_for_nid(0, &start_pfn, &end_pfn); > + pr_info("start_pfn=0x%lx, end_pfn=0x%lx, num_physpages:0x%lx\n", > + start_pfn, end_pfn, num_physpages); > + > + NODE_DATA(0)->node_start_pfn = start_pfn; > + NODE_DATA(0)->node_spanned_pages = end_pfn - start_pfn; > + > + /* used by finalize_initrd() */ > + max_low_pfn = end_pfn; > + > + /* Reserve the first 2MB */ > + memblock_reserve(PHYS_OFFSET, 0x200000); > + > + /* Reserve the kernel text/data/bss */ > + memblock_reserve(__pa_symbol(&_text), > + __pa_symbol(&_end) - __pa_symbol(&_text)); > +} > diff --git a/arch/loongarch/kernel/reset.c b/arch/loongarch/kernel/reset.c > new file mode 100644 > index 000000000000..b140c2aa2c5a > --- /dev/null > +++ b/arch/loongarch/kernel/reset.c > @@ -0,0 +1,90 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > +#include <linux/kernel.h> > +#include <linux/acpi.h> > +#include <linux/efi.h> > +#include <linux/export.h> > +#include <linux/pm.h> > +#include <linux/types.h> > +#include <linux/reboot.h> > +#include <linux/delay.h> > +#include <linux/console.h> > + > +#include <acpi/reboot.h> > +#include <asm/compiler.h> > +#include <asm/idle.h> > +#include <asm/loongarch.h> > +#include <asm/reboot.h> > + > +static void default_halt(void) > +{ > + local_irq_disable(); > + clear_csr_ecfg(ECFG0_IM); > + > + pr_notice("\n\n** You can safely turn off the power now **\n\n"); > + console_flush_on_panic(CONSOLE_FLUSH_PENDING); > + > + while (true) { > + __arch_cpu_idle(); > + } > +} > + > +static void default_poweroff(void) > +{ > +#ifdef CONFIG_EFI > + efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL); > +#endif > + while (true) { > + __arch_cpu_idle(); > + } > +} > + > +static void default_restart(void) > +{ > +#ifdef CONFIG_EFI > + if (efi_capsule_pending(NULL)) > + efi_reboot(REBOOT_WARM, NULL); > + else > + efi_reboot(REBOOT_COLD, NULL); > +#endif > + if (!acpi_disabled) > + acpi_reboot(); > + > + while (true) { > + __arch_cpu_idle(); > + } > +} > + > +void (*pm_restart)(void); > +EXPORT_SYMBOL(pm_restart); > + > +void (*pm_power_off)(void); > +EXPORT_SYMBOL(pm_power_off); > + > +void machine_halt(void) > +{ > + default_halt(); > +} > + > +void machine_power_off(void) > +{ > + pm_power_off(); > +} > + > +void machine_restart(char *command) > +{ > + do_kernel_restart(command); > + pm_restart(); > +} > + > +static int __init loongarch_reboot_setup(void) > +{ > + pm_restart = default_restart; > + pm_power_off = default_poweroff; > + > + return 0; > +} > + > +arch_initcall(loongarch_reboot_setup); > diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c > new file mode 100644 > index 000000000000..7ec32959b36c > --- /dev/null > +++ b/arch/loongarch/kernel/setup.c > @@ -0,0 +1,469 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + * > + * Derived from MIPS: > + * Copyright (C) 1995 Linus Torvalds > + * Copyright (C) 1995 Waldorf Electronics > + * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 01, 02, 03 Ralf Baechle > + * Copyright (C) 1996 Stoned Elipot > + * Copyright (C) 1999 Silicon Graphics, Inc. > + * Copyright (C) 2000, 2001, 2002, 2007 Maciej W. Rozycki > + */ > +#include <linux/init.h> > +#include <linux/acpi.h> > +#include <linux/dmi.h> > +#include <linux/efi.h> > +#include <linux/export.h> > +#include <linux/screen_info.h> > +#include <linux/memblock.h> > +#include <linux/initrd.h> > +#include <linux/ioport.h> > +#include <linux/root_dev.h> > +#include <linux/console.h> > +#include <linux/pfn.h> > +#include <linux/platform_device.h> > +#include <linux/sizes.h> > +#include <linux/device.h> > +#include <linux/dma-map-ops.h> > +#include <linux/swiotlb.h> > + > +#include <asm/addrspace.h> > +#include <asm/bootinfo.h> > +#include <asm/cache.h> > +#include <asm/cpu.h> > +#include <asm/dma.h> > +#include <asm/efi.h> > +#include <asm/fw.h> > +#include <asm/loongson.h> > +#include <asm/pgalloc.h> > +#include <asm/sections.h> > +#include <asm/setup.h> > +#include <asm/time.h> > + > +#define SMBIOS_BIOSSIZE_OFFSET 0x09 > +#define SMBIOS_BIOSEXTERN_OFFSET 0x13 > +#define SMBIOS_FREQLOW_OFFSET 0x16 > +#define SMBIOS_FREQHIGH_OFFSET 0x17 > +#define SMBIOS_FREQLOW_MASK 0xFF > +#define SMBIOS_CORE_PACKAGE_OFFSET 0x23 > +#define LOONGSON_EFI_ENABLE (1 << 3) > + > +#ifdef CONFIG_VT > +struct screen_info screen_info; > +#endif > + > +DEFINE_PER_CPU(unsigned long, kernelsp); > +unsigned long fw_arg0, fw_arg1, fw_arg2, fw_arg3; > +struct cpuinfo_loongarch cpu_data[NR_CPUS] __read_mostly; > + > +EXPORT_SYMBOL(cpu_data); > + > +struct loongson_board_info b_info; > +static const char dmi_empty_string[] = " "; > + > +/* > + * Setup information > + * > + * These are initialized so they are in the .data section > + */ > + > +char __initdata arcs_cmdline[COMMAND_LINE_SIZE]; > +static char __initdata command_line[COMMAND_LINE_SIZE]; > + > +static struct resource code_resource = { .name = "Kernel code", }; > +static struct resource data_resource = { .name = "Kernel data", }; > +static struct resource bss_resource = { .name = "Kernel bss", }; > + > +const char *get_system_type(void) > +{ > + return "generic-loongson-machine"; > +} > + > +static const char *dmi_string_parse(const struct dmi_header *dm, u8 s) > +{ > + const u8 *bp = ((u8 *) dm) + dm->length; > + > + if (s) { > + s--; > + while (s > 0 && *bp) { > + bp += strlen(bp) + 1; > + s--; > + } > + > + if (*bp != 0) { > + size_t len = strlen(bp)+1; > + size_t cmp_len = len > 8 ? 8 : len; > + > + if (!memcmp(bp, dmi_empty_string, cmp_len)) > + return dmi_empty_string; > + > + return bp; > + } > + } > + > + return ""; > +} > + > +static void __init parse_cpu_table(const struct dmi_header *dm) > +{ > + long freq_temp = 0; > + char *dmi_data = (char *)dm; > + > + freq_temp = ((*(dmi_data + SMBIOS_FREQHIGH_OFFSET) << 8) + > + ((*(dmi_data + SMBIOS_FREQLOW_OFFSET)) & SMBIOS_FREQLOW_MASK)); > + cpu_clock_freq = freq_temp * 1000000; > + > + loongson_sysconf.cpuname = (void *)dmi_string_parse(dm, dmi_data[16]); > + loongson_sysconf.cores_per_package = *(dmi_data + SMBIOS_CORE_PACKAGE_OFFSET); > + > + pr_info("CpuClock = %llu\n", cpu_clock_freq); > +} > + > +static void __init parse_bios_table(const struct dmi_header *dm) > +{ > + int bios_extern; > + char *dmi_data = (char *)dm; > + > + bios_extern = *(dmi_data + SMBIOS_BIOSEXTERN_OFFSET); > + b_info.bios_size = *(dmi_data + SMBIOS_BIOSSIZE_OFFSET); > + > + if (bios_extern & LOONGSON_EFI_ENABLE) > + set_bit(EFI_BOOT, &efi.flags); > + else > + clear_bit(EFI_BOOT, &efi.flags); > +} > + > +static void __init find_tokens(const struct dmi_header *dm, void *dummy) > +{ > + switch (dm->type) { > + case 0x0: /* Extern BIOS */ > + parse_bios_table(dm); > + break; > + case 0x4: /* Calling interface */ > + parse_cpu_table(dm); > + break; > + } > +} > +static void __init smbios_parse(void) > +{ > + b_info.bios_vendor = (void *)dmi_get_system_info(DMI_BIOS_VENDOR); > + b_info.bios_version = (void *)dmi_get_system_info(DMI_BIOS_VERSION); > + b_info.bios_release_date = (void *)dmi_get_system_info(DMI_BIOS_DATE); > + b_info.board_vendor = (void *)dmi_get_system_info(DMI_BOARD_VENDOR); > + b_info.board_name = (void *)dmi_get_system_info(DMI_BOARD_NAME); > + dmi_walk(find_tokens, NULL); > +} > + > +/* > + * Manage initrd > + */ > +#ifdef CONFIG_BLK_DEV_INITRD > + > +static unsigned long __init init_initrd(void) > +{ > + if (!phys_initrd_start || !phys_initrd_size) > + goto disable; > + > + initrd_start = (unsigned long)phys_to_virt(phys_initrd_start); > + initrd_end = (unsigned long)phys_to_virt(phys_initrd_start + phys_initrd_size); > + > + if (!initrd_start || initrd_end <= initrd_start) > + goto disable; > + > + if (initrd_start & ~PAGE_MASK) { > + pr_err("initrd start must be page aligned\n"); > + goto disable; > + } > + if (initrd_start < PAGE_OFFSET) { > + pr_err("initrd start < PAGE_OFFSET\n"); > + goto disable; > + } > + > + ROOT_DEV = Root_RAM0; > + > + return 0; > +disable: > + initrd_start = 0; > + initrd_end = 0; > + return 0; > +} > + > +static void __init finalize_initrd(void) > +{ > + unsigned long size = initrd_end - initrd_start; > + > + if (size == 0) { > + pr_info("Initrd not found or empty"); > + goto disable; > + } > + if (__pa(initrd_end) > PFN_PHYS(max_low_pfn)) { > + pr_err("Initrd extends beyond end of memory"); > + goto disable; > + } > + > + > + memblock_reserve(__pa(initrd_start), size); > + initrd_below_start_ok = 1; > + > + pr_info("Initial ramdisk at: 0x%lx (%lu bytes)\n", > + initrd_start, size); > + return; > +disable: > + pr_cont(" - disabling initrd\n"); > + initrd_start = 0; > + initrd_end = 0; > +} > + > +#else /* !CONFIG_BLK_DEV_INITRD */ > + > +static unsigned long __init init_initrd(void) > +{ > + return 0; > +} > + > +#define finalize_initrd() do {} while (0) > + > +#endif > + > +static int usermem __initdata; > + > +static int __init early_parse_mem(char *p) > +{ > + phys_addr_t start, size; > + > + /* > + * If a user specifies memory size, we > + * blow away any automatically generated > + * size. > + */ > + if (usermem == 0) { > + usermem = 1; > + memblock_remove(memblock_start_of_DRAM(), > + memblock_end_of_DRAM() - memblock_start_of_DRAM()); > + } > + start = 0; > + size = memparse(p, &p); > + if (*p == '@') > + start = memparse(p + 1, &p); > + > + memblock_add(start, size); > + > + return 0; > +} > +early_param("mem", early_parse_mem); > + > +static int __init early_parse_memmap(char *p) > +{ > + char *oldp; > + u64 start_at, mem_size; > + > + if (!p) > + return -EINVAL; > + > + if (!strncmp(p, "exactmap", 8)) { > + pr_err("\"memmap=exactmap\" invalid on LoongArch\n"); > + return 0; > + } > + > + oldp = p; > + mem_size = memparse(p, &p); > + if (p == oldp) > + return -EINVAL; > + > + if (*p == '@') { > + start_at = memparse(p+1, &p); > + memblock_add(start_at, mem_size); > + } else if (*p == '#') { > + pr_err("\"memmap=nn#ss\" (force ACPI data) invalid on LoongArch\n"); > + return -EINVAL; > + } else if (*p == '$') { > + start_at = memparse(p+1, &p); > + memblock_add(start_at, mem_size); > + memblock_reserve(start_at, mem_size); > + } else { > + pr_err("\"memmap\" invalid format!\n"); > + return -EINVAL; > + } > + > + if (*p == '\0') { > + usermem = 1; > + return 0; > + } else > + return -EINVAL; > +} > +early_param("memmap", early_parse_memmap); > + > +static void __init check_kernel_sections_mem(void) > +{ > + phys_addr_t start = __pa_symbol(&_text); > + phys_addr_t size = __pa_symbol(&_end) - start; > + > + if (!memblock_is_region_memory(start, size)) { > + pr_info("Kernel sections are not in the memory maps\n"); > + memblock_add(start, size); > + } > +} > + > +static void __init bootcmdline_append(const char *s, size_t max) > +{ > + if (!s[0] || !max) > + return; > + > + if (boot_command_line[0]) > + strlcat(boot_command_line, " ", COMMAND_LINE_SIZE); > + > + strlcat(boot_command_line, s, max); > +} > + > +static void __init bootcmdline_init(char **cmdline_p) > +{ > + boot_command_line[0] = 0; > + > + /* > + * Take arguments from the bootloader at first. Early code should have > + * filled arcs_cmdline with arguments from the bootloader. > + */ > + bootcmdline_append(arcs_cmdline, COMMAND_LINE_SIZE); > + > + strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE); > + *cmdline_p = command_line; > + > + parse_early_param(); > +} > + > +/* > + * arch_mem_init - initialize memory management subsystem > + */ > +static void __init arch_mem_init(char **cmdline_p) > +{ > + if (usermem) > + pr_info("User-defined physical RAM map overwrite\n"); > + > + check_kernel_sections_mem(); > + > + memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0); > + pagetable_init(); > + > + /* > + * Prevent memblock from allocating high memory. > + * This cannot be done before max_low_pfn is detected, so up > + * to this point is possible to only reserve physical memory > + * with memblock_reserve; memblock_alloc* can be used > + * only after this point > + */ > + memblock_set_current_limit(PFN_PHYS(max_low_pfn)); > + > + /* > + * In order to reduce the possibility of kernel panic when failed to > + * get IO TLB memory under CONFIG_SWIOTLB, it is better to allocate > + * low memory as small as possible before plat_swiotlb_setup(), so > + * make sparse_init() using top-down allocation. > + */ > + memblock_set_bottom_up(false); > + sparse_init(); > + memblock_set_bottom_up(true); > + > + swiotlb_init(1); > + > + dma_contiguous_reserve(PFN_PHYS(max_low_pfn)); > + > + memblock_dump_all(); > + > + early_memtest(PFN_PHYS(ARCH_PFN_OFFSET), PFN_PHYS(max_low_pfn)); > +} > + > +static void __init resource_init(void) > +{ > + u64 i; > + phys_addr_t start, end; > + > + code_resource.start = __pa_symbol(&_text); > + code_resource.end = __pa_symbol(&_etext) - 1; > + data_resource.start = __pa_symbol(&_etext); > + data_resource.end = __pa_symbol(&_edata) - 1; > + bss_resource.start = __pa_symbol(&__bss_start); > + bss_resource.end = __pa_symbol(&__bss_stop) - 1; > + > + for_each_mem_range(i, &start, &end) { > + struct resource *res; > + > + res = memblock_alloc(sizeof(struct resource), SMP_CACHE_BYTES); > + if (!res) > + panic("%s: Failed to allocate %zu bytes\n", __func__, > + sizeof(struct resource)); > + > + res->start = start; > + /* > + * In memblock, end points to the first byte after the > + * range while in resourses, end points to the last byte in > + * the range. > + */ > + res->end = end - 1; > + res->flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY; > + res->name = "System RAM"; > + > + request_resource(&iomem_resource, res); > + > + /* > + * We don't know which RAM region contains kernel data, > + * so we try it repeatedly and let the resource manager > + * test it. > + */ > + request_resource(res, &code_resource); > + request_resource(res, &data_resource); > + request_resource(res, &bss_resource); > + } > +} > + > +void __init platform_init(void) > +{ > + efi_init(); > +#ifdef CONFIG_ACPI_TABLE_UPGRADE > + acpi_table_upgrade(); > +#endif > +#ifdef CONFIG_ACPI > + acpi_gbl_use_default_register_widths = false; > + acpi_boot_table_init(); > + acpi_boot_init(); > +#endif > + > + fw_init_memory(); > + dmi_setup(); > + smbios_parse(); > + pr_info("The BIOS Version: %s\n", b_info.bios_version); > + > + efi_runtime_init(); > +} > + > +void __init setup_arch(char **cmdline_p) > +{ > + cpu_probe(); > + > + fw_init_cmdline(); > + fw_init_environ(); > + early_memblock_init(); > + bootcmdline_init(cmdline_p); > + > + init_initrd(); > + platform_init(); > + finalize_initrd(); > + > + arch_mem_init(cmdline_p); > + > + resource_init(); > + > + paging_init(); > +} > + > +static int __init register_gop_device(void) > +{ > + void *pd; > + > + if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) > + return 0; > + pd = platform_device_register_data(NULL, "efi-framebuffer", 0, > + &screen_info, sizeof(screen_info)); > + return PTR_ERR_OR_ZERO(pd); > +} > +subsys_initcall(register_gop_device); > diff --git a/arch/loongarch/kernel/time.c b/arch/loongarch/kernel/time.c > new file mode 100644 > index 000000000000..2e00802a3742 > --- /dev/null > +++ b/arch/loongarch/kernel/time.c > @@ -0,0 +1,237 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Common time service routines for LoongArch machines. > + * > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > + */ > +#include <linux/clockchips.h> > +#include <linux/delay.h> > +#include <linux/export.h> > +#include <linux/init.h> > +#include <linux/interrupt.h> > +#include <linux/kernel.h> > +#include <linux/sched_clock.h> > +#include <linux/spinlock.h> > + > +#include <asm/cpu-features.h> > +#include <asm/loongarch.h> > +#include <asm/time.h> > + > +u64 cpu_clock_freq; > +EXPORT_SYMBOL(cpu_clock_freq); > +u64 const_clock_freq; > +EXPORT_SYMBOL(const_clock_freq); > + > +static DEFINE_RAW_SPINLOCK(state_lock); > +static DEFINE_PER_CPU(struct clock_event_device, constant_clockevent_device); > + > +static void constant_event_handler(struct clock_event_device *dev) > +{ > +} > + > +irqreturn_t constant_timer_interrupt(int irq, void *data) > +{ > + int cpu = smp_processor_id(); > + struct clock_event_device *cd; > + > + /* Clear Timer Interrupt */ > + write_csr_tintclear(CSR_TINTCLR_TI); > + cd = &per_cpu(constant_clockevent_device, cpu); > + cd->event_handler(cd); > + > + return IRQ_HANDLED; > +} > + > +static int constant_set_state_oneshot(struct clock_event_device *evt) > +{ > + unsigned long timer_config; > + > + raw_spin_lock(&state_lock); > + > + timer_config = csr_readq(LOONGARCH_CSR_TCFG); > + timer_config |= CSR_TCFG_EN; > + timer_config &= ~CSR_TCFG_PERIOD; > + csr_writeq(timer_config, LOONGARCH_CSR_TCFG); > + > + raw_spin_unlock(&state_lock); > + > + return 0; > +} > + > +static int constant_set_state_oneshot_stopped(struct clock_event_device *evt) > +{ > + unsigned long timer_config; > + > + raw_spin_lock(&state_lock); > + > + timer_config = csr_readq(LOONGARCH_CSR_TCFG); > + timer_config &= ~CSR_TCFG_EN; > + csr_writeq(timer_config, LOONGARCH_CSR_TCFG); > + > + raw_spin_unlock(&state_lock); > + > + return 0; > +} > + > +static int constant_set_state_periodic(struct clock_event_device *evt) > +{ > + unsigned long period; > + unsigned long timer_config; > + > + raw_spin_lock(&state_lock); > + > + period = const_clock_freq / HZ; > + timer_config = period & CSR_TCFG_VAL; > + timer_config |= (CSR_TCFG_PERIOD | CSR_TCFG_EN); > + csr_writeq(timer_config, LOONGARCH_CSR_TCFG); > + > + raw_spin_unlock(&state_lock); > + > + return 0; > +} > + > +static int constant_set_state_shutdown(struct clock_event_device *evt) > +{ > + return 0; > +} > + > +static int constant_timer_next_event(unsigned long delta, struct clock_event_device *evt) > +{ > + unsigned long timer_config; > + > + delta &= CSR_TCFG_VAL; > + timer_config = delta | CSR_TCFG_EN; > + csr_writeq(timer_config, LOONGARCH_CSR_TCFG); > + > + return 0; > +} > + > +static unsigned long __init get_loops_per_jiffy(void) > +{ > + unsigned long lpj = (unsigned long)const_clock_freq; > + > + do_div(lpj, HZ); > + > + return lpj; > +} > + > +static long init_timeval; > + > +void sync_counter(void) > +{ > + /* Ensure counter begin at 0 */ > + csr_writeq(-init_timeval, LOONGARCH_CSR_CNTC); > +} > + > +int constant_clockevent_init(void) > +{ > + unsigned int irq; > + unsigned int cpu = smp_processor_id(); > + unsigned long min_delta = 0x600; > + unsigned long max_delta = (1UL << 48) - 1; > + struct clock_event_device *cd; > + static int timer_irq_installed = 0; > + > + irq = get_timer_irq(); > + > + cd = &per_cpu(constant_clockevent_device, cpu); > + > + cd->name = "Constant"; > + cd->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_PERCPU; > + > + cd->irq = irq; > + cd->rating = 320; > + cd->cpumask = cpumask_of(cpu); > + cd->set_state_oneshot = constant_set_state_oneshot; > + cd->set_state_oneshot_stopped = constant_set_state_oneshot_stopped; > + cd->set_state_periodic = constant_set_state_periodic; > + cd->set_state_shutdown = constant_set_state_shutdown; > + cd->set_next_event = constant_timer_next_event; > + cd->event_handler = constant_event_handler; > + > + clockevents_config_and_register(cd, const_clock_freq, min_delta, max_delta); > + > + if (timer_irq_installed) > + return 0; > + > + timer_irq_installed = 1; > + > + sync_counter(); > + > + if (request_irq(irq, constant_timer_interrupt, IRQF_PERCPU | IRQF_TIMER, "timer", NULL)) > + pr_err("Failed to request irq %d (timer)\n", irq); > + > + lpj_fine = get_loops_per_jiffy(); > + pr_info("Constant clock event device register\n"); > + > + return 0; > +} > + > +static u64 read_const_counter(struct clocksource *clk) > +{ > + return drdtime(); > +} > + > +static struct clocksource clocksource_const = { > + .name = "Constant", > + .rating = 400, > + .read = read_const_counter, > + .mask = CLOCKSOURCE_MASK(64), > + .flags = CLOCK_SOURCE_IS_CONTINUOUS, > + .mult = 0, > + .shift = 10, > +}; > + > +unsigned long long notrace sched_clock(void) > +{ > + /* 64-bit arithmetic can overflow, so use 128-bit. */ > + u64 t1, t2, t3; > + unsigned long long rv; > + u64 mult = clocksource_const.mult; > + u64 shift = clocksource_const.shift; > + u64 cnt = read_const_counter(NULL); > + > + __asm__ ( > + "nor %[t1], $r0, %[shift] \n\t" > + "mulh.du %[t2], %[cnt], %[mult] \n\t" > + "mul.d %[t3], %[cnt], %[mult] \n\t" > + "slli.d %[t2], %[t2], 1 \n\t" > + "srl.d %[rv], %[t3], %[shift] \n\t" > + "sll.d %[t1], %[t2], %[t1] \n\t" > + "or %[rv], %[t1], %[rv] \n\t" > + : [rv] "=&r" (rv), [t1] "=&r" (t1), [t2] "=&r" (t2), [t3] "=&r" (t3) > + : [cnt] "r" (cnt), [mult] "r" (mult), [shift] "r" (shift) > + : ); > + > + return rv; > +} > + > +int __init constant_clocksource_init(void) > +{ > + int res; > + unsigned long freq; > + > + freq = const_clock_freq; > + > + clocksource_const.mult = > + clocksource_hz2mult(freq, clocksource_const.shift); > + > + res = clocksource_register_hz(&clocksource_const, freq); > + > + pr_info("Constant clock source device register\n"); > + > + return res; > +} > + > +void __init time_init(void) > +{ > + if (!cpu_has_cpucfg) > + const_clock_freq = cpu_clock_freq; > + else > + const_clock_freq = calc_const_freq(); > + > + init_timeval = drdtime() - csr_readq(LOONGARCH_CSR_CNTC); > + > + constant_clockevent_init(); > + constant_clocksource_init(); > +} > diff --git a/arch/loongarch/kernel/topology.c b/arch/loongarch/kernel/topology.c > new file mode 100644 > index 000000000000..3b2cbb95875b > --- /dev/null > +++ b/arch/loongarch/kernel/topology.c > @@ -0,0 +1,13 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#include <linux/cpu.h> > +#include <linux/init.h> > +#include <linux/percpu.h> > + > +static struct cpu cpu_device; > + > +static int __init topology_init(void) > +{ > + return register_cpu(&cpu_device, 0); > +} > + > +subsys_initcall(topology_init); > -- > 2.27.0 > ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH V4 09/22] LoongArch: Add boot and setup routines 2021-09-27 6:52 ` Ard Biesheuvel @ 2021-09-29 3:24 ` Huacai Chen 0 siblings, 0 replies; 3+ messages in thread From: Huacai Chen @ 2021-09-29 3:24 UTC (permalink / raw) To: Ard Biesheuvel Cc: Huacai Chen, Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton, David Airlie, Jonathan Corbet, Linus Torvalds, linux-arch, Linux Doc Mailing List, Linux Kernel Mailing List, Xuefeng Li, Yanteng Si, Jiaxun Yang, linux-efi Hi, Ard, On Mon, Sep 27, 2021 at 2:53 PM Ard Biesheuvel <ardb@kernel.org> wrote: > > On Mon, 27 Sept 2021 at 08:49, Huacai Chen <chenhuacai@loongson.cn> wrote: > > > > This patch adds basic boot, setup and reset routines for LoongArch. > > LoongArch uses UEFI-based firmware and uses ACPI as the boot protocol. > > > > Now the boot information passed to kernel is like this: > > 1, kernel get a0, a1 and a2 from bootloader. > > 2, a0 is "argc", a1 is "argc", so "cmdline" comes from a0/a1. > > 3, a2 is "envrion", which is a pointer to a "struct bootparamsinterface" > > 4, "struct bootparamsinterface" include a "systemtable" pointer, whose > > type is "efi_system_table_t". Most information, include ACPI tables, > > come from here. > > > > Could you please quote the relevant sections of the UEFI and ACPI > specifications where this boot protocol is described? There is no UEFI/ACPI spec. sections to describe this. Maybe I misused the "boot protocol" terminology here, we use UEFI firmware to load the elf format kernel (not efistub), the above just describe how to pass information to the kernel. Huacai > > > Cc: Ard Biesheuvel <ardb@kernel.org> > > Cc: linux-efi@vger.kernel.org > > Signed-off-by: Huacai Chen <chenhuacai@loongson.cn> > > --- > > arch/loongarch/include/asm/acenv.h | 17 + > > arch/loongarch/include/asm/acpi.h | 37 ++ > > arch/loongarch/include/asm/boot_param.h | 91 +++++ > > arch/loongarch/include/asm/bootinfo.h | 33 ++ > > arch/loongarch/include/asm/dmi.h | 24 ++ > > arch/loongarch/include/asm/efi.h | 32 ++ > > arch/loongarch/include/asm/fw.h | 18 + > > arch/loongarch/include/asm/reboot.h | 10 + > > arch/loongarch/include/asm/setup.h | 21 ++ > > arch/loongarch/kernel/acpi.c | 324 ++++++++++++++++ > > arch/loongarch/kernel/cacheinfo.c | 122 ++++++ > > arch/loongarch/kernel/cmdline.c | 28 ++ > > arch/loongarch/kernel/cpu-probe.c | 301 +++++++++++++++ > > arch/loongarch/kernel/efi.c | 95 +++++ > > arch/loongarch/kernel/env.c | 170 +++++++++ > > arch/loongarch/kernel/head.S | 72 ++++ > > arch/loongarch/kernel/mem.c | 83 +++++ > > arch/loongarch/kernel/reset.c | 90 +++++ > > arch/loongarch/kernel/setup.c | 469 ++++++++++++++++++++++++ > > arch/loongarch/kernel/time.c | 237 ++++++++++++ > > arch/loongarch/kernel/topology.c | 13 + > > 21 files changed, 2287 insertions(+) > > create mode 100644 arch/loongarch/include/asm/acenv.h > > create mode 100644 arch/loongarch/include/asm/acpi.h > > create mode 100644 arch/loongarch/include/asm/boot_param.h > > create mode 100644 arch/loongarch/include/asm/bootinfo.h > > create mode 100644 arch/loongarch/include/asm/dmi.h > > create mode 100644 arch/loongarch/include/asm/efi.h > > create mode 100644 arch/loongarch/include/asm/fw.h > > create mode 100644 arch/loongarch/include/asm/reboot.h > > create mode 100644 arch/loongarch/include/asm/setup.h > > create mode 100644 arch/loongarch/kernel/acpi.c > > create mode 100644 arch/loongarch/kernel/cacheinfo.c > > create mode 100644 arch/loongarch/kernel/cmdline.c > > create mode 100644 arch/loongarch/kernel/cpu-probe.c > > create mode 100644 arch/loongarch/kernel/efi.c > > create mode 100644 arch/loongarch/kernel/env.c > > create mode 100644 arch/loongarch/kernel/head.S > > create mode 100644 arch/loongarch/kernel/mem.c > > create mode 100644 arch/loongarch/kernel/reset.c > > create mode 100644 arch/loongarch/kernel/setup.c > > create mode 100644 arch/loongarch/kernel/time.c > > create mode 100644 arch/loongarch/kernel/topology.c > > > > diff --git a/arch/loongarch/include/asm/acenv.h b/arch/loongarch/include/asm/acenv.h > > new file mode 100644 > > index 000000000000..d1f41fbc9070 > > --- /dev/null > > +++ b/arch/loongarch/include/asm/acenv.h > > @@ -0,0 +1,17 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * LoongArch specific ACPICA environments and implementation > > + * > > + * Author: Jianmin Lv <lvjianmin@loongson.cn> > > + * Huacai Chen <chenhuacai@loongson.cn> > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > + > > +#ifndef _ASM_LOONGARCH_ACENV_H > > +#define _ASM_LOONGARCH_ACENV_H > > + > > +/* The head file is required by ACPI core, but we have nothing to fill > > + * it now, update it later when needed. > > + */ > > + > > +#endif /* _ASM_LOONGARCH_ACENV_H */ > > diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h > > new file mode 100644 > > index 000000000000..29e2214d5901 > > --- /dev/null > > +++ b/arch/loongarch/include/asm/acpi.h > > @@ -0,0 +1,37 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * Author: Jianmin Lv <lvjianmin@loongson.cn> > > + * Huacai Chen <chenhuacai@loongson.cn> > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > + > > +#ifndef _ASM_LOONGARCH_ACPI_H > > +#define _ASM_LOONGARCH_ACPI_H > > + > > +#ifdef CONFIG_ACPI > > +extern int acpi_strict; > > +extern int acpi_disabled; > > +extern int acpi_pci_disabled; > > +extern int acpi_noirq; > > + > > +#define acpi_os_ioremap ioremap > > + > > +static inline void disable_acpi(void) > > +{ > > + acpi_disabled = 1; > > + acpi_pci_disabled = 1; > > + acpi_noirq = 1; > > +} > > + > > +static inline bool acpi_has_cpu_in_madt(void) > > +{ > > + return true; > > +} > > + > > +extern struct list_head acpi_wakeup_device_list; > > + > > +#endif /* !CONFIG_ACPI */ > > + > > +#define ACPI_TABLE_UPGRADE_MAX_PHYS (max_low_pfn << PAGE_SHIFT) > > + > > +#endif /* _ASM_LOONGARCH_ACPI_H */ > > diff --git a/arch/loongarch/include/asm/boot_param.h b/arch/loongarch/include/asm/boot_param.h > > new file mode 100644 > > index 000000000000..f515431d838b > > --- /dev/null > > +++ b/arch/loongarch/include/asm/boot_param.h > > @@ -0,0 +1,91 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +#ifndef __ASM_BOOT_PARAM_H_ > > +#define __ASM_BOOT_PARAM_H_ > > + > > +#ifdef CONFIG_VT > > +#include <linux/screen_info.h> > > +#endif > > + > > +#define ADDRESS_TYPE_SYSRAM 1 > > +#define ADDRESS_TYPE_RESERVED 2 > > +#define ADDRESS_TYPE_ACPI 3 > > +#define ADDRESS_TYPE_NVS 4 > > +#define ADDRESS_TYPE_PMEM 5 > > + > > +#define LOONGSON3_BOOT_MEM_MAP_MAX 128 > > + > > +#define LOONGSON_EFIBOOT_SIGNATURE "BPI" > > +#define LOONGSON_MEM_LINKLIST "MEM" > > +#define LOONGSON_VBIOS_LINKLIST "VBIOS" > > +#define LOONGSON_SCREENINFO_LINKLIST "SINFO" > > + > > +/* Values for Version BPI */ > > +enum bpi_version { > > + BPI_VERSION_V1 = 1000, /* Signature="BPI01000" */ > > + BPI_VERSION_V2 = 1001, /* Signature="BPI01001" */ > > +}; > > + > > +/* Flags in bootparamsinterface */ > > +#define BPI_FLAGS_UEFI_SUPPORTED BIT(0) > > + > > +struct _extention_list_hdr { > > + u64 signature; > > + u32 length; > > + u8 revision; > > + u8 checksum; > > + struct _extention_list_hdr *next; > > +} __packed; > > + > > +struct bootparamsinterface { > > + u64 signature; /* {"B", "P", "I", "0", "1", ... } */ > > + void *systemtable; > > + struct _extention_list_hdr *extlist; > > + u64 flags; > > +} __packed; > > + > > +struct loongsonlist_mem_map { > > + struct _extention_list_hdr header; /* {"M", "E", "M"} */ > > + u8 map_count; > > + struct loongson_mem_map { > > + u32 mem_type; > > + u64 mem_start; > > + u64 mem_size; > > + } __packed map[LOONGSON3_BOOT_MEM_MAP_MAX]; > > +} __packed; > > + > > +struct loongsonlist_vbios { > > + struct _extention_list_hdr header; /* {"V", "B", "I", "O", "S"} */ > > + u64 vbios_addr; > > +} __packed; > > + > > +struct loongsonlist_screeninfo { > > + struct _extention_list_hdr header; /* {"S", "I", "N", "F", "O"} */ > > + struct screen_info si; > > +} __packed; > > + > > +struct loongson_board_info { > > + int bios_size; > > + char *bios_vendor; > > + char *bios_version; > > + char *bios_release_date; > > + char *board_name; > > + char *board_vendor; > > +}; > > + > > +struct loongson_system_configuration { > > + int bpi_ver; > > + int nr_cpus; > > + int nr_nodes; > > + int nr_io_pics; > > + int boot_cpu_id; > > + int cores_per_node; > > + int cores_per_package; > > + char *cpuname; > > + u64 vgabios_addr; > > +}; > > + > > +extern struct loongson_board_info b_info; > > +extern struct bootparamsinterface *efi_bp; > > +extern struct loongsonlist_mem_map *loongson_mem_map; > > +extern struct loongson_system_configuration loongson_sysconf; > > +#endif > > diff --git a/arch/loongarch/include/asm/bootinfo.h b/arch/loongarch/include/asm/bootinfo.h > > new file mode 100644 > > index 000000000000..41838633d1aa > > --- /dev/null > > +++ b/arch/loongarch/include/asm/bootinfo.h > > @@ -0,0 +1,33 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > +#ifndef _ASM_BOOTINFO_H > > +#define _ASM_BOOTINFO_H > > + > > +#include <linux/types.h> > > +#include <asm/setup.h> > > + > > +const char *get_system_type(void); > > + > > +extern void early_memblock_init(void); > > +extern void detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_addr_t sz_max); > > + > > +extern void early_init(void); > > +extern void platform_init(void); > > + > > +extern void free_init_pages(const char *what, unsigned long begin, unsigned long end); > > + > > +/* > > + * Initial kernel command line, usually setup by fw_init_cmdline() > > + */ > > +extern char arcs_cmdline[COMMAND_LINE_SIZE]; > > + > > +/* > > + * Registers a0, a1, a3 and a4 as passed to the kernel entry by firmware > > + */ > > +extern unsigned long fw_arg0, fw_arg1, fw_arg2, fw_arg3; > > + > > +extern unsigned long initrd_start, initrd_end; > > + > > +#endif /* _ASM_BOOTINFO_H */ > > diff --git a/arch/loongarch/include/asm/dmi.h b/arch/loongarch/include/asm/dmi.h > > new file mode 100644 > > index 000000000000..fbe98f0e56e0 > > --- /dev/null > > +++ b/arch/loongarch/include/asm/dmi.h > > @@ -0,0 +1,24 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > +#ifndef _ASM_DMI_H > > +#define _ASM_DMI_H > > + > > +#include <linux/io.h> > > +#include <linux/memblock.h> > > + > > +#define dmi_early_remap(x, l) dmi_remap(x, l) > > +#define dmi_early_unmap(x, l) dmi_unmap(x) > > +#define dmi_alloc(l) memblock_alloc_low(l, PAGE_SIZE) > > + > > +static inline void *dmi_remap(u64 phys_addr, unsigned long size) > > +{ > > + return ((void *)TO_CAC(phys_addr)); > > +} > > + > > +static inline void dmi_unmap(void *addr) > > +{ > > +} > > + > > +#endif /* _ASM_DMI_H */ > > diff --git a/arch/loongarch/include/asm/efi.h b/arch/loongarch/include/asm/efi.h > > new file mode 100644 > > index 000000000000..90e8e35d7f4b > > --- /dev/null > > +++ b/arch/loongarch/include/asm/efi.h > > @@ -0,0 +1,32 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > +#ifndef _ASM_LOONGARCH_EFI_H > > +#define _ASM_LOONGARCH_EFI_H > > + > > +#include <linux/efi.h> > > + > > +extern void __init efi_init(void); > > +extern void __init efi_runtime_init(void); > > +extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt); > > + > > +#define ARCH_EFI_IRQ_FLAGS_MASK 0x00000001 /*bit0: CP0 Status.IE*/ > > + > > +#define arch_efi_call_virt_setup() \ > > +({ \ > > +}) > > + > > +#define arch_efi_call_virt(p, f, args...) \ > > +({ \ > > + efi_##f##_t * __f; \ > > + __f = p->f; \ > > + __f(args); \ > > +}) > > + > > +#define arch_efi_call_virt_teardown() \ > > +({ \ > > +}) > > + > > + > > +#endif /* _ASM_LOONGARCH_EFI_H */ > > diff --git a/arch/loongarch/include/asm/fw.h b/arch/loongarch/include/asm/fw.h > > new file mode 100644 > > index 000000000000..8a0e8e7c625f > > --- /dev/null > > +++ b/arch/loongarch/include/asm/fw.h > > @@ -0,0 +1,18 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > +#ifndef __ASM_FW_H_ > > +#define __ASM_FW_H_ > > + > > +#include <asm/bootinfo.h> > > + > > +extern int fw_argc; > > +extern long *_fw_argv, *_fw_envp; > > + > > +#define fw_argv(index) ((char *)(long)_fw_argv[(index)]) > > +#define fw_envp(index) ((char *)(long)_fw_envp[(index)]) > > + > > +extern void fw_init_cmdline(void); > > + > > +#endif /* __ASM_FW_H_ */ > > diff --git a/arch/loongarch/include/asm/reboot.h b/arch/loongarch/include/asm/reboot.h > > new file mode 100644 > > index 000000000000..0e96b0f42993 > > --- /dev/null > > +++ b/arch/loongarch/include/asm/reboot.h > > @@ -0,0 +1,10 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > +#ifndef _ASM_REBOOT_H > > +#define _ASM_REBOOT_H > > + > > +extern void (*pm_restart)(void); > > + > > +#endif /* _ASM_REBOOT_H */ > > diff --git a/arch/loongarch/include/asm/setup.h b/arch/loongarch/include/asm/setup.h > > new file mode 100644 > > index 000000000000..fc1fb741b4da > > --- /dev/null > > +++ b/arch/loongarch/include/asm/setup.h > > @@ -0,0 +1,21 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > + > > +#ifndef _LOONGARCH_SETUP_H > > +#define _LOONGARCH_SETUP_H > > + > > +#include <linux/types.h> > > +#include <uapi/asm/setup.h> > > + > > +#define VECSIZE 0x200 > > + > > +extern unsigned long eentry; > > +extern unsigned long tlbrentry; > > +extern void cpu_cache_init(void); > > +extern void per_cpu_trap_init(int cpu); > > +extern void set_handler(unsigned long offset, void *addr, unsigned long len); > > +extern void set_merr_handler(unsigned long offset, void *addr, unsigned long len); > > + > > +#endif /* __SETUP_H */ > > diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c > > new file mode 100644 > > index 000000000000..3f77c937e9ad > > --- /dev/null > > +++ b/arch/loongarch/kernel/acpi.c > > @@ -0,0 +1,324 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * acpi.c - Architecture-Specific Low-Level ACPI Boot Support > > + * > > + * Author: Jianmin Lv <lvjianmin@loongson.cn> > > + * Huacai Chen <chenhuacai@loongson.cn> > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > + > > +#include <linux/init.h> > > +#include <linux/acpi.h> > > +#include <linux/irq.h> > > +#include <linux/irqdomain.h> > > +#include <linux/memblock.h> > > +#include <linux/serial_core.h> > > +#include <asm/io.h> > > +#include <asm/loongson.h> > > + > > +int acpi_disabled; > > +EXPORT_SYMBOL(acpi_disabled); > > +int acpi_noirq; > > +int acpi_pci_disabled; > > +EXPORT_SYMBOL(acpi_pci_disabled); > > +int acpi_strict = 1; /* We have no workarounds on LoongArch */ > > +int num_processors; > > +int disabled_cpus; > > +enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM; > > + > > +u64 acpi_saved_sp; > > + > > +#define MAX_CORE_PIC 256 > > + > > +#define PREFIX "ACPI: " > > + > > +/* > > + * Following __acpi_xx functions should be implemented for sepecific cpu. > > + */ > > +int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp) > > +{ > > + if (irqp != NULL) > > + *irqp = acpi_register_gsi(NULL, gsi, -1, -1); > > + return 0; > > +} > > +EXPORT_SYMBOL_GPL(acpi_gsi_to_irq); > > + > > +int acpi_isa_irq_to_gsi(unsigned int isa_irq, u32 *gsi) > > +{ > > + if (gsi) > > + *gsi = isa_irq; > > + return 0; > > +} > > + > > +/* > > + * success: return IRQ number (>=0) > > + * failure: return < 0 > > + */ > > +int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity) > > +{ > > + int id, hwirq; > > + struct irq_domain *domain; > > + struct irq_fwspec fwspec; > > + > > + switch (gsi) { > > + case GSI_MIN_CPU_IRQ ... GSI_MAX_CPU_IRQ: > > + hwirq = gsi - GSI_MIN_CPU_IRQ; > > + return irq_create_mapping(liointc_domain, hwirq); > > + > > + case GSI_MIN_PCH_IRQ ... GSI_MAX_PCH_IRQ: > > + id = find_pch_pic(gsi); > > + if (id < 0) > > + return -1; > > + > > + domain = pch_pic_domain[id]; > > + if (!domain) > > + return gsi; > > + > > + hwirq = gsi - acpi_pchpic[id]->gsi_base; > > + fwspec.param_count = 2; > > + fwspec.param[0] = hwirq; > > + fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; > > + > > + return irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, &fwspec); > > + } > > + > > + return -1; > > +} > > +EXPORT_SYMBOL_GPL(acpi_register_gsi); > > + > > +void acpi_unregister_gsi(u32 gsi) > > +{ > > + > > +} > > +EXPORT_SYMBOL_GPL(acpi_unregister_gsi); > > + > > +void __iomem *__init __acpi_map_table(unsigned long phys, unsigned long size) > > +{ > > + > > + if (!phys || !size) > > + return NULL; > > + > > + return early_memremap(phys, size); > > +} > > +void __init __acpi_unmap_table(void __iomem *map, unsigned long size) > > +{ > > + if (!map || !size) > > + return; > > + > > + early_memunmap(map, size); > > +} > > + > > +void __init acpi_boot_table_init(void) > > +{ > > + /* > > + * If acpi_disabled, bail out > > + */ > > + if (acpi_disabled) > > + return; > > + > > + /* > > + * Initialize the ACPI boot-time table parser. > > + */ > > + if (acpi_table_init()) { > > + disable_acpi(); > > + return; > > + } > > +} > > + > > +static int __init > > +acpi_parse_cpuintc(union acpi_subtable_headers *header, const unsigned long end) > > +{ > > + struct acpi_madt_core_pic *processor = NULL; > > + > > + processor = (struct acpi_madt_core_pic *)header; > > + if (BAD_MADT_ENTRY(processor, end)) > > + return -EINVAL; > > + > > + acpi_table_print_madt_entry(&header->common); > > + > > + return 0; > > +} > > + > > +static int __init > > +acpi_parse_liointc(union acpi_subtable_headers *header, const unsigned long end) > > +{ > > + struct acpi_madt_lio_pic *liointc = NULL; > > + > > + liointc = (struct acpi_madt_lio_pic *)header; > > + > > + if (BAD_MADT_ENTRY(liointc, end)) > > + return -EINVAL; > > + > > + acpi_liointc = liointc; > > + > > + return 0; > > +} > > + > > +static int __init > > +acpi_parse_eiointc(union acpi_subtable_headers *header, const unsigned long end) > > +{ > > + static int id = 0; > > + struct acpi_madt_eio_pic *eiointc = NULL; > > + > > + eiointc = (struct acpi_madt_eio_pic *)header; > > + > > + if (BAD_MADT_ENTRY(eiointc, end)) > > + return -EINVAL; > > + > > + acpi_eiointc[id++] = eiointc; > > + loongson_sysconf.nr_io_pics = id; > > + > > + return 0; > > +} > > + > > +static int __init > > +acpi_parse_htintc(union acpi_subtable_headers *header, const unsigned long end) > > +{ > > + struct acpi_madt_ht_pic *htintc = NULL; > > + > > + htintc = (struct acpi_madt_ht_pic *)header; > > + > > + if (BAD_MADT_ENTRY(htintc, end)) > > + return -EINVAL; > > + > > + acpi_htintc = htintc; > > + loongson_sysconf.nr_io_pics = 1; > > + > > + return 0; > > +} > > + > > +static int __init > > +acpi_parse_pch_pic(union acpi_subtable_headers *header, const unsigned long end) > > +{ > > + static int id = 0; > > + struct acpi_madt_bio_pic *pchpic = NULL; > > + > > + pchpic = (struct acpi_madt_bio_pic *)header; > > + > > + if (BAD_MADT_ENTRY(pchpic, end)) > > + return -EINVAL; > > + > > + acpi_pchpic[id++] = pchpic; > > + > > + return 0; > > +} > > + > > +static int __init > > +acpi_parse_pch_msi(union acpi_subtable_headers *header, const unsigned long end) > > +{ > > + static int id = 0; > > + struct acpi_madt_msi_pic *pchmsi = NULL; > > + > > + pchmsi = (struct acpi_madt_msi_pic *)header; > > + > > + if (BAD_MADT_ENTRY(pchmsi, end)) > > + return -EINVAL; > > + > > + acpi_pchmsi[id++] = pchmsi; > > + > > + return 0; > > +} > > + > > +static int __init > > +acpi_parse_pch_lpc(union acpi_subtable_headers *header, const unsigned long end) > > +{ > > + struct acpi_madt_lpc_pic *pchlpc = NULL; > > + > > + pchlpc = (struct acpi_madt_lpc_pic *)header; > > + > > + if (BAD_MADT_ENTRY(pchlpc, end)) > > + return -EINVAL; > > + > > + acpi_pchlpc = pchlpc; > > + > > + return 0; > > +} > > + > > +static void __init acpi_process_madt(void) > > +{ > > + int error; > > + > > + /* Parse MADT CPUINTC entries */ > > + error = acpi_table_parse_madt(ACPI_MADT_TYPE_CORE_PIC, acpi_parse_cpuintc, MAX_CORE_PIC); > > + if (error < 0) { > > + disable_acpi(); > > + pr_err(PREFIX "Invalid BIOS MADT (CPUINTC entries), ACPI disabled\n"); > > + return; > > + } > > + > > + loongson_sysconf.nr_cpus = num_processors; > > + > > + /* Parse MADT LIOINTC entries */ > > + error = acpi_table_parse_madt(ACPI_MADT_TYPE_LIO_PIC, acpi_parse_liointc, 1); > > + if (error < 0) { > > + disable_acpi(); > > + pr_err(PREFIX "Invalid BIOS MADT (LIOINTC entries), ACPI disabled\n"); > > + return; > > + } > > + > > + /* Parse MADT EIOINTC entries */ > > + error = acpi_table_parse_madt(ACPI_MADT_TYPE_EIO_PIC, acpi_parse_eiointc, MAX_IO_PICS); > > + if (error < 0) { > > + disable_acpi(); > > + pr_err(PREFIX "Invalid BIOS MADT (EIOINTC entries), ACPI disabled\n"); > > + return; > > + } > > + > > + /* Parse MADT HTVEC entries */ > > + error = acpi_table_parse_madt(ACPI_MADT_TYPE_HT_PIC, acpi_parse_htintc, 1); > > + if (error < 0) { > > + disable_acpi(); > > + pr_err(PREFIX "Invalid BIOS MADT (HTVEC entries), ACPI disabled\n"); > > + return; > > + } > > + > > + /* Parse MADT PCHPIC entries */ > > + error = acpi_table_parse_madt(ACPI_MADT_TYPE_BIO_PIC, acpi_parse_pch_pic, MAX_IO_PICS); > > + if (error < 0) { > > + disable_acpi(); > > + pr_err(PREFIX "Invalid BIOS MADT (PCHPIC entries), ACPI disabled\n"); > > + return; > > + } > > + > > + /* Parse MADT PCHMSI entries */ > > + error = acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, acpi_parse_pch_msi, MAX_IO_PICS); > > + if (error < 0) { > > + disable_acpi(); > > + pr_err(PREFIX "Invalid BIOS MADT (PCHMSI entries), ACPI disabled\n"); > > + return; > > + } > > + > > + /* Parse MADT PCHLPC entries */ > > + error = acpi_table_parse_madt(ACPI_MADT_TYPE_LPC_PIC, acpi_parse_pch_lpc, 1); > > + if (error < 0) { > > + disable_acpi(); > > + pr_err(PREFIX "Invalid BIOS MADT (PCHLPC entries), ACPI disabled\n"); > > + return; > > + } > > +} > > + > > +int __init acpi_boot_init(void) > > +{ > > + /* > > + * If acpi_disabled, bail out > > + */ > > + if (acpi_disabled) > > + return -1; > > + > > + loongson_sysconf.boot_cpu_id = read_csr_cpuid(); > > + > > + /* > > + * Process the Multiple APIC Description Table (MADT), if present > > + */ > > + acpi_process_madt(); > > + > > + /* Do not enable ACPI SPCR console by default */ > > + acpi_parse_spcr(earlycon_acpi_spcr_enable, false); > > + > > + return 0; > > +} > > + > > +void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size) > > +{ > > + memblock_mark_nomap(addr, size); > > +} > > diff --git a/arch/loongarch/kernel/cacheinfo.c b/arch/loongarch/kernel/cacheinfo.c > > new file mode 100644 > > index 000000000000..bedeb3eb1157 > > --- /dev/null > > +++ b/arch/loongarch/kernel/cacheinfo.c > > @@ -0,0 +1,122 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * LoongArch cacheinfo support > > + * > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > +#include <linux/cacheinfo.h> > > + > > +/* Populates leaf and increments to next leaf */ > > +#define populate_cache(cache, leaf, c_level, c_type) \ > > +do { \ > > + leaf->type = c_type; \ > > + leaf->level = c_level; \ > > + leaf->coherency_line_size = c->cache.linesz; \ > > + leaf->number_of_sets = c->cache.sets; \ > > + leaf->ways_of_associativity = c->cache.ways; \ > > + leaf->size = c->cache.linesz * c->cache.sets * \ > > + c->cache.ways; \ > > + leaf++; \ > > +} while (0) > > + > > +int init_cache_level(unsigned int cpu) > > +{ > > + struct cpuinfo_loongarch *c = ¤t_cpu_data; > > + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); > > + int levels = 0, leaves = 0; > > + > > + /* > > + * If Dcache is not set, we assume the cache structures > > + * are not properly initialized. > > + */ > > + if (c->dcache.waysize) > > + levels += 1; > > + else > > + return -ENOENT; > > + > > + > > + leaves += (c->icache.waysize) ? 2 : 1; > > + > > + if (c->vcache.waysize) { > > + levels++; > > + leaves++; > > + } > > + > > + if (c->scache.waysize) { > > + levels++; > > + leaves++; > > + } > > + > > + if (c->tcache.waysize) { > > + levels++; > > + leaves++; > > + } > > + > > + this_cpu_ci->num_levels = levels; > > + this_cpu_ci->num_leaves = leaves; > > + return 0; > > +} > > + > > +static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, > > + struct cacheinfo *sib_leaf) > > +{ > > + return !((this_leaf->level == 1) || (this_leaf->level == 2)); > > +} > > + > > +static void cache_cpumap_setup(unsigned int cpu) > > +{ > > + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); > > + struct cacheinfo *this_leaf, *sib_leaf; > > + unsigned int index; > > + > > + for (index = 0; index < this_cpu_ci->num_leaves; index++) { > > + unsigned int i; > > + > > + this_leaf = this_cpu_ci->info_list + index; > > + /* skip if shared_cpu_map is already populated */ > > + if (!cpumask_empty(&this_leaf->shared_cpu_map)) > > + continue; > > + > > + cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map); > > + for_each_online_cpu(i) { > > + struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i); > > + > > + if (i == cpu || !sib_cpu_ci->info_list) > > + continue;/* skip if itself or no cacheinfo */ > > + sib_leaf = sib_cpu_ci->info_list + index; > > + if (cache_leaves_are_shared(this_leaf, sib_leaf)) { > > + cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map); > > + cpumask_set_cpu(i, &this_leaf->shared_cpu_map); > > + } > > + } > > + } > > +} > > + > > +int populate_cache_leaves(unsigned int cpu) > > +{ > > + int level = 1; > > + struct cpuinfo_loongarch *c = ¤t_cpu_data; > > + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); > > + struct cacheinfo *this_leaf = this_cpu_ci->info_list; > > + > > + if (c->icache.waysize) { > > + populate_cache(dcache, this_leaf, level, CACHE_TYPE_DATA); > > + populate_cache(icache, this_leaf, level++, CACHE_TYPE_INST); > > + } else { > > + populate_cache(dcache, this_leaf, level++, CACHE_TYPE_UNIFIED); > > + } > > + > > + if (c->vcache.waysize) > > + populate_cache(vcache, this_leaf, level++, CACHE_TYPE_UNIFIED); > > + > > + if (c->scache.waysize) > > + populate_cache(scache, this_leaf, level++, CACHE_TYPE_UNIFIED); > > + > > + if (c->tcache.waysize) > > + populate_cache(tcache, this_leaf, level++, CACHE_TYPE_UNIFIED); > > + > > + cache_cpumap_setup(cpu); > > + this_cpu_ci->cpu_map_populated = true; > > + > > + return 0; > > +} > > diff --git a/arch/loongarch/kernel/cmdline.c b/arch/loongarch/kernel/cmdline.c > > new file mode 100644 > > index 000000000000..46bf4486b54c > > --- /dev/null > > +++ b/arch/loongarch/kernel/cmdline.c > > @@ -0,0 +1,28 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > +#include <linux/init.h> > > +#include <linux/kernel.h> > > +#include <linux/string.h> > > + > > +#include <asm/fw.h> > > + > > +int fw_argc; > > +long *_fw_argv, *_fw_envp; > > + > > +void __init fw_init_cmdline(void) > > +{ > > + int i; > > + > > + fw_argc = fw_arg0; > > + _fw_argv = (long *)fw_arg1; > > + _fw_envp = (long *)fw_arg2; > > + > > + arcs_cmdline[0] = '\0'; > > + for (i = 1; i < fw_argc; i++) { > > + strlcat(arcs_cmdline, fw_argv(i), COMMAND_LINE_SIZE); > > + if (i < (fw_argc - 1)) > > + strlcat(arcs_cmdline, " ", COMMAND_LINE_SIZE); > > + } > > +} > > diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c > > new file mode 100644 > > index 000000000000..acf7eb19f75d > > --- /dev/null > > +++ b/arch/loongarch/kernel/cpu-probe.c > > @@ -0,0 +1,301 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Processor capabilities determination functions. > > + * > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > +#include <linux/init.h> > > +#include <linux/kernel.h> > > +#include <linux/ptrace.h> > > +#include <linux/smp.h> > > +#include <linux/stddef.h> > > +#include <linux/export.h> > > +#include <linux/printk.h> > > +#include <linux/uaccess.h> > > + > > +#include <asm/cpu-features.h> > > +#include <asm/elf.h> > > +#include <asm/fpu.h> > > +#include <asm/loongarch.h> > > +#include <asm/pgtable-bits.h> > > +#include <asm/setup.h> > > + > > +/* Hardware capabilities */ > > +unsigned int elf_hwcap __read_mostly; > > +EXPORT_SYMBOL_GPL(elf_hwcap); > > + > > +/* > > + * Determine the FCSR mask for FPU hardware. > > + */ > > +static inline void cpu_set_fpu_fcsr_mask(struct cpuinfo_loongarch *c) > > +{ > > + unsigned long sr, mask, fcsr, fcsr0, fcsr1; > > + > > + fcsr = c->fpu_csr0; > > + mask = FPU_CSR_ALL_X | FPU_CSR_ALL_E | FPU_CSR_ALL_S | FPU_CSR_RM; > > + > > + sr = read_csr_euen(); > > + enable_fpu(); > > + > > + fcsr0 = fcsr & mask; > > + write_fcsr(LOONGARCH_FCSR0, fcsr0); > > + fcsr0 = read_fcsr(LOONGARCH_FCSR0); > > + > > + fcsr1 = fcsr | ~mask; > > + write_fcsr(LOONGARCH_FCSR0, fcsr1); > > + fcsr1 = read_fcsr(LOONGARCH_FCSR0); > > + > > + write_fcsr(LOONGARCH_FCSR0, fcsr); > > + > > + write_csr_euen(sr); > > + > > + c->fpu_mask = ~(fcsr0 ^ fcsr1) & ~mask; > > +} > > + > > +static inline void set_elf_platform(int cpu, const char *plat) > > +{ > > + if (cpu == 0) > > + __elf_platform = plat; > > +} > > + > > +/* MAP BASE */ > > +unsigned long vm_map_base; > > +EXPORT_SYMBOL_GPL(vm_map_base); > > + > > +static void cpu_probe_addrbits(struct cpuinfo_loongarch *c) > > +{ > > +#ifdef __NEED_ADDRBITS_PROBE > > + c->pabits = (read_cpucfg(LOONGARCH_CPUCFG1) & CPUCFG1_PABITS) >> 4; > > + c->vabits = (read_cpucfg(LOONGARCH_CPUCFG1) & CPUCFG1_VABITS) >> 12; > > + vm_map_base = 0UL - (1UL << c->vabits); > > +#endif > > +} > > + > > +static void set_isa(struct cpuinfo_loongarch *c, unsigned int isa) > > +{ > > + switch (isa) { > > + case LOONGARCH_CPU_ISA_LA64: > > + c->isa_level |= LOONGARCH_CPU_ISA_LA64; > > + fallthrough; > > + case LOONGARCH_CPU_ISA_LA32S: > > + c->isa_level |= LOONGARCH_CPU_ISA_LA32S; > > + fallthrough; > > + case LOONGARCH_CPU_ISA_LA32R: > > + c->isa_level |= LOONGARCH_CPU_ISA_LA32R; > > + break; > > + } > > +} > > + > > +static void cpu_probe_common(struct cpuinfo_loongarch *c) > > +{ > > + unsigned int config; > > + unsigned long asid_mask; > > + > > + c->options = LOONGARCH_CPU_CPUCFG | LOONGARCH_CPU_CSR | > > + LOONGARCH_CPU_TLB | LOONGARCH_CPU_VINT | LOONGARCH_CPU_WATCH; > > + > > + elf_hwcap |= HWCAP_LOONGARCH_CRC32; > > + > > + config = read_cpucfg(LOONGARCH_CPUCFG1); > > + if (config & CPUCFG1_UAL) { > > + c->options |= LOONGARCH_CPU_UAL; > > + elf_hwcap |= HWCAP_LOONGARCH_UAL; > > + } > > + > > + config = read_cpucfg(LOONGARCH_CPUCFG2); > > + if (config & CPUCFG2_LAM) { > > + c->options |= LOONGARCH_CPU_LAM; > > + elf_hwcap |= HWCAP_LOONGARCH_LAM; > > + } > > + if (config & CPUCFG2_FP) { > > + c->options |= LOONGARCH_CPU_FPU; > > + elf_hwcap |= HWCAP_LOONGARCH_FPU; > > + } > > + if (config & CPUCFG2_COMPLEX) { > > + c->options |= LOONGARCH_CPU_COMPLEX; > > + elf_hwcap |= HWCAP_LOONGARCH_COMPLEX; > > + } > > + if (config & CPUCFG2_CRYPTO) { > > + c->options |= LOONGARCH_CPU_CRYPTO; > > + elf_hwcap |= HWCAP_LOONGARCH_CRYPTO; > > + } > > + if (config & CPUCFG2_LVZP) { > > + c->options |= LOONGARCH_CPU_LVZ; > > + elf_hwcap |= HWCAP_LOONGARCH_LVZ; > > + } > > + > > + config = read_cpucfg(LOONGARCH_CPUCFG6); > > + if (config & CPUCFG6_PMP) > > + c->options |= LOONGARCH_CPU_PMP; > > + > > + config = iocsr_readl(LOONGARCH_IOCSR_FEATURES); > > + if (config & IOCSRF_CSRIPI) > > + c->options |= LOONGARCH_CPU_CSRIPI; > > + if (config & IOCSRF_EXTIOI) > > + c->options |= LOONGARCH_CPU_EXTIOI; > > + if (config & IOCSRF_FREQSCALE) > > + c->options |= LOONGARCH_CPU_SCALEFREQ; > > + if (config & IOCSRF_VM) > > + c->options |= LOONGARCH_CPU_HYPERVISOR; > > + > > + config = csr_readl(LOONGARCH_CSR_ASID); > > + config = (config & CSR_ASID_BIT) >> CSR_ASID_BIT_SHIFT; > > + asid_mask = GENMASK(config - 1, 0); > > + set_cpu_asid_mask(c, asid_mask); > > + > > + config = read_csr_prcfg1(); > > + c->kscratch_mask = GENMASK((config & CSR_CONF1_KSNUM) - 1, 0); > > + c->kscratch_mask &= ~(EXC_KSCRATCH_MASK | PERCPU_KSCRATCH_MASK | KVM_KSCRATCH_MASK); > > + > > + config = read_csr_prcfg3(); > > + switch (config & CSR_CONF3_TLBTYPE) { > > + case 0: > > + c->tlbsizemtlb = 0; > > + c->tlbsizestlbsets = 0; > > + c->tlbsizestlbways = 0; > > + c->tlbsize = 0; > > + break; > > + case 1: > > + c->tlbsizemtlb = ((config & CSR_CONF3_MTLBSIZE) >> CSR_CONF3_MTLBSIZE_SHIFT) + 1; > > + c->tlbsizestlbsets = 0; > > + c->tlbsizestlbways = 0; > > + c->tlbsize = c->tlbsizemtlb + c->tlbsizestlbsets * c->tlbsizestlbways; > > + break; > > + case 2: > > + c->tlbsizemtlb = ((config & CSR_CONF3_MTLBSIZE) >> CSR_CONF3_MTLBSIZE_SHIFT) + 1; > > + c->tlbsizestlbsets = 1 << ((config & CSR_CONF3_STLBIDX) >> CSR_CONF3_STLBIDX_SHIFT); > > + c->tlbsizestlbways = ((config & CSR_CONF3_STLBWAYS) >> CSR_CONF3_STLBWAYS_SHIFT) + 1; > > + c->tlbsize = c->tlbsizemtlb + c->tlbsizestlbsets * c->tlbsizestlbways; > > + break; > > + default: > > + pr_warn("Warning: unimplemented tlb type\n"); > > + } > > +} > > + > > +#define MAX_NAME_LEN 32 > > +#define VENDOR_OFFSET 0 > > +#define CPUNAME_OFFSET 9 > > + > > +static char cpu_full_name[MAX_NAME_LEN] = " - "; > > + > > +static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int cpu) > > +{ > > + uint64_t *vendor = (void *)(&cpu_full_name[VENDOR_OFFSET]); > > + uint64_t *cpuname = (void *)(&cpu_full_name[CPUNAME_OFFSET]); > > + > > + __cpu_full_name[cpu] = cpu_full_name; > > + *vendor = iocsr_readq(LOONGARCH_IOCSR_VENDOR); > > + *cpuname = iocsr_readq(LOONGARCH_IOCSR_CPUNAME); > > + > > + switch (c->processor_id & PRID_IMP_MASK) { > > + case PRID_IMP_LOONGSON_32: > > + c->cputype = CPU_LOONGSON32; > > + set_isa(c, LOONGARCH_CPU_ISA_LA32S); > > + __cpu_family[cpu] = "Loongson-32bit"; > > + pr_info("Standard 32-bit Loongson Processor probed\n"); > > + break; > > + case PRID_IMP_LOONGSON_64R: > > + c->cputype = CPU_LOONGSON64; > > + set_isa(c, LOONGARCH_CPU_ISA_LA64); > > + __cpu_family[cpu] = "Loongson-64bit"; > > + pr_info("Reduced 64-bit Loongson Processor probed\n"); > > + break; > > + case PRID_IMP_LOONGSON_64C: > > + c->cputype = CPU_LOONGSON64; > > + set_isa(c, LOONGARCH_CPU_ISA_LA64); > > + __cpu_family[cpu] = "Loongson-64bit"; > > + pr_info("Classic 64-bit Loongson Processor probed\n"); > > + break; > > + case PRID_IMP_LOONGSON_64G: > > + c->cputype = CPU_LOONGSON64; > > + set_isa(c, LOONGARCH_CPU_ISA_LA64); > > + __cpu_family[cpu] = "Loongson-64bit"; > > + pr_info("Generic 64-bit Loongson Processor probed\n"); > > + break; > > + default: /* Default to 64 bit */ > > + c->cputype = CPU_LOONGSON64; > > + set_isa(c, LOONGARCH_CPU_ISA_LA64); > > + __cpu_family[cpu] = "Loongson-64bit"; > > + pr_info("Unknown 64-bit Loongson Processor probed\n"); > > + } > > +} > > + > > +#ifdef CONFIG_64BIT > > +/* For use by uaccess.h */ > > +u64 __ua_limit; > > +EXPORT_SYMBOL(__ua_limit); > > +#endif > > + > > +const char *__cpu_family[NR_CPUS]; > > +const char *__cpu_full_name[NR_CPUS]; > > +const char *__elf_platform; > > + > > +static void cpu_report(void) > > +{ > > + struct cpuinfo_loongarch *c = ¤t_cpu_data; > > + > > + pr_info("CPU%d revision is: %08x (%s)\n", > > + smp_processor_id(), c->processor_id, cpu_family_string()); > > + if (c->options & LOONGARCH_CPU_FPU) > > + pr_info("FPU%d revision is: %08x\n", smp_processor_id(), c->fpu_vers); > > +} > > + > > +void cpu_probe(void) > > +{ > > + unsigned int cpu = smp_processor_id(); > > + struct cpuinfo_loongarch *c = ¤t_cpu_data; > > + > > + /* > > + * Set a default elf platform, cpu probe may later > > + * overwrite it with a more precise value > > + */ > > + set_elf_platform(cpu, "loongarch"); > > + > > + c->cputype = CPU_UNKNOWN; > > + c->processor_id = read_cpucfg(LOONGARCH_CPUCFG0); > > + c->fpu_vers = (read_cpucfg(LOONGARCH_CPUCFG2) >> 3) & 0x3; > > + > > + c->fpu_csr0 = FPU_CSR_RN; > > + c->fpu_mask = FPU_CSR_RSVD; > > + > > + cpu_probe_common(c); > > + > > + per_cpu_trap_init(cpu); > > + > > + switch (c->processor_id & PRID_COMP_MASK) { > > + case PRID_COMP_LOONGSON: > > + cpu_probe_loongson(c, cpu); > > + break; > > + } > > + > > + BUG_ON(!__cpu_family[cpu]); > > + BUG_ON(c->cputype == CPU_UNKNOWN); > > + > > + cpu_probe_addrbits(c); > > + > > +#ifdef CONFIG_64BIT > > + if (cpu == 0) > > + __ua_limit = ~((1ull << cpu_vabits) - 1); > > +#endif > > + > > + cpu_report(); > > +} > > + > > +void cpu_set_cluster(struct cpuinfo_loongarch *cpuinfo, unsigned int cluster) > > +{ > > + /* Ensure the core number fits in the field */ > > + WARN_ON(cluster > (LOONGARCH_GLOBALNUMBER_CLUSTER >> > > + LOONGARCH_GLOBALNUMBER_CLUSTER_SHF)); > > + > > + cpuinfo->globalnumber &= ~LOONGARCH_GLOBALNUMBER_CLUSTER; > > + cpuinfo->globalnumber |= cluster << LOONGARCH_GLOBALNUMBER_CLUSTER_SHF; > > +} > > + > > +void cpu_set_core(struct cpuinfo_loongarch *cpuinfo, unsigned int core) > > +{ > > + /* Ensure the core number fits in the field */ > > + WARN_ON(core > (LOONGARCH_GLOBALNUMBER_CORE >> LOONGARCH_GLOBALNUMBER_CORE_SHF)); > > + > > + cpuinfo->globalnumber &= ~LOONGARCH_GLOBALNUMBER_CORE; > > + cpuinfo->globalnumber |= core << LOONGARCH_GLOBALNUMBER_CORE_SHF; > > +} > > diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c > > new file mode 100644 > > index 000000000000..c05abd143e44 > > --- /dev/null > > +++ b/arch/loongarch/kernel/efi.c > > @@ -0,0 +1,95 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * EFI initialization > > + * > > + * Author: Jianmin Lv <lvjianmin@loongson.cn> > > + * Huacai Chen <chenhuacai@loongson.cn> > > + * > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > + > > +#include <linux/acpi.h> > > +#include <linux/efi.h> > > +#include <linux/efi-bgrt.h> > > +#include <linux/init.h> > > +#include <linux/kernel.h> > > +#include <linux/export.h> > > +#include <linux/io.h> > > +#include <linux/kobject.h> > > +#include <linux/memblock.h> > > +#include <linux/reboot.h> > > +#include <linux/uaccess.h> > > + > > +#include <asm/efi.h> > > +#include <asm/boot_param.h> > > + > > +static efi_config_table_type_t arch_tables[] __initdata = {{},}; > > + > > +void __init efi_runtime_init(void) > > +{ > > + if (!efi_enabled(EFI_BOOT)) > > + return; > > + > > + if (!efi.runtime) > > + return; > > + > > + if (efi_runtime_disabled()) { > > + pr_info("EFI runtime services will be disabled.\n"); > > + return; > > + } > > + > > + efi_native_runtime_setup(); > > + set_bit(EFI_RUNTIME_SERVICES, &efi.flags); > > +} > > + > > +void __init efi_init(void) > > +{ > > + unsigned long efi_config_table; > > + efi_system_table_t *efi_systab; > > + > > + if (!efi_bp) > > + return; > > + > > + efi_systab = (efi_system_table_t *)efi_bp->systemtable; > > + if (!efi_systab) { > > + pr_err("Can't find EFI system table.\n"); > > + return; > > + } > > + > > + set_bit(EFI_64BIT, &efi.flags); > > + efi_config_table = (unsigned long)efi_systab->tables; > > + efi.runtime = (efi_runtime_services_t *)efi_systab->runtime; > > + efi.runtime_version = efi.runtime ? (unsigned int)efi.runtime->hdr.revision : 0; > > + > > + efi_config_parse_tables((void *)efi_systab->tables, efi_systab->nr_tables, arch_tables); > > +} > > + > > +static ssize_t boardinfo_show(struct kobject *kobj, > > + struct kobj_attribute *attr, char *buf) > > +{ > > + return sprintf(buf, > > + "BIOS Information\n" > > + "Vendor\t\t\t: %s\n" > > + "Version\t\t\t: %s\n" > > + "ROM Size\t\t: %d KB\n" > > + "Release Date\t\t: %s\n\n" > > + "Board Information\n" > > + "Manufacturer\t\t: %s\n" > > + "Board Name\t\t: %s\n" > > + "Family\t\t\t: LOONGSON64\n\n", > > + b_info.bios_vendor, b_info.bios_version, > > + b_info.bios_size, b_info.bios_release_date, > > + b_info.board_vendor, b_info.board_name); > > +} > > + > > +static struct kobj_attribute boardinfo_attr = __ATTR(boardinfo, 0444, > > + boardinfo_show, NULL); > > + > > +static int __init boardinfo_init(void) > > +{ > > + if (!efi_kobj) > > + return -EINVAL; > > + > > + return sysfs_create_file(efi_kobj, &boardinfo_attr.attr); > > +} > > +late_initcall(boardinfo_init); > > diff --git a/arch/loongarch/kernel/env.c b/arch/loongarch/kernel/env.c > > new file mode 100644 > > index 000000000000..234fe282a98c > > --- /dev/null > > +++ b/arch/loongarch/kernel/env.c > > @@ -0,0 +1,170 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Author: Huacai Chen <chenhuacai@loongson.cn> > > + * > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > +#include <linux/export.h> > > +#include <linux/acpi.h> > > +#include <linux/efi.h> > > +#include <asm/fw.h> > > +#include <asm/time.h> > > +#include <asm/bootinfo.h> > > +#include <asm/loongson.h> > > + > > +struct bootparamsinterface *efi_bp; > > +struct loongsonlist_mem_map *loongson_mem_map; > > +struct loongsonlist_vbios *pvbios; > > +struct loongson_system_configuration loongson_sysconf; > > +EXPORT_SYMBOL(loongson_sysconf); > > + > > +u64 loongson_chipcfg[MAX_PACKAGES]; > > +u64 loongson_chiptemp[MAX_PACKAGES]; > > +u64 loongson_freqctrl[MAX_PACKAGES]; > > +unsigned long long smp_group[MAX_PACKAGES]; > > + > > +static void __init register_addrs_set(u64 *registers, const u64 addr, int num) > > +{ > > + u64 i; > > + > > + for (i = 0; i < num; i++) { > > + *registers = (i << 44) | addr; > > + registers++; > > + } > > +} > > + > > +static u8 ext_listhdr_checksum(u8 *buffer, u32 length) > > +{ > > + u8 sum = 0; > > + u8 *end = buffer + length; > > + > > + while (buffer < end) { > > + sum = (u8)(sum + *(buffer++)); > > + } > > + > > + return (sum); > > +} > > + > > +static int parse_mem(struct _extention_list_hdr *head) > > +{ > > + loongson_mem_map = (struct loongsonlist_mem_map *)head; > > + if (ext_listhdr_checksum((u8 *)loongson_mem_map, head->length)) { > > + pr_warn("mem checksum error\n"); > > + return -EPERM; > > + } > > + > > + return 0; > > +} > > + > > +static int parse_vbios(struct _extention_list_hdr *head) > > +{ > > + pvbios = (struct loongsonlist_vbios *)head; > > + > > + if (ext_listhdr_checksum((u8 *)pvbios, head->length)) { > > + pr_warn("vbios_addr checksum error\n"); > > + return -EPERM; > > + } > > + > > + loongson_sysconf.vgabios_addr = pvbios->vbios_addr; > > + > > + return 0; > > +} > > + > > +static int parse_screeninfo(struct _extention_list_hdr *head) > > +{ > > + struct loongsonlist_screeninfo *pscreeninfo; > > + > > + pscreeninfo = (struct loongsonlist_screeninfo *)head; > > + if (ext_listhdr_checksum((u8 *)pscreeninfo, head->length)) { > > + pr_warn("screeninfo_addr checksum error\n"); > > + return -EPERM; > > + } > > + > > + memcpy(&screen_info, &pscreeninfo->si, sizeof(screen_info)); > > + > > + return 0; > > +} > > + > > +static int list_find(struct _extention_list_hdr *head) > > +{ > > + struct _extention_list_hdr *fhead = head; > > + > > + if (fhead == NULL) { > > + pr_warn("the link is empty!\n"); > > + return -1; > > + } > > + > > + while (fhead != NULL) { > > + if (memcmp(&(fhead->signature), LOONGSON_MEM_LINKLIST, 3) == 0) { > > + if (parse_mem(fhead) != 0) { > > + pr_warn("parse mem failed\n"); > > + return -EPERM; > > + } > > + } else if (memcmp(&(fhead->signature), LOONGSON_VBIOS_LINKLIST, 5) == 0) { > > + if (parse_vbios(fhead) != 0) { > > + pr_warn("parse vbios failed\n"); > > + return -EPERM; > > + } > > + } else if (memcmp(&(fhead->signature), LOONGSON_SCREENINFO_LINKLIST, 5) == 0) { > > + if (parse_screeninfo(fhead) != 0) { > > + pr_warn("parse screeninfo failed\n"); > > + return -EPERM; > > + } > > + } > > + fhead = fhead->next; > > + } > > + > > + return 0; > > +} > > + > > +static void __init parse_bpi_flags(void) > > +{ > > + if (efi_bp->flags & BPI_FLAGS_UEFI_SUPPORTED) > > + set_bit(EFI_BOOT, &efi.flags); > > + else > > + clear_bit(EFI_BOOT, &efi.flags); > > +} > > + > > +static int get_bpi_version(void *signature) > > +{ > > + char data[8]; > > + int r, version = 0; > > + > > + memset(data, 0, 8); > > + memcpy(data, signature + 4, 4); > > + r = kstrtoint(data, 10, &version); > > + > > + if (r < 0 || version < BPI_VERSION_V1) > > + panic("Fatal error, invalid BPI version: %d\n", version); > > + > > + if (version >= BPI_VERSION_V2) > > + parse_bpi_flags(); > > + > > + return version; > > +} > > + > > +void __init fw_init_environ(void) > > +{ > > + efi_bp = (struct bootparamsinterface *)_fw_envp; > > + loongson_sysconf.bpi_ver = get_bpi_version(&efi_bp->signature); > > + > > + register_addrs_set(smp_group, TO_UNCAC(0x1fe01000), 16); > > + register_addrs_set(loongson_chipcfg, TO_UNCAC(0x1fe00180), 16); > > + register_addrs_set(loongson_chiptemp, TO_UNCAC(0x1fe0019c), 16); > > + register_addrs_set(loongson_freqctrl, TO_UNCAC(0x1fe001d0), 16); > > + > > + if (list_find(efi_bp->extlist)) > > + pr_warn("Scan bootparam failed\n"); > > +} > > + > > +static int __init init_cpu_fullname(void) > > +{ > > + int cpu; > > + > > + if (loongson_sysconf.cpuname && !strncmp(loongson_sysconf.cpuname, "Loongson", 8)) { > > + for (cpu = 0; cpu < NR_CPUS; cpu++) > > + __cpu_full_name[cpu] = loongson_sysconf.cpuname; > > + } > > + return 0; > > +} > > +arch_initcall(init_cpu_fullname); > > diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S > > new file mode 100644 > > index 000000000000..53e02fb0559d > > --- /dev/null > > +++ b/arch/loongarch/kernel/head.S > > @@ -0,0 +1,72 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > +#include <linux/init.h> > > +#include <linux/threads.h> > > + > > +#include <asm/addrspace.h> > > +#include <asm/asm.h> > > +#include <asm/asmmacro.h> > > +#include <asm/regdef.h> > > +#include <asm/loongarch.h> > > +#include <asm/stackframe.h> > > + > > +SYM_ENTRY(_stext, SYM_L_GLOBAL, SYM_A_NONE) > > + > > + __REF > > + > > +SYM_CODE_START(kernel_entry) # kernel entry point > > + > > + /* We might not get launched at the address the kernel is linked to, > > + so we jump there. */ > > + la.abs t0, 0f > > + jirl zero, t0, 0 > > +0: > > + la t0, __bss_start # clear .bss > > + st.d zero, t0, 0 > > + la t1, __bss_stop - LONGSIZE > > +1: > > + addi.d t0, t0, LONGSIZE > > + st.d zero, t0, 0 > > + bne t0, t1, 1b > > + > > + la t0, fw_arg0 > > + st.d a0, t0, 0 # firmware arguments > > + la t0, fw_arg1 > > + st.d a1, t0, 0 > > + la t0, fw_arg2 > > + st.d a2, t0, 0 > > + la t0, fw_arg3 > > + st.d a3, t0, 0 > > + > > + /* Config direct window and set PG */ > > + li.d t0, CSR_DMW0_INIT # UC, PLV0, 0x8000 xxxx xxxx xxxx > > + csrwr t0, LOONGARCH_CSR_DMWIN0 > > + li.d t0, CSR_DMW1_INIT # CA, PLV0, 0x9000 xxxx xxxx xxxx > > + csrwr t0, LOONGARCH_CSR_DMWIN1 > > + /* Enable PG */ > > + li.w t0, 0xb0 # PLV=0, IE=0, PG=1 > > + csrwr t0, LOONGARCH_CSR_CRMD > > + li.w t0, 0x04 # PLV=0, PIE=1, PWE=0 > > + csrwr t0, LOONGARCH_CSR_PRMD > > + li.w t0, 0x00 # FPE=0, SXE=0, ASXE=0, BTE=0 > > + csrwr t0, LOONGARCH_CSR_EUEN > > + > > + /* KScratch3 used for percpu base, initialized as 0 */ > > + csrwr zero, PERCPU_BASE_KS > > + /* GPR21 used for percpu base (runtime), initialized as 0 */ > > + or x0, zero, zero > > + > > + la tp, init_thread_union > > + /* Set the SP after an empty pt_regs. */ > > + PTR_LI sp, (_THREAD_SIZE - 32 - PT_SIZE) > > + PTR_ADDU sp, sp, tp > > + set_saved_sp sp, t0, t1 > > + PTR_ADDIU sp, sp, -4 * SZREG # init stack pointer > > + > > + bl start_kernel > > + > > +SYM_CODE_END(kernel_entry) > > + > > +SYM_ENTRY(kernel_entry_end, SYM_L_GLOBAL, SYM_A_NONE) > > diff --git a/arch/loongarch/kernel/mem.c b/arch/loongarch/kernel/mem.c > > new file mode 100644 > > index 000000000000..c1ac71034448 > > --- /dev/null > > +++ b/arch/loongarch/kernel/mem.c > > @@ -0,0 +1,83 @@ > > +// SPDX-License-Identifier: GPL-2.0-or-later > > +/* > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > +#include <linux/fs.h> > > +#include <linux/mm.h> > > +#include <linux/memblock.h> > > + > > +#include <asm/bootinfo.h> > > +#include <asm/loongson.h> > > +#include <asm/sections.h> > > + > > +void __init early_memblock_init(void) > > +{ > > + int i; > > + u32 mem_type; > > + u64 mem_start, mem_end, mem_size; > > + > > + /* parse memory information */ > > + for (i = 0; i < loongson_mem_map->map_count; i++) { > > + mem_type = loongson_mem_map->map[i].mem_type; > > + mem_start = loongson_mem_map->map[i].mem_start; > > + mem_size = loongson_mem_map->map[i].mem_size; > > + mem_end = mem_start + mem_size; > > + > > + switch (mem_type) { > > + case ADDRESS_TYPE_SYSRAM: > > + memblock_add(mem_start, mem_size); > > + if (max_low_pfn < (mem_end >> PAGE_SHIFT)) > > + max_low_pfn = mem_end >> PAGE_SHIFT; > > + break; > > + } > > + } > > + memblock_set_current_limit(PFN_PHYS(max_low_pfn)); > > +} > > + > > +void __init fw_init_memory(void) > > +{ > > + int i; > > + u32 mem_type; > > + u64 mem_start, mem_end, mem_size; > > + unsigned long start_pfn, end_pfn; > > + static unsigned long num_physpages; > > + > > + /* parse memory information */ > > + for (i = 0; i < loongson_mem_map->map_count; i++) { > > + mem_type = loongson_mem_map->map[i].mem_type; > > + mem_start = loongson_mem_map->map[i].mem_start; > > + mem_size = loongson_mem_map->map[i].mem_size; > > + mem_end = mem_start + mem_size; > > + > > + switch (mem_type) { > > + case ADDRESS_TYPE_SYSRAM: > > + mem_start = PFN_ALIGN(mem_start); > > + mem_end = PFN_ALIGN(mem_end - PAGE_SIZE + 1); > > + num_physpages += (mem_size >> PAGE_SHIFT); > > + memblock_set_node(mem_start, mem_size, &memblock.memory, 0); > > + break; > > + case ADDRESS_TYPE_ACPI: > > + case ADDRESS_TYPE_RESERVED: > > + memblock_reserve(loongson_mem_map->map[i].mem_start, > > + loongson_mem_map->map[i].mem_size); > > + break; > > + } > > + } > > + > > + get_pfn_range_for_nid(0, &start_pfn, &end_pfn); > > + pr_info("start_pfn=0x%lx, end_pfn=0x%lx, num_physpages:0x%lx\n", > > + start_pfn, end_pfn, num_physpages); > > + > > + NODE_DATA(0)->node_start_pfn = start_pfn; > > + NODE_DATA(0)->node_spanned_pages = end_pfn - start_pfn; > > + > > + /* used by finalize_initrd() */ > > + max_low_pfn = end_pfn; > > + > > + /* Reserve the first 2MB */ > > + memblock_reserve(PHYS_OFFSET, 0x200000); > > + > > + /* Reserve the kernel text/data/bss */ > > + memblock_reserve(__pa_symbol(&_text), > > + __pa_symbol(&_end) - __pa_symbol(&_text)); > > +} > > diff --git a/arch/loongarch/kernel/reset.c b/arch/loongarch/kernel/reset.c > > new file mode 100644 > > index 000000000000..b140c2aa2c5a > > --- /dev/null > > +++ b/arch/loongarch/kernel/reset.c > > @@ -0,0 +1,90 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > +#include <linux/kernel.h> > > +#include <linux/acpi.h> > > +#include <linux/efi.h> > > +#include <linux/export.h> > > +#include <linux/pm.h> > > +#include <linux/types.h> > > +#include <linux/reboot.h> > > +#include <linux/delay.h> > > +#include <linux/console.h> > > + > > +#include <acpi/reboot.h> > > +#include <asm/compiler.h> > > +#include <asm/idle.h> > > +#include <asm/loongarch.h> > > +#include <asm/reboot.h> > > + > > +static void default_halt(void) > > +{ > > + local_irq_disable(); > > + clear_csr_ecfg(ECFG0_IM); > > + > > + pr_notice("\n\n** You can safely turn off the power now **\n\n"); > > + console_flush_on_panic(CONSOLE_FLUSH_PENDING); > > + > > + while (true) { > > + __arch_cpu_idle(); > > + } > > +} > > + > > +static void default_poweroff(void) > > +{ > > +#ifdef CONFIG_EFI > > + efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL); > > +#endif > > + while (true) { > > + __arch_cpu_idle(); > > + } > > +} > > + > > +static void default_restart(void) > > +{ > > +#ifdef CONFIG_EFI > > + if (efi_capsule_pending(NULL)) > > + efi_reboot(REBOOT_WARM, NULL); > > + else > > + efi_reboot(REBOOT_COLD, NULL); > > +#endif > > + if (!acpi_disabled) > > + acpi_reboot(); > > + > > + while (true) { > > + __arch_cpu_idle(); > > + } > > +} > > + > > +void (*pm_restart)(void); > > +EXPORT_SYMBOL(pm_restart); > > + > > +void (*pm_power_off)(void); > > +EXPORT_SYMBOL(pm_power_off); > > + > > +void machine_halt(void) > > +{ > > + default_halt(); > > +} > > + > > +void machine_power_off(void) > > +{ > > + pm_power_off(); > > +} > > + > > +void machine_restart(char *command) > > +{ > > + do_kernel_restart(command); > > + pm_restart(); > > +} > > + > > +static int __init loongarch_reboot_setup(void) > > +{ > > + pm_restart = default_restart; > > + pm_power_off = default_poweroff; > > + > > + return 0; > > +} > > + > > +arch_initcall(loongarch_reboot_setup); > > diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c > > new file mode 100644 > > index 000000000000..7ec32959b36c > > --- /dev/null > > +++ b/arch/loongarch/kernel/setup.c > > @@ -0,0 +1,469 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + * > > + * Derived from MIPS: > > + * Copyright (C) 1995 Linus Torvalds > > + * Copyright (C) 1995 Waldorf Electronics > > + * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 01, 02, 03 Ralf Baechle > > + * Copyright (C) 1996 Stoned Elipot > > + * Copyright (C) 1999 Silicon Graphics, Inc. > > + * Copyright (C) 2000, 2001, 2002, 2007 Maciej W. Rozycki > > + */ > > +#include <linux/init.h> > > +#include <linux/acpi.h> > > +#include <linux/dmi.h> > > +#include <linux/efi.h> > > +#include <linux/export.h> > > +#include <linux/screen_info.h> > > +#include <linux/memblock.h> > > +#include <linux/initrd.h> > > +#include <linux/ioport.h> > > +#include <linux/root_dev.h> > > +#include <linux/console.h> > > +#include <linux/pfn.h> > > +#include <linux/platform_device.h> > > +#include <linux/sizes.h> > > +#include <linux/device.h> > > +#include <linux/dma-map-ops.h> > > +#include <linux/swiotlb.h> > > + > > +#include <asm/addrspace.h> > > +#include <asm/bootinfo.h> > > +#include <asm/cache.h> > > +#include <asm/cpu.h> > > +#include <asm/dma.h> > > +#include <asm/efi.h> > > +#include <asm/fw.h> > > +#include <asm/loongson.h> > > +#include <asm/pgalloc.h> > > +#include <asm/sections.h> > > +#include <asm/setup.h> > > +#include <asm/time.h> > > + > > +#define SMBIOS_BIOSSIZE_OFFSET 0x09 > > +#define SMBIOS_BIOSEXTERN_OFFSET 0x13 > > +#define SMBIOS_FREQLOW_OFFSET 0x16 > > +#define SMBIOS_FREQHIGH_OFFSET 0x17 > > +#define SMBIOS_FREQLOW_MASK 0xFF > > +#define SMBIOS_CORE_PACKAGE_OFFSET 0x23 > > +#define LOONGSON_EFI_ENABLE (1 << 3) > > + > > +#ifdef CONFIG_VT > > +struct screen_info screen_info; > > +#endif > > + > > +DEFINE_PER_CPU(unsigned long, kernelsp); > > +unsigned long fw_arg0, fw_arg1, fw_arg2, fw_arg3; > > +struct cpuinfo_loongarch cpu_data[NR_CPUS] __read_mostly; > > + > > +EXPORT_SYMBOL(cpu_data); > > + > > +struct loongson_board_info b_info; > > +static const char dmi_empty_string[] = " "; > > + > > +/* > > + * Setup information > > + * > > + * These are initialized so they are in the .data section > > + */ > > + > > +char __initdata arcs_cmdline[COMMAND_LINE_SIZE]; > > +static char __initdata command_line[COMMAND_LINE_SIZE]; > > + > > +static struct resource code_resource = { .name = "Kernel code", }; > > +static struct resource data_resource = { .name = "Kernel data", }; > > +static struct resource bss_resource = { .name = "Kernel bss", }; > > + > > +const char *get_system_type(void) > > +{ > > + return "generic-loongson-machine"; > > +} > > + > > +static const char *dmi_string_parse(const struct dmi_header *dm, u8 s) > > +{ > > + const u8 *bp = ((u8 *) dm) + dm->length; > > + > > + if (s) { > > + s--; > > + while (s > 0 && *bp) { > > + bp += strlen(bp) + 1; > > + s--; > > + } > > + > > + if (*bp != 0) { > > + size_t len = strlen(bp)+1; > > + size_t cmp_len = len > 8 ? 8 : len; > > + > > + if (!memcmp(bp, dmi_empty_string, cmp_len)) > > + return dmi_empty_string; > > + > > + return bp; > > + } > > + } > > + > > + return ""; > > +} > > + > > +static void __init parse_cpu_table(const struct dmi_header *dm) > > +{ > > + long freq_temp = 0; > > + char *dmi_data = (char *)dm; > > + > > + freq_temp = ((*(dmi_data + SMBIOS_FREQHIGH_OFFSET) << 8) + > > + ((*(dmi_data + SMBIOS_FREQLOW_OFFSET)) & SMBIOS_FREQLOW_MASK)); > > + cpu_clock_freq = freq_temp * 1000000; > > + > > + loongson_sysconf.cpuname = (void *)dmi_string_parse(dm, dmi_data[16]); > > + loongson_sysconf.cores_per_package = *(dmi_data + SMBIOS_CORE_PACKAGE_OFFSET); > > + > > + pr_info("CpuClock = %llu\n", cpu_clock_freq); > > +} > > + > > +static void __init parse_bios_table(const struct dmi_header *dm) > > +{ > > + int bios_extern; > > + char *dmi_data = (char *)dm; > > + > > + bios_extern = *(dmi_data + SMBIOS_BIOSEXTERN_OFFSET); > > + b_info.bios_size = *(dmi_data + SMBIOS_BIOSSIZE_OFFSET); > > + > > + if (bios_extern & LOONGSON_EFI_ENABLE) > > + set_bit(EFI_BOOT, &efi.flags); > > + else > > + clear_bit(EFI_BOOT, &efi.flags); > > +} > > + > > +static void __init find_tokens(const struct dmi_header *dm, void *dummy) > > +{ > > + switch (dm->type) { > > + case 0x0: /* Extern BIOS */ > > + parse_bios_table(dm); > > + break; > > + case 0x4: /* Calling interface */ > > + parse_cpu_table(dm); > > + break; > > + } > > +} > > +static void __init smbios_parse(void) > > +{ > > + b_info.bios_vendor = (void *)dmi_get_system_info(DMI_BIOS_VENDOR); > > + b_info.bios_version = (void *)dmi_get_system_info(DMI_BIOS_VERSION); > > + b_info.bios_release_date = (void *)dmi_get_system_info(DMI_BIOS_DATE); > > + b_info.board_vendor = (void *)dmi_get_system_info(DMI_BOARD_VENDOR); > > + b_info.board_name = (void *)dmi_get_system_info(DMI_BOARD_NAME); > > + dmi_walk(find_tokens, NULL); > > +} > > + > > +/* > > + * Manage initrd > > + */ > > +#ifdef CONFIG_BLK_DEV_INITRD > > + > > +static unsigned long __init init_initrd(void) > > +{ > > + if (!phys_initrd_start || !phys_initrd_size) > > + goto disable; > > + > > + initrd_start = (unsigned long)phys_to_virt(phys_initrd_start); > > + initrd_end = (unsigned long)phys_to_virt(phys_initrd_start + phys_initrd_size); > > + > > + if (!initrd_start || initrd_end <= initrd_start) > > + goto disable; > > + > > + if (initrd_start & ~PAGE_MASK) { > > + pr_err("initrd start must be page aligned\n"); > > + goto disable; > > + } > > + if (initrd_start < PAGE_OFFSET) { > > + pr_err("initrd start < PAGE_OFFSET\n"); > > + goto disable; > > + } > > + > > + ROOT_DEV = Root_RAM0; > > + > > + return 0; > > +disable: > > + initrd_start = 0; > > + initrd_end = 0; > > + return 0; > > +} > > + > > +static void __init finalize_initrd(void) > > +{ > > + unsigned long size = initrd_end - initrd_start; > > + > > + if (size == 0) { > > + pr_info("Initrd not found or empty"); > > + goto disable; > > + } > > + if (__pa(initrd_end) > PFN_PHYS(max_low_pfn)) { > > + pr_err("Initrd extends beyond end of memory"); > > + goto disable; > > + } > > + > > + > > + memblock_reserve(__pa(initrd_start), size); > > + initrd_below_start_ok = 1; > > + > > + pr_info("Initial ramdisk at: 0x%lx (%lu bytes)\n", > > + initrd_start, size); > > + return; > > +disable: > > + pr_cont(" - disabling initrd\n"); > > + initrd_start = 0; > > + initrd_end = 0; > > +} > > + > > +#else /* !CONFIG_BLK_DEV_INITRD */ > > + > > +static unsigned long __init init_initrd(void) > > +{ > > + return 0; > > +} > > + > > +#define finalize_initrd() do {} while (0) > > + > > +#endif > > + > > +static int usermem __initdata; > > + > > +static int __init early_parse_mem(char *p) > > +{ > > + phys_addr_t start, size; > > + > > + /* > > + * If a user specifies memory size, we > > + * blow away any automatically generated > > + * size. > > + */ > > + if (usermem == 0) { > > + usermem = 1; > > + memblock_remove(memblock_start_of_DRAM(), > > + memblock_end_of_DRAM() - memblock_start_of_DRAM()); > > + } > > + start = 0; > > + size = memparse(p, &p); > > + if (*p == '@') > > + start = memparse(p + 1, &p); > > + > > + memblock_add(start, size); > > + > > + return 0; > > +} > > +early_param("mem", early_parse_mem); > > + > > +static int __init early_parse_memmap(char *p) > > +{ > > + char *oldp; > > + u64 start_at, mem_size; > > + > > + if (!p) > > + return -EINVAL; > > + > > + if (!strncmp(p, "exactmap", 8)) { > > + pr_err("\"memmap=exactmap\" invalid on LoongArch\n"); > > + return 0; > > + } > > + > > + oldp = p; > > + mem_size = memparse(p, &p); > > + if (p == oldp) > > + return -EINVAL; > > + > > + if (*p == '@') { > > + start_at = memparse(p+1, &p); > > + memblock_add(start_at, mem_size); > > + } else if (*p == '#') { > > + pr_err("\"memmap=nn#ss\" (force ACPI data) invalid on LoongArch\n"); > > + return -EINVAL; > > + } else if (*p == '$') { > > + start_at = memparse(p+1, &p); > > + memblock_add(start_at, mem_size); > > + memblock_reserve(start_at, mem_size); > > + } else { > > + pr_err("\"memmap\" invalid format!\n"); > > + return -EINVAL; > > + } > > + > > + if (*p == '\0') { > > + usermem = 1; > > + return 0; > > + } else > > + return -EINVAL; > > +} > > +early_param("memmap", early_parse_memmap); > > + > > +static void __init check_kernel_sections_mem(void) > > +{ > > + phys_addr_t start = __pa_symbol(&_text); > > + phys_addr_t size = __pa_symbol(&_end) - start; > > + > > + if (!memblock_is_region_memory(start, size)) { > > + pr_info("Kernel sections are not in the memory maps\n"); > > + memblock_add(start, size); > > + } > > +} > > + > > +static void __init bootcmdline_append(const char *s, size_t max) > > +{ > > + if (!s[0] || !max) > > + return; > > + > > + if (boot_command_line[0]) > > + strlcat(boot_command_line, " ", COMMAND_LINE_SIZE); > > + > > + strlcat(boot_command_line, s, max); > > +} > > + > > +static void __init bootcmdline_init(char **cmdline_p) > > +{ > > + boot_command_line[0] = 0; > > + > > + /* > > + * Take arguments from the bootloader at first. Early code should have > > + * filled arcs_cmdline with arguments from the bootloader. > > + */ > > + bootcmdline_append(arcs_cmdline, COMMAND_LINE_SIZE); > > + > > + strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE); > > + *cmdline_p = command_line; > > + > > + parse_early_param(); > > +} > > + > > +/* > > + * arch_mem_init - initialize memory management subsystem > > + */ > > +static void __init arch_mem_init(char **cmdline_p) > > +{ > > + if (usermem) > > + pr_info("User-defined physical RAM map overwrite\n"); > > + > > + check_kernel_sections_mem(); > > + > > + memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0); > > + pagetable_init(); > > + > > + /* > > + * Prevent memblock from allocating high memory. > > + * This cannot be done before max_low_pfn is detected, so up > > + * to this point is possible to only reserve physical memory > > + * with memblock_reserve; memblock_alloc* can be used > > + * only after this point > > + */ > > + memblock_set_current_limit(PFN_PHYS(max_low_pfn)); > > + > > + /* > > + * In order to reduce the possibility of kernel panic when failed to > > + * get IO TLB memory under CONFIG_SWIOTLB, it is better to allocate > > + * low memory as small as possible before plat_swiotlb_setup(), so > > + * make sparse_init() using top-down allocation. > > + */ > > + memblock_set_bottom_up(false); > > + sparse_init(); > > + memblock_set_bottom_up(true); > > + > > + swiotlb_init(1); > > + > > + dma_contiguous_reserve(PFN_PHYS(max_low_pfn)); > > + > > + memblock_dump_all(); > > + > > + early_memtest(PFN_PHYS(ARCH_PFN_OFFSET), PFN_PHYS(max_low_pfn)); > > +} > > + > > +static void __init resource_init(void) > > +{ > > + u64 i; > > + phys_addr_t start, end; > > + > > + code_resource.start = __pa_symbol(&_text); > > + code_resource.end = __pa_symbol(&_etext) - 1; > > + data_resource.start = __pa_symbol(&_etext); > > + data_resource.end = __pa_symbol(&_edata) - 1; > > + bss_resource.start = __pa_symbol(&__bss_start); > > + bss_resource.end = __pa_symbol(&__bss_stop) - 1; > > + > > + for_each_mem_range(i, &start, &end) { > > + struct resource *res; > > + > > + res = memblock_alloc(sizeof(struct resource), SMP_CACHE_BYTES); > > + if (!res) > > + panic("%s: Failed to allocate %zu bytes\n", __func__, > > + sizeof(struct resource)); > > + > > + res->start = start; > > + /* > > + * In memblock, end points to the first byte after the > > + * range while in resourses, end points to the last byte in > > + * the range. > > + */ > > + res->end = end - 1; > > + res->flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY; > > + res->name = "System RAM"; > > + > > + request_resource(&iomem_resource, res); > > + > > + /* > > + * We don't know which RAM region contains kernel data, > > + * so we try it repeatedly and let the resource manager > > + * test it. > > + */ > > + request_resource(res, &code_resource); > > + request_resource(res, &data_resource); > > + request_resource(res, &bss_resource); > > + } > > +} > > + > > +void __init platform_init(void) > > +{ > > + efi_init(); > > +#ifdef CONFIG_ACPI_TABLE_UPGRADE > > + acpi_table_upgrade(); > > +#endif > > +#ifdef CONFIG_ACPI > > + acpi_gbl_use_default_register_widths = false; > > + acpi_boot_table_init(); > > + acpi_boot_init(); > > +#endif > > + > > + fw_init_memory(); > > + dmi_setup(); > > + smbios_parse(); > > + pr_info("The BIOS Version: %s\n", b_info.bios_version); > > + > > + efi_runtime_init(); > > +} > > + > > +void __init setup_arch(char **cmdline_p) > > +{ > > + cpu_probe(); > > + > > + fw_init_cmdline(); > > + fw_init_environ(); > > + early_memblock_init(); > > + bootcmdline_init(cmdline_p); > > + > > + init_initrd(); > > + platform_init(); > > + finalize_initrd(); > > + > > + arch_mem_init(cmdline_p); > > + > > + resource_init(); > > + > > + paging_init(); > > +} > > + > > +static int __init register_gop_device(void) > > +{ > > + void *pd; > > + > > + if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) > > + return 0; > > + pd = platform_device_register_data(NULL, "efi-framebuffer", 0, > > + &screen_info, sizeof(screen_info)); > > + return PTR_ERR_OR_ZERO(pd); > > +} > > +subsys_initcall(register_gop_device); > > diff --git a/arch/loongarch/kernel/time.c b/arch/loongarch/kernel/time.c > > new file mode 100644 > > index 000000000000..2e00802a3742 > > --- /dev/null > > +++ b/arch/loongarch/kernel/time.c > > @@ -0,0 +1,237 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Common time service routines for LoongArch machines. > > + * > > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited > > + */ > > +#include <linux/clockchips.h> > > +#include <linux/delay.h> > > +#include <linux/export.h> > > +#include <linux/init.h> > > +#include <linux/interrupt.h> > > +#include <linux/kernel.h> > > +#include <linux/sched_clock.h> > > +#include <linux/spinlock.h> > > + > > +#include <asm/cpu-features.h> > > +#include <asm/loongarch.h> > > +#include <asm/time.h> > > + > > +u64 cpu_clock_freq; > > +EXPORT_SYMBOL(cpu_clock_freq); > > +u64 const_clock_freq; > > +EXPORT_SYMBOL(const_clock_freq); > > + > > +static DEFINE_RAW_SPINLOCK(state_lock); > > +static DEFINE_PER_CPU(struct clock_event_device, constant_clockevent_device); > > + > > +static void constant_event_handler(struct clock_event_device *dev) > > +{ > > +} > > + > > +irqreturn_t constant_timer_interrupt(int irq, void *data) > > +{ > > + int cpu = smp_processor_id(); > > + struct clock_event_device *cd; > > + > > + /* Clear Timer Interrupt */ > > + write_csr_tintclear(CSR_TINTCLR_TI); > > + cd = &per_cpu(constant_clockevent_device, cpu); > > + cd->event_handler(cd); > > + > > + return IRQ_HANDLED; > > +} > > + > > +static int constant_set_state_oneshot(struct clock_event_device *evt) > > +{ > > + unsigned long timer_config; > > + > > + raw_spin_lock(&state_lock); > > + > > + timer_config = csr_readq(LOONGARCH_CSR_TCFG); > > + timer_config |= CSR_TCFG_EN; > > + timer_config &= ~CSR_TCFG_PERIOD; > > + csr_writeq(timer_config, LOONGARCH_CSR_TCFG); > > + > > + raw_spin_unlock(&state_lock); > > + > > + return 0; > > +} > > + > > +static int constant_set_state_oneshot_stopped(struct clock_event_device *evt) > > +{ > > + unsigned long timer_config; > > + > > + raw_spin_lock(&state_lock); > > + > > + timer_config = csr_readq(LOONGARCH_CSR_TCFG); > > + timer_config &= ~CSR_TCFG_EN; > > + csr_writeq(timer_config, LOONGARCH_CSR_TCFG); > > + > > + raw_spin_unlock(&state_lock); > > + > > + return 0; > > +} > > + > > +static int constant_set_state_periodic(struct clock_event_device *evt) > > +{ > > + unsigned long period; > > + unsigned long timer_config; > > + > > + raw_spin_lock(&state_lock); > > + > > + period = const_clock_freq / HZ; > > + timer_config = period & CSR_TCFG_VAL; > > + timer_config |= (CSR_TCFG_PERIOD | CSR_TCFG_EN); > > + csr_writeq(timer_config, LOONGARCH_CSR_TCFG); > > + > > + raw_spin_unlock(&state_lock); > > + > > + return 0; > > +} > > + > > +static int constant_set_state_shutdown(struct clock_event_device *evt) > > +{ > > + return 0; > > +} > > + > > +static int constant_timer_next_event(unsigned long delta, struct clock_event_device *evt) > > +{ > > + unsigned long timer_config; > > + > > + delta &= CSR_TCFG_VAL; > > + timer_config = delta | CSR_TCFG_EN; > > + csr_writeq(timer_config, LOONGARCH_CSR_TCFG); > > + > > + return 0; > > +} > > + > > +static unsigned long __init get_loops_per_jiffy(void) > > +{ > > + unsigned long lpj = (unsigned long)const_clock_freq; > > + > > + do_div(lpj, HZ); > > + > > + return lpj; > > +} > > + > > +static long init_timeval; > > + > > +void sync_counter(void) > > +{ > > + /* Ensure counter begin at 0 */ > > + csr_writeq(-init_timeval, LOONGARCH_CSR_CNTC); > > +} > > + > > +int constant_clockevent_init(void) > > +{ > > + unsigned int irq; > > + unsigned int cpu = smp_processor_id(); > > + unsigned long min_delta = 0x600; > > + unsigned long max_delta = (1UL << 48) - 1; > > + struct clock_event_device *cd; > > + static int timer_irq_installed = 0; > > + > > + irq = get_timer_irq(); > > + > > + cd = &per_cpu(constant_clockevent_device, cpu); > > + > > + cd->name = "Constant"; > > + cd->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_PERCPU; > > + > > + cd->irq = irq; > > + cd->rating = 320; > > + cd->cpumask = cpumask_of(cpu); > > + cd->set_state_oneshot = constant_set_state_oneshot; > > + cd->set_state_oneshot_stopped = constant_set_state_oneshot_stopped; > > + cd->set_state_periodic = constant_set_state_periodic; > > + cd->set_state_shutdown = constant_set_state_shutdown; > > + cd->set_next_event = constant_timer_next_event; > > + cd->event_handler = constant_event_handler; > > + > > + clockevents_config_and_register(cd, const_clock_freq, min_delta, max_delta); > > + > > + if (timer_irq_installed) > > + return 0; > > + > > + timer_irq_installed = 1; > > + > > + sync_counter(); > > + > > + if (request_irq(irq, constant_timer_interrupt, IRQF_PERCPU | IRQF_TIMER, "timer", NULL)) > > + pr_err("Failed to request irq %d (timer)\n", irq); > > + > > + lpj_fine = get_loops_per_jiffy(); > > + pr_info("Constant clock event device register\n"); > > + > > + return 0; > > +} > > + > > +static u64 read_const_counter(struct clocksource *clk) > > +{ > > + return drdtime(); > > +} > > + > > +static struct clocksource clocksource_const = { > > + .name = "Constant", > > + .rating = 400, > > + .read = read_const_counter, > > + .mask = CLOCKSOURCE_MASK(64), > > + .flags = CLOCK_SOURCE_IS_CONTINUOUS, > > + .mult = 0, > > + .shift = 10, > > +}; > > + > > +unsigned long long notrace sched_clock(void) > > +{ > > + /* 64-bit arithmetic can overflow, so use 128-bit. */ > > + u64 t1, t2, t3; > > + unsigned long long rv; > > + u64 mult = clocksource_const.mult; > > + u64 shift = clocksource_const.shift; > > + u64 cnt = read_const_counter(NULL); > > + > > + __asm__ ( > > + "nor %[t1], $r0, %[shift] \n\t" > > + "mulh.du %[t2], %[cnt], %[mult] \n\t" > > + "mul.d %[t3], %[cnt], %[mult] \n\t" > > + "slli.d %[t2], %[t2], 1 \n\t" > > + "srl.d %[rv], %[t3], %[shift] \n\t" > > + "sll.d %[t1], %[t2], %[t1] \n\t" > > + "or %[rv], %[t1], %[rv] \n\t" > > + : [rv] "=&r" (rv), [t1] "=&r" (t1), [t2] "=&r" (t2), [t3] "=&r" (t3) > > + : [cnt] "r" (cnt), [mult] "r" (mult), [shift] "r" (shift) > > + : ); > > + > > + return rv; > > +} > > + > > +int __init constant_clocksource_init(void) > > +{ > > + int res; > > + unsigned long freq; > > + > > + freq = const_clock_freq; > > + > > + clocksource_const.mult = > > + clocksource_hz2mult(freq, clocksource_const.shift); > > + > > + res = clocksource_register_hz(&clocksource_const, freq); > > + > > + pr_info("Constant clock source device register\n"); > > + > > + return res; > > +} > > + > > +void __init time_init(void) > > +{ > > + if (!cpu_has_cpucfg) > > + const_clock_freq = cpu_clock_freq; > > + else > > + const_clock_freq = calc_const_freq(); > > + > > + init_timeval = drdtime() - csr_readq(LOONGARCH_CSR_CNTC); > > + > > + constant_clockevent_init(); > > + constant_clocksource_init(); > > +} > > diff --git a/arch/loongarch/kernel/topology.c b/arch/loongarch/kernel/topology.c > > new file mode 100644 > > index 000000000000..3b2cbb95875b > > --- /dev/null > > +++ b/arch/loongarch/kernel/topology.c > > @@ -0,0 +1,13 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +#include <linux/cpu.h> > > +#include <linux/init.h> > > +#include <linux/percpu.h> > > + > > +static struct cpu cpu_device; > > + > > +static int __init topology_init(void) > > +{ > > + return register_cpu(&cpu_device, 0); > > +} > > + > > +subsys_initcall(topology_init); > > -- > > 2.27.0 > > ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2021-09-29 3:24 UTC | newest] Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- [not found] <20210927064300.624279-1-chenhuacai@loongson.cn> 2021-09-27 6:42 ` [PATCH V4 09/22] LoongArch: Add boot and setup routines Huacai Chen 2021-09-27 6:52 ` Ard Biesheuvel 2021-09-29 3:24 ` Huacai Chen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).