mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-04-04 15:55:59 -04:00
Merge branch 'acpi-irq'
Merge ARM-related irq subsystem changes based on the recent ACPICA updates for 6.20-rc1/7.0-rc1: - Add support for GICv5 ACPI probing on ARM which is based on the GICv5 MADT structures and ARM IORT IWB node definitions recently added to ACPICA (Lorenzo Pieralisi) * acpi-irq: irqchip/gic-v5: Add ACPI IWB probing irqchip/gic-v5: Add ACPI ITS probing irqchip/gic-v5: Add ACPI IRS probing irqchip/gic-v5: Split IRS probing into OF and generic portions PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic irqdomain: Add parent field to struct irqchip_fwid
This commit is contained in:
@@ -264,39 +264,47 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
|
||||
struct device *dev = context;
|
||||
acpi_status status = AE_NOT_FOUND;
|
||||
|
||||
if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
|
||||
if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT ||
|
||||
node->type == ACPI_IORT_NODE_IWB) {
|
||||
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
struct acpi_device *adev;
|
||||
struct acpi_iort_named_component *ncomp;
|
||||
struct device *nc_dev = dev;
|
||||
struct acpi_iort_iwb *iwb;
|
||||
struct device *cdev = dev;
|
||||
struct acpi_device *adev;
|
||||
const char *device_name;
|
||||
|
||||
/*
|
||||
* Walk the device tree to find a device with an
|
||||
* ACPI companion; there is no point in scanning
|
||||
* IORT for a device matching a named component if
|
||||
* IORT for a device matching a named component or IWB if
|
||||
* the device does not have an ACPI companion to
|
||||
* start with.
|
||||
*/
|
||||
do {
|
||||
adev = ACPI_COMPANION(nc_dev);
|
||||
adev = ACPI_COMPANION(cdev);
|
||||
if (adev)
|
||||
break;
|
||||
|
||||
nc_dev = nc_dev->parent;
|
||||
} while (nc_dev);
|
||||
cdev = cdev->parent;
|
||||
} while (cdev);
|
||||
|
||||
if (!adev)
|
||||
goto out;
|
||||
|
||||
status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_warn(nc_dev, "Can't get device full path name\n");
|
||||
dev_warn(cdev, "Can't get device full path name\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ncomp = (struct acpi_iort_named_component *)node->node_data;
|
||||
status = !strcmp(ncomp->device_name, buf.pointer) ?
|
||||
AE_OK : AE_NOT_FOUND;
|
||||
if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
|
||||
ncomp = (struct acpi_iort_named_component *)node->node_data;
|
||||
device_name = ncomp->device_name;
|
||||
} else {
|
||||
iwb = (struct acpi_iort_iwb *)node->node_data;
|
||||
device_name = iwb->device_name;
|
||||
}
|
||||
status = !strcmp(device_name, buf.pointer) ? AE_OK : AE_NOT_FOUND;
|
||||
acpi_os_free(buf.pointer);
|
||||
} else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
|
||||
struct acpi_iort_root_complex *pci_rc;
|
||||
@@ -317,12 +325,28 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
|
||||
return status;
|
||||
}
|
||||
|
||||
static acpi_status iort_match_iwb_callback(struct acpi_iort_node *node, void *context)
|
||||
{
|
||||
struct acpi_iort_iwb *iwb;
|
||||
u32 *id = context;
|
||||
|
||||
if (node->type != ACPI_IORT_NODE_IWB)
|
||||
return AE_NOT_FOUND;
|
||||
|
||||
iwb = (struct acpi_iort_iwb *)node->node_data;
|
||||
if (iwb->iwb_index != *id)
|
||||
return AE_NOT_FOUND;
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
|
||||
u32 *rid_out, bool check_overlap)
|
||||
{
|
||||
/* Single mapping does not care for input id */
|
||||
if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) {
|
||||
if (type == ACPI_IORT_NODE_NAMED_COMPONENT ||
|
||||
type == ACPI_IORT_NODE_IWB ||
|
||||
type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
|
||||
*rid_out = map->output_base;
|
||||
return 0;
|
||||
@@ -392,6 +416,7 @@ static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
|
||||
|
||||
if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) {
|
||||
if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT ||
|
||||
node->type == ACPI_IORT_NODE_IWB ||
|
||||
node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX ||
|
||||
node->type == ACPI_IORT_NODE_SMMU_V3 ||
|
||||
node->type == ACPI_IORT_NODE_PMCG) {
|
||||
@@ -562,9 +587,14 @@ static struct acpi_iort_node *iort_find_dev_node(struct device *dev)
|
||||
return node;
|
||||
/*
|
||||
* if not, then it should be a platform device defined in
|
||||
* DSDT/SSDT (with Named Component node in IORT)
|
||||
* DSDT/SSDT (with Named Component node in IORT) or an
|
||||
* IWB device in the DSDT/SSDT.
|
||||
*/
|
||||
return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
|
||||
node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
|
||||
iort_match_node_callback, dev);
|
||||
if (node)
|
||||
return node;
|
||||
return iort_scan_node(ACPI_IORT_NODE_IWB,
|
||||
iort_match_node_callback, dev);
|
||||
}
|
||||
|
||||
@@ -595,45 +625,45 @@ u32 iort_msi_map_id(struct device *dev, u32 input_id)
|
||||
}
|
||||
|
||||
/**
|
||||
* iort_pmsi_get_dev_id() - Get the device id for a device
|
||||
* iort_msi_xlate() - Map a MSI input ID for a device
|
||||
* @dev: The device for which the mapping is to be done.
|
||||
* @dev_id: The device ID found.
|
||||
* @input_id: The device input ID.
|
||||
* @fwnode: Pointer to store the fwnode.
|
||||
*
|
||||
* Returns: 0 for successful find a dev id, -ENODEV on error
|
||||
* Returns: mapped MSI ID on success, input ID otherwise
|
||||
* On success, the fwnode pointer is initialized to the MSI
|
||||
* controller fwnode handle.
|
||||
*/
|
||||
int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
|
||||
u32 iort_msi_xlate(struct device *dev, u32 input_id, struct fwnode_handle **fwnode)
|
||||
{
|
||||
int i, index;
|
||||
struct acpi_iort_its_group *its;
|
||||
struct acpi_iort_node *node;
|
||||
u32 dev_id;
|
||||
|
||||
node = iort_find_dev_node(dev);
|
||||
if (!node)
|
||||
return -ENODEV;
|
||||
return input_id;
|
||||
|
||||
index = iort_get_id_mapping_index(node);
|
||||
/* if there is a valid index, go get the dev_id directly */
|
||||
if (index >= 0) {
|
||||
if (iort_node_get_id(node, dev_id, index))
|
||||
return 0;
|
||||
} else {
|
||||
for (i = 0; i < node->mapping_count; i++) {
|
||||
if (iort_node_map_platform_id(node, dev_id,
|
||||
IORT_MSI_TYPE, i))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
node = iort_node_map_id(node, input_id, &dev_id, IORT_MSI_TYPE);
|
||||
if (!node)
|
||||
return input_id;
|
||||
|
||||
return -ENODEV;
|
||||
/* Move to ITS specific data */
|
||||
its = (struct acpi_iort_its_group *)node->node_data;
|
||||
|
||||
*fwnode = iort_find_domain_token(its->identifiers[0]);
|
||||
|
||||
return dev_id;
|
||||
}
|
||||
|
||||
static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
|
||||
int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base)
|
||||
{
|
||||
struct iort_its_msi_chip *its_msi_chip;
|
||||
int ret = -ENODEV;
|
||||
|
||||
spin_lock(&iort_msi_chip_lock);
|
||||
list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) {
|
||||
if (its_msi_chip->translation_id == its_id) {
|
||||
if (its_msi_chip->fw_node == node) {
|
||||
*base = its_msi_chip->base_addr;
|
||||
ret = 0;
|
||||
break;
|
||||
@@ -644,6 +674,62 @@ static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
|
||||
{
|
||||
struct fwnode_handle *fwnode = iort_find_domain_token(its_id);
|
||||
|
||||
if (!fwnode)
|
||||
return -ENODEV;
|
||||
|
||||
return iort_its_translate_pa(fwnode, base);
|
||||
}
|
||||
|
||||
/**
|
||||
* iort_pmsi_get_msi_info() - Get the device id and translate frame PA for a device
|
||||
* @dev: The device for which the mapping is to be done.
|
||||
* @dev_id: The device ID found.
|
||||
* @pa: optional pointer to store translate frame address.
|
||||
*
|
||||
* Returns: 0 for successful devid and pa retrieval, -ENODEV on error
|
||||
*/
|
||||
int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *pa)
|
||||
{
|
||||
struct acpi_iort_node *node, *parent = NULL;
|
||||
struct acpi_iort_its_group *its;
|
||||
int i, index;
|
||||
|
||||
node = iort_find_dev_node(dev);
|
||||
if (!node)
|
||||
return -ENODEV;
|
||||
|
||||
index = iort_get_id_mapping_index(node);
|
||||
/* if there is a valid index, go get the dev_id directly */
|
||||
if (index >= 0) {
|
||||
parent = iort_node_get_id(node, dev_id, index);
|
||||
} else {
|
||||
for (i = 0; i < node->mapping_count; i++) {
|
||||
parent = iort_node_map_platform_id(node, dev_id,
|
||||
IORT_MSI_TYPE, i);
|
||||
if (parent)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!parent)
|
||||
return -ENODEV;
|
||||
|
||||
if (pa) {
|
||||
int ret;
|
||||
|
||||
its = (struct acpi_iort_its_group *)node->node_data;
|
||||
ret = iort_find_its_base(its->identifiers[0], pa);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* iort_dev_find_its_id() - Find the ITS identifier for a device
|
||||
* @dev: The device.
|
||||
@@ -703,6 +789,35 @@ struct irq_domain *iort_get_device_domain(struct device *dev, u32 id,
|
||||
return irq_find_matching_fwnode(handle, bus_token);
|
||||
}
|
||||
|
||||
struct fwnode_handle *iort_iwb_handle(u32 iwb_id)
|
||||
{
|
||||
struct fwnode_handle *fwnode;
|
||||
struct acpi_iort_node *node;
|
||||
struct acpi_device *device;
|
||||
struct acpi_iort_iwb *iwb;
|
||||
acpi_status status;
|
||||
acpi_handle handle;
|
||||
|
||||
/* find its associated IWB node */
|
||||
node = iort_scan_node(ACPI_IORT_NODE_IWB, iort_match_iwb_callback, &iwb_id);
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
iwb = (struct acpi_iort_iwb *)node->node_data;
|
||||
status = acpi_get_handle(NULL, iwb->device_name, &handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return NULL;
|
||||
|
||||
device = acpi_get_acpi_dev(handle);
|
||||
if (!device)
|
||||
return NULL;
|
||||
|
||||
fwnode = acpi_fwnode_handle(device);
|
||||
acpi_put_acpi_dev(device);
|
||||
|
||||
return fwnode;
|
||||
}
|
||||
|
||||
static void iort_set_device_domain(struct device *dev,
|
||||
struct acpi_iort_node *node)
|
||||
{
|
||||
@@ -763,8 +878,14 @@ static struct irq_domain *iort_get_platform_device_domain(struct device *dev)
|
||||
/* find its associated iort node */
|
||||
node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
|
||||
iort_match_node_callback, dev);
|
||||
if (!node)
|
||||
return NULL;
|
||||
if (!node) {
|
||||
/* find its associated iort node */
|
||||
node = iort_scan_node(ACPI_IORT_NODE_IWB,
|
||||
iort_match_node_callback, dev);
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* then find its msi parent node */
|
||||
for (i = 0; i < node->mapping_count; i++) {
|
||||
|
||||
@@ -1197,6 +1197,9 @@ static int __init acpi_bus_init_irq(void)
|
||||
case ACPI_IRQ_MODEL_GIC:
|
||||
message = "GIC";
|
||||
break;
|
||||
case ACPI_IRQ_MODEL_GIC_V5:
|
||||
message = "GICv5";
|
||||
break;
|
||||
case ACPI_IRQ_MODEL_PLATFORM:
|
||||
message = "platform specific model";
|
||||
break;
|
||||
|
||||
@@ -19,18 +19,24 @@
|
||||
MSI_FLAG_PCI_MSIX | \
|
||||
MSI_FLAG_MULTI_PCI_MSI)
|
||||
|
||||
static int its_translate_frame_address(struct device_node *msi_node, phys_addr_t *pa)
|
||||
static int its_translate_frame_address(struct fwnode_handle *msi_node, phys_addr_t *pa)
|
||||
{
|
||||
struct resource res;
|
||||
int ret;
|
||||
|
||||
ret = of_property_match_string(msi_node, "reg-names", "ns-translate");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (is_of_node(msi_node)) {
|
||||
struct device_node *msi_np = to_of_node(msi_node);
|
||||
|
||||
ret = of_address_to_resource(msi_node, ret, &res);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = of_property_match_string(msi_np, "reg-names", "ns-translate");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = of_address_to_resource(msi_np, ret, &res);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
ret = iort_its_translate_pa(msi_node, &res.start);
|
||||
}
|
||||
|
||||
*pa = res.start;
|
||||
return 0;
|
||||
@@ -104,7 +110,7 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
|
||||
static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
|
||||
int nvec, msi_alloc_info_t *info)
|
||||
{
|
||||
struct device_node *msi_node = NULL;
|
||||
struct fwnode_handle *msi_node = NULL;
|
||||
struct msi_domain_info *msi_info;
|
||||
struct pci_dev *pdev;
|
||||
phys_addr_t pa;
|
||||
@@ -116,7 +122,7 @@ static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
|
||||
|
||||
pdev = to_pci_dev(dev);
|
||||
|
||||
rid = pci_msi_map_rid_ctlr_node(pdev, &msi_node);
|
||||
rid = pci_msi_map_rid_ctlr_node(domain->parent, pdev, &msi_node);
|
||||
if (!msi_node)
|
||||
return -ENODEV;
|
||||
|
||||
@@ -124,7 +130,7 @@ static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
|
||||
if (ret)
|
||||
return -ENODEV;
|
||||
|
||||
of_node_put(msi_node);
|
||||
fwnode_handle_put(msi_node);
|
||||
|
||||
/* ITS specific DeviceID */
|
||||
info->scratchpad[0].ul = rid;
|
||||
@@ -161,7 +167,7 @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u
|
||||
ret = -EINVAL;
|
||||
|
||||
if (!ret && pa)
|
||||
ret = its_translate_frame_address(it.node, pa);
|
||||
ret = its_translate_frame_address(of_fwnode_handle(it.node), pa);
|
||||
|
||||
if (!ret)
|
||||
*dev_id = args;
|
||||
@@ -176,11 +182,6 @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u
|
||||
return of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &msi_ctrl, dev_id);
|
||||
}
|
||||
|
||||
int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
|
||||
int nvec, msi_alloc_info_t *info)
|
||||
{
|
||||
@@ -191,7 +192,7 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
|
||||
if (dev->of_node)
|
||||
ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, NULL);
|
||||
else
|
||||
ret = iort_pmsi_get_dev_id(dev, &dev_id);
|
||||
ret = iort_pmsi_get_msi_info(dev, &dev_id, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -214,10 +215,10 @@ static int its_v5_pmsi_prepare(struct irq_domain *domain, struct device *dev,
|
||||
u32 dev_id;
|
||||
int ret;
|
||||
|
||||
if (!dev->of_node)
|
||||
return -ENODEV;
|
||||
|
||||
ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa);
|
||||
if (dev->of_node)
|
||||
ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa);
|
||||
else
|
||||
ret = iort_pmsi_get_msi_info(dev, &dev_id, &pa);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#define pr_fmt(fmt) "GICv5 IRS: " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/kmemleak.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/of.h>
|
||||
@@ -545,15 +546,13 @@ int gicv5_irs_register_cpu(int cpuid)
|
||||
|
||||
static void __init gicv5_irs_init_bases(struct gicv5_irs_chip_data *irs_data,
|
||||
void __iomem *irs_base,
|
||||
struct fwnode_handle *handle)
|
||||
bool noncoherent)
|
||||
{
|
||||
struct device_node *np = to_of_node(handle);
|
||||
u32 cr0, cr1;
|
||||
|
||||
irs_data->fwnode = handle;
|
||||
irs_data->irs_base = irs_base;
|
||||
|
||||
if (of_property_read_bool(np, "dma-noncoherent")) {
|
||||
if (noncoherent) {
|
||||
/*
|
||||
* A non-coherent IRS implies that some cache levels cannot be
|
||||
* used coherently by the cores and GIC. Our only option is to mark
|
||||
@@ -678,49 +677,13 @@ static void irs_setup_pri_bits(u32 idr1)
|
||||
}
|
||||
}
|
||||
|
||||
static int __init gicv5_irs_init(struct device_node *node)
|
||||
static int __init gicv5_irs_init(struct gicv5_irs_chip_data *irs_data)
|
||||
{
|
||||
struct gicv5_irs_chip_data *irs_data;
|
||||
void __iomem *irs_base;
|
||||
u32 idr, spi_count;
|
||||
u8 iaffid_bits;
|
||||
int ret;
|
||||
u32 spi_count, idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR2);
|
||||
|
||||
irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL);
|
||||
if (!irs_data)
|
||||
return -ENOMEM;
|
||||
|
||||
raw_spin_lock_init(&irs_data->spi_config_lock);
|
||||
|
||||
ret = of_property_match_string(node, "reg-names", "ns-config");
|
||||
if (ret < 0) {
|
||||
pr_err("%pOF: ns-config reg-name not present\n", node);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
irs_base = of_io_request_and_map(node, ret, of_node_full_name(node));
|
||||
if (IS_ERR(irs_base)) {
|
||||
pr_err("%pOF: unable to map GICv5 IRS registers\n", node);
|
||||
ret = PTR_ERR(irs_base);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
gicv5_irs_init_bases(irs_data, irs_base, &node->fwnode);
|
||||
|
||||
idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1);
|
||||
iaffid_bits = FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1;
|
||||
|
||||
ret = gicv5_irs_of_init_affinity(node, irs_data, iaffid_bits);
|
||||
if (ret) {
|
||||
pr_err("Failed to parse CPU IAFFIDs from the device tree!\n");
|
||||
goto out_iomem;
|
||||
}
|
||||
|
||||
idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR2);
|
||||
if (WARN(!FIELD_GET(GICV5_IRS_IDR2_LPI, idr),
|
||||
"LPI support not available - no IPIs, can't proceed\n")) {
|
||||
ret = -ENODEV;
|
||||
goto out_iomem;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR7);
|
||||
@@ -729,14 +692,6 @@ static int __init gicv5_irs_init(struct device_node *node)
|
||||
idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR6);
|
||||
irs_data->spi_range = FIELD_GET(GICV5_IRS_IDR6_SPI_IRS_RANGE, idr);
|
||||
|
||||
if (irs_data->spi_range) {
|
||||
pr_info("%s detected SPI range [%u-%u]\n",
|
||||
of_node_full_name(node),
|
||||
irs_data->spi_min,
|
||||
irs_data->spi_min +
|
||||
irs_data->spi_range - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the global setting only on the first IRS.
|
||||
* Global properties (iaffid_bits, global spi count) are guaranteed to
|
||||
@@ -760,6 +715,60 @@ static int __init gicv5_irs_init(struct device_node *node)
|
||||
list_add_tail(&irs_data->entry, &irs_nodes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init gicv5_irs_of_init(struct device_node *node)
|
||||
{
|
||||
struct gicv5_irs_chip_data *irs_data;
|
||||
void __iomem *irs_base;
|
||||
u8 iaffid_bits;
|
||||
u32 idr;
|
||||
int ret;
|
||||
|
||||
irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL);
|
||||
if (!irs_data)
|
||||
return -ENOMEM;
|
||||
|
||||
raw_spin_lock_init(&irs_data->spi_config_lock);
|
||||
|
||||
ret = of_property_match_string(node, "reg-names", "ns-config");
|
||||
if (ret < 0) {
|
||||
pr_err("%pOF: ns-config reg-name not present\n", node);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
irs_base = of_io_request_and_map(node, ret, of_node_full_name(node));
|
||||
if (IS_ERR(irs_base)) {
|
||||
pr_err("%pOF: unable to map GICv5 IRS registers\n", node);
|
||||
ret = PTR_ERR(irs_base);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
irs_data->fwnode = of_fwnode_handle(node);
|
||||
gicv5_irs_init_bases(irs_data, irs_base, of_property_read_bool(node, "dma-noncoherent"));
|
||||
|
||||
idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1);
|
||||
iaffid_bits = FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1;
|
||||
|
||||
ret = gicv5_irs_of_init_affinity(node, irs_data, iaffid_bits);
|
||||
if (ret) {
|
||||
pr_err("Failed to parse CPU IAFFIDs from the device tree!\n");
|
||||
goto out_iomem;
|
||||
}
|
||||
|
||||
ret = gicv5_irs_init(irs_data);
|
||||
if (ret)
|
||||
goto out_iomem;
|
||||
|
||||
if (irs_data->spi_range) {
|
||||
pr_info("%s detected SPI range [%u-%u]\n",
|
||||
of_node_full_name(node),
|
||||
irs_data->spi_min,
|
||||
irs_data->spi_min +
|
||||
irs_data->spi_range - 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
out_iomem:
|
||||
iounmap(irs_base);
|
||||
@@ -805,8 +814,11 @@ void __init gicv5_irs_its_probe(void)
|
||||
{
|
||||
struct gicv5_irs_chip_data *irs_data;
|
||||
|
||||
list_for_each_entry(irs_data, &irs_nodes, entry)
|
||||
gicv5_its_of_probe(to_of_node(irs_data->fwnode));
|
||||
if (acpi_disabled)
|
||||
list_for_each_entry(irs_data, &irs_nodes, entry)
|
||||
gicv5_its_of_probe(to_of_node(irs_data->fwnode));
|
||||
else
|
||||
gicv5_its_acpi_probe();
|
||||
}
|
||||
|
||||
int __init gicv5_irs_of_probe(struct device_node *parent)
|
||||
@@ -818,10 +830,137 @@ int __init gicv5_irs_of_probe(struct device_node *parent)
|
||||
if (!of_device_is_compatible(np, "arm,gic-v5-irs"))
|
||||
continue;
|
||||
|
||||
ret = gicv5_irs_init(np);
|
||||
ret = gicv5_irs_of_init(np);
|
||||
if (ret)
|
||||
pr_err("Failed to init IRS %s\n", np->full_name);
|
||||
}
|
||||
|
||||
return list_empty(&irs_nodes) ? -ENODEV : 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
#define ACPI_GICV5_IRS_MEM_SIZE (SZ_64K)
|
||||
static struct gicv5_irs_chip_data *current_irs_data __initdata;
|
||||
static int current_irsid __initdata = -1;
|
||||
static u8 current_iaffid_bits __initdata;
|
||||
|
||||
static int __init gic_acpi_parse_iaffid(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_generic_interrupt *gicc = (struct acpi_madt_generic_interrupt *)header;
|
||||
int cpu;
|
||||
|
||||
if (!(gicc->flags & (ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE)))
|
||||
return 0;
|
||||
|
||||
if (gicc->irs_id != current_irsid)
|
||||
return 0;
|
||||
|
||||
cpu = get_logical_index(gicc->arm_mpidr);
|
||||
|
||||
if (gicc->iaffid & ~GENMASK(current_iaffid_bits - 1, 0)) {
|
||||
pr_warn("CPU %d iaffid 0x%x exceeds IRS iaffid bits\n", cpu, gicc->iaffid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Bind the IAFFID and the CPU */
|
||||
per_cpu(cpu_iaffid, cpu).iaffid = gicc->iaffid;
|
||||
per_cpu(cpu_iaffid, cpu).valid = true;
|
||||
pr_debug("Processed IAFFID %u for CPU%d", per_cpu(cpu_iaffid, cpu).iaffid, cpu);
|
||||
|
||||
/* We also know that the CPU is connected to this IRS */
|
||||
per_cpu(per_cpu_irs_data, cpu) = current_irs_data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init gicv5_irs_acpi_init_affinity(u32 irsid, struct gicv5_irs_chip_data *irs_data)
|
||||
{
|
||||
u32 idr;
|
||||
|
||||
current_irsid = irsid;
|
||||
current_irs_data = irs_data;
|
||||
|
||||
idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1);
|
||||
current_iaffid_bits = FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1;
|
||||
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, gic_acpi_parse_iaffid, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct resource * __init gic_request_region(resource_size_t base, resource_size_t size,
|
||||
const char *name)
|
||||
{
|
||||
struct resource *r = request_mem_region(base, size, name);
|
||||
|
||||
if (!r)
|
||||
pr_warn_once(FW_BUG "%s region %pa has overlapping address\n", name, &base);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __init gic_acpi_parse_madt_irs(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header;
|
||||
struct gicv5_irs_chip_data *irs_data;
|
||||
void __iomem *irs_base;
|
||||
struct resource *r;
|
||||
int ret;
|
||||
|
||||
/* Per-IRS data structure */
|
||||
irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL);
|
||||
if (!irs_data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* This spinlock is used for SPI config changes */
|
||||
raw_spin_lock_init(&irs_data->spi_config_lock);
|
||||
|
||||
r = gic_request_region(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE, "GICv5 IRS");
|
||||
if (!r) {
|
||||
ret = -EBUSY;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
irs_base = ioremap(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE);
|
||||
if (!irs_base) {
|
||||
pr_err("Unable to map GIC IRS registers\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
gicv5_irs_init_bases(irs_data, irs_base, irs->flags & ACPI_MADT_IRS_NON_COHERENT);
|
||||
|
||||
gicv5_irs_acpi_init_affinity(irs->irs_id, irs_data);
|
||||
|
||||
ret = gicv5_irs_init(irs_data);
|
||||
if (ret)
|
||||
goto out_map;
|
||||
|
||||
if (irs_data->spi_range) {
|
||||
pr_info("%s @%llx detected SPI range [%u-%u]\n", "IRS", irs->config_base_address,
|
||||
irs_data->spi_min,
|
||||
irs_data->spi_min +
|
||||
irs_data->spi_range - 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_map:
|
||||
iounmap(irs_base);
|
||||
out_release:
|
||||
release_mem_region(r->start, resource_size(r));
|
||||
out_free:
|
||||
kfree(irs_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __init gicv5_irs_acpi_probe(void)
|
||||
{
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_IRS, gic_acpi_parse_madt_irs, 0);
|
||||
|
||||
return list_empty(&irs_nodes) ? -ENODEV : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#define pr_fmt(fmt) "GICv5 ITS: " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/acpi_iort.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/init.h>
|
||||
@@ -1115,7 +1117,7 @@ static int gicv5_its_init_domain(struct gicv5_its_chip_data *its, struct irq_dom
|
||||
}
|
||||
|
||||
static int __init gicv5_its_init_bases(void __iomem *its_base, struct fwnode_handle *handle,
|
||||
struct irq_domain *parent_domain)
|
||||
struct irq_domain *parent_domain, bool noncoherent)
|
||||
{
|
||||
struct device_node *np = to_of_node(handle);
|
||||
struct gicv5_its_chip_data *its_node;
|
||||
@@ -1208,7 +1210,8 @@ static int __init gicv5_its_init(struct device_node *node)
|
||||
}
|
||||
|
||||
ret = gicv5_its_init_bases(its_base, of_fwnode_handle(node),
|
||||
gicv5_global_data.lpi_domain);
|
||||
gicv5_global_data.lpi_domain,
|
||||
of_property_read_bool(node, "dma-noncoherent"));
|
||||
if (ret)
|
||||
goto out_unmap;
|
||||
|
||||
@@ -1231,3 +1234,128 @@ void __init gicv5_its_of_probe(struct device_node *parent)
|
||||
pr_err("Failed to init ITS %s\n", np->full_name);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
#define ACPI_GICV5_ITS_MEM_SIZE (SZ_64K)
|
||||
|
||||
static struct acpi_madt_gicv5_translator *current_its_entry __initdata;
|
||||
static struct fwnode_handle *current_its_fwnode __initdata;
|
||||
|
||||
static int __init gic_acpi_parse_madt_its_translate(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_gicv5_translate_frame *its_frame;
|
||||
struct fwnode_handle *msi_dom_handle;
|
||||
struct resource res = {};
|
||||
int err;
|
||||
|
||||
its_frame = (struct acpi_madt_gicv5_translate_frame *)header;
|
||||
if (its_frame->linked_translator_id != current_its_entry->translator_id)
|
||||
return 0;
|
||||
|
||||
res.start = its_frame->base_address;
|
||||
res.end = its_frame->base_address + ACPI_GICV5_ITS_MEM_SIZE - 1;
|
||||
res.flags = IORESOURCE_MEM;
|
||||
|
||||
msi_dom_handle = irq_domain_alloc_parented_fwnode(&res.start, current_its_fwnode);
|
||||
if (!msi_dom_handle) {
|
||||
pr_err("ITS@%pa: Unable to allocate GICv5 ITS translate domain token\n",
|
||||
&res.start);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = iort_register_domain_token(its_frame->translate_frame_id, res.start,
|
||||
msi_dom_handle);
|
||||
if (err) {
|
||||
pr_err("ITS@%pa: Unable to register GICv5 ITS domain token (ITS TRANSLATE FRAME ID %d) to IORT\n",
|
||||
&res.start, its_frame->translate_frame_id);
|
||||
irq_domain_free_fwnode(msi_dom_handle);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init gic_acpi_free_madt_its_translate(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_gicv5_translate_frame *its_frame;
|
||||
struct fwnode_handle *msi_dom_handle;
|
||||
|
||||
its_frame = (struct acpi_madt_gicv5_translate_frame *)header;
|
||||
if (its_frame->linked_translator_id != current_its_entry->translator_id)
|
||||
return 0;
|
||||
|
||||
msi_dom_handle = iort_find_domain_token(its_frame->translate_frame_id);
|
||||
if (!msi_dom_handle)
|
||||
return 0;
|
||||
|
||||
iort_deregister_domain_token(its_frame->translate_frame_id);
|
||||
irq_domain_free_fwnode(msi_dom_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_gicv5_translator *its_entry;
|
||||
struct fwnode_handle *dom_handle;
|
||||
struct resource res = {};
|
||||
void __iomem *its_base;
|
||||
int err;
|
||||
|
||||
its_entry = (struct acpi_madt_gicv5_translator *)header;
|
||||
res.start = its_entry->base_address;
|
||||
res.end = its_entry->base_address + ACPI_GICV5_ITS_MEM_SIZE - 1;
|
||||
res.flags = IORESOURCE_MEM;
|
||||
|
||||
if (!request_mem_region(res.start, resource_size(&res), "GICv5 ITS"))
|
||||
return -EBUSY;
|
||||
|
||||
dom_handle = irq_domain_alloc_fwnode(&res.start);
|
||||
if (!dom_handle) {
|
||||
pr_err("ITS@%pa: Unable to allocate GICv5 ITS domain token\n",
|
||||
&res.start);
|
||||
err = -ENOMEM;
|
||||
goto out_rel_res;
|
||||
}
|
||||
|
||||
current_its_entry = its_entry;
|
||||
current_its_fwnode = dom_handle;
|
||||
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE,
|
||||
gic_acpi_parse_madt_its_translate, 0);
|
||||
|
||||
its_base = ioremap(res.start, ACPI_GICV5_ITS_MEM_SIZE);
|
||||
if (!its_base) {
|
||||
err = -ENOMEM;
|
||||
goto out_unregister;
|
||||
}
|
||||
|
||||
err = gicv5_its_init_bases(its_base, dom_handle, gicv5_global_data.lpi_domain,
|
||||
its_entry->flags & ACPI_MADT_GICV5_ITS_NON_COHERENT);
|
||||
if (err)
|
||||
goto out_unmap;
|
||||
|
||||
return 0;
|
||||
|
||||
out_unmap:
|
||||
iounmap(its_base);
|
||||
out_unregister:
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE,
|
||||
gic_acpi_free_madt_its_translate, 0);
|
||||
irq_domain_free_fwnode(dom_handle);
|
||||
out_rel_res:
|
||||
release_mem_region(res.start, resource_size(&res));
|
||||
return err;
|
||||
}
|
||||
|
||||
void __init gicv5_its_acpi_probe(void)
|
||||
{
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS, gic_acpi_parse_madt_its, 0);
|
||||
}
|
||||
#else
|
||||
void __init gicv5_its_acpi_probe(void) { }
|
||||
#endif
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
#define pr_fmt(fmt) "GICv5 IWB: " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/msi.h>
|
||||
@@ -136,18 +137,31 @@ static int gicv5_iwb_irq_domain_translate(struct irq_domain *d, struct irq_fwspe
|
||||
irq_hw_number_t *hwirq,
|
||||
unsigned int *type)
|
||||
{
|
||||
if (!is_of_node(fwspec->fwnode))
|
||||
return -EINVAL;
|
||||
if (is_of_node(fwspec->fwnode)) {
|
||||
|
||||
if (fwspec->param_count < 2)
|
||||
return -EINVAL;
|
||||
if (fwspec->param_count < 2)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* param[0] is be the wire
|
||||
* param[1] is the interrupt type
|
||||
*/
|
||||
*hwirq = fwspec->param[0];
|
||||
*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
|
||||
/*
|
||||
* param[0] is be the wire
|
||||
* param[1] is the interrupt type
|
||||
*/
|
||||
*hwirq = fwspec->param[0];
|
||||
*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
|
||||
}
|
||||
|
||||
if (is_acpi_device_node(fwspec->fwnode)) {
|
||||
|
||||
if (fwspec->param_count < 2)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Extract the wire from param[0]
|
||||
* param[1] is the interrupt type
|
||||
*/
|
||||
*hwirq = FIELD_GET(GICV5_GSI_IWB_WIRE, fwspec->param[0]);
|
||||
*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -265,10 +279,18 @@ static const struct of_device_id gicv5_iwb_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gicv5_iwb_of_match);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id iwb_acpi_match[] = {
|
||||
{ "ARMH0003", 0 },
|
||||
{}
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct platform_driver gicv5_iwb_platform_driver = {
|
||||
.driver = {
|
||||
.name = "GICv5 IWB",
|
||||
.of_match_table = gicv5_iwb_of_match,
|
||||
.acpi_match_table = ACPI_PTR(iwb_acpi_match),
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = gicv5_iwb_device_probe,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#define pr_fmt(fmt) "GICv5: " fmt
|
||||
|
||||
#include <linux/acpi_iort.h>
|
||||
#include <linux/cpuhotplug.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/irqdomain.h>
|
||||
@@ -579,16 +580,36 @@ static __always_inline int gicv5_irq_domain_translate(struct irq_domain *d,
|
||||
unsigned int *type,
|
||||
const u8 hwirq_type)
|
||||
{
|
||||
if (!is_of_node(fwspec->fwnode))
|
||||
return -EINVAL;
|
||||
unsigned int hwirq_trigger;
|
||||
u8 fwspec_irq_type;
|
||||
|
||||
if (fwspec->param_count < 3)
|
||||
return -EINVAL;
|
||||
if (is_of_node(fwspec->fwnode)) {
|
||||
|
||||
if (fwspec->param[0] != hwirq_type)
|
||||
return -EINVAL;
|
||||
if (fwspec->param_count < 3)
|
||||
return -EINVAL;
|
||||
|
||||
*hwirq = fwspec->param[1];
|
||||
fwspec_irq_type = fwspec->param[0];
|
||||
|
||||
if (fwspec->param[0] != hwirq_type)
|
||||
return -EINVAL;
|
||||
|
||||
*hwirq = fwspec->param[1];
|
||||
hwirq_trigger = fwspec->param[2];
|
||||
}
|
||||
|
||||
if (is_fwnode_irqchip(fwspec->fwnode)) {
|
||||
|
||||
if (fwspec->param_count != 2)
|
||||
return -EINVAL;
|
||||
|
||||
fwspec_irq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]);
|
||||
|
||||
if (fwspec_irq_type != hwirq_type)
|
||||
return -EINVAL;
|
||||
|
||||
*hwirq = FIELD_GET(GICV5_HWIRQ_ID, fwspec->param[0]);
|
||||
hwirq_trigger = fwspec->param[1];
|
||||
}
|
||||
|
||||
switch (hwirq_type) {
|
||||
case GICV5_HWIRQ_TYPE_PPI:
|
||||
@@ -600,7 +621,7 @@ static __always_inline int gicv5_irq_domain_translate(struct irq_domain *d,
|
||||
IRQ_TYPE_EDGE_RISING;
|
||||
break;
|
||||
case GICV5_HWIRQ_TYPE_SPI:
|
||||
*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
|
||||
*type = hwirq_trigger & IRQ_TYPE_SENSE_MASK;
|
||||
break;
|
||||
default:
|
||||
BUILD_BUG_ON(1);
|
||||
@@ -660,10 +681,18 @@ static void gicv5_irq_domain_free(struct irq_domain *domain, unsigned int virq,
|
||||
static int gicv5_irq_ppi_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
|
||||
enum irq_domain_bus_token bus_token)
|
||||
{
|
||||
u32 hwirq_type;
|
||||
|
||||
if (fwspec->fwnode != d->fwnode)
|
||||
return 0;
|
||||
|
||||
if (fwspec->param[0] != GICV5_HWIRQ_TYPE_PPI)
|
||||
if (is_of_node(fwspec->fwnode))
|
||||
hwirq_type = fwspec->param[0];
|
||||
|
||||
if (is_fwnode_irqchip(fwspec->fwnode))
|
||||
hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]);
|
||||
|
||||
if (hwirq_type != GICV5_HWIRQ_TYPE_PPI)
|
||||
return 0;
|
||||
|
||||
return (d == gicv5_global_data.ppi_domain);
|
||||
@@ -718,10 +747,18 @@ static int gicv5_irq_spi_domain_alloc(struct irq_domain *domain, unsigned int vi
|
||||
static int gicv5_irq_spi_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
|
||||
enum irq_domain_bus_token bus_token)
|
||||
{
|
||||
u32 hwirq_type;
|
||||
|
||||
if (fwspec->fwnode != d->fwnode)
|
||||
return 0;
|
||||
|
||||
if (fwspec->param[0] != GICV5_HWIRQ_TYPE_SPI)
|
||||
if (is_of_node(fwspec->fwnode))
|
||||
hwirq_type = fwspec->param[0];
|
||||
|
||||
if (is_fwnode_irqchip(fwspec->fwnode))
|
||||
hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]);
|
||||
|
||||
if (hwirq_type != GICV5_HWIRQ_TYPE_SPI)
|
||||
return 0;
|
||||
|
||||
return (d == gicv5_global_data.spi_domain);
|
||||
@@ -1082,16 +1119,12 @@ static inline void __init gic_of_setup_kvm_info(struct device_node *node)
|
||||
}
|
||||
#endif // CONFIG_KVM
|
||||
|
||||
static int __init gicv5_of_init(struct device_node *node, struct device_node *parent)
|
||||
static int __init gicv5_init_common(struct fwnode_handle *parent_domain)
|
||||
{
|
||||
int ret = gicv5_irs_of_probe(node);
|
||||
int ret = gicv5_init_domains(parent_domain);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gicv5_init_domains(of_fwnode_handle(node));
|
||||
if (ret)
|
||||
goto out_irs;
|
||||
|
||||
gicv5_set_cpuif_pribits();
|
||||
gicv5_set_cpuif_idbits();
|
||||
|
||||
@@ -1113,18 +1146,85 @@ static int __init gicv5_of_init(struct device_node *node, struct device_node *pa
|
||||
gicv5_smp_init();
|
||||
|
||||
gicv5_irs_its_probe();
|
||||
|
||||
gic_of_setup_kvm_info(node);
|
||||
|
||||
return 0;
|
||||
|
||||
out_int:
|
||||
gicv5_cpu_disable_interrupts();
|
||||
out_dom:
|
||||
gicv5_free_domains();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init gicv5_of_init(struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
int ret = gicv5_irs_of_probe(node);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gicv5_init_common(of_fwnode_handle(node));
|
||||
if (ret)
|
||||
goto out_irs;
|
||||
|
||||
gic_of_setup_kvm_info(node);
|
||||
|
||||
return 0;
|
||||
out_irs:
|
||||
gicv5_irs_remove();
|
||||
|
||||
return ret;
|
||||
}
|
||||
IRQCHIP_DECLARE(gic_v5, "arm,gic-v5", gicv5_of_init);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static bool __init acpi_validate_gic_table(struct acpi_subtable_header *header,
|
||||
struct acpi_probe_entry *ape)
|
||||
{
|
||||
struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header;
|
||||
|
||||
return (irs->version == ape->driver_data);
|
||||
}
|
||||
|
||||
static struct fwnode_handle *gsi_domain_handle;
|
||||
|
||||
static struct fwnode_handle *gic_v5_get_gsi_domain_id(u32 gsi)
|
||||
{
|
||||
if (FIELD_GET(GICV5_GSI_IC_TYPE, gsi) == GICV5_GSI_IWB_TYPE)
|
||||
return iort_iwb_handle(FIELD_GET(GICV5_GSI_IWB_FRAME_ID, gsi));
|
||||
|
||||
return gsi_domain_handle;
|
||||
}
|
||||
|
||||
static int __init gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header;
|
||||
int ret;
|
||||
|
||||
if (gsi_domain_handle)
|
||||
return 0;
|
||||
|
||||
gsi_domain_handle = irq_domain_alloc_fwnode(&irs->config_base_address);
|
||||
if (!gsi_domain_handle)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = gicv5_irs_acpi_probe();
|
||||
if (ret)
|
||||
goto out_fwnode;
|
||||
|
||||
ret = gicv5_init_common(gsi_domain_handle);
|
||||
if (ret)
|
||||
goto out_irs;
|
||||
|
||||
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC_V5, gic_v5_get_gsi_domain_id);
|
||||
|
||||
return 0;
|
||||
|
||||
out_irs:
|
||||
gicv5_irs_remove();
|
||||
out_fwnode:
|
||||
irq_domain_free_fwnode(gsi_domain_handle);
|
||||
return ret;
|
||||
}
|
||||
IRQCHIP_ACPI_DECLARE(gic_v5, ACPI_MADT_TYPE_GICV5_IRS,
|
||||
acpi_validate_gic_table, ACPI_MADT_GIC_VERSION_V5,
|
||||
gic_acpi_init);
|
||||
#endif
|
||||
|
||||
@@ -376,23 +376,36 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev)
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_msi_map_rid_ctlr_node - Get the MSI controller node and MSI requester id (RID)
|
||||
* pci_msi_map_rid_ctlr_node - Get the MSI controller fwnode_handle and MSI requester id (RID)
|
||||
* @domain: The interrupt domain
|
||||
* @pdev: The PCI device
|
||||
* @node: Pointer to store the MSI controller device node
|
||||
* @node: Pointer to store the MSI controller fwnode_handle
|
||||
*
|
||||
* Use the firmware data to find the MSI controller node for @pdev.
|
||||
* Use the firmware data to find the MSI controller fwnode_handle for @pdev.
|
||||
* If found map the RID and initialize @node with it. @node value must
|
||||
* be set to NULL on entry.
|
||||
*
|
||||
* Returns: The RID.
|
||||
*/
|
||||
u32 pci_msi_map_rid_ctlr_node(struct pci_dev *pdev, struct device_node **node)
|
||||
u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *pdev,
|
||||
struct fwnode_handle **node)
|
||||
{
|
||||
u32 rid = pci_dev_id(pdev);
|
||||
|
||||
pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
|
||||
|
||||
return of_msi_xlate(&pdev->dev, node, rid);
|
||||
/* Check whether the domain fwnode is an OF node */
|
||||
if (irq_domain_get_of_node(domain)) {
|
||||
struct device_node *msi_ctlr_node = NULL;
|
||||
|
||||
rid = of_msi_xlate(&pdev->dev, &msi_ctlr_node, rid);
|
||||
if (msi_ctlr_node)
|
||||
*node = of_fwnode_handle(msi_ctlr_node);
|
||||
} else {
|
||||
rid = iort_msi_xlate(&pdev->dev, rid, node);
|
||||
}
|
||||
|
||||
return rid;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -107,6 +107,7 @@ enum acpi_irq_model_id {
|
||||
ACPI_IRQ_MODEL_IOSAPIC,
|
||||
ACPI_IRQ_MODEL_PLATFORM,
|
||||
ACPI_IRQ_MODEL_GIC,
|
||||
ACPI_IRQ_MODEL_GIC_V5,
|
||||
ACPI_IRQ_MODEL_LPIC,
|
||||
ACPI_IRQ_MODEL_RINTC,
|
||||
ACPI_IRQ_MODEL_COUNT
|
||||
|
||||
@@ -27,12 +27,15 @@ int iort_register_domain_token(int trans_id, phys_addr_t base,
|
||||
struct fwnode_handle *fw_node);
|
||||
void iort_deregister_domain_token(int trans_id);
|
||||
struct fwnode_handle *iort_find_domain_token(int trans_id);
|
||||
int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id);
|
||||
struct fwnode_handle *iort_iwb_handle(u32 iwb_id);
|
||||
|
||||
#ifdef CONFIG_ACPI_IORT
|
||||
u32 iort_msi_map_id(struct device *dev, u32 id);
|
||||
u32 iort_msi_xlate(struct device *dev, u32 id, struct fwnode_handle **node);
|
||||
int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base);
|
||||
struct irq_domain *iort_get_device_domain(struct device *dev, u32 id,
|
||||
enum irq_domain_bus_token bus_token);
|
||||
int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *pa);
|
||||
void acpi_configure_pmsi_domain(struct device *dev);
|
||||
void iort_get_rmr_sids(struct fwnode_handle *iommu_fwnode,
|
||||
struct list_head *head);
|
||||
@@ -46,9 +49,15 @@ phys_addr_t acpi_iort_dma_get_max_cpu_address(void);
|
||||
#else
|
||||
static inline u32 iort_msi_map_id(struct device *dev, u32 id)
|
||||
{ return id; }
|
||||
static inline u32 iort_msi_xlate(struct device *dev, u32 id, struct fwnode_handle **node)
|
||||
{ return id; }
|
||||
static inline int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base)
|
||||
{ return -ENODEV; }
|
||||
static inline struct irq_domain *iort_get_device_domain(
|
||||
struct device *dev, u32 id, enum irq_domain_bus_token bus_token)
|
||||
{ return NULL; }
|
||||
static inline int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *pa)
|
||||
{ return -ENODEV; }
|
||||
static inline void acpi_configure_pmsi_domain(struct device *dev) { }
|
||||
static inline
|
||||
void iort_get_rmr_sids(struct fwnode_handle *iommu_fwnode, struct list_head *head) { }
|
||||
|
||||
@@ -265,6 +265,12 @@
|
||||
|
||||
#define GICV5_IWB_WENABLE_STATUSR_IDLE BIT(0)
|
||||
|
||||
#define GICV5_GSI_IC_TYPE GENMASK(31, 29)
|
||||
#define GICV5_GSI_IWB_TYPE 0x7
|
||||
|
||||
#define GICV5_GSI_IWB_FRAME_ID GENMASK(28, 16)
|
||||
#define GICV5_GSI_IWB_WIRE GENMASK(15, 0)
|
||||
|
||||
/*
|
||||
* Global Data structures and functions
|
||||
*/
|
||||
@@ -344,6 +350,7 @@ void __init gicv5_init_lpi_domain(void);
|
||||
void __init gicv5_free_lpi_domain(void);
|
||||
|
||||
int gicv5_irs_of_probe(struct device_node *parent);
|
||||
int gicv5_irs_acpi_probe(void);
|
||||
void gicv5_irs_remove(void);
|
||||
int gicv5_irs_enable(void);
|
||||
void gicv5_irs_its_probe(void);
|
||||
@@ -391,4 +398,5 @@ int gicv5_alloc_lpi(void);
|
||||
void gicv5_free_lpi(u32 lpi);
|
||||
|
||||
void __init gicv5_its_of_probe(struct device_node *parent);
|
||||
void __init gicv5_its_acpi_probe(void);
|
||||
#endif
|
||||
|
||||
@@ -257,7 +257,8 @@ static inline void irq_domain_set_pm_device(struct irq_domain *d, struct device
|
||||
|
||||
#ifdef CONFIG_IRQ_DOMAIN
|
||||
struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
|
||||
const char *name, phys_addr_t *pa);
|
||||
const char *name, phys_addr_t *pa,
|
||||
struct fwnode_handle *parent);
|
||||
|
||||
enum {
|
||||
IRQCHIP_FWNODE_REAL,
|
||||
@@ -267,18 +268,39 @@ enum {
|
||||
|
||||
static inline struct fwnode_handle *irq_domain_alloc_named_fwnode(const char *name)
|
||||
{
|
||||
return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL);
|
||||
return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, NULL);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct fwnode_handle *irq_domain_alloc_named_parented_fwnode(const char *name,
|
||||
struct fwnode_handle *parent)
|
||||
{
|
||||
return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, parent);
|
||||
}
|
||||
|
||||
static inline struct fwnode_handle *irq_domain_alloc_named_id_fwnode(const char *name, int id)
|
||||
{
|
||||
return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED_ID, id, name,
|
||||
NULL);
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct fwnode_handle *irq_domain_alloc_named_id_parented_fwnode(const char *name, int id,
|
||||
struct fwnode_handle *parent)
|
||||
{
|
||||
return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED_ID, id, name,
|
||||
NULL, parent);
|
||||
}
|
||||
|
||||
static inline struct fwnode_handle *irq_domain_alloc_fwnode(phys_addr_t *pa)
|
||||
{
|
||||
return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_REAL, 0, NULL, pa);
|
||||
return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_REAL, 0, NULL, pa, NULL);
|
||||
}
|
||||
|
||||
static inline struct fwnode_handle *irq_domain_alloc_parented_fwnode(phys_addr_t *pa,
|
||||
struct fwnode_handle *parent)
|
||||
{
|
||||
return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_REAL, 0, NULL, pa, parent);
|
||||
}
|
||||
|
||||
void irq_domain_free_fwnode(struct fwnode_handle *fwnode);
|
||||
|
||||
@@ -702,7 +702,8 @@ void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
|
||||
void pci_msi_mask_irq(struct irq_data *data);
|
||||
void pci_msi_unmask_irq(struct irq_data *data);
|
||||
u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev);
|
||||
u32 pci_msi_map_rid_ctlr_node(struct pci_dev *pdev, struct device_node **node);
|
||||
u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *pdev,
|
||||
struct fwnode_handle **node);
|
||||
struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev);
|
||||
void pci_msix_prepare_desc(struct irq_domain *domain, msi_alloc_info_t *arg,
|
||||
struct msi_desc *desc);
|
||||
|
||||
@@ -33,6 +33,7 @@ static void irq_domain_free_one_irq(struct irq_domain *domain, unsigned int virq
|
||||
|
||||
struct irqchip_fwid {
|
||||
struct fwnode_handle fwnode;
|
||||
struct fwnode_handle *parent;
|
||||
unsigned int type;
|
||||
char *name;
|
||||
phys_addr_t *pa;
|
||||
@@ -53,8 +54,16 @@ static const char *irqchip_fwnode_get_name(const struct fwnode_handle *fwnode)
|
||||
return fwid->name;
|
||||
}
|
||||
|
||||
static struct fwnode_handle *irqchip_fwnode_get_parent(const struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct irqchip_fwid *fwid = container_of(fwnode, struct irqchip_fwid, fwnode);
|
||||
|
||||
return fwid->parent;
|
||||
}
|
||||
|
||||
const struct fwnode_operations irqchip_fwnode_ops = {
|
||||
.get_name = irqchip_fwnode_get_name,
|
||||
.get_parent = irqchip_fwnode_get_parent,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(irqchip_fwnode_ops);
|
||||
|
||||
@@ -65,6 +74,7 @@ EXPORT_SYMBOL_GPL(irqchip_fwnode_ops);
|
||||
* @id: Optional user provided id if name != NULL
|
||||
* @name: Optional user provided domain name
|
||||
* @pa: Optional user-provided physical address
|
||||
* @parent: Optional parent fwnode_handle
|
||||
*
|
||||
* Allocate a struct irqchip_fwid, and return a pointer to the embedded
|
||||
* fwnode_handle (or NULL on failure).
|
||||
@@ -76,7 +86,8 @@ EXPORT_SYMBOL_GPL(irqchip_fwnode_ops);
|
||||
*/
|
||||
struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
|
||||
const char *name,
|
||||
phys_addr_t *pa)
|
||||
phys_addr_t *pa,
|
||||
struct fwnode_handle *parent)
|
||||
{
|
||||
struct irqchip_fwid *fwid;
|
||||
char *n;
|
||||
@@ -104,6 +115,7 @@ struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
|
||||
fwid->type = type;
|
||||
fwid->name = n;
|
||||
fwid->pa = pa;
|
||||
fwid->parent = parent;
|
||||
fwnode_init(&fwid->fwnode, &irqchip_fwnode_ops);
|
||||
return &fwid->fwnode;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user