Commit a793e452 authored by Mao Minkai's avatar Mao Minkai Committed by guzitao
Browse files

sw64: add ACPI support

Sunway inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I8YQTI



--------------------------------

Add basic ACPI support for SW64.

Signed-off-by: default avatarMao Minkai <maominkai@wxiat.com>
Reviewed-by: default avatarHe Sheng <hesheng@wxiat.com>
Signed-off-by: default avatarGu Zitao <guzitao@wxiat.com>
parent 29e191f0
Loading
Loading
Loading
Loading
+40 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */

#ifndef _ASM_SW64_ACENV_H
#define _ASM_SW64_ACENV_H

#define COMPILER_DEPENDENT_INT64   long
#define COMPILER_DEPENDENT_UINT64  unsigned long

/*
 * Calling conventions:
 *
 * ACPI_SYSTEM_XFACE        - Interfaces to host OS (handlers, threads)
 * ACPI_EXTERNAL_XFACE      - External ACPI interfaces
 * ACPI_INTERNAL_XFACE      - Internal ACPI interfaces
 * ACPI_INTERNAL_VAR_XFACE  - Internal variable-parameter list interfaces
 */
#define ACPI_SYSTEM_XFACE
#define ACPI_EXTERNAL_XFACE
#define ACPI_INTERNAL_XFACE
#define ACPI_INTERNAL_VAR_XFACE

/* Asm macros */
#define ACPI_FLUSH_CPU_CACHE()

int __acpi_acquire_global_lock(unsigned int *lock);
int __acpi_release_global_lock(unsigned int *lock);

#define ACPI_ACQUIRE_GLOBAL_LOCK(facs, Acq) \
	((Acq) = __acpi_acquire_global_lock(&facs->global_lock))

#define ACPI_RELEASE_GLOBAL_LOCK(facs, Acq) \
	((Acq) = __acpi_release_global_lock(&facs->global_lock))

/*
 * Math helper asm macros
 */
#define ACPI_DIV_64_BY_32(n_hi, n_lo, d32, q32, r32)

#define ACPI_SHIFT_RIGHT_64(n_hi, n_lo)
#endif /* _ASM_SW64_ACENV_H */
+117 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */

#ifndef _ASM_SW64_ACPI_H
#define _ASM_SW64_ACPI_H

#include <asm/processor.h>
#include <asm/mmu.h>
#include <asm/numa.h>
#include <asm/early_ioremap.h>

#ifdef CONFIG_ACPI
extern int acpi_noirq;
extern int acpi_strict;
extern int acpi_disabled;
extern int acpi_pci_disabled;

/* _ASM_SW64_PDC_H */
#define ACPI_PDC_P_FFH                  (0x0001)
#define ACPI_PDC_C_C1_HALT              (0x0002)
#define ACPI_PDC_T_FFH                  (0x0004)
#define ACPI_PDC_SMP_C1PT               (0x0008)
#define ACPI_PDC_SMP_C2C3               (0x0010)
#define ACPI_PDC_SMP_P_SWCOORD          (0x0020)
#define ACPI_PDC_SMP_C_SWCOORD          (0x0040)
#define ACPI_PDC_SMP_T_SWCOORD          (0x0080)
#define ACPI_PDC_C_C1_FFH               (0x0100)
#define ACPI_PDC_C_C2C3_FFH             (0x0200)
#define ACPI_PDC_SMP_P_HWCOORD          (0x0800)

#define ACPI_PDC_EST_CAPABILITY_SMP     (ACPI_PDC_SMP_C1PT | \
					ACPI_PDC_C_C1_HALT | \
					ACPI_PDC_P_FFH)

#define ACPI_PDC_EST_CAPABILITY_SWSMP   (ACPI_PDC_SMP_C1PT | \
					ACPI_PDC_C_C1_HALT | \
					ACPI_PDC_SMP_P_SWCOORD | \
					ACPI_PDC_SMP_P_HWCOORD | \
					ACPI_PDC_P_FFH)

#define ACPI_PDC_C_CAPABILITY_SMP	(ACPI_PDC_SMP_C2C3 | \
					ACPI_PDC_SMP_C1PT  | \
					ACPI_PDC_C_C1_HALT | \
					ACPI_PDC_C_C1_FFH  | \
					ACPI_PDC_C_C2C3_FFH)

#define ACPI_TABLE_UPGRADE_MAX_PHYS MEMBLOCK_ALLOC_ACCESSIBLE

/**
 * Use the number 64 is just because this number is the most
 * frequently used number in other architectures. Actually,
 * SW64 does not have fixmap area in memory layout.
 */
#define NR_FIX_BTMAPS 64

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;
}

static inline void acpi_noirq_set(void)
{
	acpi_noirq = 1;
}

static inline void acpi_disable_pci(void)
{
	acpi_pci_disabled = 1;
	acpi_noirq_set();
}

static inline bool acpi_has_cpu_in_madt(void)
{
	return true;
}

/* Low-level suspend routine. */
extern int (*acpi_suspend_lowlevel)(void);
extern unsigned long long arch_acpi_wakeup_start;

/* Physical address to resume after wakeup */
#define acpi_wakeup_address arch_acpi_wakeup_start

/*
 * Check if the CPU can handle C2 and deeper
 */
static inline unsigned int acpi_processor_cstate_check(unsigned int max_cstate)
{
	return max_cstate;
}

static inline bool arch_has_acpi_pdc(void)
{
	return false;
}

static inline void arch_acpi_set_pdc_bits(u32 *buf)
{
}
#else /* !CONFIG_ACPI */

static inline void acpi_noirq_set(void) { }
static inline void acpi_disable_pci(void) { }
static inline void disable_acpi(void) { }

#endif /* !CONFIG_ACPI */

#define acpi_unlazy_tlb(x)
#endif /* _ASM_SW64_ACPI_H */
+304 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/irqdomain.h>
#include <linux/memblock.h>

#include <asm/early_ioremap.h>

#ifdef CONFIG_ACPI_HOTPLUG_CPU
#include <acpi/processor.h>
#endif

int acpi_disabled = 1;
EXPORT_SYMBOL(acpi_disabled);

int acpi_noirq = 1;		/* skip ACPI IRQ initialization */
int acpi_pci_disabled = 1;	/* skip ACPI PCI scan and IRQ initialization */
EXPORT_SYMBOL(acpi_pci_disabled);

static bool param_acpi_on  __initdata;
static bool param_acpi_off __initdata;

int acpi_strict;
u64 arch_acpi_wakeup_start;
u64 acpi_saved_sp_s3;

#define MAX_LOCAL_APIC 256

#define PREFIX			"ACPI: "
/*
 * The default interrupt routing model is PIC (8259).  This gets
 * overridden if IOAPICs are enumerated (below).
 */
enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_IOSAPIC;
void __iomem *__init __acpi_map_table(unsigned long phys, unsigned long size)
{
	if (!phys || !size)
		return NULL;

	return early_ioremap(phys, size);
}
void __init __acpi_unmap_table(void __iomem *map, unsigned long size)
{
	if (!map || !size)
		return;

	early_iounmap(map, size);
}
/*
 * Following __acpi_xx functions should be implemented for sepecific cpu.
 */
int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp)
{
	if (irqp != NULL)
		*irqp = acpi_register_gsi(NULL, gsi, -1, -1);

	return 0;
}
EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);

int acpi_isa_irq_to_gsi(unsigned int isa_irq, u32 *gsi)
{
	if (gsi)
		*gsi = isa_irq;

	return 0;
}

int (*acpi_suspend_lowlevel)(void);

/*
 * success: return IRQ number (>=0)
 * failure: return < 0
 */
static struct irq_domain *irq_default_domain;
int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity)
{
	u32 irq;

	irq = irq_find_mapping(irq_default_domain, gsi);

	return irq;
}
EXPORT_SYMBOL_GPL(acpi_register_gsi);

void acpi_unregister_gsi(u32 gsi)
{

}
EXPORT_SYMBOL_GPL(acpi_unregister_gsi);

/*
 *  ACPI based hotplug support for CPU
 */
#ifdef CONFIG_ACPI_HOTPLUG_CPU
/* wrapper to silence section mismatch warning */
int __ref acpi_map_lsapic(acpi_handle handle, int physid, int *pcpu)
{
	return 0;
}
EXPORT_SYMBOL(acpi_map_lsapic);

int acpi_unmap_lsapic(int cpu)
{
	return 0;
}
EXPORT_SYMBOL(acpi_unmap_lsapic);
#endif /* CONFIG_ACPI_HOTPLUG_CPU */

u8 acpi_checksum(u8 *table, u32 length)
{
	u8 ret = 0;

	while (length--) {
		ret += *table;
		table++;
	}
	return -ret;
}

static int __init parse_acpi(char *arg)
{
	if (!arg)
		return -EINVAL;

	/* disable both ACPI table parsing and interpreter */
	if (strcmp(arg, "off") == 0)
		param_acpi_off = true;
	else if (strcmp(arg, "on") == 0) /* prefer ACPI over device tree */
		param_acpi_on = true;
	else
		return -EINVAL; /* Core will printk when we return error. */

	return 0;
}
early_param("acpi", parse_acpi);

/*
 * __acpi_acquire_global_lock
 * will always return -1 indicating owning the lock.
 *
 * __acpi_release_global_lock will always return 0 indicating
 * no acquring request pending.
 */
int __acpi_acquire_global_lock(unsigned int *lock)
{
	return -1;
}

int __acpi_release_global_lock(unsigned int *lock)
{
	return 0;
}

#ifdef CONFIG_ACPI_NUMA
static int rcid_to_cpu(int physical_id)
{
	int i;

	for (i = 0; i < NR_CPUS; ++i) {
		if (__cpu_to_rcid[i] == physical_id)
			return i;
	}

	/* physical id not found */
	return -1;
}

/* Callback for Proximity Domain -> CPUID mapping */
void __init
acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa)
{
	int pxm, node;
	int cpu; // logical core id

	if (srat_disabled())
		return;
	if (pa->header.length != sizeof(struct acpi_srat_cpu_affinity)) {
		bad_srat();
		return;
	}
	if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0)
		return;
	pxm = pa->proximity_domain_lo;
	if (acpi_srat_revision >= 2) {
		pxm |= (pa->proximity_domain_hi[0] << 8);
		pxm |= (pa->proximity_domain_hi[1] << 16);
		pxm |= (pa->proximity_domain_hi[2] << 24);
	}

	node = acpi_map_pxm_to_node(pxm);
	if (node < 0) {
		pr_err("SRAT: Too many proximity domains %x\n", pxm);
		bad_srat();
		return;
	}

	if (pa->apic_id >= CONFIG_NR_CPUS) {
		pr_err("SRAT: PXM %u -> CPU 0x%02x -> Node %u skipped apicid that is too big\n", pxm, pa->apic_id, node);
		return;
	}

	/* Record the mapping from logical core id to node id */
	cpu = rcid_to_cpu(pa->apic_id);
	if (cpu < 0) {
		pr_err("SRAT: Can not find the logical id for physical Core 0x%02x\n", pa->apic_id);
		return;
	}

	early_map_cpu_to_node(cpu, node);

	node_set(node, numa_nodes_parsed);
	pr_info("SRAT: PXM %u -> CPU 0x%02x -> Node %u\n", pxm, pa->apic_id, node);
}

#ifdef CONFIG_MEMORY_HOTPLUG
static inline int save_add_info(void) { return 1; }
#else
static inline int save_add_info(void) { return 0; }
#endif

#endif

void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
{
}

#ifdef CONFIG_ACPI_HOTPLUG_CPU
static int acpi_map_cpu2node(acpi_handle handle, int cpu, int physid)
{
#ifdef CONFIG_ACPI_NUMA
	int nid;

	nid = acpi_get_node(handle);
	if (nid != NUMA_NO_NODE) {
		set_cpuid_to_node(cpu, nid);
		node_set(nid, numa_nodes_parsed);
	}
#endif
	return 0;
}

int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id,
			int *pcpu)
{
	int cpu;
	struct acpi_madt_local_apic *processor;

	processor = kzalloc(sizeof(struct acpi_madt_local_apic), GFP_KERNEL);
	processor->id = physid;
	processor->processor_id = acpi_id;
	processor->lapic_flags = ACPI_MADT_ENABLED;

	cpu = set_processor_mask(processor);
	if (cpu < 0) {
		pr_info(PREFIX "Unable to map lapic to logical cpu number\n");
		return cpu;
	}

	acpi_map_cpu2node(handle, cpu, physid);

	*pcpu = cpu;
	return 0;
}
EXPORT_SYMBOL(acpi_map_cpu);

int acpi_unmap_cpu(int cpu)
{
#ifdef CONFIG_ACPI_NUMA
	set_cpuid_to_node(cpu, NUMA_NO_NODE);
#endif
	set_cpu_present(cpu, false);
	num_processors--;

	pr_info("cpu%d hot remove!\n", cpu);

	return 0;
}
EXPORT_SYMBOL(acpi_unmap_cpu);
#endif /* CONFIG_ACPI_HOTPLUG_CPU */

void __init acpi_boot_table_init(void)
{
	/**
	 * ACPI is disabled by default.
	 * ACPI is only enabled when firmware passes ACPI table
	 * and sets boot parameter "acpi=on".
	 */
	if (param_acpi_on)
		enable_acpi();

	/*
	 * If acpi_disabled, bail out
	 */
	if (!acpi_disabled) {
		pr_warn("Currently, ACPI is an experimental feature!\n");
		if (acpi_table_init()) {
			pr_err("Failed to init ACPI tables\n");
			disable_acpi();
		} else
			pr_info("Successfully parsed ACPI table\n");
	}
}

arch/sw_64/pci/acpi.c

0 → 100644
+245 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
#include <linux/pci.h>
#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/pci-acpi.h>
#include <linux/pci-ecam.h>

struct pci_root_info {
	struct acpi_pci_root_info info;
	struct pci_config_window *cfg;
};

static void pci_acpi_release_root_info(struct acpi_pci_root_info *ci)
{
	struct pci_root_info *pci_ri;

	pci_ri = container_of(ci, struct pci_root_info, info);
	pci_ecam_free(pci_ri->cfg);
	kfree(ci->ops);
	kfree(pci_ri);
}

int acpi_pci_bus_find_domain_nr(struct pci_bus *bus)
{
	struct pci_config_window *cfg = bus->sysdata;
	struct acpi_device *adev = to_acpi_device(cfg->parent);
	struct acpi_pci_root *root = acpi_driver_data(adev);

	return root->segment;
}

/**
 * Lookup the MCFG table entry corresponding to the current
 * PCI host controller, and set up config space mapping.
 */
static struct pci_config_window *
pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root)
{
	struct device *dev = &root->device->dev;
	struct pci_config_window *cfg = NULL;
	const struct pci_ecam_ops *ecam_ops = NULL;
	struct resource *bus_res = &root->secondary;
	struct resource cfg_res;
	struct acpi_device *adev = NULL;
	int ret = 0, bus_shift = 0;
	u16 seg = root->segment;

	ret = pci_mcfg_lookup(root, &cfg_res, &ecam_ops);
	if (ret < 0) {
		dev_err(dev, "%04x:%pR ECAM region not found\n", seg, bus_res);
		return NULL;
	}

	/**
	 * Do the quirk of bus shift here, since we can not
	 * know the ECAM addr in MCFG table when fill mcfg_quirks
	 */
	bus_shift     = ecam_ops->bus_shift;
	cfg_res.start = root->mcfg_addr + (bus_res->start << bus_shift);
	cfg_res.end   = cfg_res.start + ((resource_size(bus_res)) << bus_shift) - 1;
	cfg_res.flags = IORESOURCE_MEM;

	/**
	 * ECAM area considered as the mem resource of the current
	 * PCI host controller, we'd better record this resource
	 * in ACPI namespace(_CRS).
	 */
	adev = acpi_resource_consumer(&cfg_res);
	if (adev)
		dev_info(dev, "ECAM area %pR reserved by %s\n", &cfg_res,
				dev_name(&adev->dev));
	else
		dev_info(dev, "Note: ECAM area %pR not reserved in ACPI namespace\n",
				&cfg_res);

	cfg = pci_ecam_create(dev, &cfg_res, bus_res, ecam_ops);
	if (IS_ERR(cfg)) {
		dev_err(dev, "%04x:%pR error %ld mapping ECAM\n", seg, bus_res,
				PTR_ERR(cfg));
		return NULL;
	}

	return cfg;
}

static int pci_acpi_prepare_root_resources(struct acpi_pci_root_info *ci)
{
	int status = 0;
	acpi_status rc;
	unsigned long long mem_space_base = 0;
	struct resource_entry *entry = NULL, *tmp = NULL;
	struct acpi_device *device = ci->bridge;

	/**
	 * Get host bridge resources via _CRS method, the return value
	 * is the num of resource parsed.
	 */
	status = acpi_pci_probe_root_resources(ci);
	if (status > 0) {
		/**
		 * To distinguish between mem and pre_mem, firmware only pass the
		 * lower 32bits of mem via acpi and use vendor specific "MEMH" to
		 * record the upper 32 bits of mem.
		 *
		 * Get the upper 32 bits here.
		 */
		rc = acpi_evaluate_integer(ci->bridge->handle,
				"MEMH", NULL, &mem_space_base);
		if (rc != AE_OK) {
			dev_err(&device->dev, "unable to retrieve MEMH\n");
			return -EEXIST;
		}

		resource_list_for_each_entry_safe(entry, tmp, &ci->resources) {
			if (entry->res->flags & IORESOURCE_MEM) {
				if (!(entry->res->end & 0xFFFFFFFF00000000ULL)) {
					/* Patch the mem resource with upper 32 bits */
					entry->res->start |= (mem_space_base << 32);
					entry->res->end   |= (mem_space_base << 32);
				} else {
					/**
					 * Add PREFETCH and MEM_64 flags for pre_mem,
					 * so that we can distinguish between mem and
					 * pre_mem.
					 */
					entry->res->flags |= IORESOURCE_PREFETCH;
					entry->res->flags |= IORESOURCE_MEM_64;
				}
			}

			dev_dbg(&device->dev,
				"host bridge resource: 0x%llx-0x%llx flags [0x%lx]\n",
				entry->res->start, entry->res->end, entry->res->flags);
		}
		return status;
	}

	/**
	 * If not successfully parse resources, destroy
	 * resources which have been parsed.
	 */
	resource_list_for_each_entry_safe(entry, tmp, &ci->resources) {
		dev_info(&device->dev,
			"host bridge resource(ignored): 0x%llx-0x%llx flags [0x%lx]\n",
			entry->res->start, entry->res->end, entry->res->flags);
		resource_list_destroy_entry(entry);
	}

	return 0;
}

/**
 * This function is called from ACPI code and used to
 * setup PCI host controller.
 */
struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
{
	struct pci_bus *bus = NULL, *child = NULL;
	struct pci_root_info *pci_ri = NULL;
	struct acpi_pci_root_ops *root_ops = NULL;
	int domain = root->segment;
	int busnum = root->secondary.start;

	pci_ri = kzalloc(sizeof(*pci_ri), GFP_KERNEL);
	if (!pci_ri)
		goto out_of_mem_0;

	root_ops = kzalloc(sizeof(*root_ops), GFP_KERNEL);
	if (!root_ops)
		goto out_of_mem_1;

	pci_ri->cfg = pci_acpi_setup_ecam_mapping(root);
	if (!pci_ri->cfg)
		goto setup_ecam_err;

	root_ops->release_info = pci_acpi_release_root_info;
	root_ops->prepare_resources = pci_acpi_prepare_root_resources;
	root_ops->pci_ops = (struct pci_ops *)&pci_ri->cfg->ops->pci_ops;

	bus = pci_find_bus(domain, busnum);
	if (bus) {
		memcpy(bus->sysdata, pci_ri->cfg, sizeof(struct pci_config_window));
		kfree(pci_ri->cfg);
		kfree(pci_ri);
		kfree(root_ops);
	} else {
		bus = acpi_pci_root_create(root, root_ops, &pci_ri->info, pci_ri->cfg);

		/**
		 * No need to do kfree here, because acpi_pci_root_create will free
		 * mem alloced when it cannot create pci_bus.
		 */
		if (!bus)
			return NULL;

		/* Some quirks for pci controller of Sunway after scanning Root Complex */
		sw64_pci_root_bridge_scan_finish_up(pci_find_host_bridge(bus));

		pci_bus_size_bridges(bus);
		pci_bus_assign_resources(bus);

		list_for_each_entry(child, &bus->children, node)
			pcie_bus_configure_settings(child);
	}

	return bus;

setup_ecam_err:
	kfree(root_ops);
out_of_mem_1:
	kfree(pci_ri);
out_of_mem_0:
	pr_warn("RC [%04x:%02x:] failed (out of memory or setup ecam error)!\n",
			domain, busnum);

	return NULL;
}

int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
{
	if (!acpi_disabled) {
		struct pci_config_window *cfg = bridge->sysdata;
		struct acpi_device *adev = to_acpi_device(cfg->parent);
		struct pci_controller *hose = cfg->priv;
		struct device *bus_dev = &bridge->bus->dev;

		ACPI_COMPANION_SET(&bridge->dev, adev);
		set_dev_node(bus_dev, hose->node);

		/* Some quirks for pci controller of Sunway before scanning Root Complex */
		sw64_pci_root_bridge_prepare(bridge);
	}

	return 0;
}

void pcibios_add_bus(struct pci_bus *bus)
{
	acpi_pci_add_bus(bus);
}

void pcibios_remove_bus(struct pci_bus *bus)
{
	acpi_pci_remove_bus(bus);
}