Commit 90502d51 authored by Palmer Dabbelt's avatar Palmer Dabbelt

Merge patch series "Add basic ACPI support for RISC-V"

Sunil V L <sunilvl@ventanamicro.com> says:

This patch series enables the basic ACPI infrastructure for RISC-V.
Supporting external interrupt controllers is in progress and hence it is
tested using poll based HVC SBI console and RAM disk.

The first patch in this series is one of the patch from Jisheng's
series [1] which is not merged yet. This patch is required to support
ACPI since efi_init() which gets called before sbi_init() can enable
static branches and hits a panic.

Below are two ECRs approved by ASWG.
RINTC - https://drive.google.com/file/d/1R6k4MshhN3WTT-hwqAquu5nX6xSEqK2l/view
RHCT - https://drive.google.com/file/d/1nP3nFiH4jkPMp6COOxP6123DCZKR-tia/view

Testing:
1) Build latest Qemu

2) Build EDK2 as per instructions in
https://github.com/vlsunil/riscv-uefi-edk2-docs/wiki/RISC-V-Qemu-Virt-support

3) Build Linux after enabling SBI HVC and SBI earlycon
CONFIG_RISCV_SBI_V01=y
CONFIG_SERIAL_EARLYCON_RISCV_SBI=y
CONFIG_HVC_RISCV_SBI=y

4) Build buildroot.

Run with below command.
qemu-system-riscv64   -nographic \
-drive file=Build/RiscVVirtQemu/RELEASE_GCC5/FV/RISCV_VIRT.fd,if=pflash,format=raw,unit=1 \
-machine virt -smp 16 -m 2G \
-kernel arch/riscv/boot/Image \
-initrd buildroot/output/images/rootfs.cpio \
-append "root=/dev/ram ro console=hvc0 earlycon=sbi"

* b4-shazam-merge:
  RISC-V: Enable ACPI in defconfig
  RISC-V: time.c: Add ACPI support for time_init()
  clocksource/timer-riscv: Add ACPI support
  clocksource/timer-riscv: Refactor riscv_timer_init_dt()
  irqchip/riscv-intc: Add ACPI support
  RISC-V: cpu: Enable cpuinfo for ACPI systems
  RISC-V: cpufeature: Add ACPI support in riscv_fill_hwcap()
  RISC-V: only iterate over possible CPUs in ISA string parser
  RISC-V: smpboot: Add ACPI support in setup_smp()
  RISC-V: smpboot: Create wrapper setup_smp()
  drivers/acpi: RISC-V: Add RHCT related code
  RISC-V: ACPI: Cache and retrieve the RINTC structure
  RISC-V: Add ACPI initialization in setup_arch()
  ACPI: processor_core: RISC-V: Enable mapping processor to the hartid
  RISC-V: Add support to build the ACPI core
  ACPI: OSL: Make should_use_kmap() 0 for RISC-V
  ACPI: tables: Print RINTC information when MADT is parsed
  crypto: hisilicon/qm: Fix to enable build with RISC-V clang
  platform/surface: Disable for RISC-V
  riscv: move sbi_init() earlier before jump_label_init()

Link: https://lore.kernel.org/r/20230515054928.2079268-1-sunilvl@ventanamicro.comSigned-off-by: default avatarPalmer Dabbelt <palmer@rivosinc.com>
parents ac9a7868 cc9e654a
acpi= [HW,ACPI,X86,ARM64]
acpi= [HW,ACPI,X86,ARM64,RISCV64]
Advanced Configuration and Power Interface
Format: { force | on | off | strict | noirq | rsdt |
copy_dsdt }
force -- enable ACPI if default was off
on -- enable ACPI but allow fallback to DT [arm64]
on -- enable ACPI but allow fallback to DT [arm64,riscv64]
off -- disable ACPI if default was on
noirq -- do not use ACPI for IRQ routing
strict -- Be less tolerant of platforms that are not
strictly ACPI specification compliant.
rsdt -- prefer RSDT over (default) XSDT
copy_dsdt -- copy DSDT to memory
For ARM64, ONLY "acpi=off", "acpi=on" or "acpi=force"
are available
For ARM64 and RISCV64, ONLY "acpi=off", "acpi=on" or
"acpi=force" are available
See also Documentation/power/runtime_pm.rst, pci=noacpi
......
......@@ -412,6 +412,13 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/serial-multi-instantiate.c
ACPI FOR RISC-V (ACPI/riscv)
M: Sunil V L <sunilvl@ventanamicro.com>
L: linux-acpi@vger.kernel.org
L: linux-riscv@lists.infradead.org
S: Maintained
F: drivers/acpi/riscv/
ACPI PCC(Platform Communication Channel) MAILBOX DRIVER
M: Sudeep Holla <sudeep.holla@arm.com>
L: linux-acpi@vger.kernel.org
......
......@@ -12,6 +12,8 @@ config 32BIT
config RISCV
def_bool y
select ACPI_GENERIC_GSI if ACPI
select ACPI_REDUCED_HARDWARE_ONLY if ACPI
select ARCH_DMA_DEFAULT_COHERENT
select ARCH_ENABLE_HUGEPAGE_MIGRATION if HUGETLB_PAGE && MIGRATION
select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2
......@@ -707,6 +709,7 @@ config EFI
depends on OF && !XIP_KERNEL
depends on MMU
default y
select ARCH_SUPPORTS_ACPI if 64BIT
select EFI_GENERIC_STUB
select EFI_PARAMS_FROM_FDT
select EFI_RUNTIME_WRAPPERS
......@@ -816,3 +819,5 @@ source "drivers/cpufreq/Kconfig"
endmenu # "CPU Power Management"
source "arch/riscv/kvm/Kconfig"
source "drivers/acpi/Kconfig"
......@@ -37,6 +37,7 @@ CONFIG_PM=y
CONFIG_CPU_IDLE=y
CONFIG_VIRTUALIZATION=y
CONFIG_KVM=m
CONFIG_ACPI=y
CONFIG_JUMP_LABEL=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
......
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* RISC-V specific ACPICA environments and implementation
*/
#ifndef _ASM_ACENV_H
#define _ASM_ACENV_H
/* This header is required unconditionally by the ACPI core */
#endif /* _ASM_ACENV_H */
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2013-2014, Linaro Ltd.
* Author: Al Stone <al.stone@linaro.org>
* Author: Graeme Gregory <graeme.gregory@linaro.org>
* Author: Hanjun Guo <hanjun.guo@linaro.org>
*
* Copyright (C) 2021-2023, Ventana Micro Systems Inc.
* Author: Sunil V L <sunilvl@ventanamicro.com>
*/
#ifndef _ASM_ACPI_H
#define _ASM_ACPI_H
/* Basic configuration for ACPI */
#ifdef CONFIG_ACPI
typedef u64 phys_cpuid_t;
#define PHYS_CPUID_INVALID INVALID_HARTID
/* ACPI table mapping after acpi_permanent_mmap is set */
void *acpi_os_ioremap(acpi_physical_address phys, acpi_size size);
#define acpi_os_ioremap acpi_os_ioremap
#define acpi_strict 1 /* No out-of-spec workarounds on RISC-V */
extern int acpi_disabled;
extern int acpi_noirq;
extern int acpi_pci_disabled;
static inline void disable_acpi(void)
{
acpi_disabled = 1;
acpi_pci_disabled = 1;
acpi_noirq = 1;
}
static inline void enable_acpi(void)
{
acpi_disabled = 0;
acpi_pci_disabled = 0;
acpi_noirq = 0;
}
/*
* The ACPI processor driver for ACPI core code needs this macro
* to find out whether this cpu was already mapped (mapping from CPU hardware
* ID to CPU logical ID) or not.
*/
#define cpu_physical_id(cpu) cpuid_to_hartid_map(cpu)
/*
* Since MADT must provide at least one RINTC structure, the
* CPU will be always available in MADT on RISC-V.
*/
static inline bool acpi_has_cpu_in_madt(void)
{
return true;
}
static inline void arch_fix_phys_package_id(int num, u32 slot) { }
void acpi_init_rintc_map(void);
struct acpi_madt_rintc *acpi_cpu_get_madt_rintc(int cpu);
u32 get_acpi_id_for_cpu(int cpu);
int acpi_get_riscv_isa(struct acpi_table_header *table,
unsigned int cpu, const char **isa);
static inline int acpi_numa_get_nid(unsigned int cpu) { return NUMA_NO_NODE; }
#else
static inline void acpi_init_rintc_map(void) { }
static inline struct acpi_madt_rintc *acpi_cpu_get_madt_rintc(int cpu)
{
return NULL;
}
static inline int acpi_get_riscv_isa(struct acpi_table_header *table,
unsigned int cpu, const char **isa)
{
return -EINVAL;
}
#endif /* CONFIG_ACPI */
#endif /*_ASM_ACPI_H*/
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _ASM_CPU_H
#define _ASM_CPU_H
/* This header is required unconditionally by the ACPI core */
#endif /* _ASM_CPU_H */
......@@ -92,3 +92,4 @@ obj-$(CONFIG_COMPAT) += compat_signal.o
obj-$(CONFIG_COMPAT) += compat_vdso/
obj-$(CONFIG_64BIT) += pi/
obj-$(CONFIG_ACPI) += acpi.o
// SPDX-License-Identifier: GPL-2.0-only
/*
* RISC-V Specific Low-Level ACPI Boot Support
*
* Copyright (C) 2013-2014, Linaro Ltd.
* Author: Al Stone <al.stone@linaro.org>
* Author: Graeme Gregory <graeme.gregory@linaro.org>
* Author: Hanjun Guo <hanjun.guo@linaro.org>
* Author: Tomasz Nowicki <tomasz.nowicki@linaro.org>
* Author: Naresh Bhat <naresh.bhat@linaro.org>
*
* Copyright (C) 2021-2023, Ventana Micro Systems Inc.
* Author: Sunil V L <sunilvl@ventanamicro.com>
*/
#include <linux/acpi.h>
#include <linux/io.h>
#include <linux/pci.h>
#include <linux/efi.h>
int acpi_noirq = 1; /* skip ACPI IRQ initialization */
int acpi_disabled = 1;
EXPORT_SYMBOL(acpi_disabled);
int acpi_pci_disabled = 1; /* skip ACPI PCI scan and IRQ initialization */
EXPORT_SYMBOL(acpi_pci_disabled);
static bool param_acpi_off __initdata;
static bool param_acpi_on __initdata;
static bool param_acpi_force __initdata;
static struct acpi_madt_rintc cpu_madt_rintc[NR_CPUS];
static int __init parse_acpi(char *arg)
{
if (!arg)
return -EINVAL;
/* "acpi=off" disables both ACPI table parsing and interpreter */
if (strcmp(arg, "off") == 0)
param_acpi_off = true;
else if (strcmp(arg, "on") == 0) /* prefer ACPI over DT */
param_acpi_on = true;
else if (strcmp(arg, "force") == 0) /* force ACPI to be enabled */
param_acpi_force = true;
else
return -EINVAL; /* Core will print when we return error */
return 0;
}
early_param("acpi", parse_acpi);
/*
* acpi_fadt_sanity_check() - Check FADT presence and carry out sanity
* checks on it
*
* Return 0 on success, <0 on failure
*/
static int __init acpi_fadt_sanity_check(void)
{
struct acpi_table_header *table;
struct acpi_table_fadt *fadt;
acpi_status status;
int ret = 0;
/*
* FADT is required on riscv; retrieve it to check its presence
* and carry out revision and ACPI HW reduced compliancy tests
*/
status = acpi_get_table(ACPI_SIG_FADT, 0, &table);
if (ACPI_FAILURE(status)) {
const char *msg = acpi_format_exception(status);
pr_err("Failed to get FADT table, %s\n", msg);
return -ENODEV;
}
fadt = (struct acpi_table_fadt *)table;
/*
* The revision in the table header is the FADT's Major revision. The
* FADT also has a minor revision, which is stored in the FADT itself.
*
* TODO: Currently, we check for 6.5 as the minimum version to check
* for HW_REDUCED flag. However, once RISC-V updates are released in
* the ACPI spec, we need to update this check for exact minor revision
*/
if (table->revision < 6 || (table->revision == 6 && fadt->minor_revision < 5))
pr_err(FW_BUG "Unsupported FADT revision %d.%d, should be 6.5+\n",
table->revision, fadt->minor_revision);
if (!(fadt->flags & ACPI_FADT_HW_REDUCED)) {
pr_err("FADT not ACPI hardware reduced compliant\n");
ret = -EINVAL;
}
/*
* acpi_get_table() creates FADT table mapping that
* should be released after parsing and before resuming boot
*/
acpi_put_table(table);
return ret;
}
/*
* acpi_boot_table_init() called from setup_arch(), always.
* 1. find RSDP and get its address, and then find XSDT
* 2. extract all tables and checksums them all
* 3. check ACPI FADT HW reduced flag
*
* We can parse ACPI boot-time tables such as MADT after
* this function is called.
*
* On return ACPI is enabled if either:
*
* - ACPI tables are initialized and sanity checks passed
* - acpi=force was passed in the command line and ACPI was not disabled
* explicitly through acpi=off command line parameter
*
* ACPI is disabled on function return otherwise
*/
void __init acpi_boot_table_init(void)
{
/*
* Enable ACPI instead of device tree unless
* - ACPI has been disabled explicitly (acpi=off), or
* - firmware has not populated ACPI ptr in EFI system table
* and ACPI has not been [force] enabled (acpi=on|force)
*/
if (param_acpi_off ||
(!param_acpi_on && !param_acpi_force &&
efi.acpi20 == EFI_INVALID_TABLE_ADDR))
return;
/*
* ACPI is disabled at this point. Enable it in order to parse
* the ACPI tables and carry out sanity checks
*/
enable_acpi();
/*
* If ACPI tables are initialized and FADT sanity checks passed,
* leave ACPI enabled and carry on booting; otherwise disable ACPI
* on initialization error.
* If acpi=force was passed on the command line it forces ACPI
* to be enabled even if its initialization failed.
*/
if (acpi_table_init() || acpi_fadt_sanity_check()) {
pr_err("Failed to init ACPI tables\n");
if (!param_acpi_force)
disable_acpi();
}
}
static int acpi_parse_madt_rintc(union acpi_subtable_headers *header, const unsigned long end)
{
struct acpi_madt_rintc *rintc = (struct acpi_madt_rintc *)header;
int cpuid;
if (!(rintc->flags & ACPI_MADT_ENABLED))
return 0;
cpuid = riscv_hartid_to_cpuid(rintc->hart_id);
/*
* When CONFIG_SMP is disabled, mapping won't be created for
* all cpus.
* CPUs more than num_possible_cpus, will be ignored.
*/
if (cpuid >= 0 && cpuid < num_possible_cpus())
cpu_madt_rintc[cpuid] = *rintc;
return 0;
}
/*
* Instead of parsing (and freeing) the ACPI table, cache
* the RINTC structures since they are frequently used
* like in cpuinfo.
*/
void __init acpi_init_rintc_map(void)
{
if (acpi_table_parse_madt(ACPI_MADT_TYPE_RINTC, acpi_parse_madt_rintc, 0) <= 0) {
pr_err("No valid RINTC entries exist\n");
BUG();
}
}
struct acpi_madt_rintc *acpi_cpu_get_madt_rintc(int cpu)
{
return &cpu_madt_rintc[cpu];
}
u32 get_acpi_id_for_cpu(int cpu)
{
return acpi_cpu_get_madt_rintc(cpu)->uid;
}
/*
* __acpi_map_table() will be called before paging_init(), so early_ioremap()
* or early_memremap() should be called here to for ACPI table mapping.
*/
void __init __iomem *__acpi_map_table(unsigned long phys, unsigned long size)
{
if (!size)
return NULL;
return early_memremap(phys, size);
}
void __init __acpi_unmap_table(void __iomem *map, unsigned long size)
{
if (!map || !size)
return;
early_memunmap(map, size);
}
void *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)
{
return memremap(phys, size, MEMREMAP_WB);
}
#ifdef CONFIG_PCI
/*
* These interfaces are defined just to enable building ACPI core.
* TODO: Update it with actual implementation when external interrupt
* controller support is added in RISC-V ACPI.
*/
int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn,
int reg, int len, u32 *val)
{
return PCIBIOS_DEVICE_NOT_FOUND;
}
int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
int reg, int len, u32 val)
{
return PCIBIOS_DEVICE_NOT_FOUND;
}
int acpi_pci_bus_find_domain_nr(struct pci_bus *bus)
{
return -1;
}
struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
{
return NULL;
}
#endif /* CONFIG_PCI */
......@@ -3,10 +3,12 @@
* Copyright (C) 2012 Regents of the University of California
*/
#include <linux/acpi.h>
#include <linux/cpu.h>
#include <linux/init.h>
#include <linux/seq_file.h>
#include <linux/of.h>
#include <asm/acpi.h>
#include <asm/cpufeature.h>
#include <asm/csr.h>
#include <asm/hwcap.h>
......@@ -283,23 +285,35 @@ static void c_stop(struct seq_file *m, void *v)
static int c_show(struct seq_file *m, void *v)
{
unsigned long cpu_id = (unsigned long)v - 1;
struct device_node *node = of_get_cpu_node(cpu_id, NULL);
struct riscv_cpuinfo *ci = per_cpu_ptr(&riscv_cpuinfo, cpu_id);
struct device_node *node;
const char *compat, *isa;
seq_printf(m, "processor\t: %lu\n", cpu_id);
seq_printf(m, "hart\t\t: %lu\n", cpuid_to_hartid_map(cpu_id));
if (!of_property_read_string(node, "riscv,isa", &isa))
print_isa(m, isa);
print_mmu(m);
if (!of_property_read_string(node, "compatible", &compat)
&& strcmp(compat, "riscv"))
seq_printf(m, "uarch\t\t: %s\n", compat);
if (acpi_disabled) {
node = of_get_cpu_node(cpu_id, NULL);
if (!of_property_read_string(node, "riscv,isa", &isa))
print_isa(m, isa);
print_mmu(m);
if (!of_property_read_string(node, "compatible", &compat) &&
strcmp(compat, "riscv"))
seq_printf(m, "uarch\t\t: %s\n", compat);
of_node_put(node);
} else {
if (!acpi_get_riscv_isa(NULL, cpu_id, &isa))
print_isa(m, isa);
print_mmu(m);
}
seq_printf(m, "mvendorid\t: 0x%lx\n", ci->mvendorid);
seq_printf(m, "marchid\t\t: 0x%lx\n", ci->marchid);
seq_printf(m, "mimpid\t\t: 0x%lx\n", ci->mimpid);
seq_puts(m, "\n");
of_node_put(node);
return 0;
}
......
......@@ -6,12 +6,15 @@
* Copyright (C) 2017 SiFive
*/
#include <linux/acpi.h>
#include <linux/bitmap.h>
#include <linux/ctype.h>
#include <linux/log2.h>
#include <linux/memory.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <asm/acpi.h>
#include <asm/alternative.h>
#include <asm/cacheflush.h>
#include <asm/cpufeature.h>
......@@ -99,7 +102,9 @@ void __init riscv_fill_hwcap(void)
char print_str[NUM_ALPHA_EXTS + 1];
int i, j, rc;
unsigned long isa2hwcap[26] = {0};
unsigned long hartid;
struct acpi_table_header *rhct;
acpi_status status;
unsigned int cpu;
isa2hwcap['i' - 'a'] = COMPAT_HWCAP_ISA_I;
isa2hwcap['m' - 'a'] = COMPAT_HWCAP_ISA_M;
......@@ -112,18 +117,36 @@ void __init riscv_fill_hwcap(void)
bitmap_zero(riscv_isa, RISCV_ISA_EXT_MAX);
for_each_of_cpu_node(node) {
if (!acpi_disabled) {
status = acpi_get_table(ACPI_SIG_RHCT, 0, &rhct);
if (ACPI_FAILURE(status))
return;
}
for_each_possible_cpu(cpu) {
unsigned long this_hwcap = 0;
DECLARE_BITMAP(this_isa, RISCV_ISA_EXT_MAX);
const char *temp;
rc = riscv_of_processor_hartid(node, &hartid);
if (rc < 0)
continue;
if (acpi_disabled) {
node = of_cpu_device_node_get(cpu);
if (!node) {
pr_warn("Unable to find cpu node\n");
continue;
}
if (of_property_read_string(node, "riscv,isa", &isa)) {
pr_warn("Unable to find \"riscv,isa\" devicetree entry\n");
continue;
rc = of_property_read_string(node, "riscv,isa", &isa);
of_node_put(node);
if (rc) {
pr_warn("Unable to find \"riscv,isa\" devicetree entry\n");
continue;
}
} else {
rc = acpi_get_riscv_isa(rhct, cpu, &isa);
if (rc < 0) {
pr_warn("Unable to get ISA for the hart - %d\n", cpu);
continue;
}
}
temp = isa;
......@@ -260,6 +283,9 @@ void __init riscv_fill_hwcap(void)
bitmap_and(riscv_isa, riscv_isa, this_isa, RISCV_ISA_EXT_MAX);
}
if (!acpi_disabled && rhct)
acpi_put_table((struct acpi_table_header *)rhct);
/* We don't support systems with F but without D, so mask those out
* here. */
if ((elf_hwcap & COMPAT_HWCAP_ISA_F) && !(elf_hwcap & COMPAT_HWCAP_ISA_D)) {
......
......@@ -8,6 +8,7 @@
* Nick Kossifidis <mick@ics.forth.gr>
*/
#include <linux/acpi.h>
#include <linux/cpu.h>
#include <linux/init.h>
#include <linux/mm.h>
......@@ -21,6 +22,7 @@
#include <linux/efi.h>
#include <linux/crash_dump.h>
#include <asm/acpi.h>
#include <asm/alternative.h>
#include <asm/cacheflush.h>
#include <asm/cpu_ops.h>
......@@ -270,11 +272,16 @@ void __init setup_arch(char **cmdline_p)
*cmdline_p = boot_command_line;
early_ioremap_setup();
sbi_init();
jump_label_init();
parse_early_param();
efi_init();
paging_init();
/* Parse the ACPI tables for possible boot-time configuration */
acpi_boot_table_init();
#if IS_ENABLED(CONFIG_BUILTIN_DTB)
unflatten_and_copy_device_tree();
#else
......@@ -283,7 +290,6 @@ void __init setup_arch(char **cmdline_p)
misc_mem_init();
init_resources();
sbi_init();
#ifdef CONFIG_KASAN
kasan_init();
......@@ -293,6 +299,9 @@ void __init setup_arch(char **cmdline_p)
setup_smp();
#endif
if (!acpi_disabled)
acpi_init_rintc_map();
riscv_init_cbo_blocksizes();
riscv_fill_hwcap();
apply_boot_alternatives();
......
......@@ -8,6 +8,7 @@
* Copyright (C) 2017 SiFive
*/
#include <linux/acpi.h>
#include <linux/arch_topology.h>
#include <linux/module.h>
#include <linux/init.h>
......@@ -70,7 +71,73 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
}
}
void __init setup_smp(void)
#ifdef CONFIG_ACPI
static unsigned int cpu_count = 1;
static int __init acpi_parse_rintc(union acpi_subtable_headers *header, const unsigned long end)
{
unsigned long hart;
static bool found_boot_cpu;
struct acpi_madt_rintc *processor = (struct acpi_madt_rintc *)header;
/*
* Each RINTC structure in MADT will have a flag. If ACPI_MADT_ENABLED
* bit in the flag is not enabled, it means OS should not try to enable
* the cpu to which RINTC belongs.
*/
if (!(processor->flags & ACPI_MADT_ENABLED))
return 0;
if (BAD_MADT_ENTRY(processor, end))
return -EINVAL;
acpi_table_print_madt_entry(&header->common);
hart = processor->hart_id;
if (hart == INVALID_HARTID) {
pr_warn("Invalid hartid\n");
return 0;
}
if (hart == cpuid_to_hartid_map(0)) {
BUG_ON(found_boot_cpu);
found_boot_cpu = true;
early_map_cpu_to_node(0, acpi_numa_get_nid(cpu_count));
return 0;
}
if (cpu_count >= NR_CPUS) {
pr_warn("NR_CPUS is too small for the number of ACPI tables.\n");
return 0;
}
cpuid_to_hartid_map(cpu_count) = hart;
early_map_cpu_to_node(cpu_count, acpi_numa_get_nid(cpu_count));
cpu_count++;
return 0;
}
static void __init acpi_parse_and_init_cpus(void)
{
int cpuid;
cpu_set_ops(0);
acpi_table_parse_madt(ACPI_MADT_TYPE_RINTC, acpi_parse_rintc, 0);
for (cpuid = 1; cpuid < nr_cpu_ids; cpuid++) {
if (cpuid_to_hartid_map(cpuid) != INVALID_HARTID) {
cpu_set_ops(cpuid);
set_cpu_possible(cpuid, true);
}
}
}
#else
#define acpi_parse_and_init_cpus(...) do { } while (0)
#endif
static void __init of_parse_and_init_cpus(void)
{
struct device_node *dn;
unsigned long hart;
......@@ -116,6 +183,14 @@ void __init setup_smp(void)
}
}
void __init setup_smp(void)
{
if (acpi_disabled)
of_parse_and_init_cpus();
else
acpi_parse_and_init_cpus();
}
static int start_secondary_cpu(int cpu, struct task_struct *tidle)
{
if (cpu_ops[cpu]->cpu_start)
......
......@@ -4,6 +4,7 @@
* Copyright (C) 2017 SiFive
*/
#include <linux/acpi.h>
#include <linux/of_clk.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
......@@ -18,17 +19,29 @@ EXPORT_SYMBOL_GPL(riscv_timebase);
void __init time_init(void)
{
struct device_node *cpu;
struct acpi_table_rhct *rhct;
acpi_status status;
u32 prop;
cpu = of_find_node_by_path("/cpus");
if (!cpu || of_property_read_u32(cpu, "timebase-frequency", &prop))
panic(KERN_WARNING "RISC-V system with no 'timebase-frequency' in DTS\n");
of_node_put(cpu);
riscv_timebase = prop;
if (acpi_disabled) {
cpu = of_find_node_by_path("/cpus");
if (!cpu || of_property_read_u32(cpu, "timebase-frequency", &prop))
panic("RISC-V system with no 'timebase-frequency' in DTS\n");
of_node_put(cpu);
riscv_timebase = prop;
of_clk_init(NULL);
} else {
status = acpi_get_table(ACPI_SIG_RHCT, 0, (struct acpi_table_header **)&rhct);
if (ACPI_FAILURE(status))
panic("RISC-V ACPI system with no RHCT table\n");
riscv_timebase = rhct->time_base_freq;
acpi_put_table((struct acpi_table_header *)rhct);
}
lpj_fine = riscv_timebase / HZ;
of_clk_init(NULL);
timer_probe();
tick_setup_hrtimer_broadcast();
......
......@@ -131,3 +131,5 @@ obj-y += dptf/
obj-$(CONFIG_ARM64) += arm64/
obj-$(CONFIG_ACPI_VIOT) += viot.o
obj-$(CONFIG_RISCV) += riscv/
......@@ -276,7 +276,7 @@ acpi_map_lookup_virt(void __iomem *virt, acpi_size size)
return NULL;
}
#if defined(CONFIG_IA64) || defined(CONFIG_ARM64)
#if defined(CONFIG_IA64) || defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
/* ioremap will take care of cache attributes */
#define should_use_kmap(pfn) 0
#else
......
......@@ -106,6 +106,32 @@ static int map_gicc_mpidr(struct acpi_subtable_header *entry,
return -EINVAL;
}
/*
* Retrieve the RISC-V hartid for the processor
*/
static int map_rintc_hartid(struct acpi_subtable_header *entry,
int device_declaration, u32 acpi_id,
phys_cpuid_t *hartid)
{
struct acpi_madt_rintc *rintc =
container_of(entry, struct acpi_madt_rintc, header);
if (!(rintc->flags & ACPI_MADT_ENABLED))
return -ENODEV;
/* device_declaration means Device object in DSDT, in the
* RISC-V, logical processors are required to
* have a Processor Device object in the DSDT, so we should
* check device_declaration here
*/
if (device_declaration && rintc->uid == acpi_id) {
*hartid = rintc->hart_id;
return 0;
}
return -EINVAL;
}
static phys_cpuid_t map_madt_entry(struct acpi_table_madt *madt,
int type, u32 acpi_id)
{
......@@ -136,6 +162,9 @@ static phys_cpuid_t map_madt_entry(struct acpi_table_madt *madt,
} else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) {
if (!map_gicc_mpidr(header, type, acpi_id, &phys_id))
break;
} else if (header->type == ACPI_MADT_TYPE_RINTC) {
if (!map_rintc_hartid(header, type, acpi_id, &phys_id))
break;
}
entry += header->length;
}
......
# SPDX-License-Identifier: GPL-2.0-only
obj-y += rhct.o
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2022-2023, Ventana Micro Systems Inc
* Author: Sunil V L <sunilvl@ventanamicro.com>
*
*/
#define pr_fmt(fmt) "ACPI: RHCT: " fmt
#include <linux/acpi.h>
static struct acpi_table_header *acpi_get_rhct(void)
{
static struct acpi_table_header *rhct;
acpi_status status;
/*
* RHCT will be used at runtime on every CPU, so we
* don't need to call acpi_put_table() to release the table mapping.
*/
if (!rhct) {
status = acpi_get_table(ACPI_SIG_RHCT, 0, &rhct);
if (ACPI_FAILURE(status)) {
pr_warn_once("No RHCT table found\n");
return NULL;
}
}
return rhct;
}
/*
* During early boot, the caller should call acpi_get_table() and pass its pointer to
* these functions(and free up later). At run time, since this table can be used
* multiple times, NULL may be passed in order to use the cached table.
*/
int acpi_get_riscv_isa(struct acpi_table_header *table, unsigned int cpu, const char **isa)
{
struct acpi_rhct_node_header *node, *ref_node, *end;
u32 size_hdr = sizeof(struct acpi_rhct_node_header);
u32 size_hartinfo = sizeof(struct acpi_rhct_hart_info);
struct acpi_rhct_hart_info *hart_info;
struct acpi_rhct_isa_string *isa_node;
struct acpi_table_rhct *rhct;
u32 *hart_info_node_offset;
u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
BUG_ON(acpi_disabled);
if (!table) {
rhct = (struct acpi_table_rhct *)acpi_get_rhct();
if (!rhct)
return -ENOENT;
} else {
rhct = (struct acpi_table_rhct *)table;
}
end = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->header.length);
for (node = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->node_offset);
node < end;
node = ACPI_ADD_PTR(struct acpi_rhct_node_header, node, node->length)) {
if (node->type == ACPI_RHCT_NODE_TYPE_HART_INFO) {
hart_info = ACPI_ADD_PTR(struct acpi_rhct_hart_info, node, size_hdr);
hart_info_node_offset = ACPI_ADD_PTR(u32, hart_info, size_hartinfo);
if (acpi_cpu_id != hart_info->uid)
continue;
for (int i = 0; i < hart_info->num_offsets; i++) {
ref_node = ACPI_ADD_PTR(struct acpi_rhct_node_header,
rhct, hart_info_node_offset[i]);
if (ref_node->type == ACPI_RHCT_NODE_TYPE_ISA_STRING) {
isa_node = ACPI_ADD_PTR(struct acpi_rhct_isa_string,
ref_node, size_hdr);
*isa = isa_node->isa;
return 0;
}
}
}
}
return -1;
}
......@@ -220,6 +220,16 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
}
break;
case ACPI_MADT_TYPE_RINTC:
{
struct acpi_madt_rintc *p = (struct acpi_madt_rintc *)header;
pr_debug("RISC-V INTC (acpi_uid[0x%04x] hart_id[0x%llx] %s)\n",
p->uid, p->hart_id,
(p->flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled");
}
break;
default:
pr_warn("Found unsupported MADT entry (type = 0x%x)\n",
header->type);
......
......@@ -10,6 +10,7 @@
#define pr_fmt(fmt) "riscv-timer: " fmt
#include <linux/acpi.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/cpu.h>
......@@ -124,61 +125,28 @@ static irqreturn_t riscv_timer_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
static int __init riscv_timer_init_dt(struct device_node *n)
static int __init riscv_timer_init_common(void)
{
int cpuid, error;
unsigned long hartid;
struct device_node *child;
int error;
struct irq_domain *domain;
struct fwnode_handle *intc_fwnode = riscv_get_intc_hwnode();
error = riscv_of_processor_hartid(n, &hartid);
if (error < 0) {
pr_warn("Not valid hartid for node [%pOF] error = [%lu]\n",
n, hartid);
return error;
}
cpuid = riscv_hartid_to_cpuid(hartid);
if (cpuid < 0) {
pr_warn("Invalid cpuid for hartid [%lu]\n", hartid);
return cpuid;
}
if (cpuid != smp_processor_id())
return 0;
child = of_find_compatible_node(NULL, NULL, "riscv,timer");
if (child) {
riscv_timer_cannot_wake_cpu = of_property_read_bool(child,
"riscv,timer-cannot-wake-cpu");
of_node_put(child);
}
domain = NULL;
child = of_get_compatible_child(n, "riscv,cpu-intc");
if (!child) {
pr_err("Failed to find INTC node [%pOF]\n", n);
return -ENODEV;
}
domain = irq_find_host(child);
of_node_put(child);
domain = irq_find_matching_fwnode(intc_fwnode, DOMAIN_BUS_ANY);
if (!domain) {
pr_err("Failed to find IRQ domain for node [%pOF]\n", n);
pr_err("Failed to find irq_domain for INTC node [%pfwP]\n",
intc_fwnode);
return -ENODEV;
}
riscv_clock_event_irq = irq_create_mapping(domain, RV_IRQ_TIMER);
if (!riscv_clock_event_irq) {
pr_err("Failed to map timer interrupt for node [%pOF]\n", n);
pr_err("Failed to map timer interrupt for node [%pfwP]\n", intc_fwnode);
return -ENODEV;
}
pr_info("%s: Registering clocksource cpuid [%d] hartid [%lu]\n",
__func__, cpuid, hartid);
error = clocksource_register_hz(&riscv_clocksource, riscv_timebase);
if (error) {
pr_err("RISCV timer register failed [%d] for cpu = [%d]\n",
error, cpuid);
pr_err("RISCV timer registration failed [%d]\n", error);
return error;
}
......@@ -207,4 +175,46 @@ static int __init riscv_timer_init_dt(struct device_node *n)
return error;
}
static int __init riscv_timer_init_dt(struct device_node *n)
{
int cpuid, error;
unsigned long hartid;
struct device_node *child;
error = riscv_of_processor_hartid(n, &hartid);
if (error < 0) {
pr_warn("Invalid hartid for node [%pOF] error = [%lu]\n",
n, hartid);
return error;
}
cpuid = riscv_hartid_to_cpuid(hartid);
if (cpuid < 0) {
pr_warn("Invalid cpuid for hartid [%lu]\n", hartid);
return cpuid;
}
if (cpuid != smp_processor_id())
return 0;
child = of_find_compatible_node(NULL, NULL, "riscv,timer");
if (child) {
riscv_timer_cannot_wake_cpu = of_property_read_bool(child,
"riscv,timer-cannot-wake-cpu");
of_node_put(child);
}
return riscv_timer_init_common();
}
TIMER_OF_DECLARE(riscv_timer, "riscv", riscv_timer_init_dt);
#ifdef CONFIG_ACPI
static int __init riscv_timer_acpi_init(struct acpi_table_header *table)
{
return riscv_timer_init_common();
}
TIMER_ACPI_DECLARE(aclint_mtimer, ACPI_SIG_RHCT, riscv_timer_acpi_init);
#endif
......@@ -610,7 +610,10 @@ EXPORT_SYMBOL_GPL(hisi_qm_wait_mb_ready);
static void qm_mb_write(struct hisi_qm *qm, const void *src)
{
void __iomem *fun_base = qm->io_base + QM_MB_CMD_SEND_BASE;
#if IS_ENABLED(CONFIG_ARM64)
unsigned long tmp0 = 0, tmp1 = 0;
#endif
if (!IS_ENABLED(CONFIG_ARM64)) {
memcpy_toio(fun_base, src, 16);
......@@ -618,6 +621,7 @@ static void qm_mb_write(struct hisi_qm *qm, const void *src)
return;
}
#if IS_ENABLED(CONFIG_ARM64)
asm volatile("ldp %0, %1, %3\n"
"stp %0, %1, %2\n"
"dmb oshst\n"
......@@ -626,6 +630,7 @@ static void qm_mb_write(struct hisi_qm *qm, const void *src)
"+Q" (*((char __iomem *)fun_base))
: "Q" (*((char *)src))
: "memory");
#endif
}
static int qm_mb_nolock(struct hisi_qm *qm, struct qm_mailbox *mailbox)
......
......@@ -6,6 +6,7 @@
*/
#define pr_fmt(fmt) "riscv-intc: " fmt
#include <linux/acpi.h>
#include <linux/atomic.h>
#include <linux/bits.h>
#include <linux/cpu.h>
......@@ -112,6 +113,30 @@ static struct fwnode_handle *riscv_intc_hwnode(void)
return intc_domain->fwnode;
}
static int __init riscv_intc_init_common(struct fwnode_handle *fn)
{
int rc;
intc_domain = irq_domain_create_linear(fn, BITS_PER_LONG,
&riscv_intc_domain_ops, NULL);
if (!intc_domain) {
pr_err("unable to add IRQ domain\n");
return -ENXIO;
}
rc = set_handle_irq(&riscv_intc_irq);
if (rc) {
pr_err("failed to set irq handler\n");
return rc;
}
riscv_set_intc_hwnode_fn(riscv_intc_hwnode);
pr_info("%d local interrupts mapped\n", BITS_PER_LONG);
return 0;
}
static int __init riscv_intc_init(struct device_node *node,
struct device_node *parent)
{
......@@ -133,24 +158,39 @@ static int __init riscv_intc_init(struct device_node *node,
if (riscv_hartid_to_cpuid(hartid) != smp_processor_id())
return 0;
intc_domain = irq_domain_add_linear(node, BITS_PER_LONG,
&riscv_intc_domain_ops, NULL);
if (!intc_domain) {
pr_err("unable to add IRQ domain\n");
return -ENXIO;
}
return riscv_intc_init_common(of_node_to_fwnode(node));
}
rc = set_handle_irq(&riscv_intc_irq);
if (rc) {
pr_err("failed to set irq handler\n");
return rc;
}
IRQCHIP_DECLARE(riscv, "riscv,cpu-intc", riscv_intc_init);
riscv_set_intc_hwnode_fn(riscv_intc_hwnode);
#ifdef CONFIG_ACPI
pr_info("%d local interrupts mapped\n", BITS_PER_LONG);
static int __init riscv_intc_acpi_init(union acpi_subtable_headers *header,
const unsigned long end)
{
struct fwnode_handle *fn;
struct acpi_madt_rintc *rintc;
return 0;
rintc = (struct acpi_madt_rintc *)header;
/*
* The ACPI MADT will have one INTC for each CPU (or HART)
* so riscv_intc_acpi_init() function will be called once
* for each INTC. We only do INTC initialization
* for the INTC belonging to the boot CPU (or boot HART).
*/
if (riscv_hartid_to_cpuid(rintc->hart_id) != smp_processor_id())
return 0;
fn = irq_domain_alloc_named_fwnode("RISCV-INTC");
if (!fn) {
pr_err("unable to allocate INTC FW node\n");
return -ENOMEM;
}
return riscv_intc_init_common(fn);
}
IRQCHIP_DECLARE(riscv, "riscv,cpu-intc", riscv_intc_init);
IRQCHIP_ACPI_DECLARE(riscv_intc, ACPI_MADT_TYPE_RINTC, NULL,
ACPI_MADT_RINTC_VERSION_V1, riscv_intc_acpi_init);
#endif
......@@ -4,7 +4,7 @@
menuconfig SURFACE_AGGREGATOR
tristate "Microsoft Surface System Aggregator Module Subsystem and Drivers"
depends on SERIAL_DEV_BUS
depends on ACPI
depends on ACPI && !RISCV
select CRC_CCITT
help
The Surface System Aggregator Module (Surface SAM or SSAM) is an
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment