Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0
if MACH_LOONGSON64
config RS780_HPET
bool "RS780/SBX00 HPET Timer"
depends on MACH_LOONGSON64
depends on BROKEN
select MIPS_EXTERNAL_TIMER
help
This option enables the hpet timer of AMD RS780/SBX00.
Note: This driver is doing some dangerous hack. Please only enable
it on RS780E systems.
endif # MACH_LOONGSON64
+14
View File
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for Loongson-3 family machines
#
obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o dma.o \
setup.o init.o env.o time.o reset.o \
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_NUMA) += numa.o
obj-$(CONFIG_RS780_HPET) += hpet.o
obj-$(CONFIG_SUSPEND) += pm.o sleeper.o
obj-$(CONFIG_PCI_QUIRKS) += vbios_quirk.o
obj-$(CONFIG_CPU_LOONGSON3_CPUCFG_EMULATION) += cpucfg-emul.o
obj-$(CONFIG_SYSFS) += boardinfo.o
@@ -0,0 +1,7 @@
#
# Loongson Machines' Support
#
cflags-$(CONFIG_MACH_LOONGSON64) += -I$(srctree)/arch/mips/include/asm/mach-loongson64
cflags-$(CONFIG_CC_HAS_MNO_BRANCH_LIKELY) += -mno-branch-likely
load-$(CONFIG_CPU_LOONGSON64) += 0xffffffff80200000
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kobject.h>
#include <linux/string.h>
#include <boot_param.h>
static ssize_t boardinfo_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
char board_manufacturer[64];
char *tmp_board_manufacturer = board_manufacturer;
char bios_vendor[64];
char *tmp_bios_vendor = bios_vendor;
strscpy_pad(board_manufacturer, eboard->name);
strscpy_pad(bios_vendor, einter->description);
return sprintf(buf,
"Board Info\n"
"Manufacturer\t\t: %s\n"
"Board Name\t\t: %s\n"
"Family\t\t\t: LOONGSON3\n\n"
"BIOS Info\n"
"Vendor\t\t\t: %s\n"
"Version\t\t\t: %s\n"
"Release Date\t\t: %s\n",
strsep(&tmp_board_manufacturer, "-"),
eboard->name,
strsep(&tmp_bios_vendor, "-"),
einter->description,
especial->special_name);
}
static struct kobj_attribute boardinfo_attr = __ATTR(boardinfo, 0444,
boardinfo_show, NULL);
static int __init boardinfo_init(void)
{
struct kobject *lefi_kobj;
lefi_kobj = kobject_create_and_add("lefi", firmware_kobj);
if (!lefi_kobj) {
pr_err("lefi: Firmware registration failed.\n");
return -ENOMEM;
}
return sysfs_create_file(lefi_kobj, &boardinfo_attr.attr);
}
late_initcall(boardinfo_init);
+341
View File
@@ -0,0 +1,341 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2014 Lemote Corporation.
* written by Huacai Chen <chenhc@lemote.com>
*
* based on arch/mips/cavium-octeon/cpu.c
* Copyright (C) 2009 Wind River Systems,
* written by Ralf Baechle <ralf@linux-mips.org>
*/
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/notifier.h>
#include <linux/ptrace.h>
#include <linux/uaccess.h>
#include <linux/sched/signal.h>
#include <asm/fpu.h>
#include <asm/cop2.h>
#include <asm/inst.h>
#include <asm/branch.h>
#include <asm/current.h>
#include <asm/mipsregs.h>
#include <asm/unaligned-emul.h>
static int loongson_cu2_call(struct notifier_block *nfb, unsigned long action,
void *data)
{
unsigned int res, fpu_owned;
unsigned long ra, value, value_next;
union mips_instruction insn;
int fr = !test_thread_flag(TIF_32BIT_FPREGS);
struct pt_regs *regs = (struct pt_regs *)data;
void __user *addr = (void __user *)regs->cp0_badvaddr;
unsigned int __user *pc = (unsigned int __user *)exception_epc(regs);
ra = regs->regs[31];
__get_user(insn.word, pc);
switch (action) {
case CU2_EXCEPTION:
preempt_disable();
fpu_owned = __is_fpu_owner();
if (!fr)
set_c0_status(ST0_CU1 | ST0_CU2);
else
set_c0_status(ST0_CU1 | ST0_CU2 | ST0_FR);
enable_fpu_hazard();
KSTK_STATUS(current) |= (ST0_CU1 | ST0_CU2);
if (fr)
KSTK_STATUS(current) |= ST0_FR;
else
KSTK_STATUS(current) &= ~ST0_FR;
/* If FPU is owned, we needn't init or restore fp */
if (!fpu_owned) {
set_thread_flag(TIF_USEDFPU);
init_fp_ctx(current);
_restore_fp(current);
}
preempt_enable();
return NOTIFY_STOP; /* Don't call default notifier */
case CU2_LWC2_OP:
if (insn.loongson3_lswc2_format.ls == 0)
goto sigbus;
if (insn.loongson3_lswc2_format.fr == 0) { /* gslq */
if (!access_ok(addr, 16))
goto sigbus;
LoadDW(addr, value, res);
if (res)
goto fault;
LoadDW(addr + 8, value_next, res);
if (res)
goto fault;
regs->regs[insn.loongson3_lswc2_format.rt] = value;
regs->regs[insn.loongson3_lswc2_format.rq] = value_next;
compute_return_epc(regs);
} else { /* gslqc1 */
if (!access_ok(addr, 16))
goto sigbus;
lose_fpu(1);
LoadDW(addr, value, res);
if (res)
goto fault;
LoadDW(addr + 8, value_next, res);
if (res)
goto fault;
set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rt], 0, value);
set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rq], 0, value_next);
compute_return_epc(regs);
own_fpu(1);
}
return NOTIFY_STOP; /* Don't call default notifier */
case CU2_SWC2_OP:
if (insn.loongson3_lswc2_format.ls == 0)
goto sigbus;
if (insn.loongson3_lswc2_format.fr == 0) { /* gssq */
if (!access_ok(addr, 16))
goto sigbus;
/* write upper 8 bytes first */
value_next = regs->regs[insn.loongson3_lswc2_format.rq];
StoreDW(addr + 8, value_next, res);
if (res)
goto fault;
value = regs->regs[insn.loongson3_lswc2_format.rt];
StoreDW(addr, value, res);
if (res)
goto fault;
compute_return_epc(regs);
} else { /* gssqc1 */
if (!access_ok(addr, 16))
goto sigbus;
lose_fpu(1);
value_next = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rq], 0);
StoreDW(addr + 8, value_next, res);
if (res)
goto fault;
value = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rt], 0);
StoreDW(addr, value, res);
if (res)
goto fault;
compute_return_epc(regs);
own_fpu(1);
}
return NOTIFY_STOP; /* Don't call default notifier */
case CU2_LDC2_OP:
switch (insn.loongson3_lsdc2_format.opcode1) {
/*
* Loongson-3 overridden ldc2 instructions.
* opcode1 instruction
* 0x1 gslhx: load 2 bytes to GPR
* 0x2 gslwx: load 4 bytes to GPR
* 0x3 gsldx: load 8 bytes to GPR
* 0x6 gslwxc1: load 4 bytes to FPR
* 0x7 gsldxc1: load 8 bytes to FPR
*/
case 0x1:
if (!access_ok(addr, 2))
goto sigbus;
LoadHW(addr, value, res);
if (res)
goto fault;
compute_return_epc(regs);
regs->regs[insn.loongson3_lsdc2_format.rt] = value;
break;
case 0x2:
if (!access_ok(addr, 4))
goto sigbus;
LoadW(addr, value, res);
if (res)
goto fault;
compute_return_epc(regs);
regs->regs[insn.loongson3_lsdc2_format.rt] = value;
break;
case 0x3:
if (!access_ok(addr, 8))
goto sigbus;
LoadDW(addr, value, res);
if (res)
goto fault;
compute_return_epc(regs);
regs->regs[insn.loongson3_lsdc2_format.rt] = value;
break;
case 0x6:
die_if_kernel("Unaligned FP access in kernel code", regs);
BUG_ON(!used_math());
if (!access_ok(addr, 4))
goto sigbus;
lose_fpu(1);
LoadW(addr, value, res);
if (res)
goto fault;
set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0, value);
compute_return_epc(regs);
own_fpu(1);
break;
case 0x7:
die_if_kernel("Unaligned FP access in kernel code", regs);
BUG_ON(!used_math());
if (!access_ok(addr, 8))
goto sigbus;
lose_fpu(1);
LoadDW(addr, value, res);
if (res)
goto fault;
set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0, value);
compute_return_epc(regs);
own_fpu(1);
break;
}
return NOTIFY_STOP; /* Don't call default notifier */
case CU2_SDC2_OP:
switch (insn.loongson3_lsdc2_format.opcode1) {
/*
* Loongson-3 overridden sdc2 instructions.
* opcode1 instruction
* 0x1 gsshx: store 2 bytes from GPR
* 0x2 gsswx: store 4 bytes from GPR
* 0x3 gssdx: store 8 bytes from GPR
* 0x6 gsswxc1: store 4 bytes from FPR
* 0x7 gssdxc1: store 8 bytes from FPR
*/
case 0x1:
if (!access_ok(addr, 2))
goto sigbus;
compute_return_epc(regs);
value = regs->regs[insn.loongson3_lsdc2_format.rt];
StoreHW(addr, value, res);
if (res)
goto fault;
break;
case 0x2:
if (!access_ok(addr, 4))
goto sigbus;
compute_return_epc(regs);
value = regs->regs[insn.loongson3_lsdc2_format.rt];
StoreW(addr, value, res);
if (res)
goto fault;
break;
case 0x3:
if (!access_ok(addr, 8))
goto sigbus;
compute_return_epc(regs);
value = regs->regs[insn.loongson3_lsdc2_format.rt];
StoreDW(addr, value, res);
if (res)
goto fault;
break;
case 0x6:
die_if_kernel("Unaligned FP access in kernel code", regs);
BUG_ON(!used_math());
if (!access_ok(addr, 4))
goto sigbus;
lose_fpu(1);
value = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0);
StoreW(addr, value, res);
if (res)
goto fault;
compute_return_epc(regs);
own_fpu(1);
break;
case 0x7:
die_if_kernel("Unaligned FP access in kernel code", regs);
BUG_ON(!used_math());
if (!access_ok(addr, 8))
goto sigbus;
lose_fpu(1);
value = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0);
StoreDW(addr, value, res);
if (res)
goto fault;
compute_return_epc(regs);
own_fpu(1);
break;
}
return NOTIFY_STOP; /* Don't call default notifier */
}
return NOTIFY_OK; /* Let default notifier send signals */
fault:
/* roll back jump/branch */
regs->regs[31] = ra;
regs->cp0_epc = (unsigned long)pc;
/* Did we have an exception handler installed? */
if (fixup_exception(regs))
return NOTIFY_STOP; /* Don't call default notifier */
die_if_kernel("Unhandled kernel unaligned access", regs);
force_sig(SIGSEGV);
return NOTIFY_STOP; /* Don't call default notifier */
sigbus:
die_if_kernel("Unhandled kernel unaligned access", regs);
force_sig(SIGBUS);
return NOTIFY_STOP; /* Don't call default notifier */
}
static int __init loongson_cu2_setup(void)
{
return cu2_notifier(loongson_cu2_call, 0);
}
early_initcall(loongson_cu2_setup);
@@ -0,0 +1,227 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/smp.h>
#include <linux/types.h>
#include <asm/cpu.h>
#include <asm/cpu-info.h>
#include <asm/elf.h>
#include <loongson_regs.h>
#include <cpucfg-emul.h>
static bool is_loongson(struct cpuinfo_mips *c)
{
switch (c->processor_id & PRID_COMP_MASK) {
case PRID_COMP_LEGACY:
return ((c->processor_id & PRID_IMP_MASK) ==
PRID_IMP_LOONGSON_64C);
case PRID_COMP_LOONGSON:
return true;
default:
return false;
}
}
static u32 get_loongson_fprev(struct cpuinfo_mips *c)
{
return c->fpu_id & LOONGSON_FPREV_MASK;
}
static bool cpu_has_uca(void)
{
u32 diag = read_c0_diag();
u32 new_diag;
if (diag & LOONGSON_DIAG_UCAC)
/* UCA is already enabled. */
return true;
/* See if UCAC bit can be flipped on. This should be safe. */
new_diag = diag | LOONGSON_DIAG_UCAC;
write_c0_diag(new_diag);
new_diag = read_c0_diag();
write_c0_diag(diag);
return (new_diag & LOONGSON_DIAG_UCAC) != 0;
}
static void probe_uca(struct cpuinfo_mips *c)
{
if (cpu_has_uca())
c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LSUCA;
}
static void decode_loongson_config6(struct cpuinfo_mips *c)
{
u32 config6 = read_c0_config6();
if (config6 & LOONGSON_CONF6_SFBEN)
c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_SFBP;
if (config6 & LOONGSON_CONF6_LLEXC)
c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LLEXC;
if (config6 & LOONGSON_CONF6_SCRAND)
c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_SCRAND;
}
static void patch_cpucfg_sel1(struct cpuinfo_mips *c)
{
u64 ases = c->ases;
u64 options = c->options;
u32 data = c->loongson3_cpucfg_data[0];
if (options & MIPS_CPU_FPU) {
data |= LOONGSON_CFG1_FP;
data |= get_loongson_fprev(c) << LOONGSON_CFG1_FPREV_OFFSET;
}
if (ases & MIPS_ASE_LOONGSON_MMI)
data |= LOONGSON_CFG1_MMI;
if (ases & MIPS_ASE_MSA)
data |= LOONGSON_CFG1_MSA1;
c->loongson3_cpucfg_data[0] = data;
}
static void patch_cpucfg_sel2(struct cpuinfo_mips *c)
{
u64 ases = c->ases;
u64 options = c->options;
u32 data = c->loongson3_cpucfg_data[1];
if (ases & MIPS_ASE_LOONGSON_EXT)
data |= LOONGSON_CFG2_LEXT1;
if (ases & MIPS_ASE_LOONGSON_EXT2)
data |= LOONGSON_CFG2_LEXT2;
if (options & MIPS_CPU_LDPTE)
data |= LOONGSON_CFG2_LSPW;
if (ases & MIPS_ASE_VZ)
data |= LOONGSON_CFG2_LVZP;
else
data &= ~LOONGSON_CFG2_LVZREV;
c->loongson3_cpucfg_data[1] = data;
}
static void patch_cpucfg_sel3(struct cpuinfo_mips *c)
{
u64 ases = c->ases;
u32 data = c->loongson3_cpucfg_data[2];
if (ases & MIPS_ASE_LOONGSON_CAM) {
data |= LOONGSON_CFG3_LCAMP;
} else {
data &= ~LOONGSON_CFG3_LCAMREV;
data &= ~LOONGSON_CFG3_LCAMNUM;
data &= ~LOONGSON_CFG3_LCAMKW;
data &= ~LOONGSON_CFG3_LCAMVW;
}
c->loongson3_cpucfg_data[2] = data;
}
void loongson3_cpucfg_synthesize_data(struct cpuinfo_mips *c)
{
/* Only engage the logic on Loongson processors. */
if (!is_loongson(c))
return;
/* CPUs with CPUCFG support don't need to synthesize anything. */
if (cpu_has_cfg())
goto have_cpucfg_now;
c->loongson3_cpucfg_data[0] = 0;
c->loongson3_cpucfg_data[1] = 0;
c->loongson3_cpucfg_data[2] = 0;
/* Add CPUCFG features non-discoverable otherwise. */
switch (c->processor_id & (PRID_IMP_MASK | PRID_REV_MASK)) {
case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_0:
case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_1:
case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_2:
case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_3:
decode_loongson_config6(c);
probe_uca(c);
c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 |
LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LLSYNC |
LOONGSON_CFG1_TGTSYNC);
c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 |
LOONGSON_CFG2_LBT2 | LOONGSON_CFG2_LPMP |
LOONGSON_CFG2_LPM_REV2);
c->loongson3_cpucfg_data[2] = 0;
break;
case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R1:
c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 |
LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LSUCA |
LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC);
c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 |
LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1);
c->loongson3_cpucfg_data[2] |= (
LOONGSON_CFG3_LCAM_REV1 |
LOONGSON_CFG3_LCAMNUM_REV1 |
LOONGSON_CFG3_LCAMKW_REV1 |
LOONGSON_CFG3_LCAMVW_REV1);
break;
case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3B_R1:
case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3B_R2:
c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 |
LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LSUCA |
LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC);
c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 |
LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1);
c->loongson3_cpucfg_data[2] |= (
LOONGSON_CFG3_LCAM_REV1 |
LOONGSON_CFG3_LCAMNUM_REV1 |
LOONGSON_CFG3_LCAMKW_REV1 |
LOONGSON_CFG3_LCAMVW_REV1);
break;
case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_0:
case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_1:
case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R3_0:
case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R3_1:
decode_loongson_config6(c);
probe_uca(c);
c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_CNT64 |
LOONGSON_CFG1_LSLDR0 | LOONGSON_CFG1_LSPREF |
LOONGSON_CFG1_LSPREFX | LOONGSON_CFG1_LSSYNCI |
LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC);
c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 |
LOONGSON_CFG2_LBT2 | LOONGSON_CFG2_LBTMMU |
LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1 |
LOONGSON_CFG2_LVZ_REV1);
c->loongson3_cpucfg_data[2] |= (LOONGSON_CFG3_LCAM_REV1 |
LOONGSON_CFG3_LCAMNUM_REV1 |
LOONGSON_CFG3_LCAMKW_REV1 |
LOONGSON_CFG3_LCAMVW_REV1);
break;
default:
/* It is possible that some future Loongson cores still do
* not have CPUCFG, so do not emulate anything for these
* cores.
*/
return;
}
/* This feature is set by firmware, but all known Loongson-64 systems
* are configured this way.
*/
c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_CDMAP;
/* Patch in dynamically probed bits. */
patch_cpucfg_sel1(c);
patch_cpucfg_sel2(c);
patch_cpucfg_sel3(c);
have_cpucfg_now:
/* We have usable CPUCFG now, emulated or not.
* Announce CPUCFG availability to userspace via hwcap.
*/
elf_hwcap |= HWCAP_LOONGSON_CPUCFG;
}
+29
View File
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/dma-direct.h>
#include <linux/init.h>
#include <linux/swiotlb.h>
#include <asm/bootinfo.h>
#include <boot_param.h>
dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
{
/* We extract 2bit node id (bit 44~47, only bit 44~45 used now) from
* Loongson-3's 48bit address space and embed it into 40bit */
long nid = (paddr >> 44) & 0x3;
return ((nid << 44) ^ paddr) | (nid << node_id_offset);
}
phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr)
{
/* We extract 2bit node id (bit 44~47, only bit 44~45 used now) from
* Loongson-3's 48bit address space and embed it into 40bit */
long nid = (daddr >> node_id_offset) & 0x3;
return ((nid << node_id_offset) ^ daddr) | (nid << 44);
}
void __init plat_swiotlb_setup(void)
{
swiotlb_init(true, SWIOTLB_VERBOSE);
}
+354
View File
@@ -0,0 +1,354 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Based on Ocelot Linux port, which is
* Copyright 2001 MontaVista Software Inc.
* Author: jsun@mvista.com or jsun@junsun.net
*
* Copyright 2003 ICT CAS
* Author: Michael Guo <guoyi@ict.ac.cn>
*
* Copyright (C) 2007 Lemote Inc. & Institute of Computing Technology
* Author: Fuxin Zhang, zhangfx@lemote.com
*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <linux/dma-map-ops.h>
#include <linux/export.h>
#include <linux/libfdt.h>
#include <linux/minmax.h>
#include <linux/pci_ids.h>
#include <linux/serial_core.h>
#include <linux/string_choices.h>
#include <asm/bootinfo.h>
#include <loongson.h>
#include <boot_param.h>
#include <builtin_dtbs.h>
#include <workarounds.h>
#define HOST_BRIDGE_CONFIG_ADDR ((void __iomem *)TO_UNCAC(0x1a000000))
u32 cpu_clock_freq;
EXPORT_SYMBOL(cpu_clock_freq);
struct efi_memory_map_loongson *loongson_memmap;
struct loongson_system_configuration loongson_sysconf;
struct board_devices *eboard;
struct interface_info *einter;
struct loongson_special_attribute *especial;
u64 loongson_chipcfg[MAX_PACKAGES] = {0xffffffffbfc00180};
u64 loongson_chiptemp[MAX_PACKAGES];
u64 loongson_freqctrl[MAX_PACKAGES];
unsigned long long smp_group[4];
const char *get_system_type(void)
{
return "Generic Loongson64 System";
}
void __init prom_dtb_init_env(void)
{
if ((fw_arg2 < CKSEG0 || fw_arg2 > CKSEG1)
&& (fw_arg2 < XKPHYS || fw_arg2 > XKSEG))
loongson_fdt_blob = __dtb_loongson64_2core_2k1000_begin;
else
loongson_fdt_blob = (void *)fw_arg2;
}
static int __init lefi_fixup_fdt_serial(void *fdt, u64 uart_addr, u32 uart_clk)
{
int node, len, depth = -1;
const fdt64_t *reg;
fdt32_t *clk;
for (node = fdt_next_node(fdt, -1, &depth);
node >= 0 && depth >= 0;
node = fdt_next_node(fdt, node, &depth)) {
reg = fdt_getprop(fdt, node, "reg", &len);
if (!reg || len <= 8 || fdt64_ld(reg) != uart_addr)
continue;
clk = fdt_getprop_w(fdt, node, "clock-frequency", &len);
if (!clk) {
pr_warn("UART 0x%llx misses clock-frequency property\n",
uart_addr);
return -ENOENT;
} else if (len != 4) {
pr_warn("UART 0x%llx has invalid clock-frequency property\n",
uart_addr);
return -EINVAL;
}
fdt32_st(clk, uart_clk);
return 0;
}
return -ENODEV;
}
static void __init lefi_fixup_fdt(struct system_loongson *system)
{
static unsigned char fdt_buf[16 << 10] __initdata;
struct uart_device *uartdev;
bool is_loongson64g;
u64 uart_base;
int ret, i;
ret = fdt_open_into(loongson_fdt_blob, fdt_buf, sizeof(fdt_buf));
if (ret) {
pr_err("Failed to open FDT to fix up\n");
return;
}
is_loongson64g = (read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64G;
for (i = 0; i < min(system->nr_uarts, MAX_UARTS); i++) {
uartdev = &system->uarts[i];
/*
* Some firmware does not set nr_uarts properly and passes empty
* items. Ignore them silently.
*/
if (uartdev->uart_base == 0)
continue;
/* Our DT only works with UPIO_MEM. */
if (uartdev->iotype != UPIO_MEM) {
pr_warn("Ignore UART 0x%llx with iotype %u passed by firmware\n",
uartdev->uart_base, uartdev->iotype);
continue;
}
ret = lefi_fixup_fdt_serial(fdt_buf, uartdev->uart_base,
uartdev->uartclk);
/*
* LOONGSON64G's CPU serials are mapped to two different
* addresses, one full-featured but differs from
* previous generations, one fully compatible with them.
*
* It's unspecified that which mapping should uart_base refer
* to, thus we should try fixing up with both.
*/
if (ret == -ENODEV && is_loongson64g) {
switch (uartdev->uart_base) {
case 0x1fe00100:
uart_base = 0x1fe001e0;
break;
case 0x1fe00110:
uart_base = 0x1fe001e8;
break;
case 0x1fe001e0:
uart_base = 0x1fe00100;
break;
case 0x1fe001e8:
uart_base = 0x1fe00110;
break;
default:
pr_err("Unexpected UART address 0x%llx passed by firmware\n",
uartdev->uart_base);
ret = -EINVAL;
goto err_fixup;
}
ret = lefi_fixup_fdt_serial(fdt_buf, uart_base,
uartdev->uartclk);
}
err_fixup:
if (ret)
pr_err("Couldn't fix up FDT node for UART 0x%llx\n",
uartdev->uart_base);
}
loongson_fdt_blob = fdt_buf;
}
void __init prom_lefi_init_env(void)
{
struct boot_params *boot_p;
struct loongson_params *loongson_p;
struct system_loongson *esys;
struct efi_cpuinfo_loongson *ecpu;
struct irq_source_routing_table *eirq_source;
u32 id;
u16 vendor;
/* firmware arguments are initialized in head.S */
boot_p = (struct boot_params *)fw_arg2;
loongson_p = &(boot_p->efi.smbios.lp);
esys = (struct system_loongson *)
((u64)loongson_p + loongson_p->system_offset);
ecpu = (struct efi_cpuinfo_loongson *)
((u64)loongson_p + loongson_p->cpu_offset);
eboard = (struct board_devices *)
((u64)loongson_p + loongson_p->boarddev_table_offset);
einter = (struct interface_info *)
((u64)loongson_p + loongson_p->interface_offset);
especial = (struct loongson_special_attribute *)
((u64)loongson_p + loongson_p->special_offset);
eirq_source = (struct irq_source_routing_table *)
((u64)loongson_p + loongson_p->irq_offset);
loongson_memmap = (struct efi_memory_map_loongson *)
((u64)loongson_p + loongson_p->memory_offset);
cpu_clock_freq = ecpu->cpu_clock_freq;
loongson_sysconf.cputype = ecpu->cputype;
switch (ecpu->cputype) {
case Legacy_2K:
case Loongson_2K:
smp_group[0] = 0x900000001fe11000;
loongson_sysconf.cores_per_node = 2;
loongson_sysconf.cores_per_package = 2;
break;
case Legacy_3A:
case Loongson_3A:
loongson_sysconf.cores_per_node = 4;
loongson_sysconf.cores_per_package = 4;
smp_group[0] = 0x900000003ff01000;
smp_group[1] = 0x900010003ff01000;
smp_group[2] = 0x900020003ff01000;
smp_group[3] = 0x900030003ff01000;
loongson_chipcfg[0] = 0x900000001fe00180;
loongson_chipcfg[1] = 0x900010001fe00180;
loongson_chipcfg[2] = 0x900020001fe00180;
loongson_chipcfg[3] = 0x900030001fe00180;
loongson_chiptemp[0] = 0x900000001fe0019c;
loongson_chiptemp[1] = 0x900010001fe0019c;
loongson_chiptemp[2] = 0x900020001fe0019c;
loongson_chiptemp[3] = 0x900030001fe0019c;
loongson_freqctrl[0] = 0x900000001fe001d0;
loongson_freqctrl[1] = 0x900010001fe001d0;
loongson_freqctrl[2] = 0x900020001fe001d0;
loongson_freqctrl[3] = 0x900030001fe001d0;
loongson_sysconf.workarounds = WORKAROUND_CPUFREQ;
break;
case Legacy_3B:
case Loongson_3B:
loongson_sysconf.cores_per_node = 4; /* One chip has 2 nodes */
loongson_sysconf.cores_per_package = 8;
smp_group[0] = 0x900000003ff01000;
smp_group[1] = 0x900010003ff05000;
smp_group[2] = 0x900020003ff09000;
smp_group[3] = 0x900030003ff0d000;
loongson_chipcfg[0] = 0x900000001fe00180;
loongson_chipcfg[1] = 0x900020001fe00180;
loongson_chipcfg[2] = 0x900040001fe00180;
loongson_chipcfg[3] = 0x900060001fe00180;
loongson_chiptemp[0] = 0x900000001fe0019c;
loongson_chiptemp[1] = 0x900020001fe0019c;
loongson_chiptemp[2] = 0x900040001fe0019c;
loongson_chiptemp[3] = 0x900060001fe0019c;
loongson_freqctrl[0] = 0x900000001fe001d0;
loongson_freqctrl[1] = 0x900020001fe001d0;
loongson_freqctrl[2] = 0x900040001fe001d0;
loongson_freqctrl[3] = 0x900060001fe001d0;
loongson_sysconf.workarounds = WORKAROUND_CPUHOTPLUG;
break;
default:
loongson_sysconf.cores_per_node = 1;
loongson_sysconf.cores_per_package = 1;
loongson_chipcfg[0] = 0x900000001fe00180;
}
loongson_sysconf.nr_cpus = ecpu->nr_cpus;
loongson_sysconf.boot_cpu_id = ecpu->cpu_startup_core_id;
loongson_sysconf.reserved_cpus_mask = ecpu->reserved_cores_mask;
if (ecpu->nr_cpus > NR_CPUS || ecpu->nr_cpus == 0)
loongson_sysconf.nr_cpus = NR_CPUS;
loongson_sysconf.nr_nodes = (loongson_sysconf.nr_cpus +
loongson_sysconf.cores_per_node - 1) /
loongson_sysconf.cores_per_node;
loongson_sysconf.dma_mask_bits = eirq_source->dma_mask_bits;
if (loongson_sysconf.dma_mask_bits < 32 ||
loongson_sysconf.dma_mask_bits > 64) {
loongson_sysconf.dma_mask_bits = 32;
dma_default_coherent = true;
} else {
dma_default_coherent = !eirq_source->dma_noncoherent;
}
pr_info("Firmware: Coherent DMA: %s\n", str_on_off(dma_default_coherent));
loongson_sysconf.restart_addr = boot_p->reset_system.ResetWarm;
loongson_sysconf.poweroff_addr = boot_p->reset_system.Shutdown;
loongson_sysconf.suspend_addr = boot_p->reset_system.DoSuspend;
loongson_sysconf.vgabios_addr = boot_p->efi.smbios.vga_bios;
pr_debug("Shutdown Addr: %llx, Restart Addr: %llx, VBIOS Addr: %llx\n",
loongson_sysconf.poweroff_addr, loongson_sysconf.restart_addr,
loongson_sysconf.vgabios_addr);
loongson_sysconf.workarounds |= esys->workarounds;
pr_info("CpuClock = %u\n", cpu_clock_freq);
/* Read the ID of PCI host bridge to detect bridge type */
id = readl(HOST_BRIDGE_CONFIG_ADDR);
vendor = id & 0xffff;
switch (vendor) {
case PCI_VENDOR_ID_LOONGSON:
pr_info("The bridge chip is LS7A\n");
loongson_sysconf.bridgetype = LS7A;
loongson_sysconf.early_config = ls7a_early_config;
break;
case PCI_VENDOR_ID_AMD:
case PCI_VENDOR_ID_ATI:
pr_info("The bridge chip is RS780E or SR5690\n");
loongson_sysconf.bridgetype = RS780E;
loongson_sysconf.early_config = rs780e_early_config;
break;
default:
pr_info("The bridge chip is VIRTUAL\n");
loongson_sysconf.bridgetype = VIRTUAL;
loongson_sysconf.early_config = virtual_early_config;
loongson_fdt_blob = __dtb_loongson64v_4core_virtio_begin;
break;
}
if ((read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64C) {
switch (read_c0_prid() & PRID_REV_MASK) {
case PRID_REV_LOONGSON3A_R1:
case PRID_REV_LOONGSON3A_R2_0:
case PRID_REV_LOONGSON3A_R2_1:
case PRID_REV_LOONGSON3A_R3_0:
case PRID_REV_LOONGSON3A_R3_1:
switch (loongson_sysconf.bridgetype) {
case LS7A:
loongson_fdt_blob = __dtb_loongson64c_4core_ls7a_begin;
break;
case RS780E:
loongson_fdt_blob = __dtb_loongson64c_4core_rs780e_begin;
break;
default:
break;
}
break;
case PRID_REV_LOONGSON3B_R1:
case PRID_REV_LOONGSON3B_R2:
if (loongson_sysconf.bridgetype == RS780E)
loongson_fdt_blob = __dtb_loongson64c_8core_rs780e_begin;
break;
default:
break;
}
} else if ((read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64R) {
loongson_fdt_blob = __dtb_loongson64_2core_2k1000_begin;
} else if ((read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64G) {
if (loongson_sysconf.bridgetype == LS7A)
loongson_fdt_blob = __dtb_loongson64g_4core_ls7a_begin;
}
if (!loongson_fdt_blob)
pr_err("Failed to determine built-in Loongson64 dtb\n");
else
lefi_fixup_fdt(esys);
}
+285
View File
@@ -0,0 +1,285 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/percpu.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <asm/hpet.h>
#include <asm/time.h>
#define SMBUS_CFG_BASE (loongson_sysconf.ht_control_base + 0x0300a000)
#define SMBUS_PCI_REG40 0x40
#define SMBUS_PCI_REG64 0x64
#define SMBUS_PCI_REGB4 0xb4
#define HPET_MIN_CYCLES 16
#define HPET_MIN_PROG_DELTA (HPET_MIN_CYCLES * 12)
static DEFINE_SPINLOCK(hpet_lock);
DEFINE_PER_CPU(struct clock_event_device, hpet_clockevent_device);
static unsigned int smbus_read(int offset)
{
return *(volatile unsigned int *)(SMBUS_CFG_BASE + offset);
}
static void smbus_write(int offset, int data)
{
*(volatile unsigned int *)(SMBUS_CFG_BASE + offset) = data;
}
static void smbus_enable(int offset, int bit)
{
unsigned int cfg = smbus_read(offset);
cfg |= bit;
smbus_write(offset, cfg);
}
static int hpet_read(int offset)
{
return *(volatile unsigned int *)(HPET_MMIO_ADDR + offset);
}
static void hpet_write(int offset, int data)
{
*(volatile unsigned int *)(HPET_MMIO_ADDR + offset) = data;
}
static void hpet_start_counter(void)
{
unsigned int cfg = hpet_read(HPET_CFG);
cfg |= HPET_CFG_ENABLE;
hpet_write(HPET_CFG, cfg);
}
static void hpet_stop_counter(void)
{
unsigned int cfg = hpet_read(HPET_CFG);
cfg &= ~HPET_CFG_ENABLE;
hpet_write(HPET_CFG, cfg);
}
static void hpet_reset_counter(void)
{
hpet_write(HPET_COUNTER, 0);
hpet_write(HPET_COUNTER + 4, 0);
}
static void hpet_restart_counter(void)
{
hpet_stop_counter();
hpet_reset_counter();
hpet_start_counter();
}
static void hpet_enable_legacy_int(void)
{
/* Do nothing on Loongson-3 */
}
static int hpet_set_state_periodic(struct clock_event_device *evt)
{
int cfg;
spin_lock(&hpet_lock);
pr_info("set clock event to periodic mode!\n");
/* stop counter */
hpet_stop_counter();
/* enables the timer0 to generate a periodic interrupt */
cfg = hpet_read(HPET_T0_CFG);
cfg &= ~HPET_TN_LEVEL;
cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL |
HPET_TN_32BIT;
hpet_write(HPET_T0_CFG, cfg);
/* set the comparator */
hpet_write(HPET_T0_CMP, HPET_COMPARE_VAL);
udelay(1);
hpet_write(HPET_T0_CMP, HPET_COMPARE_VAL);
/* start counter */
hpet_start_counter();
spin_unlock(&hpet_lock);
return 0;
}
static int hpet_set_state_shutdown(struct clock_event_device *evt)
{
int cfg;
spin_lock(&hpet_lock);
cfg = hpet_read(HPET_T0_CFG);
cfg &= ~HPET_TN_ENABLE;
hpet_write(HPET_T0_CFG, cfg);
spin_unlock(&hpet_lock);
return 0;
}
static int hpet_set_state_oneshot(struct clock_event_device *evt)
{
int cfg;
spin_lock(&hpet_lock);
pr_info("set clock event to one shot mode!\n");
cfg = hpet_read(HPET_T0_CFG);
/*
* set timer0 type
* 1 : periodic interrupt
* 0 : non-periodic(oneshot) interrupt
*/
cfg &= ~HPET_TN_PERIODIC;
cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
hpet_write(HPET_T0_CFG, cfg);
spin_unlock(&hpet_lock);
return 0;
}
static int hpet_tick_resume(struct clock_event_device *evt)
{
spin_lock(&hpet_lock);
hpet_enable_legacy_int();
spin_unlock(&hpet_lock);
return 0;
}
static int hpet_next_event(unsigned long delta,
struct clock_event_device *evt)
{
u32 cnt;
s32 res;
cnt = hpet_read(HPET_COUNTER);
cnt += (u32) delta;
hpet_write(HPET_T0_CMP, cnt);
res = (s32)(cnt - hpet_read(HPET_COUNTER));
return res < HPET_MIN_CYCLES ? -ETIME : 0;
}
static irqreturn_t hpet_irq_handler(int irq, void *data)
{
int is_irq;
struct clock_event_device *cd;
unsigned int cpu = smp_processor_id();
is_irq = hpet_read(HPET_STATUS);
if (is_irq & HPET_T0_IRS) {
/* clear the TIMER0 irq status register */
hpet_write(HPET_STATUS, HPET_T0_IRS);
cd = &per_cpu(hpet_clockevent_device, cpu);
cd->event_handler(cd);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
/*
* hpet address assignation and irq setting should be done in bios.
* but pmon don't do this, we just setup here directly.
* The operation under is normal. unfortunately, hpet_setup process
* is before pci initialize.
*
* {
* struct pci_dev *pdev;
*
* pdev = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
* pci_write_config_word(pdev, SMBUS_PCI_REGB4, HPET_ADDR);
*
* ...
* }
*/
static void hpet_setup(void)
{
/* set hpet base address */
smbus_write(SMBUS_PCI_REGB4, HPET_ADDR);
/* enable decoding of access to HPET MMIO*/
smbus_enable(SMBUS_PCI_REG40, (1 << 28));
/* HPET irq enable */
smbus_enable(SMBUS_PCI_REG64, (1 << 10));
hpet_enable_legacy_int();
}
void __init setup_hpet_timer(void)
{
unsigned long flags = IRQF_NOBALANCING | IRQF_TIMER;
unsigned int cpu = smp_processor_id();
struct clock_event_device *cd;
hpet_setup();
cd = &per_cpu(hpet_clockevent_device, cpu);
cd->name = "hpet";
cd->rating = 100;
cd->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
cd->set_state_shutdown = hpet_set_state_shutdown;
cd->set_state_periodic = hpet_set_state_periodic;
cd->set_state_oneshot = hpet_set_state_oneshot;
cd->tick_resume = hpet_tick_resume;
cd->set_next_event = hpet_next_event;
cd->irq = HPET_T0_IRQ;
cd->cpumask = cpumask_of(cpu);
clockevent_set_clock(cd, HPET_FREQ);
cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
cd->max_delta_ticks = 0x7fffffff;
cd->min_delta_ns = clockevent_delta2ns(HPET_MIN_PROG_DELTA, cd);
cd->min_delta_ticks = HPET_MIN_PROG_DELTA;
clockevents_register_device(cd);
if (request_irq(HPET_T0_IRQ, hpet_irq_handler, flags, "hpet", NULL))
pr_err("Failed to request irq %d (hpet)\n", HPET_T0_IRQ);
pr_info("hpet clock event device register\n");
}
static u64 hpet_read_counter(struct clocksource *cs)
{
return (u64)hpet_read(HPET_COUNTER);
}
static void hpet_suspend(struct clocksource *cs)
{
}
static void hpet_resume(struct clocksource *cs)
{
hpet_setup();
hpet_restart_counter();
}
static struct clocksource csrc_hpet = {
.name = "hpet",
/* mips clocksource rating is less than 300, so hpet is better. */
.rating = 300,
.read = hpet_read_counter,
.mask = CLOCKSOURCE_MASK(32),
/* oneshot mode work normal with this flag */
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.suspend = hpet_suspend,
.resume = hpet_resume,
.mult = 0,
.shift = 10,
};
int __init init_hpet_clocksource(void)
{
csrc_hpet.mult = clocksource_hz2mult(HPET_FREQ, csrc_hpet.shift);
return clocksource_register_hz(&csrc_hpet, HPET_FREQ);
}
arch_initcall(init_hpet_clocksource);
+235
View File
@@ -0,0 +1,235 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <linux/irqchip.h>
#include <linux/logic_pio.h>
#include <linux/memblock.h>
#include <linux/minmax.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/bootinfo.h>
#include <asm/traps.h>
#include <asm/smp-ops.h>
#include <asm/cacheflush.h>
#include <asm/fw/fw.h>
#include <loongson.h>
#include <boot_param.h>
#define NODE_ID_OFFSET_ADDR ((void __iomem *)TO_UNCAC(0x1001041c))
u32 node_id_offset;
static void __init mips_nmi_setup(void)
{
void *base;
base = (void *)(CAC_BASE + 0x380);
memcpy(base, except_vec_nmi, 0x80);
flush_icache_range((unsigned long)base, (unsigned long)base + 0x80);
}
void ls7a_early_config(void)
{
node_id_offset = ((readl(NODE_ID_OFFSET_ADDR) >> 8) & 0x1f) + 36;
}
void rs780e_early_config(void)
{
node_id_offset = 37;
}
void virtual_early_config(void)
{
node_id_offset = 44;
}
void __init szmem(unsigned int node)
{
u32 i, mem_type;
phys_addr_t node_id, mem_start, mem_size;
/* Otherwise come from DTB */
if (loongson_sysconf.fw_interface != LOONGSON_LEFI)
return;
/* Parse memory information and activate */
for (i = 0; i < loongson_memmap->nr_map; i++) {
node_id = loongson_memmap->map[i].node_id;
if (node_id != node)
continue;
mem_type = loongson_memmap->map[i].mem_type;
mem_size = loongson_memmap->map[i].mem_size;
/* Memory size comes in MB if MEM_SIZE_IS_IN_BYTES not set */
if (mem_size & MEM_SIZE_IS_IN_BYTES)
mem_size &= ~MEM_SIZE_IS_IN_BYTES;
else
mem_size = mem_size << 20;
mem_start = (node_id << 44) | loongson_memmap->map[i].mem_start;
switch (mem_type) {
case SYSTEM_RAM_LOW:
case SYSTEM_RAM_HIGH:
case UMA_VIDEO_RAM:
pr_info("Node %d, mem_type:%d\t[%pa], %pa bytes usable\n",
(u32)node_id, mem_type, &mem_start, &mem_size);
memblock_add_node(mem_start, mem_size, node,
MEMBLOCK_NONE);
break;
case SYSTEM_RAM_RESERVED:
case VIDEO_ROM:
case ADAPTER_ROM:
case ACPI_TABLE:
case SMBIOS_TABLE:
pr_info("Node %d, mem_type:%d\t[%pa], %pa bytes reserved\n",
(u32)node_id, mem_type, &mem_start, &mem_size);
memblock_reserve(mem_start, mem_size);
break;
/* We should not reserve VUMA_VIDEO_RAM as it overlaps with MMIO */
case VUMA_VIDEO_RAM:
default:
pr_info("Node %d, mem_type:%d\t[%pa], %pa bytes unhandled\n",
(u32)node_id, mem_type, &mem_start, &mem_size);
break;
}
}
/* Reserve vgabios if it comes from firmware */
if (loongson_sysconf.vgabios_addr)
memblock_reserve(virt_to_phys((void *)loongson_sysconf.vgabios_addr),
SZ_256K);
/* set nid for reserved memory */
memblock_set_node((u64)node << 44, (u64)(node + 1) << 44,
&memblock.reserved, node);
}
#ifndef CONFIG_NUMA
static void __init prom_init_memory(void)
{
szmem(0);
}
#endif
void __init prom_init(void)
{
fw_init_cmdline();
if (fw_arg2 == 0 || (fdt_magic(fw_arg2) == FDT_MAGIC)) {
loongson_sysconf.fw_interface = LOONGSON_DTB;
prom_dtb_init_env();
} else {
loongson_sysconf.fw_interface = LOONGSON_LEFI;
prom_lefi_init_env();
}
/* init base address of io space */
set_io_port_base((unsigned long)PCI_IOBASE);
if (loongson_sysconf.early_config)
loongson_sysconf.early_config();
#ifdef CONFIG_NUMA
prom_init_numa_memory();
#else
prom_init_memory();
#endif
/* Hardcode to CPU UART 0 */
if ((read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64R)
setup_8250_early_printk_port(TO_UNCAC(LOONGSON_REG_BASE), 0, 1024);
else
setup_8250_early_printk_port(TO_UNCAC(LOONGSON_REG_BASE + 0x1e0), 0, 1024);
register_smp_ops(&loongson3_smp_ops);
board_nmi_handler_setup = mips_nmi_setup;
}
static int __init add_legacy_isa_io(struct fwnode_handle *fwnode, resource_size_t hw_start,
resource_size_t size)
{
int ret = 0;
struct logic_pio_hwaddr *range;
unsigned long vaddr;
range = kzalloc_obj(*range, GFP_ATOMIC);
if (!range)
return -ENOMEM;
range->fwnode = fwnode;
range->size = size = round_up(size, PAGE_SIZE);
range->hw_start = hw_start;
range->flags = LOGIC_PIO_CPU_MMIO;
ret = logic_pio_register_range(range);
if (ret) {
kfree(range);
return ret;
}
/* Legacy ISA must placed at the start of PCI_IOBASE */
if (range->io_start != 0) {
logic_pio_unregister_range(range);
kfree(range);
return -EINVAL;
}
vaddr = (unsigned long)PCI_IOBASE + range->io_start;
vmap_page_range(vaddr, vaddr + size, hw_start, pgprot_device(PAGE_KERNEL));
return 0;
}
static __init void reserve_pio_range(void)
{
struct device_node *np;
for_each_node_by_name(np, "isa") {
struct of_range range;
struct of_range_parser parser;
pr_info("ISA Bridge: %pOF\n", np);
if (of_range_parser_init(&parser, np)) {
pr_info("Failed to parse resources.\n");
of_node_put(np);
break;
}
for_each_of_range(&parser, &range) {
switch (range.flags & IORESOURCE_TYPE_BITS) {
case IORESOURCE_IO:
pr_info(" IO 0x%016llx..0x%016llx -> 0x%016llx\n",
range.cpu_addr,
range.cpu_addr + range.size - 1,
range.bus_addr);
if (add_legacy_isa_io(&np->fwnode, range.cpu_addr, range.size))
pr_warn("Failed to reserve legacy IO in Logic PIO\n");
break;
case IORESOURCE_MEM:
pr_info(" MEM 0x%016llx..0x%016llx -> 0x%016llx\n",
range.cpu_addr,
range.cpu_addr + range.size - 1,
range.bus_addr);
break;
}
}
}
}
void __init arch_init_irq(void)
{
reserve_pio_range();
irqchip_init();
}
unsigned int arch_dynirq_lower_bound(unsigned int from)
{
return MAX(from, NR_IRQS_LEGACY);
}
+175
View File
@@ -0,0 +1,175 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2010 Loongson Inc. & Lemote Inc. &
* Institute of Computing Technology
* Author: Xiang Gao, gaoxiang@ict.ac.cn
* Huacai Chen, chenhc@lemote.com
* Xiaofu Meng, Shuangshuang Zhang
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/mmzone.h>
#include <linux/export.h>
#include <linux/nodemask.h>
#include <linux/swap.h>
#include <linux/memblock.h>
#include <linux/pfn.h>
#include <linux/highmem.h>
#include <asm/page.h>
#include <asm/pgalloc.h>
#include <asm/sections.h>
#include <linux/irq.h>
#include <asm/bootinfo.h>
#include <asm/mc146818-time.h>
#include <asm/time.h>
#include <asm/wbflush.h>
#include <boot_param.h>
#include <loongson.h>
unsigned char __node_distances[MAX_NUMNODES][MAX_NUMNODES];
EXPORT_SYMBOL(__node_distances);
cpumask_t __node_cpumask[MAX_NUMNODES];
EXPORT_SYMBOL(__node_cpumask);
static void cpu_node_probe(void)
{
int i;
nodes_clear(node_possible_map);
nodes_clear(node_online_map);
for (i = 0; i < loongson_sysconf.nr_nodes; i++) {
node_set_state(num_online_nodes(), N_POSSIBLE);
node_set_online(num_online_nodes());
}
pr_info("NUMA: Discovered %d cpus on %d nodes\n",
loongson_sysconf.nr_cpus, num_online_nodes());
}
static int __init compute_node_distance(int row, int col)
{
int package_row = row * loongson_sysconf.cores_per_node /
loongson_sysconf.cores_per_package;
int package_col = col * loongson_sysconf.cores_per_node /
loongson_sysconf.cores_per_package;
if (col == row)
return LOCAL_DISTANCE;
else if (package_row == package_col)
return 40;
else
return 100;
}
static void __init init_topology_matrix(void)
{
int row, col;
for (row = 0; row < MAX_NUMNODES; row++)
for (col = 0; col < MAX_NUMNODES; col++)
__node_distances[row][col] = -1;
for_each_online_node(row) {
for_each_online_node(col) {
__node_distances[row][col] =
compute_node_distance(row, col);
}
}
}
static void __init node_mem_init(unsigned int node)
{
unsigned long node_addrspace_offset;
unsigned long start_pfn, end_pfn;
node_addrspace_offset = nid_to_addrbase(node);
pr_info("Node%d's addrspace_offset is 0x%lx\n",
node, node_addrspace_offset);
get_pfn_range_for_nid(node, &start_pfn, &end_pfn);
pr_info("Node%d: start_pfn=0x%lx, end_pfn=0x%lx\n",
node, start_pfn, end_pfn);
alloc_node_data(node);
NODE_DATA(node)->node_start_pfn = start_pfn;
NODE_DATA(node)->node_spanned_pages = end_pfn - start_pfn;
if (node == 0) {
/* kernel start address */
unsigned long kernel_start_pfn = PFN_DOWN(__pa_symbol(&_text));
/* kernel end address */
unsigned long kernel_end_pfn = PFN_UP(__pa_symbol(&_end));
/* used by finalize_initrd() */
max_low_pfn = end_pfn;
/* Reserve the kernel text/data/bss */
memblock_reserve(kernel_start_pfn << PAGE_SHIFT,
((kernel_end_pfn - kernel_start_pfn) << PAGE_SHIFT));
/* Reserve 0xfe000000~0xffffffff for RS780E integrated GPU */
if (node_end_pfn(0) >= (0xffffffff >> PAGE_SHIFT))
memblock_reserve((node_addrspace_offset | 0xfe000000),
32 << 20);
/* Reserve pfn range 0~node[0]->node_start_pfn */
memblock_reserve(0, PAGE_SIZE * start_pfn);
/* set nid for reserved memory on node 0 */
memblock_set_node(0, 1ULL << 44, &memblock.reserved, 0);
}
}
static __init void prom_meminit(void)
{
unsigned int node, cpu, active_cpu = 0;
cpu_node_probe();
init_topology_matrix();
for (node = 0; node < loongson_sysconf.nr_nodes; node++) {
if (node_online(node)) {
szmem(node);
node_mem_init(node);
cpumask_clear(&__node_cpumask[node]);
}
}
max_low_pfn = PHYS_PFN(memblock_end_of_DRAM());
for (cpu = 0; cpu < loongson_sysconf.nr_cpus; cpu++) {
node = cpu / loongson_sysconf.cores_per_node;
if (node >= num_online_nodes())
node = 0;
if (loongson_sysconf.reserved_cpus_mask & (1<<cpu))
continue;
cpumask_set_cpu(active_cpu, &__node_cpumask[node]);
pr_info("NUMA: set cpumask cpu %d on node %d\n", active_cpu, node);
active_cpu++;
}
}
void __init arch_zone_limits_init(unsigned long *max_zone_pfns)
{
max_zone_pfns[ZONE_DMA32] = MAX_DMA32_PFN;
max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
}
/* All PCI device belongs to logical Node-0 */
int pcibus_to_node(struct pci_bus *bus)
{
return 0;
}
EXPORT_SYMBOL(pcibus_to_node);
void __init prom_init_numa_memory(void)
{
pr_info("CP0_Config3: CP0 16.3 (0x%x)\n", read_c0_config3());
pr_info("CP0_PageGrain: CP0 5.1 (0x%x)\n", read_c0_pagegrain());
prom_meminit();
}
+52
View File
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* loongson-specific suspend support
*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin <wuzhangjin@gmail.com>
*/
#include <linux/suspend.h>
#include <linux/pm.h>
#include <asm/mipsregs.h>
#include <loongson.h>
asmlinkage void loongson_lefi_sleep(unsigned long sleep_addr);
static int lefi_pm_enter(suspend_state_t state)
{
switch (state) {
case PM_SUSPEND_MEM:
pm_set_suspend_via_firmware();
loongson_lefi_sleep(loongson_sysconf.suspend_addr);
pm_set_resume_via_firmware();
return 0;
default:
return -EINVAL;
}
}
static int lefi_pm_valid_state(suspend_state_t state)
{
switch (state) {
case PM_SUSPEND_MEM:
return !!loongson_sysconf.suspend_addr;
default:
return 0;
}
}
static const struct platform_suspend_ops lefi_pm_ops = {
.valid = lefi_pm_valid_state,
.enter = lefi_pm_enter,
};
static int __init loongson_pm_init(void)
{
if (loongson_sysconf.fw_interface == LOONGSON_LEFI)
suspend_set_ops(&lefi_pm_ops);
return 0;
}
arch_initcall(loongson_pm_init);
+179
View File
@@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
*
* Copyright (C) 2007 Lemote, Inc. & Institute of Computing Technology
* Author: Fuxin Zhang, zhangfx@lemote.com
* Copyright (C) 2009 Lemote, Inc.
* Author: Zhangjin Wu, wuzhangjin@gmail.com
*/
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/kexec.h>
#include <linux/pm.h>
#include <linux/reboot.h>
#include <linux/slab.h>
#include <asm/bootinfo.h>
#include <asm/idle.h>
#include <asm/reboot.h>
#include <asm/bug.h>
#include <loongson.h>
#include <boot_param.h>
static int firmware_restart(struct sys_off_data *unusedd)
{
void (*fw_restart)(void) = (void *)loongson_sysconf.restart_addr;
fw_restart();
return NOTIFY_DONE;
}
static int firmware_poweroff(struct sys_off_data *unused)
{
void (*fw_poweroff)(void) = (void *)loongson_sysconf.poweroff_addr;
fw_poweroff();
return NOTIFY_DONE;
}
#ifdef CONFIG_KEXEC_CORE
/* 0X80000000~0X80200000 is safe */
#define MAX_ARGS 64
#define KEXEC_CTRL_CODE 0xFFFFFFFF80100000UL
#define KEXEC_ARGV_ADDR 0xFFFFFFFF80108000UL
#define KEXEC_ARGV_SIZE COMMAND_LINE_SIZE
#define KEXEC_ENVP_SIZE 4800
static int kexec_argc;
static int kdump_argc;
static void *kexec_argv;
static void *kdump_argv;
static void *kexec_envp;
static int loongson_kexec_prepare(struct kimage *image)
{
int i, argc = 0;
unsigned int *argv;
char *str, *ptr, *bootloader = "kexec";
/* argv at offset 0, argv[] at offset KEXEC_ARGV_SIZE/2 */
if (image->type == KEXEC_TYPE_DEFAULT)
argv = (unsigned int *)kexec_argv;
else
argv = (unsigned int *)kdump_argv;
argv[argc++] = (unsigned int)(KEXEC_ARGV_ADDR + KEXEC_ARGV_SIZE/2);
for (i = 0; i < image->nr_segments; i++) {
if (!strncmp(bootloader, (char *)image->segment[i].buf,
strlen(bootloader))) {
/*
* convert command line string to array
* of parameters (as bootloader does).
*/
int offt;
str = (char *)argv + KEXEC_ARGV_SIZE/2;
memcpy(str, image->segment[i].buf, KEXEC_ARGV_SIZE/2);
ptr = strchr(str, ' ');
while (ptr && (argc < MAX_ARGS)) {
*ptr = '\0';
if (ptr[1] != ' ') {
offt = (int)(ptr - str + 1);
argv[argc] = KEXEC_ARGV_ADDR + KEXEC_ARGV_SIZE/2 + offt;
argc++;
}
ptr = strchr(ptr + 1, ' ');
}
break;
}
}
if (image->type == KEXEC_TYPE_DEFAULT)
kexec_argc = argc;
else
kdump_argc = argc;
/* kexec/kdump need a safe page to save reboot_code_buffer */
image->control_code_page = virt_to_page((void *)KEXEC_CTRL_CODE);
return 0;
}
static void loongson_kexec_shutdown(void)
{
#ifdef CONFIG_SMP
int cpu;
/* All CPUs go to reboot_code_buffer */
for_each_possible_cpu(cpu)
if (!cpu_online(cpu))
cpu_device_up(get_cpu_device(cpu));
secondary_kexec_args[0] = TO_UNCAC(0x3ff01000);
#endif
kexec_args[0] = kexec_argc;
kexec_args[1] = fw_arg1;
kexec_args[2] = fw_arg2;
memcpy((void *)fw_arg1, kexec_argv, KEXEC_ARGV_SIZE);
memcpy((void *)fw_arg2, kexec_envp, KEXEC_ENVP_SIZE);
}
static void loongson_crash_shutdown(struct pt_regs *regs)
{
default_machine_crash_shutdown(regs);
kexec_args[0] = kdump_argc;
kexec_args[1] = fw_arg1;
kexec_args[2] = fw_arg2;
#ifdef CONFIG_SMP
secondary_kexec_args[0] = TO_UNCAC(0x3ff01000);
#endif
memcpy((void *)fw_arg1, kdump_argv, KEXEC_ARGV_SIZE);
memcpy((void *)fw_arg2, kexec_envp, KEXEC_ENVP_SIZE);
}
#endif
static int __init mips_reboot_setup(void)
{
if (loongson_sysconf.restart_addr) {
register_sys_off_handler(SYS_OFF_MODE_RESTART,
SYS_OFF_PRIO_FIRMWARE,
firmware_restart, NULL);
}
if (loongson_sysconf.poweroff_addr) {
register_sys_off_handler(SYS_OFF_MODE_POWER_OFF,
SYS_OFF_PRIO_FIRMWARE,
firmware_poweroff, NULL);
}
#ifdef CONFIG_KEXEC_CORE
kexec_argv = kmalloc(KEXEC_ARGV_SIZE, GFP_KERNEL);
if (WARN_ON(!kexec_argv))
return -ENOMEM;
kdump_argv = kmalloc(KEXEC_ARGV_SIZE, GFP_KERNEL);
if (WARN_ON(!kdump_argv))
return -ENOMEM;
kexec_envp = kmalloc(KEXEC_ENVP_SIZE, GFP_KERNEL);
if (WARN_ON(!kexec_envp))
return -ENOMEM;
fw_arg1 = KEXEC_ARGV_ADDR;
memcpy(kexec_envp, (void *)fw_arg2, KEXEC_ENVP_SIZE);
_machine_kexec_prepare = loongson_kexec_prepare;
_machine_kexec_shutdown = loongson_kexec_shutdown;
_machine_crash_shutdown = loongson_crash_shutdown;
#endif
return 0;
}
arch_initcall(mips_reboot_setup);
+22
View File
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2007 Lemote Inc. & Institute of Computing Technology
* Author: Fuxin Zhang, zhangfx@lemote.com
*/
#include <linux/init.h>
#include <asm/bootinfo.h>
#include <linux/libfdt.h>
#include <linux/of_fdt.h>
#include <asm/prom.h>
#include <loongson.h>
void *loongson_fdt_blob;
void __init plat_mem_setup(void)
{
if (loongson_fdt_blob)
__dt_setup_arch(loongson_fdt_blob);
}
@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2024, Jiaxun Yang <jiaxun.yang@flygoat.com>
* Loongson EFI firmware sleeper routine
*/
#include <asm/asm.h>
#include <asm/pm.h>
#include <kernel-entry-init.h>
LEAF(loongson_lefi_sleep)
SUSPEND_SAVE
move t9, a0
PTR_LA a0, wake
move a1, sp
jalr t9
wake:
smp_slave_setup
RESUME_RESTORE_REGS_RETURN
END(loongson_lefi_sleep)
+868
View File
@@ -0,0 +1,868 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2010, 2011, 2012, Lemote, Inc.
* Author: Chen Huacai, chenhc@lemote.com
*/
#include <irq.h>
#include <linux/init.h>
#include <linux/cpu.h>
#include <linux/sched.h>
#include <linux/sched/hotplug.h>
#include <linux/sched/task_stack.h>
#include <linux/smp.h>
#include <linux/cpufreq.h>
#include <linux/kexec.h>
#include <asm/processor.h>
#include <asm/smp.h>
#include <asm/time.h>
#include <asm/tlbflush.h>
#include <asm/cacheflush.h>
#include <loongson.h>
#include <loongson_regs.h>
#include <workarounds.h>
#include "smp.h"
DEFINE_PER_CPU(int, cpu_state);
#define LS_IPI_IRQ (MIPS_CPU_IRQ_BASE + 6)
static void __iomem *ipi_set0_regs[16];
static void __iomem *ipi_clear0_regs[16];
static void __iomem *ipi_status0_regs[16];
static void __iomem *ipi_en0_regs[16];
static void __iomem *ipi_mailbox_buf[16];
static u32 (*ipi_read_clear)(int cpu);
static void (*ipi_write_action)(int cpu, u32 action);
static void (*ipi_write_enable)(int cpu);
static void (*ipi_clear_buf)(int cpu);
static void (*ipi_write_buf)(int cpu, struct task_struct *idle);
/* send mail via Mail_Send register for 3A4000+ CPU */
static void csr_mail_send(uint64_t data, int cpu, int mailbox)
{
uint64_t val;
/* send high 32 bits */
val = CSR_MAIL_SEND_BLOCK;
val |= (CSR_MAIL_SEND_BOX_HIGH(mailbox) << CSR_MAIL_SEND_BOX_SHIFT);
val |= (cpu << CSR_MAIL_SEND_CPU_SHIFT);
val |= (data & CSR_MAIL_SEND_H32_MASK);
csr_writeq(val, LOONGSON_CSR_MAIL_SEND);
/* send low 32 bits */
val = CSR_MAIL_SEND_BLOCK;
val |= (CSR_MAIL_SEND_BOX_LOW(mailbox) << CSR_MAIL_SEND_BOX_SHIFT);
val |= (cpu << CSR_MAIL_SEND_CPU_SHIFT);
val |= (data << CSR_MAIL_SEND_BUF_SHIFT);
csr_writeq(val, LOONGSON_CSR_MAIL_SEND);
};
static u32 csr_ipi_read_clear(int cpu)
{
u32 action;
/* Load the ipi register to figure out what we're supposed to do */
action = csr_readl(LOONGSON_CSR_IPI_STATUS);
/* Clear the ipi register to clear the interrupt */
csr_writel(action, LOONGSON_CSR_IPI_CLEAR);
return action;
}
static void csr_ipi_write_action(int cpu, u32 action)
{
unsigned int irq = 0;
while ((irq = ffs(action))) {
uint32_t val = CSR_IPI_SEND_BLOCK;
val |= (irq - 1);
val |= (cpu << CSR_IPI_SEND_CPU_SHIFT);
csr_writel(val, LOONGSON_CSR_IPI_SEND);
action &= ~BIT(irq - 1);
}
}
static void csr_ipi_write_enable(int cpu)
{
csr_writel(0xffffffff, LOONGSON_CSR_IPI_EN);
}
static void csr_ipi_clear_buf(int cpu)
{
csr_writeq(0, LOONGSON_CSR_MAIL_BUF0);
}
static void csr_ipi_write_buf(int cpu, struct task_struct *idle)
{
unsigned long startargs[4];
/* startargs[] are initial PC, SP and GP for secondary CPU */
startargs[0] = (unsigned long)&smp_bootstrap;
startargs[1] = (unsigned long)__KSTK_TOS(idle);
startargs[2] = (unsigned long)task_thread_info(idle);
startargs[3] = 0;
pr_debug("CPU#%d, func_pc=%lx, sp=%lx, gp=%lx\n",
cpu, startargs[0], startargs[1], startargs[2]);
csr_mail_send(startargs[3], cpu_logical_map(cpu), 3);
csr_mail_send(startargs[2], cpu_logical_map(cpu), 2);
csr_mail_send(startargs[1], cpu_logical_map(cpu), 1);
csr_mail_send(startargs[0], cpu_logical_map(cpu), 0);
}
static u32 legacy_ipi_read_clear(int cpu)
{
u32 action;
/* Load the ipi register to figure out what we're supposed to do */
action = readl_relaxed(ipi_status0_regs[cpu_logical_map(cpu)]);
/* Clear the ipi register to clear the interrupt */
writel_relaxed(action, ipi_clear0_regs[cpu_logical_map(cpu)]);
nudge_writes();
return action;
}
static void legacy_ipi_write_action(int cpu, u32 action)
{
writel_relaxed((u32)action, ipi_set0_regs[cpu]);
nudge_writes();
}
static void legacy_ipi_write_enable(int cpu)
{
writel_relaxed(0xffffffff, ipi_en0_regs[cpu_logical_map(cpu)]);
}
static void legacy_ipi_clear_buf(int cpu)
{
writeq_relaxed(0, ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x0);
}
static void legacy_ipi_write_buf(int cpu, struct task_struct *idle)
{
unsigned long startargs[4];
/* startargs[] are initial PC, SP and GP for secondary CPU */
startargs[0] = (unsigned long)&smp_bootstrap;
startargs[1] = (unsigned long)__KSTK_TOS(idle);
startargs[2] = (unsigned long)task_thread_info(idle);
startargs[3] = 0;
pr_debug("CPU#%d, func_pc=%lx, sp=%lx, gp=%lx\n",
cpu, startargs[0], startargs[1], startargs[2]);
writeq_relaxed(startargs[3],
ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x18);
writeq_relaxed(startargs[2],
ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x10);
writeq_relaxed(startargs[1],
ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x8);
writeq_relaxed(startargs[0],
ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x0);
nudge_writes();
}
static void csr_ipi_probe(void)
{
if (cpu_has_csr() && csr_readl(LOONGSON_CSR_FEATURES) & LOONGSON_CSRF_IPI) {
ipi_read_clear = csr_ipi_read_clear;
ipi_write_action = csr_ipi_write_action;
ipi_write_enable = csr_ipi_write_enable;
ipi_clear_buf = csr_ipi_clear_buf;
ipi_write_buf = csr_ipi_write_buf;
} else {
ipi_read_clear = legacy_ipi_read_clear;
ipi_write_action = legacy_ipi_write_action;
ipi_write_enable = legacy_ipi_write_enable;
ipi_clear_buf = legacy_ipi_clear_buf;
ipi_write_buf = legacy_ipi_write_buf;
}
}
static void ipi_set0_regs_init(void)
{
ipi_set0_regs[0] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + SET0);
ipi_set0_regs[1] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + SET0);
ipi_set0_regs[2] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + SET0);
ipi_set0_regs[3] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + SET0);
ipi_set0_regs[4] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + SET0);
ipi_set0_regs[5] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + SET0);
ipi_set0_regs[6] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + SET0);
ipi_set0_regs[7] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + SET0);
ipi_set0_regs[8] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + SET0);
ipi_set0_regs[9] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + SET0);
ipi_set0_regs[10] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + SET0);
ipi_set0_regs[11] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + SET0);
ipi_set0_regs[12] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + SET0);
ipi_set0_regs[13] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + SET0);
ipi_set0_regs[14] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + SET0);
ipi_set0_regs[15] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + SET0);
}
static void ipi_clear0_regs_init(void)
{
ipi_clear0_regs[0] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + CLEAR0);
ipi_clear0_regs[1] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + CLEAR0);
ipi_clear0_regs[2] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + CLEAR0);
ipi_clear0_regs[3] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + CLEAR0);
ipi_clear0_regs[4] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + CLEAR0);
ipi_clear0_regs[5] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + CLEAR0);
ipi_clear0_regs[6] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + CLEAR0);
ipi_clear0_regs[7] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + CLEAR0);
ipi_clear0_regs[8] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + CLEAR0);
ipi_clear0_regs[9] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + CLEAR0);
ipi_clear0_regs[10] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + CLEAR0);
ipi_clear0_regs[11] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + CLEAR0);
ipi_clear0_regs[12] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + CLEAR0);
ipi_clear0_regs[13] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + CLEAR0);
ipi_clear0_regs[14] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + CLEAR0);
ipi_clear0_regs[15] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + CLEAR0);
}
static void ipi_status0_regs_init(void)
{
ipi_status0_regs[0] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + STATUS0);
ipi_status0_regs[1] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + STATUS0);
ipi_status0_regs[2] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + STATUS0);
ipi_status0_regs[3] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + STATUS0);
ipi_status0_regs[4] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + STATUS0);
ipi_status0_regs[5] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + STATUS0);
ipi_status0_regs[6] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + STATUS0);
ipi_status0_regs[7] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + STATUS0);
ipi_status0_regs[8] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + STATUS0);
ipi_status0_regs[9] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + STATUS0);
ipi_status0_regs[10] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + STATUS0);
ipi_status0_regs[11] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + STATUS0);
ipi_status0_regs[12] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + STATUS0);
ipi_status0_regs[13] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + STATUS0);
ipi_status0_regs[14] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + STATUS0);
ipi_status0_regs[15] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + STATUS0);
}
static void ipi_en0_regs_init(void)
{
ipi_en0_regs[0] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + EN0);
ipi_en0_regs[1] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + EN0);
ipi_en0_regs[2] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + EN0);
ipi_en0_regs[3] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + EN0);
ipi_en0_regs[4] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + EN0);
ipi_en0_regs[5] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + EN0);
ipi_en0_regs[6] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + EN0);
ipi_en0_regs[7] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + EN0);
ipi_en0_regs[8] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + EN0);
ipi_en0_regs[9] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + EN0);
ipi_en0_regs[10] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + EN0);
ipi_en0_regs[11] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + EN0);
ipi_en0_regs[12] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + EN0);
ipi_en0_regs[13] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + EN0);
ipi_en0_regs[14] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + EN0);
ipi_en0_regs[15] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + EN0);
}
static void ipi_mailbox_buf_init(void)
{
ipi_mailbox_buf[0] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + BUF);
ipi_mailbox_buf[1] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + BUF);
ipi_mailbox_buf[2] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + BUF);
ipi_mailbox_buf[3] = (void __iomem *)
(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + BUF);
ipi_mailbox_buf[4] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + BUF);
ipi_mailbox_buf[5] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + BUF);
ipi_mailbox_buf[6] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + BUF);
ipi_mailbox_buf[7] = (void __iomem *)
(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + BUF);
ipi_mailbox_buf[8] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + BUF);
ipi_mailbox_buf[9] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + BUF);
ipi_mailbox_buf[10] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + BUF);
ipi_mailbox_buf[11] = (void __iomem *)
(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + BUF);
ipi_mailbox_buf[12] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + BUF);
ipi_mailbox_buf[13] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + BUF);
ipi_mailbox_buf[14] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + BUF);
ipi_mailbox_buf[15] = (void __iomem *)
(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + BUF);
}
/*
* Simple enough, just poke the appropriate ipi register
*/
static void loongson3_send_ipi_single(int cpu, unsigned int action)
{
ipi_write_action(cpu_logical_map(cpu), (u32)action);
}
static void
loongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action)
{
unsigned int i;
for_each_cpu(i, mask)
ipi_write_action(cpu_logical_map(i), (u32)action);
}
#ifdef CONFIG_IRQ_WORK
void arch_irq_work_raise(void)
{
loongson3_send_ipi_single(smp_processor_id(), SMP_IRQ_WORK);
}
#endif
static irqreturn_t loongson3_ipi_interrupt(int irq, void *dev_id)
{
int cpu = smp_processor_id();
unsigned int action;
action = ipi_read_clear(cpu);
if (action & SMP_RESCHEDULE_YOURSELF)
scheduler_ipi();
if (action & SMP_CALL_FUNCTION) {
irq_enter();
generic_smp_call_function_interrupt();
irq_exit();
}
if (action & SMP_IRQ_WORK)
irq_work_run();
return IRQ_HANDLED;
}
/*
* SMP init and finish on secondary CPUs
*/
static void loongson3_init_secondary(void)
{
unsigned int cpu = smp_processor_id();
unsigned int imask = STATUSF_IP7 | STATUSF_IP6 |
STATUSF_IP3 | STATUSF_IP2;
/* Set interrupt mask, but don't enable */
change_c0_status(ST0_IM, imask);
ipi_write_enable(cpu);
per_cpu(cpu_state, cpu) = CPU_ONLINE;
cpu_set_core(&cpu_data[cpu],
cpu_logical_map(cpu) % loongson_sysconf.cores_per_package);
cpu_data[cpu].package =
cpu_logical_map(cpu) / loongson_sysconf.cores_per_package;
}
static void loongson3_smp_finish(void)
{
int cpu = smp_processor_id();
write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ);
local_irq_enable();
ipi_clear_buf(cpu);
pr_info("CPU#%d finished, CP0_ST=%x\n",
smp_processor_id(), read_c0_status());
}
static void __init loongson3_smp_setup(void)
{
int i = 0, num = 0; /* i: physical id, num: logical id */
int max_cpus = 0;
init_cpu_possible(cpu_none_mask);
for (i = 0; i < ARRAY_SIZE(smp_group); i++) {
if (!smp_group[i])
break;
max_cpus += loongson_sysconf.cores_per_node;
}
if (max_cpus < loongson_sysconf.nr_cpus) {
pr_err("SMP Groups are less than the number of CPUs\n");
loongson_sysconf.nr_cpus = max_cpus ? max_cpus : 1;
}
/* For unified kernel, NR_CPUS is the maximum possible value,
* loongson_sysconf.nr_cpus is the really present value
*/
i = 0;
while (i < loongson_sysconf.nr_cpus) {
if (loongson_sysconf.reserved_cpus_mask & (1<<i)) {
/* Reserved physical CPU cores */
__cpu_number_map[i] = -1;
} else {
__cpu_number_map[i] = num;
__cpu_logical_map[num] = i;
set_cpu_possible(num, true);
/* Loongson processors are always grouped by 4 */
cpu_set_cluster(&cpu_data[num], i / 4);
num++;
}
i++;
}
pr_info("Detected %i available CPU(s)\n", num);
while (num < loongson_sysconf.nr_cpus) {
__cpu_logical_map[num] = -1;
num++;
}
csr_ipi_probe();
ipi_set0_regs_init();
ipi_clear0_regs_init();
ipi_status0_regs_init();
ipi_en0_regs_init();
ipi_mailbox_buf_init();
if (smp_group[0])
ipi_write_enable(0);
cpu_set_core(&cpu_data[0],
cpu_logical_map(0) % loongson_sysconf.cores_per_package);
cpu_data[0].package = cpu_logical_map(0) / loongson_sysconf.cores_per_package;
}
static void __init loongson3_prepare_cpus(unsigned int max_cpus)
{
if (request_irq(LS_IPI_IRQ, loongson3_ipi_interrupt,
IRQF_PERCPU | IRQF_NO_SUSPEND, "SMP_IPI", NULL))
pr_err("Failed to request IPI IRQ\n");
init_cpu_present(cpu_possible_mask);
per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;
}
/*
* Setup the PC, SP, and GP of a secondary processor and start it running!
*/
static int loongson3_boot_secondary(int cpu, struct task_struct *idle)
{
pr_info("Booting CPU#%d...\n", cpu);
ipi_write_buf(cpu, idle);
return 0;
}
#ifdef CONFIG_HOTPLUG_CPU
static int loongson3_cpu_disable(void)
{
unsigned long flags;
unsigned int cpu = smp_processor_id();
set_cpu_online(cpu, false);
calculate_cpu_foreign_map();
local_irq_save(flags);
clear_c0_status(ST0_IM);
local_irq_restore(flags);
local_flush_tlb_all();
return 0;
}
static void loongson3_cpu_die(unsigned int cpu)
{
while (per_cpu(cpu_state, cpu) != CPU_DEAD)
cpu_relax();
mb();
}
/* To shutdown a core in Loongson 3, the target core should go to CKSEG1 and
* flush all L1 entries at first. Then, another core (usually Core 0) can
* safely disable the clock of the target core. loongson3_play_dead() is
* called via CKSEG1 (uncached and unmmaped)
*/
static void loongson3_type1_play_dead(int *state_addr)
{
register int val;
register long cpuid, core, node, count;
register void *addr, *base, *initfunc;
__asm__ __volatile__(
" .set push \n"
" .set noreorder \n"
" li %[addr], 0x80000000 \n" /* KSEG0 */
"1: cache 0, 0(%[addr]) \n" /* flush L1 ICache */
" cache 0, 1(%[addr]) \n"
" cache 0, 2(%[addr]) \n"
" cache 0, 3(%[addr]) \n"
" cache 1, 0(%[addr]) \n" /* flush L1 DCache */
" cache 1, 1(%[addr]) \n"
" cache 1, 2(%[addr]) \n"
" cache 1, 3(%[addr]) \n"
" addiu %[sets], %[sets], -1 \n"
" bnez %[sets], 1b \n"
" addiu %[addr], %[addr], 0x20 \n"
" li %[val], 0x7 \n" /* *state_addr = CPU_DEAD; */
" sw %[val], (%[state_addr]) \n"
" sync \n"
" cache 21, (%[state_addr]) \n" /* flush entry of *state_addr */
" .set pop \n"
: [addr] "=&r" (addr), [val] "=&r" (val)
: [state_addr] "r" (state_addr),
[sets] "r" (cpu_data[smp_processor_id()].dcache.sets));
__asm__ __volatile__(
" .set push \n"
" .set noreorder \n"
" .set mips64 \n"
" mfc0 %[cpuid], $15, 1 \n"
" andi %[cpuid], 0x3ff \n"
" dli %[base], 0x900000003ff01000 \n"
" andi %[core], %[cpuid], 0x3 \n"
" sll %[core], 8 \n" /* get core id */
" or %[base], %[base], %[core] \n"
" andi %[node], %[cpuid], 0xc \n"
" dsll %[node], 42 \n" /* get node id */
" or %[base], %[base], %[node] \n"
"1: li %[count], 0x100 \n" /* wait for init loop */
"2: bnez %[count], 2b \n" /* limit mailbox access */
" addiu %[count], -1 \n"
" ld %[initfunc], 0x20(%[base]) \n" /* get PC via mailbox */
" beqz %[initfunc], 1b \n"
" nop \n"
" ld $sp, 0x28(%[base]) \n" /* get SP via mailbox */
" ld $gp, 0x30(%[base]) \n" /* get GP via mailbox */
" ld $a1, 0x38(%[base]) \n"
" jr %[initfunc] \n" /* jump to initial PC */
" nop \n"
" .set pop \n"
: [core] "=&r" (core), [node] "=&r" (node),
[base] "=&r" (base), [cpuid] "=&r" (cpuid),
[count] "=&r" (count), [initfunc] "=&r" (initfunc)
: /* No Input */
: "a1");
}
static void loongson3_type2_play_dead(int *state_addr)
{
register int val;
register long cpuid, core, node, count;
register void *addr, *base, *initfunc;
__asm__ __volatile__(
" .set push \n"
" .set noreorder \n"
" li %[addr], 0x80000000 \n" /* KSEG0 */
"1: cache 0, 0(%[addr]) \n" /* flush L1 ICache */
" cache 0, 1(%[addr]) \n"
" cache 0, 2(%[addr]) \n"
" cache 0, 3(%[addr]) \n"
" cache 1, 0(%[addr]) \n" /* flush L1 DCache */
" cache 1, 1(%[addr]) \n"
" cache 1, 2(%[addr]) \n"
" cache 1, 3(%[addr]) \n"
" addiu %[sets], %[sets], -1 \n"
" bnez %[sets], 1b \n"
" addiu %[addr], %[addr], 0x20 \n"
" li %[val], 0x7 \n" /* *state_addr = CPU_DEAD; */
" sw %[val], (%[state_addr]) \n"
" sync \n"
" cache 21, (%[state_addr]) \n" /* flush entry of *state_addr */
" .set pop \n"
: [addr] "=&r" (addr), [val] "=&r" (val)
: [state_addr] "r" (state_addr),
[sets] "r" (cpu_data[smp_processor_id()].dcache.sets));
__asm__ __volatile__(
" .set push \n"
" .set noreorder \n"
" .set mips64 \n"
" mfc0 %[cpuid], $15, 1 \n"
" andi %[cpuid], 0x3ff \n"
" dli %[base], 0x900000003ff01000 \n"
" andi %[core], %[cpuid], 0x3 \n"
" sll %[core], 8 \n" /* get core id */
" or %[base], %[base], %[core] \n"
" andi %[node], %[cpuid], 0xc \n"
" dsll %[node], 42 \n" /* get node id */
" or %[base], %[base], %[node] \n"
" dsrl %[node], 30 \n" /* 15:14 */
" or %[base], %[base], %[node] \n"
"1: li %[count], 0x100 \n" /* wait for init loop */
"2: bnez %[count], 2b \n" /* limit mailbox access */
" addiu %[count], -1 \n"
" ld %[initfunc], 0x20(%[base]) \n" /* get PC via mailbox */
" beqz %[initfunc], 1b \n"
" nop \n"
" ld $sp, 0x28(%[base]) \n" /* get SP via mailbox */
" ld $gp, 0x30(%[base]) \n" /* get GP via mailbox */
" ld $a1, 0x38(%[base]) \n"
" jr %[initfunc] \n" /* jump to initial PC */
" nop \n"
" .set pop \n"
: [core] "=&r" (core), [node] "=&r" (node),
[base] "=&r" (base), [cpuid] "=&r" (cpuid),
[count] "=&r" (count), [initfunc] "=&r" (initfunc)
: /* No Input */
: "a1");
}
static void loongson3_type3_play_dead(int *state_addr)
{
register int val;
register long cpuid, core, node, count;
register void *addr, *base, *initfunc;
__asm__ __volatile__(
" .set push \n"
" .set noreorder \n"
" li %[addr], 0x80000000 \n" /* KSEG0 */
"1: cache 0, 0(%[addr]) \n" /* flush L1 ICache */
" cache 0, 1(%[addr]) \n"
" cache 0, 2(%[addr]) \n"
" cache 0, 3(%[addr]) \n"
" cache 1, 0(%[addr]) \n" /* flush L1 DCache */
" cache 1, 1(%[addr]) \n"
" cache 1, 2(%[addr]) \n"
" cache 1, 3(%[addr]) \n"
" addiu %[sets], %[sets], -1 \n"
" bnez %[sets], 1b \n"
" addiu %[addr], %[addr], 0x40 \n"
" li %[addr], 0x80000000 \n" /* KSEG0 */
"2: cache 2, 0(%[addr]) \n" /* flush L1 VCache */
" cache 2, 1(%[addr]) \n"
" cache 2, 2(%[addr]) \n"
" cache 2, 3(%[addr]) \n"
" cache 2, 4(%[addr]) \n"
" cache 2, 5(%[addr]) \n"
" cache 2, 6(%[addr]) \n"
" cache 2, 7(%[addr]) \n"
" cache 2, 8(%[addr]) \n"
" cache 2, 9(%[addr]) \n"
" cache 2, 10(%[addr]) \n"
" cache 2, 11(%[addr]) \n"
" cache 2, 12(%[addr]) \n"
" cache 2, 13(%[addr]) \n"
" cache 2, 14(%[addr]) \n"
" cache 2, 15(%[addr]) \n"
" addiu %[vsets], %[vsets], -1 \n"
" bnez %[vsets], 2b \n"
" addiu %[addr], %[addr], 0x40 \n"
" li %[val], 0x7 \n" /* *state_addr = CPU_DEAD; */
" sw %[val], (%[state_addr]) \n"
" sync \n"
" cache 21, (%[state_addr]) \n" /* flush entry of *state_addr */
" .set pop \n"
: [addr] "=&r" (addr), [val] "=&r" (val)
: [state_addr] "r" (state_addr),
[sets] "r" (cpu_data[smp_processor_id()].dcache.sets),
[vsets] "r" (cpu_data[smp_processor_id()].vcache.sets));
__asm__ __volatile__(
" .set push \n"
" .set noreorder \n"
" .set mips64 \n"
" mfc0 %[cpuid], $15, 1 \n"
" andi %[cpuid], 0x3ff \n"
" dli %[base], 0x900000003ff01000 \n"
" andi %[core], %[cpuid], 0x3 \n"
" sll %[core], 8 \n" /* get core id */
" or %[base], %[base], %[core] \n"
" andi %[node], %[cpuid], 0xc \n"
" dsll %[node], 42 \n" /* get node id */
" or %[base], %[base], %[node] \n"
"1: li %[count], 0x100 \n" /* wait for init loop */
"2: bnez %[count], 2b \n" /* limit mailbox access */
" addiu %[count], -1 \n"
" lw %[initfunc], 0x20(%[base]) \n" /* check lower 32-bit as jump indicator */
" beqz %[initfunc], 1b \n"
" nop \n"
" ld %[initfunc], 0x20(%[base]) \n" /* get PC (whole 64-bit) via mailbox */
" ld $sp, 0x28(%[base]) \n" /* get SP via mailbox */
" ld $gp, 0x30(%[base]) \n" /* get GP via mailbox */
" ld $a1, 0x38(%[base]) \n"
" jr %[initfunc] \n" /* jump to initial PC */
" nop \n"
" .set pop \n"
: [core] "=&r" (core), [node] "=&r" (node),
[base] "=&r" (base), [cpuid] "=&r" (cpuid),
[count] "=&r" (count), [initfunc] "=&r" (initfunc)
: /* No Input */
: "a1");
}
void play_dead(void)
{
int prid_imp, prid_rev, *state_addr;
unsigned int cpu = smp_processor_id();
void (*play_dead_at_ckseg1)(int *);
idle_task_exit();
cpuhp_ap_report_dead();
prid_imp = read_c0_prid() & PRID_IMP_MASK;
prid_rev = read_c0_prid() & PRID_REV_MASK;
if (prid_imp == PRID_IMP_LOONGSON_64G) {
play_dead_at_ckseg1 =
(void *)CKSEG1ADDR((unsigned long)loongson3_type3_play_dead);
goto out;
}
switch (prid_rev) {
case PRID_REV_LOONGSON3A_R1:
default:
play_dead_at_ckseg1 =
(void *)CKSEG1ADDR((unsigned long)loongson3_type1_play_dead);
break;
case PRID_REV_LOONGSON3B_R1:
case PRID_REV_LOONGSON3B_R2:
play_dead_at_ckseg1 =
(void *)CKSEG1ADDR((unsigned long)loongson3_type2_play_dead);
break;
case PRID_REV_LOONGSON3A_R2_0:
case PRID_REV_LOONGSON3A_R2_1:
case PRID_REV_LOONGSON3A_R3_0:
case PRID_REV_LOONGSON3A_R3_1:
play_dead_at_ckseg1 =
(void *)CKSEG1ADDR((unsigned long)loongson3_type3_play_dead);
break;
}
out:
state_addr = &per_cpu(cpu_state, cpu);
mb();
play_dead_at_ckseg1(state_addr);
BUG();
}
static int loongson3_disable_clock(unsigned int cpu)
{
uint64_t core_id = cpu_core(&cpu_data[cpu]);
uint64_t package_id = cpu_data[cpu].package;
if (!loongson_chipcfg[package_id] || !loongson_freqctrl[package_id])
return 0;
if ((read_c0_prid() & PRID_REV_MASK) == PRID_REV_LOONGSON3A_R1) {
LOONGSON_CHIPCFG(package_id) &= ~(1 << (12 + core_id));
} else {
if (!(loongson_sysconf.workarounds & WORKAROUND_CPUHOTPLUG))
LOONGSON_FREQCTRL(package_id) &= ~(1 << (core_id * 4 + 3));
}
return 0;
}
static int loongson3_enable_clock(unsigned int cpu)
{
uint64_t core_id = cpu_core(&cpu_data[cpu]);
uint64_t package_id = cpu_data[cpu].package;
if (!loongson_chipcfg[package_id] || !loongson_freqctrl[package_id])
return 0;
if ((read_c0_prid() & PRID_REV_MASK) == PRID_REV_LOONGSON3A_R1) {
LOONGSON_CHIPCFG(package_id) |= 1 << (12 + core_id);
} else {
if (!(loongson_sysconf.workarounds & WORKAROUND_CPUHOTPLUG))
LOONGSON_FREQCTRL(package_id) |= 1 << (core_id * 4 + 3);
}
return 0;
}
static int register_loongson3_notifier(void)
{
return cpuhp_setup_state_nocalls(CPUHP_MIPS_SOC_PREPARE,
"mips/loongson:prepare",
loongson3_enable_clock,
loongson3_disable_clock);
}
early_initcall(register_loongson3_notifier);
#endif
const struct plat_smp_ops loongson3_smp_ops = {
.send_ipi_single = loongson3_send_ipi_single,
.send_ipi_mask = loongson3_send_ipi_mask,
.init_secondary = loongson3_init_secondary,
.smp_finish = loongson3_smp_finish,
.boot_secondary = loongson3_boot_secondary,
.smp_setup = loongson3_smp_setup,
.prepare_cpus = loongson3_prepare_cpus,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_disable = loongson3_cpu_disable,
.cpu_die = loongson3_cpu_die,
#endif
#ifdef CONFIG_KEXEC_CORE
.kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
#endif
};
+31
View File
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LOONGSON_SMP_H_
#define __LOONGSON_SMP_H_
/* for Loongson-3 smp support */
extern unsigned long long smp_group[4];
/* 4 groups(nodes) in maximum in numa case */
#define SMP_CORE_GROUP0_BASE (smp_group[0])
#define SMP_CORE_GROUP1_BASE (smp_group[1])
#define SMP_CORE_GROUP2_BASE (smp_group[2])
#define SMP_CORE_GROUP3_BASE (smp_group[3])
/* 4 cores in each group(node) */
#define SMP_CORE0_OFFSET 0x000
#define SMP_CORE1_OFFSET 0x100
#define SMP_CORE2_OFFSET 0x200
#define SMP_CORE3_OFFSET 0x300
/* ipi registers offsets */
#define STATUS0 0x00
#define EN0 0x04
#define SET0 0x08
#define CLEAR0 0x0c
#define STATUS1 0x10
#define MASK1 0x14
#define SET1 0x18
#define CLEAR1 0x1c
#define BUF 0x20
#endif
+47
View File
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2007 Lemote, Inc. & Institute of Computing Technology
* Author: Fuxin Zhang, zhangfx@lemote.com
*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <asm/time.h>
#include <asm/hpet.h>
#include <loongson.h>
#include <linux/clk.h>
#include <linux/of_clk.h>
void __init plat_time_init(void)
{
struct clk *clk;
struct device_node *np;
if (loongson_sysconf.fw_interface == LOONGSON_DTB) {
of_clk_init(NULL);
np = of_get_cpu_node(0, NULL);
if (!np) {
pr_err("Failed to get CPU node\n");
return;
}
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_err("Failed to get CPU clock: %ld\n", PTR_ERR(clk));
return;
}
cpu_clock_freq = clk_get_rate(clk);
clk_put(clk);
}
/* setup mips r4k timer */
mips_hpt_frequency = cpu_clock_freq / 2;
#ifdef CONFIG_RS780_HPET
setup_hpet_timer();
#endif
}
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-2.0+
#include <linux/pci.h>
#include <loongson.h>
static void pci_fixup_video(struct pci_dev *pdev)
{
struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
if (res->start)
return;
if (!loongson_sysconf.vgabios_addr)
return;
pci_disable_rom(pdev);
if (res->parent)
release_resource(res);
res->start = virt_to_phys((void *) loongson_sysconf.vgabios_addr);
res->end = res->start + 256*1024 - 1;
res->flags = IORESOURCE_MEM | IORESOURCE_ROM_SHADOW |
IORESOURCE_PCI_FIXED;
dev_info(&pdev->dev, "Video device with shadowed ROM at %pR\n", res);
}
DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_ATI, 0x9615,
PCI_CLASS_DISPLAY_VGA, 8, pci_fixup_video);