mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 04:18:39 +08:00
Merge branch 'acpi-riscv'
Merge ACPI and irqchip updates related to external interrupt controller support on RISC-V: - Add ACPI device enumeration support for interrupt controller probing including taking dependencies into account (Sunil V L). - Implement ACPI-based interrupt controller probing on RISC-V (Sunil V L). - Add ACPI support for AIA in riscv-intc and add ACPI support to riscv-imsic, riscv-aplic, and sifive-plic (Sunil V L). * acpi-riscv: irqchip/sifive-plic: Add ACPI support irqchip/riscv-aplic: Add ACPI support irqchip/riscv-imsic: Add ACPI support irqchip/riscv-imsic-state: Create separate function for DT irqchip/riscv-intc: Add ACPI support for AIA ACPI: RISC-V: Implement function to add implicit dependencies ACPI: RISC-V: Initialize GSI mapping structures ACPI: RISC-V: Implement function to reorder irqchip probe entries ACPI: RISC-V: Implement PCI related functionality ACPI: pci_link: Clear the dependencies after probe ACPI: bus: Add RINTC IRQ model for RISC-V ACPI: scan: Define weak function to populate dependencies ACPI: scan: Add RISC-V interrupt controllers to honor list ACPI: scan: Refactor dependency creation ACPI: bus: Add acpi_riscv_init() function ACPI: scan: Add a weak arch_sort_irqchip_probe() to order the IRQCHIP probe arm64: PCI: Migrate ACPI related functions to pci-acpi.c
This commit is contained in:
commit
45de40574f
@ -6,28 +6,7 @@
|
||||
* Copyright (C) 2014 ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-acpi.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
/*
|
||||
* Try to assign the IRQ number when probing a new device
|
||||
*/
|
||||
int pcibios_alloc_irq(struct pci_dev *dev)
|
||||
{
|
||||
if (!acpi_disabled)
|
||||
acpi_pci_irq_enable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* raw_pci_read/write - Platform-specific PCI config space access.
|
||||
@ -61,173 +40,3 @@ int pcibus_to_node(struct pci_bus *bus)
|
||||
EXPORT_SYMBOL(pcibus_to_node);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
struct acpi_pci_generic_root_info {
|
||||
struct acpi_pci_root_info common;
|
||||
struct pci_config_window *cfg; /* config space mapping */
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
|
||||
{
|
||||
struct pci_config_window *cfg;
|
||||
struct acpi_device *adev;
|
||||
struct device *bus_dev;
|
||||
|
||||
if (acpi_disabled)
|
||||
return 0;
|
||||
|
||||
cfg = bridge->bus->sysdata;
|
||||
|
||||
/*
|
||||
* On Hyper-V there is no corresponding ACPI device for a root bridge,
|
||||
* therefore ->parent is set as NULL by the driver. And set 'adev' as
|
||||
* NULL in this case because there is no proper ACPI device.
|
||||
*/
|
||||
if (!cfg->parent)
|
||||
adev = NULL;
|
||||
else
|
||||
adev = to_acpi_device(cfg->parent);
|
||||
|
||||
bus_dev = &bridge->bus->dev;
|
||||
|
||||
ACPI_COMPANION_SET(&bridge->dev, adev);
|
||||
set_dev_node(bus_dev, acpi_get_node(acpi_device_handle(adev)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci)
|
||||
{
|
||||
struct resource_entry *entry, *tmp;
|
||||
int status;
|
||||
|
||||
status = acpi_pci_probe_root_resources(ci);
|
||||
resource_list_for_each_entry_safe(entry, tmp, &ci->resources) {
|
||||
if (!(entry->res->flags & IORESOURCE_WINDOW))
|
||||
resource_list_destroy_entry(entry);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup the bus range for the domain in MCFG, 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 resource *bus_res = &root->secondary;
|
||||
u16 seg = root->segment;
|
||||
const struct pci_ecam_ops *ecam_ops;
|
||||
struct resource cfgres;
|
||||
struct acpi_device *adev;
|
||||
struct pci_config_window *cfg;
|
||||
int ret;
|
||||
|
||||
ret = pci_mcfg_lookup(root, &cfgres, &ecam_ops);
|
||||
if (ret) {
|
||||
dev_err(dev, "%04x:%pR ECAM region not found\n", seg, bus_res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
adev = acpi_resource_consumer(&cfgres);
|
||||
if (adev)
|
||||
dev_info(dev, "ECAM area %pR reserved by %s\n", &cfgres,
|
||||
dev_name(&adev->dev));
|
||||
else
|
||||
dev_warn(dev, FW_BUG "ECAM area %pR not reserved in ACPI namespace\n",
|
||||
&cfgres);
|
||||
|
||||
cfg = pci_ecam_create(dev, &cfgres, 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;
|
||||
}
|
||||
|
||||
/* release_info: free resources allocated by init_info */
|
||||
static void pci_acpi_generic_release_info(struct acpi_pci_root_info *ci)
|
||||
{
|
||||
struct acpi_pci_generic_root_info *ri;
|
||||
|
||||
ri = container_of(ci, struct acpi_pci_generic_root_info, common);
|
||||
pci_ecam_free(ri->cfg);
|
||||
kfree(ci->ops);
|
||||
kfree(ri);
|
||||
}
|
||||
|
||||
/* Interface called from ACPI code to setup PCI host controller */
|
||||
struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
|
||||
{
|
||||
struct acpi_pci_generic_root_info *ri;
|
||||
struct pci_bus *bus, *child;
|
||||
struct acpi_pci_root_ops *root_ops;
|
||||
struct pci_host_bridge *host;
|
||||
|
||||
ri = kzalloc(sizeof(*ri), GFP_KERNEL);
|
||||
if (!ri)
|
||||
return NULL;
|
||||
|
||||
root_ops = kzalloc(sizeof(*root_ops), GFP_KERNEL);
|
||||
if (!root_ops) {
|
||||
kfree(ri);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ri->cfg = pci_acpi_setup_ecam_mapping(root);
|
||||
if (!ri->cfg) {
|
||||
kfree(ri);
|
||||
kfree(root_ops);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
root_ops->release_info = pci_acpi_generic_release_info;
|
||||
root_ops->prepare_resources = pci_acpi_root_prepare_resources;
|
||||
root_ops->pci_ops = (struct pci_ops *)&ri->cfg->ops->pci_ops;
|
||||
bus = acpi_pci_root_create(root, root_ops, &ri->common, ri->cfg);
|
||||
if (!bus)
|
||||
return NULL;
|
||||
|
||||
/* If we must preserve the resource configuration, claim now */
|
||||
host = pci_find_host_bridge(bus);
|
||||
if (host->preserve_config)
|
||||
pci_bus_claim_resources(bus);
|
||||
|
||||
/*
|
||||
* Assign whatever was left unassigned. If we didn't claim above,
|
||||
* this will reassign everything.
|
||||
*/
|
||||
pci_assign_unassigned_root_bus_resources(bus);
|
||||
|
||||
list_for_each_entry(child, &bus->children, node)
|
||||
pcie_bus_configure_settings(child);
|
||||
|
||||
return bus;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -13,6 +13,7 @@ config 32BIT
|
||||
config RISCV
|
||||
def_bool y
|
||||
select ACPI_GENERIC_GSI if ACPI
|
||||
select ACPI_MCFG if (ACPI && PCI)
|
||||
select ACPI_PPTT if ACPI
|
||||
select ACPI_REDUCED_HARDWARE_ONLY if ACPI
|
||||
select ACPI_SPCR_TABLE if ACPI
|
||||
@ -188,6 +189,7 @@ config RISCV
|
||||
select OF_EARLY_FLATTREE
|
||||
select OF_IRQ
|
||||
select PCI_DOMAINS_GENERIC if PCI
|
||||
select PCI_ECAM if (ACPI && PCI)
|
||||
select PCI_MSI if PCI
|
||||
select RISCV_ALTERNATIVE if !XIP_KERNEL
|
||||
select RISCV_APLIC
|
||||
|
@ -12,8 +12,63 @@
|
||||
|
||||
#include <asm-generic/irq.h>
|
||||
|
||||
#define INVALID_CONTEXT UINT_MAX
|
||||
|
||||
void riscv_set_intc_hwnode_fn(struct fwnode_handle *(*fn)(void));
|
||||
|
||||
struct fwnode_handle *riscv_get_intc_hwnode(void);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
enum riscv_irqchip_type {
|
||||
ACPI_RISCV_IRQCHIP_INTC = 0x00,
|
||||
ACPI_RISCV_IRQCHIP_IMSIC = 0x01,
|
||||
ACPI_RISCV_IRQCHIP_PLIC = 0x02,
|
||||
ACPI_RISCV_IRQCHIP_APLIC = 0x03,
|
||||
};
|
||||
|
||||
int riscv_acpi_get_gsi_info(struct fwnode_handle *fwnode, u32 *gsi_base,
|
||||
u32 *id, u32 *nr_irqs, u32 *nr_idcs);
|
||||
struct fwnode_handle *riscv_acpi_get_gsi_domain_id(u32 gsi);
|
||||
unsigned long acpi_rintc_index_to_hartid(u32 index);
|
||||
unsigned long acpi_rintc_ext_parent_to_hartid(unsigned int plic_id, unsigned int ctxt_idx);
|
||||
unsigned int acpi_rintc_get_plic_nr_contexts(unsigned int plic_id);
|
||||
unsigned int acpi_rintc_get_plic_context(unsigned int plic_id, unsigned int ctxt_idx);
|
||||
int __init acpi_rintc_get_imsic_mmio_info(u32 index, struct resource *res);
|
||||
|
||||
#else
|
||||
static inline int riscv_acpi_get_gsi_info(struct fwnode_handle *fwnode, u32 *gsi_base,
|
||||
u32 *id, u32 *nr_irqs, u32 *nr_idcs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned long acpi_rintc_index_to_hartid(u32 index)
|
||||
{
|
||||
return INVALID_HARTID;
|
||||
}
|
||||
|
||||
static inline unsigned long acpi_rintc_ext_parent_to_hartid(unsigned int plic_id,
|
||||
unsigned int ctxt_idx)
|
||||
{
|
||||
return INVALID_HARTID;
|
||||
}
|
||||
|
||||
static inline unsigned int acpi_rintc_get_plic_nr_contexts(unsigned int plic_id)
|
||||
{
|
||||
return INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
static inline unsigned int acpi_rintc_get_plic_context(unsigned int plic_id, unsigned int ctxt_idx)
|
||||
{
|
||||
return INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
static inline int __init acpi_rintc_get_imsic_mmio_info(u32 index, struct resource *res)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ACPI */
|
||||
|
||||
#endif /* _ASM_RISCV_IRQ_H */
|
||||
|
@ -311,29 +311,26 @@ void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)
|
||||
#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.
|
||||
* raw_pci_read/write - Platform-specific PCI config space access.
|
||||
*/
|
||||
int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn,
|
||||
int reg, int len, u32 *val)
|
||||
int raw_pci_read(unsigned int domain, unsigned int bus,
|
||||
unsigned int devfn, int reg, int len, u32 *val)
|
||||
{
|
||||
struct pci_bus *b = pci_find_bus(domain, bus);
|
||||
|
||||
if (!b)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
return b->ops->read(b, devfn, reg, len, val);
|
||||
}
|
||||
|
||||
int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
|
||||
int reg, int len, u32 val)
|
||||
int raw_pci_write(unsigned int domain, unsigned int bus,
|
||||
unsigned int devfn, int reg, int len, u32 val)
|
||||
{
|
||||
struct pci_bus *b = pci_find_bus(domain, bus);
|
||||
|
||||
if (!b)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
return b->ops->write(b, devfn, reg, len, val);
|
||||
}
|
||||
|
||||
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 */
|
||||
|
@ -1203,6 +1203,9 @@ static int __init acpi_bus_init_irq(void)
|
||||
case ACPI_IRQ_MODEL_LPIC:
|
||||
message = "LPIC";
|
||||
break;
|
||||
case ACPI_IRQ_MODEL_RINTC:
|
||||
message = "RINTC";
|
||||
break;
|
||||
default:
|
||||
pr_info("Unknown interrupt routing model\n");
|
||||
return -ENODEV;
|
||||
@ -1459,6 +1462,7 @@ static int __init acpi_init(void)
|
||||
acpi_hest_init();
|
||||
acpi_ghes_init();
|
||||
acpi_arm_init();
|
||||
acpi_riscv_init();
|
||||
acpi_scan_init();
|
||||
acpi_ec_init();
|
||||
acpi_debugfs_init();
|
||||
|
@ -748,6 +748,8 @@ static int acpi_pci_link_add(struct acpi_device *device,
|
||||
if (result)
|
||||
kfree(link);
|
||||
|
||||
acpi_dev_clear_dependencies(device);
|
||||
|
||||
return result < 0 ? result : 1;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-y += rhct.o
|
||||
obj-y += rhct.o init.o irq.o
|
||||
obj-$(CONFIG_ACPI_PROCESSOR_IDLE) += cpuidle.o
|
||||
obj-$(CONFIG_ACPI_CPPC_LIB) += cppc.o
|
||||
|
13
drivers/acpi/riscv/init.c
Normal file
13
drivers/acpi/riscv/init.c
Normal file
@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2023-2024, Ventana Micro Systems Inc
|
||||
* Author: Sunil V L <sunilvl@ventanamicro.com>
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include "init.h"
|
||||
|
||||
void __init acpi_riscv_init(void)
|
||||
{
|
||||
riscv_acpi_init_gsi_mapping();
|
||||
}
|
4
drivers/acpi/riscv/init.h
Normal file
4
drivers/acpi/riscv/init.h
Normal file
@ -0,0 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#include <linux/init.h>
|
||||
|
||||
void __init riscv_acpi_init_gsi_mapping(void);
|
335
drivers/acpi/riscv/irq.c
Normal file
335
drivers/acpi/riscv/irq.c
Normal file
@ -0,0 +1,335 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2023-2024, Ventana Micro Systems Inc
|
||||
* Author: Sunil V L <sunilvl@ventanamicro.com>
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include "init.h"
|
||||
|
||||
struct riscv_ext_intc_list {
|
||||
acpi_handle handle;
|
||||
u32 gsi_base;
|
||||
u32 nr_irqs;
|
||||
u32 nr_idcs;
|
||||
u32 id;
|
||||
u32 type;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct acpi_irq_dep_ctx {
|
||||
int rc;
|
||||
unsigned int index;
|
||||
acpi_handle handle;
|
||||
};
|
||||
|
||||
LIST_HEAD(ext_intc_list);
|
||||
|
||||
static int irqchip_cmp_func(const void *in0, const void *in1)
|
||||
{
|
||||
struct acpi_probe_entry *elem0 = (struct acpi_probe_entry *)in0;
|
||||
struct acpi_probe_entry *elem1 = (struct acpi_probe_entry *)in1;
|
||||
|
||||
return (elem0->type > elem1->type) - (elem0->type < elem1->type);
|
||||
}
|
||||
|
||||
/*
|
||||
* On RISC-V, RINTC structures in MADT should be probed before any other
|
||||
* interrupt controller structures and IMSIC before APLIC. The interrupt
|
||||
* controller subtypes in MADT of ACPI spec for RISC-V are defined in
|
||||
* the incremental order like RINTC(24)->IMSIC(25)->APLIC(26)->PLIC(27).
|
||||
* Hence, simply sorting the subtypes in incremental order will
|
||||
* establish the required order.
|
||||
*/
|
||||
void arch_sort_irqchip_probe(struct acpi_probe_entry *ap_head, int nr)
|
||||
{
|
||||
struct acpi_probe_entry *ape = ap_head;
|
||||
|
||||
if (nr == 1 || !ACPI_COMPARE_NAMESEG(ACPI_SIG_MADT, ape->id))
|
||||
return;
|
||||
sort(ape, nr, sizeof(*ape), irqchip_cmp_func, NULL);
|
||||
}
|
||||
|
||||
static acpi_status riscv_acpi_update_gsi_handle(u32 gsi_base, acpi_handle handle)
|
||||
{
|
||||
struct riscv_ext_intc_list *ext_intc_element;
|
||||
struct list_head *i, *tmp;
|
||||
|
||||
list_for_each_safe(i, tmp, &ext_intc_list) {
|
||||
ext_intc_element = list_entry(i, struct riscv_ext_intc_list, list);
|
||||
if (gsi_base == ext_intc_element->gsi_base) {
|
||||
ext_intc_element->handle = handle;
|
||||
return AE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return AE_NOT_FOUND;
|
||||
}
|
||||
|
||||
int riscv_acpi_get_gsi_info(struct fwnode_handle *fwnode, u32 *gsi_base,
|
||||
u32 *id, u32 *nr_irqs, u32 *nr_idcs)
|
||||
{
|
||||
struct riscv_ext_intc_list *ext_intc_element;
|
||||
struct list_head *i;
|
||||
|
||||
list_for_each(i, &ext_intc_list) {
|
||||
ext_intc_element = list_entry(i, struct riscv_ext_intc_list, list);
|
||||
if (ext_intc_element->handle == ACPI_HANDLE_FWNODE(fwnode)) {
|
||||
*gsi_base = ext_intc_element->gsi_base;
|
||||
*id = ext_intc_element->id;
|
||||
*nr_irqs = ext_intc_element->nr_irqs;
|
||||
if (nr_idcs)
|
||||
*nr_idcs = ext_intc_element->nr_idcs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
struct fwnode_handle *riscv_acpi_get_gsi_domain_id(u32 gsi)
|
||||
{
|
||||
struct riscv_ext_intc_list *ext_intc_element;
|
||||
struct acpi_device *adev;
|
||||
struct list_head *i;
|
||||
|
||||
list_for_each(i, &ext_intc_list) {
|
||||
ext_intc_element = list_entry(i, struct riscv_ext_intc_list, list);
|
||||
if (gsi >= ext_intc_element->gsi_base &&
|
||||
gsi < (ext_intc_element->gsi_base + ext_intc_element->nr_irqs)) {
|
||||
adev = acpi_fetch_acpi_dev(ext_intc_element->handle);
|
||||
if (!adev)
|
||||
return NULL;
|
||||
|
||||
return acpi_fwnode_handle(adev);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int __init riscv_acpi_register_ext_intc(u32 gsi_base, u32 nr_irqs, u32 nr_idcs,
|
||||
u32 id, u32 type)
|
||||
{
|
||||
struct riscv_ext_intc_list *ext_intc_element;
|
||||
|
||||
ext_intc_element = kzalloc(sizeof(*ext_intc_element), GFP_KERNEL);
|
||||
if (!ext_intc_element)
|
||||
return -ENOMEM;
|
||||
|
||||
ext_intc_element->gsi_base = gsi_base;
|
||||
ext_intc_element->nr_irqs = nr_irqs;
|
||||
ext_intc_element->nr_idcs = nr_idcs;
|
||||
ext_intc_element->id = id;
|
||||
list_add_tail(&ext_intc_element->list, &ext_intc_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static acpi_status __init riscv_acpi_create_gsi_map(acpi_handle handle, u32 level,
|
||||
void *context, void **return_value)
|
||||
{
|
||||
acpi_status status;
|
||||
u64 gbase;
|
||||
|
||||
if (!acpi_has_method(handle, "_GSB")) {
|
||||
acpi_handle_err(handle, "_GSB method not found\n");
|
||||
return AE_ERROR;
|
||||
}
|
||||
|
||||
status = acpi_evaluate_integer(handle, "_GSB", NULL, &gbase);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
acpi_handle_err(handle, "failed to evaluate _GSB method\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
status = riscv_acpi_update_gsi_handle((u32)gbase, handle);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
acpi_handle_err(handle, "failed to find the GSI mapping entry\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static int __init riscv_acpi_aplic_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_aplic *aplic = (struct acpi_madt_aplic *)header;
|
||||
|
||||
return riscv_acpi_register_ext_intc(aplic->gsi_base, aplic->num_sources, aplic->num_idcs,
|
||||
aplic->id, ACPI_RISCV_IRQCHIP_APLIC);
|
||||
}
|
||||
|
||||
static int __init riscv_acpi_plic_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_plic *plic = (struct acpi_madt_plic *)header;
|
||||
|
||||
return riscv_acpi_register_ext_intc(plic->gsi_base, plic->num_irqs, 0,
|
||||
plic->id, ACPI_RISCV_IRQCHIP_PLIC);
|
||||
}
|
||||
|
||||
void __init riscv_acpi_init_gsi_mapping(void)
|
||||
{
|
||||
/* There can be either PLIC or APLIC */
|
||||
if (acpi_table_parse_madt(ACPI_MADT_TYPE_PLIC, riscv_acpi_plic_parse_madt, 0) > 0) {
|
||||
acpi_get_devices("RSCV0001", riscv_acpi_create_gsi_map, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (acpi_table_parse_madt(ACPI_MADT_TYPE_APLIC, riscv_acpi_aplic_parse_madt, 0) > 0)
|
||||
acpi_get_devices("RSCV0002", riscv_acpi_create_gsi_map, NULL, NULL);
|
||||
}
|
||||
|
||||
static acpi_handle riscv_acpi_get_gsi_handle(u32 gsi)
|
||||
{
|
||||
struct riscv_ext_intc_list *ext_intc_element;
|
||||
struct list_head *i;
|
||||
|
||||
list_for_each(i, &ext_intc_list) {
|
||||
ext_intc_element = list_entry(i, struct riscv_ext_intc_list, list);
|
||||
if (gsi >= ext_intc_element->gsi_base &&
|
||||
gsi < (ext_intc_element->gsi_base + ext_intc_element->nr_irqs))
|
||||
return ext_intc_element->handle;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static acpi_status riscv_acpi_irq_get_parent(struct acpi_resource *ares, void *context)
|
||||
{
|
||||
struct acpi_irq_dep_ctx *ctx = context;
|
||||
struct acpi_resource_irq *irq;
|
||||
struct acpi_resource_extended_irq *eirq;
|
||||
|
||||
switch (ares->type) {
|
||||
case ACPI_RESOURCE_TYPE_IRQ:
|
||||
irq = &ares->data.irq;
|
||||
if (ctx->index >= irq->interrupt_count) {
|
||||
ctx->index -= irq->interrupt_count;
|
||||
return AE_OK;
|
||||
}
|
||||
ctx->handle = riscv_acpi_get_gsi_handle(irq->interrupts[ctx->index]);
|
||||
return AE_CTRL_TERMINATE;
|
||||
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
|
||||
eirq = &ares->data.extended_irq;
|
||||
if (eirq->producer_consumer == ACPI_PRODUCER)
|
||||
return AE_OK;
|
||||
|
||||
if (ctx->index >= eirq->interrupt_count) {
|
||||
ctx->index -= eirq->interrupt_count;
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
/* Support GSIs only */
|
||||
if (eirq->resource_source.string_length)
|
||||
return AE_OK;
|
||||
|
||||
ctx->handle = riscv_acpi_get_gsi_handle(eirq->interrupts[ctx->index]);
|
||||
return AE_CTRL_TERMINATE;
|
||||
}
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static int riscv_acpi_irq_get_dep(acpi_handle handle, unsigned int index, acpi_handle *gsi_handle)
|
||||
{
|
||||
struct acpi_irq_dep_ctx ctx = {-EINVAL, index, NULL};
|
||||
|
||||
if (!gsi_handle)
|
||||
return 0;
|
||||
|
||||
acpi_walk_resources(handle, METHOD_NAME__CRS, riscv_acpi_irq_get_parent, &ctx);
|
||||
*gsi_handle = ctx.handle;
|
||||
if (*gsi_handle)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 riscv_acpi_add_prt_dep(acpi_handle handle)
|
||||
{
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
struct acpi_pci_routing_table *entry;
|
||||
struct acpi_handle_list dep_devices;
|
||||
acpi_handle gsi_handle;
|
||||
acpi_handle link_handle;
|
||||
acpi_status status;
|
||||
u32 count = 0;
|
||||
|
||||
status = acpi_get_irq_routing_table(handle, &buffer);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
acpi_handle_err(handle, "failed to get IRQ routing table\n");
|
||||
kfree(buffer.pointer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
entry = buffer.pointer;
|
||||
while (entry && (entry->length > 0)) {
|
||||
if (entry->source[0]) {
|
||||
acpi_get_handle(handle, entry->source, &link_handle);
|
||||
dep_devices.count = 1;
|
||||
dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL);
|
||||
if (!dep_devices.handles) {
|
||||
acpi_handle_err(handle, "failed to allocate memory\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
dep_devices.handles[0] = link_handle;
|
||||
count += acpi_scan_add_dep(handle, &dep_devices);
|
||||
} else {
|
||||
gsi_handle = riscv_acpi_get_gsi_handle(entry->source_index);
|
||||
dep_devices.count = 1;
|
||||
dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL);
|
||||
if (!dep_devices.handles) {
|
||||
acpi_handle_err(handle, "failed to allocate memory\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
dep_devices.handles[0] = gsi_handle;
|
||||
count += acpi_scan_add_dep(handle, &dep_devices);
|
||||
}
|
||||
|
||||
entry = (struct acpi_pci_routing_table *)
|
||||
((unsigned long)entry + entry->length);
|
||||
}
|
||||
|
||||
kfree(buffer.pointer);
|
||||
return count;
|
||||
}
|
||||
|
||||
static u32 riscv_acpi_add_irq_dep(acpi_handle handle)
|
||||
{
|
||||
struct acpi_handle_list dep_devices;
|
||||
acpi_handle gsi_handle;
|
||||
u32 count = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0;
|
||||
riscv_acpi_irq_get_dep(handle, i, &gsi_handle);
|
||||
i++) {
|
||||
dep_devices.count = 1;
|
||||
dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL);
|
||||
if (!dep_devices.handles) {
|
||||
acpi_handle_err(handle, "failed to allocate memory\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
dep_devices.handles[0] = gsi_handle;
|
||||
count += acpi_scan_add_dep(handle, &dep_devices);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
u32 arch_acpi_add_auto_dep(acpi_handle handle)
|
||||
{
|
||||
if (acpi_has_method(handle, "_PRT"))
|
||||
return riscv_acpi_add_prt_dep(handle);
|
||||
|
||||
return riscv_acpi_add_irq_dep(handle);
|
||||
}
|
@ -861,6 +861,9 @@ static const char * const acpi_honor_dep_ids[] = {
|
||||
"INTC1095", /* IVSC (ADL) driver must be loaded to allow i2c access to camera sensors */
|
||||
"INTC100A", /* IVSC (RPL) driver must be loaded to allow i2c access to camera sensors */
|
||||
"INTC10CF", /* IVSC (MTL) driver must be loaded to allow i2c access to camera sensors */
|
||||
"RSCV0001", /* RISC-V PLIC */
|
||||
"RSCV0002", /* RISC-V APLIC */
|
||||
"PNP0C0F", /* PCI Link Device */
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -2013,6 +2016,49 @@ void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val)
|
||||
mutex_unlock(&acpi_scan_lock);
|
||||
}
|
||||
|
||||
int acpi_scan_add_dep(acpi_handle handle, struct acpi_handle_list *dep_devices)
|
||||
{
|
||||
u32 count;
|
||||
int i;
|
||||
|
||||
for (count = 0, i = 0; i < dep_devices->count; i++) {
|
||||
struct acpi_device_info *info;
|
||||
struct acpi_dep_data *dep;
|
||||
bool skip, honor_dep;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_get_object_info(dep_devices->handles[i], &info);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
acpi_handle_debug(handle, "Error reading _DEP device info\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
skip = acpi_info_matches_ids(info, acpi_ignore_dep_ids);
|
||||
honor_dep = acpi_info_matches_ids(info, acpi_honor_dep_ids);
|
||||
kfree(info);
|
||||
|
||||
if (skip)
|
||||
continue;
|
||||
|
||||
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
|
||||
if (!dep)
|
||||
continue;
|
||||
|
||||
count++;
|
||||
|
||||
dep->supplier = dep_devices->handles[i];
|
||||
dep->consumer = handle;
|
||||
dep->honor_dep = honor_dep;
|
||||
|
||||
mutex_lock(&acpi_dep_list_lock);
|
||||
list_add_tail(&dep->node, &acpi_dep_list);
|
||||
mutex_unlock(&acpi_dep_list_lock);
|
||||
}
|
||||
|
||||
acpi_handle_list_free(dep_devices);
|
||||
return count;
|
||||
}
|
||||
|
||||
static void acpi_scan_init_hotplug(struct acpi_device *adev)
|
||||
{
|
||||
struct acpi_hardware_id *hwid;
|
||||
@ -2032,11 +2078,21 @@ static void acpi_scan_init_hotplug(struct acpi_device *adev)
|
||||
}
|
||||
}
|
||||
|
||||
u32 __weak arch_acpi_add_auto_dep(acpi_handle handle) { return 0; }
|
||||
|
||||
static u32 acpi_scan_check_dep(acpi_handle handle)
|
||||
{
|
||||
struct acpi_handle_list dep_devices;
|
||||
u32 count;
|
||||
int i;
|
||||
u32 count = 0;
|
||||
|
||||
/*
|
||||
* Some architectures like RISC-V need to add dependencies for
|
||||
* all devices which use GSI to the interrupt controller so that
|
||||
* interrupt controller is probed before any of those devices.
|
||||
* Instead of mandating _DEP on all the devices, detect the
|
||||
* dependency and add automatically.
|
||||
*/
|
||||
count += arch_acpi_add_auto_dep(handle);
|
||||
|
||||
/*
|
||||
* Check for _HID here to avoid deferring the enumeration of:
|
||||
@ -2045,48 +2101,14 @@ static u32 acpi_scan_check_dep(acpi_handle handle)
|
||||
* Still, checking for _HID catches more then just these cases ...
|
||||
*/
|
||||
if (!acpi_has_method(handle, "_DEP") || !acpi_has_method(handle, "_HID"))
|
||||
return 0;
|
||||
return count;
|
||||
|
||||
if (!acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices)) {
|
||||
acpi_handle_debug(handle, "Failed to evaluate _DEP.\n");
|
||||
return 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
for (count = 0, i = 0; i < dep_devices.count; i++) {
|
||||
struct acpi_device_info *info;
|
||||
struct acpi_dep_data *dep;
|
||||
bool skip, honor_dep;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_get_object_info(dep_devices.handles[i], &info);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
acpi_handle_debug(handle, "Error reading _DEP device info\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
skip = acpi_info_matches_ids(info, acpi_ignore_dep_ids);
|
||||
honor_dep = acpi_info_matches_ids(info, acpi_honor_dep_ids);
|
||||
kfree(info);
|
||||
|
||||
if (skip)
|
||||
continue;
|
||||
|
||||
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
|
||||
if (!dep)
|
||||
continue;
|
||||
|
||||
count++;
|
||||
|
||||
dep->supplier = dep_devices.handles[i];
|
||||
dep->consumer = handle;
|
||||
dep->honor_dep = honor_dep;
|
||||
|
||||
mutex_lock(&acpi_dep_list_lock);
|
||||
list_add_tail(&dep->node , &acpi_dep_list);
|
||||
mutex_unlock(&acpi_dep_list_lock);
|
||||
}
|
||||
|
||||
acpi_handle_list_free(&dep_devices);
|
||||
count += acpi_scan_add_dep(handle, &dep_devices);
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -2757,6 +2779,8 @@ static int __init acpi_match_madt(union acpi_subtable_headers *header,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __weak arch_sort_irqchip_probe(struct acpi_probe_entry *ap_head, int nr) { }
|
||||
|
||||
int __init __acpi_probe_device_table(struct acpi_probe_entry *ap_head, int nr)
|
||||
{
|
||||
int count = 0;
|
||||
@ -2765,6 +2789,7 @@ int __init __acpi_probe_device_table(struct acpi_probe_entry *ap_head, int nr)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&acpi_probe_mutex);
|
||||
arch_sort_irqchip_probe(ap_head, nr);
|
||||
for (ape = ap_head; nr; ape++, nr--) {
|
||||
if (ACPI_COMPARE_NAMESEG(ACPI_SIG_MADT, ape->id)) {
|
||||
acpi_probe_count = 0;
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Copyright (C) 2022 Ventana Micro Systems Inc.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cpu.h>
|
||||
@ -189,17 +190,22 @@ static int aplic_direct_starting_cpu(unsigned int cpu)
|
||||
}
|
||||
|
||||
static int aplic_direct_parse_parent_hwirq(struct device *dev, u32 index,
|
||||
u32 *parent_hwirq, unsigned long *parent_hartid)
|
||||
u32 *parent_hwirq, unsigned long *parent_hartid,
|
||||
struct aplic_priv *priv)
|
||||
{
|
||||
struct of_phandle_args parent;
|
||||
unsigned long hartid;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Currently, only OF fwnode is supported so extend this
|
||||
* function for ACPI support.
|
||||
*/
|
||||
if (!is_of_node(dev->fwnode))
|
||||
return -EINVAL;
|
||||
if (!is_of_node(dev->fwnode)) {
|
||||
hartid = acpi_rintc_ext_parent_to_hartid(priv->acpi_aplic_id, index);
|
||||
if (hartid == INVALID_HARTID)
|
||||
return -ENODEV;
|
||||
|
||||
*parent_hartid = hartid;
|
||||
*parent_hwirq = RV_IRQ_EXT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = of_irq_parse_one(to_of_node(dev->fwnode), index, &parent);
|
||||
if (rc)
|
||||
@ -237,7 +243,7 @@ int aplic_direct_setup(struct device *dev, void __iomem *regs)
|
||||
/* Setup per-CPU IDC and target CPU mask */
|
||||
current_cpu = get_cpu();
|
||||
for (i = 0; i < priv->nr_idcs; i++) {
|
||||
rc = aplic_direct_parse_parent_hwirq(dev, i, &hwirq, &hartid);
|
||||
rc = aplic_direct_parse_parent_hwirq(dev, i, &hwirq, &hartid, priv);
|
||||
if (rc) {
|
||||
dev_warn(dev, "parent irq for IDC%d not found\n", i);
|
||||
continue;
|
||||
|
@ -4,8 +4,10 @@
|
||||
* Copyright (C) 2022 Ventana Micro Systems Inc.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/irqchip/riscv-aplic.h>
|
||||
#include <linux/irqchip/riscv-imsic.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
@ -125,23 +127,26 @@ static void aplic_init_hw_irqs(struct aplic_priv *priv)
|
||||
writel(0, priv->regs + APLIC_DOMAINCFG);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id aplic_acpi_match[] = {
|
||||
{ "RSCV0002", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, aplic_acpi_match);
|
||||
|
||||
#endif
|
||||
|
||||
int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem *regs)
|
||||
{
|
||||
struct device_node *np = to_of_node(dev->fwnode);
|
||||
struct of_phandle_args parent;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Currently, only OF fwnode is supported so extend this
|
||||
* function for ACPI support.
|
||||
*/
|
||||
if (!np)
|
||||
return -EINVAL;
|
||||
|
||||
/* Save device pointer and register base */
|
||||
priv->dev = dev;
|
||||
priv->regs = regs;
|
||||
|
||||
if (np) {
|
||||
/* Find out number of interrupt sources */
|
||||
rc = of_property_read_u32(np, "riscv,num-sources", &priv->nr_irqs);
|
||||
if (rc) {
|
||||
@ -159,6 +164,14 @@ int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem *
|
||||
while (!of_irq_parse_one(np, priv->nr_idcs, &parent))
|
||||
priv->nr_idcs++;
|
||||
}
|
||||
} else {
|
||||
rc = riscv_acpi_get_gsi_info(dev->fwnode, &priv->gsi_base, &priv->acpi_aplic_id,
|
||||
&priv->nr_irqs, &priv->nr_idcs);
|
||||
if (rc) {
|
||||
dev_err(dev, "failed to find GSI mapping\n");
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup initial state APLIC interrupts */
|
||||
aplic_init_hw_irqs(priv);
|
||||
@ -184,7 +197,11 @@ static int aplic_probe(struct platform_device *pdev)
|
||||
* If msi-parent property is present then setup APLIC MSI
|
||||
* mode otherwise setup APLIC direct mode.
|
||||
*/
|
||||
if (is_of_node(dev->fwnode))
|
||||
msi_mode = of_property_present(to_of_node(dev->fwnode), "msi-parent");
|
||||
else
|
||||
msi_mode = imsic_acpi_get_fwnode(NULL) ? 1 : 0;
|
||||
|
||||
if (msi_mode)
|
||||
rc = aplic_msi_setup(dev, regs);
|
||||
else
|
||||
@ -192,6 +209,11 @@ static int aplic_probe(struct platform_device *pdev)
|
||||
if (rc)
|
||||
dev_err(dev, "failed to setup APLIC in %s mode\n", msi_mode ? "MSI" : "direct");
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
if (!acpi_disabled)
|
||||
acpi_dev_clear_dependencies(ACPI_COMPANION(dev));
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -204,6 +226,7 @@ static struct platform_driver aplic_driver = {
|
||||
.driver = {
|
||||
.name = "riscv-aplic",
|
||||
.of_match_table = aplic_match,
|
||||
.acpi_match_table = ACPI_PTR(aplic_acpi_match),
|
||||
},
|
||||
.probe = aplic_probe,
|
||||
};
|
||||
|
@ -28,6 +28,7 @@ struct aplic_priv {
|
||||
u32 gsi_base;
|
||||
u32 nr_irqs;
|
||||
u32 nr_idcs;
|
||||
u32 acpi_aplic_id;
|
||||
void __iomem *regs;
|
||||
struct aplic_msicfg msicfg;
|
||||
};
|
||||
|
@ -175,6 +175,7 @@ static const struct msi_domain_template aplic_msi_template = {
|
||||
int aplic_msi_setup(struct device *dev, void __iomem *regs)
|
||||
{
|
||||
const struct imsic_global_config *imsic_global;
|
||||
struct irq_domain *msi_domain;
|
||||
struct aplic_priv *priv;
|
||||
struct aplic_msicfg *mc;
|
||||
phys_addr_t pa;
|
||||
@ -257,8 +258,14 @@ int aplic_msi_setup(struct device *dev, void __iomem *regs)
|
||||
* IMSIC and the IMSIC MSI domains are created later through
|
||||
* the platform driver probing so we set it explicitly here.
|
||||
*/
|
||||
if (is_of_node(dev->fwnode))
|
||||
if (is_of_node(dev->fwnode)) {
|
||||
of_msi_configure(dev, to_of_node(dev->fwnode));
|
||||
} else {
|
||||
msi_domain = irq_find_matching_fwnode(imsic_acpi_get_fwnode(dev),
|
||||
DOMAIN_BUS_PLATFORM_MSI);
|
||||
if (msi_domain)
|
||||
dev_set_msi_domain(dev, msi_domain);
|
||||
}
|
||||
}
|
||||
|
||||
if (!msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN, &aplic_msi_template,
|
||||
|
@ -5,13 +5,16 @@
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "riscv-imsic: " fmt
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqchip/riscv-imsic.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
@ -182,7 +185,7 @@ static int __init imsic_early_dt_init(struct device_node *node, struct device_no
|
||||
int rc;
|
||||
|
||||
/* Setup IMSIC state */
|
||||
rc = imsic_setup_state(fwnode);
|
||||
rc = imsic_setup_state(fwnode, NULL);
|
||||
if (rc) {
|
||||
pr_err("%pfwP: failed to setup state (error %d)\n", fwnode, rc);
|
||||
return rc;
|
||||
@ -199,3 +202,62 @@ static int __init imsic_early_dt_init(struct device_node *node, struct device_no
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(riscv_imsic, "riscv,imsics", imsic_early_dt_init);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
static struct fwnode_handle *imsic_acpi_fwnode;
|
||||
|
||||
struct fwnode_handle *imsic_acpi_get_fwnode(struct device *dev)
|
||||
{
|
||||
return imsic_acpi_fwnode;
|
||||
}
|
||||
|
||||
static int __init imsic_early_acpi_init(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_imsic *imsic = (struct acpi_madt_imsic *)header;
|
||||
int rc;
|
||||
|
||||
imsic_acpi_fwnode = irq_domain_alloc_named_fwnode("imsic");
|
||||
if (!imsic_acpi_fwnode) {
|
||||
pr_err("unable to allocate IMSIC FW node\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Setup IMSIC state */
|
||||
rc = imsic_setup_state(imsic_acpi_fwnode, imsic);
|
||||
if (rc) {
|
||||
pr_err("%pfwP: failed to setup state (error %d)\n", imsic_acpi_fwnode, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Do early setup of IMSIC state and IPIs */
|
||||
rc = imsic_early_probe(imsic_acpi_fwnode);
|
||||
if (rc) {
|
||||
irq_domain_free_fwnode(imsic_acpi_fwnode);
|
||||
imsic_acpi_fwnode = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = imsic_platform_acpi_probe(imsic_acpi_fwnode);
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
if (!rc)
|
||||
pci_msi_register_fwnode_provider(&imsic_acpi_get_fwnode);
|
||||
#endif
|
||||
|
||||
if (rc)
|
||||
pr_err("%pfwP: failed to register IMSIC for MSI functionality (error %d)\n",
|
||||
imsic_acpi_fwnode, rc);
|
||||
|
||||
/*
|
||||
* Even if imsic_platform_acpi_probe() fails, the IPI part of IMSIC can
|
||||
* continue to work. So, no need to return failure. This is similar to
|
||||
* DT where IPI works but MSI probe fails for some reason.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
IRQCHIP_ACPI_DECLARE(riscv_imsic, ACPI_MADT_TYPE_IMSIC, NULL,
|
||||
1, imsic_early_acpi_init);
|
||||
#endif
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "riscv-imsic: " fmt
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -348,18 +349,37 @@ int imsic_irqdomain_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imsic_platform_probe(struct platform_device *pdev)
|
||||
static int imsic_platform_probe_common(struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
if (imsic && imsic->fwnode != dev->fwnode) {
|
||||
dev_err(dev, "fwnode mismatch\n");
|
||||
if (imsic && imsic->fwnode != fwnode) {
|
||||
pr_err("%pfwP: fwnode mismatch\n", fwnode);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return imsic_irqdomain_init();
|
||||
}
|
||||
|
||||
static int imsic_platform_dt_probe(struct platform_device *pdev)
|
||||
{
|
||||
return imsic_platform_probe_common(pdev->dev.fwnode);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
/*
|
||||
* On ACPI based systems, PCI enumeration happens early during boot in
|
||||
* acpi_scan_init(). PCI enumeration expects MSI domain setup before
|
||||
* it calls pci_set_msi_domain(). Hence, unlike in DT where
|
||||
* imsic-platform drive probe happens late during boot, ACPI based
|
||||
* systems need to setup the MSI domain early.
|
||||
*/
|
||||
int imsic_platform_acpi_probe(struct fwnode_handle *fwnode)
|
||||
{
|
||||
return imsic_platform_probe_common(fwnode);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static const struct of_device_id imsic_platform_match[] = {
|
||||
{ .compatible = "riscv,imsics" },
|
||||
{}
|
||||
@ -370,6 +390,6 @@ static struct platform_driver imsic_platform_driver = {
|
||||
.name = "riscv-imsic",
|
||||
.of_match_table = imsic_platform_match,
|
||||
},
|
||||
.probe = imsic_platform_probe,
|
||||
.probe = imsic_platform_dt_probe,
|
||||
};
|
||||
builtin_platform_driver(imsic_platform_driver);
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "riscv-imsic: " fmt
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -510,73 +511,11 @@ static int __init imsic_matrix_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init imsic_get_parent_hartid(struct fwnode_handle *fwnode,
|
||||
u32 index, unsigned long *hartid)
|
||||
{
|
||||
struct of_phandle_args parent;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Currently, only OF fwnode is supported so extend this
|
||||
* function for ACPI support.
|
||||
*/
|
||||
if (!is_of_node(fwnode))
|
||||
return -EINVAL;
|
||||
|
||||
rc = of_irq_parse_one(to_of_node(fwnode), index, &parent);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Skip interrupts other than external interrupts for
|
||||
* current privilege level.
|
||||
*/
|
||||
if (parent.args[0] != RV_IRQ_EXT)
|
||||
return -EINVAL;
|
||||
|
||||
return riscv_of_parent_hartid(parent.np, hartid);
|
||||
}
|
||||
|
||||
static int __init imsic_get_mmio_resource(struct fwnode_handle *fwnode,
|
||||
u32 index, struct resource *res)
|
||||
{
|
||||
/*
|
||||
* Currently, only OF fwnode is supported so extend this
|
||||
* function for ACPI support.
|
||||
*/
|
||||
if (!is_of_node(fwnode))
|
||||
return -EINVAL;
|
||||
|
||||
return of_address_to_resource(to_of_node(fwnode), index, res);
|
||||
}
|
||||
|
||||
static int __init imsic_parse_fwnode(struct fwnode_handle *fwnode,
|
||||
static int __init imsic_populate_global_dt(struct fwnode_handle *fwnode,
|
||||
struct imsic_global_config *global,
|
||||
u32 *nr_parent_irqs,
|
||||
u32 *nr_mmios)
|
||||
u32 *nr_parent_irqs)
|
||||
{
|
||||
unsigned long hartid;
|
||||
struct resource res;
|
||||
int rc;
|
||||
u32 i;
|
||||
|
||||
/*
|
||||
* Currently, only OF fwnode is supported so extend this
|
||||
* function for ACPI support.
|
||||
*/
|
||||
if (!is_of_node(fwnode))
|
||||
return -EINVAL;
|
||||
|
||||
*nr_parent_irqs = 0;
|
||||
*nr_mmios = 0;
|
||||
|
||||
/* Find number of parent interrupts */
|
||||
while (!imsic_get_parent_hartid(fwnode, *nr_parent_irqs, &hartid))
|
||||
(*nr_parent_irqs)++;
|
||||
if (!*nr_parent_irqs) {
|
||||
pr_err("%pfwP: no parent irqs available\n", fwnode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Find number of guest index bits in MSI address */
|
||||
rc = of_property_read_u32(to_of_node(fwnode), "riscv,guest-index-bits",
|
||||
@ -623,6 +562,93 @@ static int __init imsic_parse_fwnode(struct fwnode_handle *fwnode,
|
||||
if (rc)
|
||||
global->nr_guest_ids = global->nr_ids;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init imsic_populate_global_acpi(struct fwnode_handle *fwnode,
|
||||
struct imsic_global_config *global,
|
||||
u32 *nr_parent_irqs, void *opaque)
|
||||
{
|
||||
struct acpi_madt_imsic *imsic = (struct acpi_madt_imsic *)opaque;
|
||||
|
||||
global->guest_index_bits = imsic->guest_index_bits;
|
||||
global->hart_index_bits = imsic->hart_index_bits;
|
||||
global->group_index_bits = imsic->group_index_bits;
|
||||
global->group_index_shift = imsic->group_index_shift;
|
||||
global->nr_ids = imsic->num_ids;
|
||||
global->nr_guest_ids = imsic->num_guest_ids;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init imsic_get_parent_hartid(struct fwnode_handle *fwnode,
|
||||
u32 index, unsigned long *hartid)
|
||||
{
|
||||
struct of_phandle_args parent;
|
||||
int rc;
|
||||
|
||||
if (!is_of_node(fwnode)) {
|
||||
if (hartid)
|
||||
*hartid = acpi_rintc_index_to_hartid(index);
|
||||
|
||||
if (!hartid || (*hartid == INVALID_HARTID))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = of_irq_parse_one(to_of_node(fwnode), index, &parent);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Skip interrupts other than external interrupts for
|
||||
* current privilege level.
|
||||
*/
|
||||
if (parent.args[0] != RV_IRQ_EXT)
|
||||
return -EINVAL;
|
||||
|
||||
return riscv_of_parent_hartid(parent.np, hartid);
|
||||
}
|
||||
|
||||
static int __init imsic_get_mmio_resource(struct fwnode_handle *fwnode,
|
||||
u32 index, struct resource *res)
|
||||
{
|
||||
if (!is_of_node(fwnode))
|
||||
return acpi_rintc_get_imsic_mmio_info(index, res);
|
||||
|
||||
return of_address_to_resource(to_of_node(fwnode), index, res);
|
||||
}
|
||||
|
||||
static int __init imsic_parse_fwnode(struct fwnode_handle *fwnode,
|
||||
struct imsic_global_config *global,
|
||||
u32 *nr_parent_irqs,
|
||||
u32 *nr_mmios,
|
||||
void *opaque)
|
||||
{
|
||||
unsigned long hartid;
|
||||
struct resource res;
|
||||
int rc;
|
||||
u32 i;
|
||||
|
||||
*nr_parent_irqs = 0;
|
||||
*nr_mmios = 0;
|
||||
|
||||
/* Find number of parent interrupts */
|
||||
while (!imsic_get_parent_hartid(fwnode, *nr_parent_irqs, &hartid))
|
||||
(*nr_parent_irqs)++;
|
||||
if (!*nr_parent_irqs) {
|
||||
pr_err("%pfwP: no parent irqs available\n", fwnode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (is_of_node(fwnode))
|
||||
rc = imsic_populate_global_dt(fwnode, global, nr_parent_irqs);
|
||||
else
|
||||
rc = imsic_populate_global_acpi(fwnode, global, nr_parent_irqs, opaque);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Sanity check guest index bits */
|
||||
i = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT;
|
||||
if (i < global->guest_index_bits) {
|
||||
@ -688,7 +714,7 @@ static int __init imsic_parse_fwnode(struct fwnode_handle *fwnode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init imsic_setup_state(struct fwnode_handle *fwnode)
|
||||
int __init imsic_setup_state(struct fwnode_handle *fwnode, void *opaque)
|
||||
{
|
||||
u32 i, j, index, nr_parent_irqs, nr_mmios, nr_handlers = 0;
|
||||
struct imsic_global_config *global;
|
||||
@ -729,7 +755,7 @@ int __init imsic_setup_state(struct fwnode_handle *fwnode)
|
||||
}
|
||||
|
||||
/* Parse IMSIC fwnode */
|
||||
rc = imsic_parse_fwnode(fwnode, global, &nr_parent_irqs, &nr_mmios);
|
||||
rc = imsic_parse_fwnode(fwnode, global, &nr_parent_irqs, &nr_mmios, opaque);
|
||||
if (rc)
|
||||
goto out_free_local;
|
||||
|
||||
|
@ -102,7 +102,7 @@ void imsic_vector_debug_show_summary(struct seq_file *m, int ind);
|
||||
|
||||
void imsic_state_online(void);
|
||||
void imsic_state_offline(void);
|
||||
int imsic_setup_state(struct fwnode_handle *fwnode);
|
||||
int imsic_setup_state(struct fwnode_handle *fwnode, void *opaque);
|
||||
int imsic_irqdomain_init(void);
|
||||
|
||||
#endif
|
||||
|
@ -250,6 +250,85 @@ IRQCHIP_DECLARE(andes, "andestech,cpu-intc", riscv_intc_init);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
struct rintc_data {
|
||||
union {
|
||||
u32 ext_intc_id;
|
||||
struct {
|
||||
u32 context_id : 16,
|
||||
reserved : 8,
|
||||
aplic_plic_id : 8;
|
||||
};
|
||||
};
|
||||
unsigned long hart_id;
|
||||
u64 imsic_addr;
|
||||
u32 imsic_size;
|
||||
};
|
||||
|
||||
static u32 nr_rintc;
|
||||
static struct rintc_data *rintc_acpi_data[NR_CPUS];
|
||||
|
||||
#define for_each_matching_plic(_plic_id) \
|
||||
unsigned int _plic; \
|
||||
\
|
||||
for (_plic = 0; _plic < nr_rintc; _plic++) \
|
||||
if (rintc_acpi_data[_plic]->aplic_plic_id != _plic_id) \
|
||||
continue; \
|
||||
else
|
||||
|
||||
unsigned int acpi_rintc_get_plic_nr_contexts(unsigned int plic_id)
|
||||
{
|
||||
unsigned int nctx = 0;
|
||||
|
||||
for_each_matching_plic(plic_id)
|
||||
nctx++;
|
||||
|
||||
return nctx;
|
||||
}
|
||||
|
||||
static struct rintc_data *get_plic_context(unsigned int plic_id, unsigned int ctxt_idx)
|
||||
{
|
||||
unsigned int ctxt = 0;
|
||||
|
||||
for_each_matching_plic(plic_id) {
|
||||
if (ctxt == ctxt_idx)
|
||||
return rintc_acpi_data[_plic];
|
||||
|
||||
ctxt++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned long acpi_rintc_ext_parent_to_hartid(unsigned int plic_id, unsigned int ctxt_idx)
|
||||
{
|
||||
struct rintc_data *data = get_plic_context(plic_id, ctxt_idx);
|
||||
|
||||
return data ? data->hart_id : INVALID_HARTID;
|
||||
}
|
||||
|
||||
unsigned int acpi_rintc_get_plic_context(unsigned int plic_id, unsigned int ctxt_idx)
|
||||
{
|
||||
struct rintc_data *data = get_plic_context(plic_id, ctxt_idx);
|
||||
|
||||
return data ? data->context_id : INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
unsigned long acpi_rintc_index_to_hartid(u32 index)
|
||||
{
|
||||
return index >= nr_rintc ? INVALID_HARTID : rintc_acpi_data[index]->hart_id;
|
||||
}
|
||||
|
||||
int acpi_rintc_get_imsic_mmio_info(u32 index, struct resource *res)
|
||||
{
|
||||
if (index >= nr_rintc)
|
||||
return -1;
|
||||
|
||||
res->start = rintc_acpi_data[index]->imsic_addr;
|
||||
res->end = res->start + rintc_acpi_data[index]->imsic_size - 1;
|
||||
res->flags = IORESOURCE_MEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init riscv_intc_acpi_init(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
@ -258,6 +337,15 @@ static int __init riscv_intc_acpi_init(union acpi_subtable_headers *header,
|
||||
int rc;
|
||||
|
||||
rintc = (struct acpi_madt_rintc *)header;
|
||||
rintc_acpi_data[nr_rintc] = kzalloc(sizeof(*rintc_acpi_data[0]), GFP_KERNEL);
|
||||
if (!rintc_acpi_data[nr_rintc])
|
||||
return -ENOMEM;
|
||||
|
||||
rintc_acpi_data[nr_rintc]->ext_intc_id = rintc->ext_intc_id;
|
||||
rintc_acpi_data[nr_rintc]->hart_id = rintc->hart_id;
|
||||
rintc_acpi_data[nr_rintc]->imsic_addr = rintc->imsic_addr;
|
||||
rintc_acpi_data[nr_rintc]->imsic_size = rintc->imsic_size;
|
||||
nr_rintc++;
|
||||
|
||||
/*
|
||||
* The ACPI MADT will have one INTC for each CPU (or HART)
|
||||
@ -277,6 +365,8 @@ static int __init riscv_intc_acpi_init(union acpi_subtable_headers *header,
|
||||
rc = riscv_intc_init_common(fn, &riscv_intc_chip);
|
||||
if (rc)
|
||||
irq_domain_free_fwnode(fn);
|
||||
else
|
||||
acpi_set_irq_model(ACPI_IRQ_MODEL_RINTC, riscv_acpi_get_gsi_domain_id);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Copyright (C) 2018 Christoph Hellwig
|
||||
*/
|
||||
#define pr_fmt(fmt) "riscv-plic: " fmt
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
@ -71,6 +72,8 @@ struct plic_priv {
|
||||
unsigned long plic_quirks;
|
||||
unsigned int nr_irqs;
|
||||
unsigned long *prio_save;
|
||||
u32 gsi_base;
|
||||
int acpi_plic_id;
|
||||
};
|
||||
|
||||
struct plic_handler {
|
||||
@ -325,6 +328,10 @@ static int plic_irq_domain_translate(struct irq_domain *d,
|
||||
{
|
||||
struct plic_priv *priv = d->host_data;
|
||||
|
||||
/* For DT, gsi_base is always zero. */
|
||||
if (fwspec->param[0] >= priv->gsi_base)
|
||||
fwspec->param[0] = fwspec->param[0] - priv->gsi_base;
|
||||
|
||||
if (test_bit(PLIC_QUIRK_EDGE_INTERRUPT, &priv->plic_quirks))
|
||||
return irq_domain_translate_twocell(d, fwspec, hwirq, type);
|
||||
|
||||
@ -426,17 +433,36 @@ static const struct of_device_id plic_match[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
static const struct acpi_device_id plic_acpi_match[] = {
|
||||
{ "RSCV0001", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, plic_acpi_match);
|
||||
|
||||
#endif
|
||||
static int plic_parse_nr_irqs_and_contexts(struct fwnode_handle *fwnode,
|
||||
u32 *nr_irqs, u32 *nr_contexts)
|
||||
u32 *nr_irqs, u32 *nr_contexts,
|
||||
u32 *gsi_base, u32 *id)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Currently, only OF fwnode is supported so extend this
|
||||
* function for ACPI support.
|
||||
*/
|
||||
if (!is_of_node(fwnode))
|
||||
if (!is_of_node(fwnode)) {
|
||||
rc = riscv_acpi_get_gsi_info(fwnode, gsi_base, id, nr_irqs, NULL);
|
||||
if (rc) {
|
||||
pr_err("%pfwP: failed to find GSI mapping\n", fwnode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
*nr_contexts = acpi_rintc_get_plic_nr_contexts(*id);
|
||||
if (WARN_ON(!*nr_contexts)) {
|
||||
pr_err("%pfwP: no PLIC context available\n", fwnode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(to_of_node(fwnode), "riscv,ndev", nr_irqs);
|
||||
if (rc) {
|
||||
@ -450,23 +476,29 @@ static int plic_parse_nr_irqs_and_contexts(struct fwnode_handle *fwnode,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*gsi_base = 0;
|
||||
*id = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int plic_parse_context_parent(struct fwnode_handle *fwnode, u32 context,
|
||||
u32 *parent_hwirq, int *parent_cpu)
|
||||
u32 *parent_hwirq, int *parent_cpu, u32 id)
|
||||
{
|
||||
struct of_phandle_args parent;
|
||||
unsigned long hartid;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Currently, only OF fwnode is supported so extend this
|
||||
* function for ACPI support.
|
||||
*/
|
||||
if (!is_of_node(fwnode))
|
||||
if (!is_of_node(fwnode)) {
|
||||
hartid = acpi_rintc_ext_parent_to_hartid(id, context);
|
||||
if (hartid == INVALID_HARTID)
|
||||
return -EINVAL;
|
||||
|
||||
*parent_cpu = riscv_hartid_to_cpuid(hartid);
|
||||
*parent_hwirq = RV_IRQ_EXT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = of_irq_parse_one(to_of_node(fwnode), context, &parent);
|
||||
if (rc)
|
||||
return rc;
|
||||
@ -489,6 +521,8 @@ static int plic_probe(struct fwnode_handle *fwnode)
|
||||
struct plic_priv *priv;
|
||||
irq_hw_number_t hwirq;
|
||||
void __iomem *regs;
|
||||
int id, context_id;
|
||||
u32 gsi_base;
|
||||
|
||||
if (is_of_node(fwnode)) {
|
||||
const struct of_device_id *id;
|
||||
@ -501,10 +535,12 @@ static int plic_probe(struct fwnode_handle *fwnode)
|
||||
if (!regs)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
regs = devm_platform_ioremap_resource(to_platform_device(fwnode->dev), 0);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
}
|
||||
|
||||
error = plic_parse_nr_irqs_and_contexts(fwnode, &nr_irqs, &nr_contexts);
|
||||
error = plic_parse_nr_irqs_and_contexts(fwnode, &nr_irqs, &nr_contexts, &gsi_base, &id);
|
||||
if (error)
|
||||
goto fail_free_regs;
|
||||
|
||||
@ -518,6 +554,8 @@ static int plic_probe(struct fwnode_handle *fwnode)
|
||||
priv->plic_quirks = plic_quirks;
|
||||
priv->nr_irqs = nr_irqs;
|
||||
priv->regs = regs;
|
||||
priv->gsi_base = gsi_base;
|
||||
priv->acpi_plic_id = id;
|
||||
|
||||
priv->prio_save = bitmap_zalloc(nr_irqs, GFP_KERNEL);
|
||||
if (!priv->prio_save) {
|
||||
@ -526,12 +564,23 @@ static int plic_probe(struct fwnode_handle *fwnode)
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_contexts; i++) {
|
||||
error = plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu);
|
||||
error = plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu,
|
||||
priv->acpi_plic_id);
|
||||
if (error) {
|
||||
pr_warn("%pfwP: hwirq for context%d not found\n", fwnode, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_of_node(fwnode)) {
|
||||
context_id = i;
|
||||
} else {
|
||||
context_id = acpi_rintc_get_plic_context(priv->acpi_plic_id, i);
|
||||
if (context_id == INVALID_CONTEXT) {
|
||||
pr_warn("%pfwP: invalid context id for context%d\n", fwnode, i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip contexts other than external interrupts for our
|
||||
* privilege level.
|
||||
@ -569,10 +618,10 @@ static int plic_probe(struct fwnode_handle *fwnode)
|
||||
cpumask_set_cpu(cpu, &priv->lmask);
|
||||
handler->present = true;
|
||||
handler->hart_base = priv->regs + CONTEXT_BASE +
|
||||
i * CONTEXT_SIZE;
|
||||
context_id * CONTEXT_SIZE;
|
||||
raw_spin_lock_init(&handler->enable_lock);
|
||||
handler->enable_base = priv->regs + CONTEXT_ENABLE_BASE +
|
||||
i * CONTEXT_ENABLE_SIZE;
|
||||
context_id * CONTEXT_ENABLE_SIZE;
|
||||
handler->priv = priv;
|
||||
|
||||
handler->enable_save = kcalloc(DIV_ROUND_UP(nr_irqs, 32),
|
||||
@ -588,7 +637,7 @@ done:
|
||||
nr_handlers++;
|
||||
}
|
||||
|
||||
priv->irqdomain = irq_domain_add_linear(to_of_node(fwnode), nr_irqs + 1,
|
||||
priv->irqdomain = irq_domain_create_linear(fwnode, nr_irqs + 1,
|
||||
&plic_irqdomain_ops, priv);
|
||||
if (WARN_ON(!priv->irqdomain))
|
||||
goto fail_cleanup_contexts;
|
||||
@ -626,13 +675,18 @@ done:
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
if (!acpi_disabled)
|
||||
acpi_dev_clear_dependencies(ACPI_COMPANION(fwnode->dev));
|
||||
#endif
|
||||
|
||||
pr_info("%pfwP: mapped %d interrupts with %d handlers for %d contexts.\n",
|
||||
fwnode, nr_irqs, nr_handlers, nr_contexts);
|
||||
return 0;
|
||||
|
||||
fail_cleanup_contexts:
|
||||
for (i = 0; i < nr_contexts; i++) {
|
||||
if (plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu))
|
||||
if (plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu, priv->acpi_plic_id))
|
||||
continue;
|
||||
if (parent_hwirq != RV_IRQ_EXT || cpu < 0)
|
||||
continue;
|
||||
@ -663,6 +717,7 @@ static struct platform_driver plic_driver = {
|
||||
.name = "riscv-plic",
|
||||
.of_match_table = plic_match,
|
||||
.suppress_bind_attrs = true,
|
||||
.acpi_match_table = ACPI_PTR(plic_acpi_match),
|
||||
},
|
||||
.probe = plic_platform_probe,
|
||||
};
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci-acpi.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/rwsem.h>
|
||||
@ -1541,3 +1542,184 @@ static int __init acpi_pci_init(void)
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(acpi_pci_init);
|
||||
|
||||
#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
|
||||
|
||||
/*
|
||||
* Try to assign the IRQ number when probing a new device
|
||||
*/
|
||||
int pcibios_alloc_irq(struct pci_dev *dev)
|
||||
{
|
||||
if (!acpi_disabled)
|
||||
acpi_pci_irq_enable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct acpi_pci_generic_root_info {
|
||||
struct acpi_pci_root_info common;
|
||||
struct pci_config_window *cfg; /* config space mapping */
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
|
||||
{
|
||||
struct pci_config_window *cfg;
|
||||
struct acpi_device *adev;
|
||||
struct device *bus_dev;
|
||||
|
||||
if (acpi_disabled)
|
||||
return 0;
|
||||
|
||||
cfg = bridge->bus->sysdata;
|
||||
|
||||
/*
|
||||
* On Hyper-V there is no corresponding ACPI device for a root bridge,
|
||||
* therefore ->parent is set as NULL by the driver. And set 'adev' as
|
||||
* NULL in this case because there is no proper ACPI device.
|
||||
*/
|
||||
if (!cfg->parent)
|
||||
adev = NULL;
|
||||
else
|
||||
adev = to_acpi_device(cfg->parent);
|
||||
|
||||
bus_dev = &bridge->bus->dev;
|
||||
|
||||
ACPI_COMPANION_SET(&bridge->dev, adev);
|
||||
set_dev_node(bus_dev, acpi_get_node(acpi_device_handle(adev)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci)
|
||||
{
|
||||
struct resource_entry *entry, *tmp;
|
||||
int status;
|
||||
|
||||
status = acpi_pci_probe_root_resources(ci);
|
||||
resource_list_for_each_entry_safe(entry, tmp, &ci->resources) {
|
||||
if (!(entry->res->flags & IORESOURCE_WINDOW))
|
||||
resource_list_destroy_entry(entry);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup the bus range for the domain in MCFG, 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 resource *bus_res = &root->secondary;
|
||||
u16 seg = root->segment;
|
||||
const struct pci_ecam_ops *ecam_ops;
|
||||
struct resource cfgres;
|
||||
struct acpi_device *adev;
|
||||
struct pci_config_window *cfg;
|
||||
int ret;
|
||||
|
||||
ret = pci_mcfg_lookup(root, &cfgres, &ecam_ops);
|
||||
if (ret) {
|
||||
dev_err(dev, "%04x:%pR ECAM region not found\n", seg, bus_res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
adev = acpi_resource_consumer(&cfgres);
|
||||
if (adev)
|
||||
dev_info(dev, "ECAM area %pR reserved by %s\n", &cfgres,
|
||||
dev_name(&adev->dev));
|
||||
else
|
||||
dev_warn(dev, FW_BUG "ECAM area %pR not reserved in ACPI namespace\n",
|
||||
&cfgres);
|
||||
|
||||
cfg = pci_ecam_create(dev, &cfgres, 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;
|
||||
}
|
||||
|
||||
/* release_info: free resources allocated by init_info */
|
||||
static void pci_acpi_generic_release_info(struct acpi_pci_root_info *ci)
|
||||
{
|
||||
struct acpi_pci_generic_root_info *ri;
|
||||
|
||||
ri = container_of(ci, struct acpi_pci_generic_root_info, common);
|
||||
pci_ecam_free(ri->cfg);
|
||||
kfree(ci->ops);
|
||||
kfree(ri);
|
||||
}
|
||||
|
||||
/* Interface called from ACPI code to setup PCI host controller */
|
||||
struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
|
||||
{
|
||||
struct acpi_pci_generic_root_info *ri;
|
||||
struct pci_bus *bus, *child;
|
||||
struct acpi_pci_root_ops *root_ops;
|
||||
struct pci_host_bridge *host;
|
||||
|
||||
ri = kzalloc(sizeof(*ri), GFP_KERNEL);
|
||||
if (!ri)
|
||||
return NULL;
|
||||
|
||||
root_ops = kzalloc(sizeof(*root_ops), GFP_KERNEL);
|
||||
if (!root_ops) {
|
||||
kfree(ri);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ri->cfg = pci_acpi_setup_ecam_mapping(root);
|
||||
if (!ri->cfg) {
|
||||
kfree(ri);
|
||||
kfree(root_ops);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
root_ops->release_info = pci_acpi_generic_release_info;
|
||||
root_ops->prepare_resources = pci_acpi_root_prepare_resources;
|
||||
root_ops->pci_ops = (struct pci_ops *)&ri->cfg->ops->pci_ops;
|
||||
bus = acpi_pci_root_create(root, root_ops, &ri->common, ri->cfg);
|
||||
if (!bus)
|
||||
return NULL;
|
||||
|
||||
/* If we must preserve the resource configuration, claim now */
|
||||
host = pci_find_host_bridge(bus);
|
||||
if (host->preserve_config)
|
||||
pci_bus_claim_resources(bus);
|
||||
|
||||
/*
|
||||
* Assign whatever was left unassigned. If we didn't claim above,
|
||||
* this will reassign everything.
|
||||
*/
|
||||
pci_assign_unassigned_root_bus_resources(bus);
|
||||
|
||||
list_for_each_entry(child, &bus->children, node)
|
||||
pcie_bus_configure_settings(child);
|
||||
|
||||
return bus;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -993,6 +993,8 @@ static inline void acpi_put_acpi_dev(struct acpi_device *adev)
|
||||
|
||||
int acpi_wait_for_acpi_ipmi(void);
|
||||
|
||||
int acpi_scan_add_dep(acpi_handle handle, struct acpi_handle_list *dep_devices);
|
||||
u32 arch_acpi_add_auto_dep(acpi_handle handle);
|
||||
#else /* CONFIG_ACPI */
|
||||
|
||||
static inline int register_acpi_bus_type(void *bus) { return 0; }
|
||||
|
@ -107,6 +107,7 @@ enum acpi_irq_model_id {
|
||||
ACPI_IRQ_MODEL_PLATFORM,
|
||||
ACPI_IRQ_MODEL_GIC,
|
||||
ACPI_IRQ_MODEL_LPIC,
|
||||
ACPI_IRQ_MODEL_RINTC,
|
||||
ACPI_IRQ_MODEL_COUNT
|
||||
};
|
||||
|
||||
@ -1343,6 +1344,8 @@ struct acpi_probe_entry {
|
||||
kernel_ulong_t driver_data;
|
||||
};
|
||||
|
||||
void arch_sort_irqchip_probe(struct acpi_probe_entry *ap_head, int nr);
|
||||
|
||||
#define ACPI_DECLARE_PROBE_ENTRY(table, name, table_id, subtable, \
|
||||
valid, data, fn) \
|
||||
static const struct acpi_probe_entry __acpi_probe_##name \
|
||||
@ -1529,6 +1532,12 @@ void acpi_arm_init(void);
|
||||
static inline void acpi_arm_init(void) { }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RISCV
|
||||
void acpi_riscv_init(void);
|
||||
#else
|
||||
static inline void acpi_riscv_init(void) { }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI_PCC
|
||||
void acpi_init_pcc(void);
|
||||
#else
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fwnode.h>
|
||||
#include <asm/csr.h>
|
||||
|
||||
#define IMSIC_MMIO_PAGE_SHIFT 12
|
||||
@ -84,4 +86,11 @@ static inline const struct imsic_global_config *imsic_get_global_config(void)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
int imsic_platform_acpi_probe(struct fwnode_handle *fwnode);
|
||||
struct fwnode_handle *imsic_acpi_get_fwnode(struct device *dev);
|
||||
#else
|
||||
static inline struct fwnode_handle *imsic_acpi_get_fwnode(struct device *dev) { return NULL; }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user