162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Based on Ocelot Linux port, which is
462306a36Sopenharmony_ci * Copyright 2001 MontaVista Software Inc.
562306a36Sopenharmony_ci * Author: jsun@mvista.com or jsun@junsun.net
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright 2003 ICT CAS
862306a36Sopenharmony_ci * Author: Michael Guo <guoyi@ict.ac.cn>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Copyright (C) 2007 Lemote Inc. & Institute of Computing Technology
1162306a36Sopenharmony_ci * Author: Fuxin Zhang, zhangfx@lemote.com
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Copyright (C) 2009 Lemote Inc.
1462306a36Sopenharmony_ci * Author: Wu Zhangjin, wuzhangjin@gmail.com
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/dma-map-ops.h>
1862306a36Sopenharmony_ci#include <linux/export.h>
1962306a36Sopenharmony_ci#include <linux/pci_ids.h>
2062306a36Sopenharmony_ci#include <asm/bootinfo.h>
2162306a36Sopenharmony_ci#include <loongson.h>
2262306a36Sopenharmony_ci#include <boot_param.h>
2362306a36Sopenharmony_ci#include <builtin_dtbs.h>
2462306a36Sopenharmony_ci#include <workarounds.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define HOST_BRIDGE_CONFIG_ADDR	((void __iomem *)TO_UNCAC(0x1a000000))
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ciu32 cpu_clock_freq;
2962306a36Sopenharmony_ciEXPORT_SYMBOL(cpu_clock_freq);
3062306a36Sopenharmony_cistruct efi_memory_map_loongson *loongson_memmap;
3162306a36Sopenharmony_cistruct loongson_system_configuration loongson_sysconf;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct board_devices *eboard;
3462306a36Sopenharmony_cistruct interface_info *einter;
3562306a36Sopenharmony_cistruct loongson_special_attribute *especial;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ciu64 loongson_chipcfg[MAX_PACKAGES] = {0xffffffffbfc00180};
3862306a36Sopenharmony_ciu64 loongson_chiptemp[MAX_PACKAGES];
3962306a36Sopenharmony_ciu64 loongson_freqctrl[MAX_PACKAGES];
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ciunsigned long long smp_group[4];
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ciconst char *get_system_type(void)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	return "Generic Loongson64 System";
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_civoid __init prom_dtb_init_env(void)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	if ((fw_arg2 < CKSEG0 || fw_arg2 > CKSEG1)
5262306a36Sopenharmony_ci		&& (fw_arg2 < XKPHYS || fw_arg2 > XKSEG))
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci		loongson_fdt_blob = __dtb_loongson64_2core_2k1000_begin;
5562306a36Sopenharmony_ci	else
5662306a36Sopenharmony_ci		loongson_fdt_blob = (void *)fw_arg2;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_civoid __init prom_lefi_init_env(void)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct boot_params *boot_p;
6262306a36Sopenharmony_ci	struct loongson_params *loongson_p;
6362306a36Sopenharmony_ci	struct system_loongson *esys;
6462306a36Sopenharmony_ci	struct efi_cpuinfo_loongson *ecpu;
6562306a36Sopenharmony_ci	struct irq_source_routing_table *eirq_source;
6662306a36Sopenharmony_ci	u32 id;
6762306a36Sopenharmony_ci	u16 vendor;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	/* firmware arguments are initialized in head.S */
7062306a36Sopenharmony_ci	boot_p = (struct boot_params *)fw_arg2;
7162306a36Sopenharmony_ci	loongson_p = &(boot_p->efi.smbios.lp);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	esys = (struct system_loongson *)
7462306a36Sopenharmony_ci		((u64)loongson_p + loongson_p->system_offset);
7562306a36Sopenharmony_ci	ecpu = (struct efi_cpuinfo_loongson *)
7662306a36Sopenharmony_ci		((u64)loongson_p + loongson_p->cpu_offset);
7762306a36Sopenharmony_ci	eboard = (struct board_devices *)
7862306a36Sopenharmony_ci		((u64)loongson_p + loongson_p->boarddev_table_offset);
7962306a36Sopenharmony_ci	einter = (struct interface_info *)
8062306a36Sopenharmony_ci		((u64)loongson_p + loongson_p->interface_offset);
8162306a36Sopenharmony_ci	especial = (struct loongson_special_attribute *)
8262306a36Sopenharmony_ci		((u64)loongson_p + loongson_p->special_offset);
8362306a36Sopenharmony_ci	eirq_source = (struct irq_source_routing_table *)
8462306a36Sopenharmony_ci		((u64)loongson_p + loongson_p->irq_offset);
8562306a36Sopenharmony_ci	loongson_memmap = (struct efi_memory_map_loongson *)
8662306a36Sopenharmony_ci		((u64)loongson_p + loongson_p->memory_offset);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	cpu_clock_freq = ecpu->cpu_clock_freq;
8962306a36Sopenharmony_ci	loongson_sysconf.cputype = ecpu->cputype;
9062306a36Sopenharmony_ci	switch (ecpu->cputype) {
9162306a36Sopenharmony_ci	case Legacy_3A:
9262306a36Sopenharmony_ci	case Loongson_3A:
9362306a36Sopenharmony_ci		loongson_sysconf.cores_per_node = 4;
9462306a36Sopenharmony_ci		loongson_sysconf.cores_per_package = 4;
9562306a36Sopenharmony_ci		smp_group[0] = 0x900000003ff01000;
9662306a36Sopenharmony_ci		smp_group[1] = 0x900010003ff01000;
9762306a36Sopenharmony_ci		smp_group[2] = 0x900020003ff01000;
9862306a36Sopenharmony_ci		smp_group[3] = 0x900030003ff01000;
9962306a36Sopenharmony_ci		loongson_chipcfg[0] = 0x900000001fe00180;
10062306a36Sopenharmony_ci		loongson_chipcfg[1] = 0x900010001fe00180;
10162306a36Sopenharmony_ci		loongson_chipcfg[2] = 0x900020001fe00180;
10262306a36Sopenharmony_ci		loongson_chipcfg[3] = 0x900030001fe00180;
10362306a36Sopenharmony_ci		loongson_chiptemp[0] = 0x900000001fe0019c;
10462306a36Sopenharmony_ci		loongson_chiptemp[1] = 0x900010001fe0019c;
10562306a36Sopenharmony_ci		loongson_chiptemp[2] = 0x900020001fe0019c;
10662306a36Sopenharmony_ci		loongson_chiptemp[3] = 0x900030001fe0019c;
10762306a36Sopenharmony_ci		loongson_freqctrl[0] = 0x900000001fe001d0;
10862306a36Sopenharmony_ci		loongson_freqctrl[1] = 0x900010001fe001d0;
10962306a36Sopenharmony_ci		loongson_freqctrl[2] = 0x900020001fe001d0;
11062306a36Sopenharmony_ci		loongson_freqctrl[3] = 0x900030001fe001d0;
11162306a36Sopenharmony_ci		loongson_sysconf.workarounds = WORKAROUND_CPUFREQ;
11262306a36Sopenharmony_ci		break;
11362306a36Sopenharmony_ci	case Legacy_3B:
11462306a36Sopenharmony_ci	case Loongson_3B:
11562306a36Sopenharmony_ci		loongson_sysconf.cores_per_node = 4; /* One chip has 2 nodes */
11662306a36Sopenharmony_ci		loongson_sysconf.cores_per_package = 8;
11762306a36Sopenharmony_ci		smp_group[0] = 0x900000003ff01000;
11862306a36Sopenharmony_ci		smp_group[1] = 0x900010003ff05000;
11962306a36Sopenharmony_ci		smp_group[2] = 0x900020003ff09000;
12062306a36Sopenharmony_ci		smp_group[3] = 0x900030003ff0d000;
12162306a36Sopenharmony_ci		loongson_chipcfg[0] = 0x900000001fe00180;
12262306a36Sopenharmony_ci		loongson_chipcfg[1] = 0x900020001fe00180;
12362306a36Sopenharmony_ci		loongson_chipcfg[2] = 0x900040001fe00180;
12462306a36Sopenharmony_ci		loongson_chipcfg[3] = 0x900060001fe00180;
12562306a36Sopenharmony_ci		loongson_chiptemp[0] = 0x900000001fe0019c;
12662306a36Sopenharmony_ci		loongson_chiptemp[1] = 0x900020001fe0019c;
12762306a36Sopenharmony_ci		loongson_chiptemp[2] = 0x900040001fe0019c;
12862306a36Sopenharmony_ci		loongson_chiptemp[3] = 0x900060001fe0019c;
12962306a36Sopenharmony_ci		loongson_freqctrl[0] = 0x900000001fe001d0;
13062306a36Sopenharmony_ci		loongson_freqctrl[1] = 0x900020001fe001d0;
13162306a36Sopenharmony_ci		loongson_freqctrl[2] = 0x900040001fe001d0;
13262306a36Sopenharmony_ci		loongson_freqctrl[3] = 0x900060001fe001d0;
13362306a36Sopenharmony_ci		loongson_sysconf.workarounds = WORKAROUND_CPUHOTPLUG;
13462306a36Sopenharmony_ci		break;
13562306a36Sopenharmony_ci	default:
13662306a36Sopenharmony_ci		loongson_sysconf.cores_per_node = 1;
13762306a36Sopenharmony_ci		loongson_sysconf.cores_per_package = 1;
13862306a36Sopenharmony_ci		loongson_chipcfg[0] = 0x900000001fe00180;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	loongson_sysconf.nr_cpus = ecpu->nr_cpus;
14262306a36Sopenharmony_ci	loongson_sysconf.boot_cpu_id = ecpu->cpu_startup_core_id;
14362306a36Sopenharmony_ci	loongson_sysconf.reserved_cpus_mask = ecpu->reserved_cores_mask;
14462306a36Sopenharmony_ci	if (ecpu->nr_cpus > NR_CPUS || ecpu->nr_cpus == 0)
14562306a36Sopenharmony_ci		loongson_sysconf.nr_cpus = NR_CPUS;
14662306a36Sopenharmony_ci	loongson_sysconf.nr_nodes = (loongson_sysconf.nr_cpus +
14762306a36Sopenharmony_ci		loongson_sysconf.cores_per_node - 1) /
14862306a36Sopenharmony_ci		loongson_sysconf.cores_per_node;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	loongson_sysconf.dma_mask_bits = eirq_source->dma_mask_bits;
15162306a36Sopenharmony_ci	if (loongson_sysconf.dma_mask_bits < 32 ||
15262306a36Sopenharmony_ci			loongson_sysconf.dma_mask_bits > 64) {
15362306a36Sopenharmony_ci		loongson_sysconf.dma_mask_bits = 32;
15462306a36Sopenharmony_ci		dma_default_coherent = true;
15562306a36Sopenharmony_ci	} else {
15662306a36Sopenharmony_ci		dma_default_coherent = !eirq_source->dma_noncoherent;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	pr_info("Firmware: Coherent DMA: %s\n", dma_default_coherent ? "on" : "off");
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	loongson_sysconf.restart_addr = boot_p->reset_system.ResetWarm;
16262306a36Sopenharmony_ci	loongson_sysconf.poweroff_addr = boot_p->reset_system.Shutdown;
16362306a36Sopenharmony_ci	loongson_sysconf.suspend_addr = boot_p->reset_system.DoSuspend;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	loongson_sysconf.vgabios_addr = boot_p->efi.smbios.vga_bios;
16662306a36Sopenharmony_ci	pr_debug("Shutdown Addr: %llx, Restart Addr: %llx, VBIOS Addr: %llx\n",
16762306a36Sopenharmony_ci		loongson_sysconf.poweroff_addr, loongson_sysconf.restart_addr,
16862306a36Sopenharmony_ci		loongson_sysconf.vgabios_addr);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	loongson_sysconf.workarounds |= esys->workarounds;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	pr_info("CpuClock = %u\n", cpu_clock_freq);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* Read the ID of PCI host bridge to detect bridge type */
17562306a36Sopenharmony_ci	id = readl(HOST_BRIDGE_CONFIG_ADDR);
17662306a36Sopenharmony_ci	vendor = id & 0xffff;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	switch (vendor) {
17962306a36Sopenharmony_ci	case PCI_VENDOR_ID_LOONGSON:
18062306a36Sopenharmony_ci		pr_info("The bridge chip is LS7A\n");
18162306a36Sopenharmony_ci		loongson_sysconf.bridgetype = LS7A;
18262306a36Sopenharmony_ci		loongson_sysconf.early_config = ls7a_early_config;
18362306a36Sopenharmony_ci		break;
18462306a36Sopenharmony_ci	case PCI_VENDOR_ID_AMD:
18562306a36Sopenharmony_ci	case PCI_VENDOR_ID_ATI:
18662306a36Sopenharmony_ci		pr_info("The bridge chip is RS780E or SR5690\n");
18762306a36Sopenharmony_ci		loongson_sysconf.bridgetype = RS780E;
18862306a36Sopenharmony_ci		loongson_sysconf.early_config = rs780e_early_config;
18962306a36Sopenharmony_ci		break;
19062306a36Sopenharmony_ci	default:
19162306a36Sopenharmony_ci		pr_info("The bridge chip is VIRTUAL\n");
19262306a36Sopenharmony_ci		loongson_sysconf.bridgetype = VIRTUAL;
19362306a36Sopenharmony_ci		loongson_sysconf.early_config = virtual_early_config;
19462306a36Sopenharmony_ci		loongson_fdt_blob = __dtb_loongson64v_4core_virtio_begin;
19562306a36Sopenharmony_ci		break;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if ((read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64C) {
19962306a36Sopenharmony_ci		switch (read_c0_prid() & PRID_REV_MASK) {
20062306a36Sopenharmony_ci		case PRID_REV_LOONGSON3A_R1:
20162306a36Sopenharmony_ci		case PRID_REV_LOONGSON3A_R2_0:
20262306a36Sopenharmony_ci		case PRID_REV_LOONGSON3A_R2_1:
20362306a36Sopenharmony_ci		case PRID_REV_LOONGSON3A_R3_0:
20462306a36Sopenharmony_ci		case PRID_REV_LOONGSON3A_R3_1:
20562306a36Sopenharmony_ci			switch (loongson_sysconf.bridgetype) {
20662306a36Sopenharmony_ci			case LS7A:
20762306a36Sopenharmony_ci				loongson_fdt_blob = __dtb_loongson64c_4core_ls7a_begin;
20862306a36Sopenharmony_ci				break;
20962306a36Sopenharmony_ci			case RS780E:
21062306a36Sopenharmony_ci				loongson_fdt_blob = __dtb_loongson64c_4core_rs780e_begin;
21162306a36Sopenharmony_ci				break;
21262306a36Sopenharmony_ci			default:
21362306a36Sopenharmony_ci				break;
21462306a36Sopenharmony_ci			}
21562306a36Sopenharmony_ci			break;
21662306a36Sopenharmony_ci		case PRID_REV_LOONGSON3B_R1:
21762306a36Sopenharmony_ci		case PRID_REV_LOONGSON3B_R2:
21862306a36Sopenharmony_ci			if (loongson_sysconf.bridgetype == RS780E)
21962306a36Sopenharmony_ci				loongson_fdt_blob = __dtb_loongson64c_8core_rs780e_begin;
22062306a36Sopenharmony_ci			break;
22162306a36Sopenharmony_ci		default:
22262306a36Sopenharmony_ci			break;
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci	} else if ((read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64G) {
22562306a36Sopenharmony_ci		if (loongson_sysconf.bridgetype == LS7A)
22662306a36Sopenharmony_ci			loongson_fdt_blob = __dtb_loongson64g_4core_ls7a_begin;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (!loongson_fdt_blob)
23062306a36Sopenharmony_ci		pr_err("Failed to determine built-in Loongson64 dtb\n");
23162306a36Sopenharmony_ci}
232