1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Author: Huacai Chen <chenhuacai@loongson.cn>
4 * Copyright (C) 2020 Loongson Technology Corporation Limited
5 */
6
7#include <linux/acpi.h>
8#include <linux/dmi.h>
9#include <linux/efi.h>
10#include <linux/memblock.h>
11#include <asm/acpi.h>
12#include <asm/bootinfo.h>
13#include <asm/cacheflush.h>
14#include <asm/efi.h>
15#include <asm/smp.h>
16#include <asm/time.h>
17
18#include <loongson.h>
19
20#define SMBIOS_BIOSSIZE_OFFSET		0x09
21#define SMBIOS_BIOSEXTERN_OFFSET 	0x13
22#define SMBIOS_FREQLOW_OFFSET		0x16
23#define SMBIOS_FREQHIGH_OFFSET		0x17
24#define SMBIOS_FREQLOW_MASK		0xFF
25#define SMBIOS_CORE_PACKAGE_OFFSET	0x23
26#define LOONGSON_EFI_ENABLE     	(1 << 3)
27
28struct loongson_board_info b_info;
29static const char dmi_empty_string[] = "        ";
30extern void __init arch_reserve_vmcore(void);
31extern void __init arch_parse_crashkernel(void);
32
33static const char *dmi_string_parse(const struct dmi_header *dm, u8 s)
34{
35	const u8 *bp = ((u8 *) dm) + dm->length;
36
37	if (s) {
38		s--;
39		while (s > 0 && *bp) {
40			bp += strlen(bp) + 1;
41			s--;
42		}
43
44		if (*bp != 0) {
45			size_t len = strlen(bp)+1;
46			size_t cmp_len = len > 8 ? 8 : len;
47
48			if (!memcmp(bp, dmi_empty_string, cmp_len))
49				return dmi_empty_string;
50
51			return bp;
52		}
53	}
54
55	return "";
56
57}
58
59static void __init parse_cpu_table(const struct dmi_header *dm)
60{
61	long freq_temp = 0;
62	char *dmi_data = (char *)dm;
63
64	freq_temp = ((*(dmi_data + SMBIOS_FREQHIGH_OFFSET) << 8) + \
65			((*(dmi_data + SMBIOS_FREQLOW_OFFSET)) & SMBIOS_FREQLOW_MASK));
66	cpu_clock_freq = freq_temp * 1000000;
67
68	loongson_sysconf.cpuname = (void *)dmi_string_parse(dm, dmi_data[16]);
69	loongson_sysconf.cores_per_package = *(dmi_data + SMBIOS_CORE_PACKAGE_OFFSET);
70
71	pr_info("CpuClock = %llu\n", cpu_clock_freq);
72
73}
74
75static void __init parse_bios_table(const struct dmi_header *dm)
76{
77	char *dmi_data = (char *)dm;
78
79	b_info.bios_size = (*(dmi_data + SMBIOS_BIOSSIZE_OFFSET) + 1) << 6;
80}
81
82static void __init find_tokens(const struct dmi_header *dm, void *dummy)
83{
84	switch (dm->type) {
85	case 0x0: /* Extern BIOS */
86		parse_bios_table(dm);
87		break;
88	case 0x4: /* Calling interface */
89		parse_cpu_table(dm);
90		break;
91	}
92}
93
94static void __init smbios_parse(void)
95{
96	b_info.bios_vendor = (void *)dmi_get_system_info(DMI_BIOS_VENDOR);
97	b_info.bios_version = (void *)dmi_get_system_info(DMI_BIOS_VERSION);
98	b_info.bios_release_date = (void *)dmi_get_system_info(DMI_BIOS_DATE);
99	b_info.board_vendor = (void *)dmi_get_system_info(DMI_BOARD_VENDOR);
100	b_info.board_name = (void *)dmi_get_system_info(DMI_BOARD_NAME);
101	dmi_walk(find_tokens, NULL);
102}
103
104void __init early_init(void)
105{
106	init_environ();
107	efi_init();
108	memblock_init();
109}
110
111void __init platform_init(void)
112{
113	arch_reserve_vmcore();
114	arch_parse_crashkernel();
115
116#ifdef CONFIG_ACPI_TABLE_UPGRADE
117	acpi_table_upgrade();
118#endif
119#ifdef CONFIG_ACPI
120	acpi_gbl_use_default_register_widths = false;
121	acpi_boot_table_init();
122#endif
123
124#ifdef CONFIG_NUMA
125	init_numa_memory();
126#endif
127	dmi_setup();
128	smbios_parse();
129	pr_info("The BIOS Version: %s\n",b_info.bios_version);
130
131	efi_runtime_init();
132
133	register_smp_ops(&loongson3_smp_ops);
134}
135