@@ -88,6 +88,18 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);
acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context);
void acpi_scan_table_handler(u32 event, void *table, void *context);
+#ifdef CONFIG_ACPI_GENERIC_GSI
+int acpi_register_irq(struct device *dev, u32 hwirq, int trigger,
+ int polarity, struct fwnode_handle *fwnode);
+#else
+static inline
+int acpi_register_irq(struct device *dev, u32 hwirq, int trigger,
+ int polarity, struct fwnode_handle *fwnode)
+{
+ return acpi_register_gsi(dev, hwirq, trigger, polarity);
+}
+#endif
+
/* --------------------------------------------------------------------------
Device Node Initialization / Removal
-------------------------------------------------------------------------- */
@@ -10,6 +10,8 @@
#include <linux/irqdomain.h>
#include <linux/of.h>
+#include "internal.h"
+
enum acpi_irq_model_id acpi_irq_model;
static struct fwnode_handle *acpi_gsi_domain_id;
@@ -38,6 +40,24 @@ int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
}
EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
+int acpi_register_irq(struct device *dev, u32 hwirq, int trigger,
+ int polarity, struct fwnode_handle *fwnode)
+{
+ struct irq_fwspec fwspec;
+
+ if (!fwnode) {
+ dev_warn(dev, "No registered irqchip for hwirq %d\n", hwirq);
+ return -EINVAL;
+ }
+
+ fwspec.fwnode = fwnode;
+ fwspec.param[0] = hwirq;
+ fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
+ fwspec.param_count = 2;
+
+ return irq_create_fwspec_mapping(&fwspec);
+}
+
/**
* acpi_register_gsi() - Map a GSI to a linux IRQ number
* @dev: device for which IRQ has to be mapped
@@ -51,19 +71,7 @@ EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
int polarity)
{
- struct irq_fwspec fwspec;
-
- if (WARN_ON(!acpi_gsi_domain_id)) {
- pr_warn("GSI: No registered irqchip, giving up\n");
- return -EINVAL;
- }
-
- fwspec.fwnode = acpi_gsi_domain_id;
- fwspec.param[0] = gsi;
- fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
- fwspec.param_count = 2;
-
- return irq_create_fwspec_mapping(&fwspec);
+ return acpi_register_irq(dev, gsi, trigger, polarity, acpi_gsi_domain_id);
}
EXPORT_SYMBOL_GPL(acpi_register_gsi);
@@ -92,7 +100,7 @@ EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
* Return:
* The referenced device fwhandle or NULL on failure
*/
-static struct fwnode_handle *
+struct fwnode_handle *
acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source)
{
struct fwnode_handle *result;
@@ -22,6 +22,8 @@
#include <linux/slab.h>
#include <linux/interrupt.h>
+#include "internal.h"
+
#define PREFIX "ACPI: "
#define _COMPONENT ACPI_PCI_COMPONENT
@@ -410,6 +412,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
char *link = NULL;
char link_desc[16];
int rc;
+ struct fwnode_handle *rs_fwnode;
pin = dev->pin;
if (!pin) {
@@ -438,7 +441,8 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
gsi = acpi_pci_link_allocate_irq(entry->link,
entry->index,
&triggering, &polarity,
- &link);
+ &link,
+ &rs_fwnode);
else
gsi = entry->index;
} else
@@ -462,7 +466,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
return 0;
}
- rc = acpi_register_gsi(&dev->dev, gsi, triggering, polarity);
+ rc = acpi_register_irq(&dev->dev, gsi, triggering, polarity, rs_fwnode);
if (rc < 0) {
dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n",
pin_name(pin));
@@ -59,6 +59,7 @@ struct acpi_pci_link_irq {
u8 resource_type;
u8 possible_count;
u32 possible[ACPI_PCI_LINK_MAX_POSSIBLE];
+ struct acpi_resource_source resource_source;
u8 initialized:1;
u8 reserved:7;
};
@@ -120,6 +121,8 @@ static acpi_status acpi_pci_link_check_possible(struct acpi_resource *resource,
{
struct acpi_resource_extended_irq *p =
&resource->data.extended_irq;
+ struct acpi_resource_source *rs =
+ &link->irq.resource_source;
if (!p || !p->interrupt_count) {
printk(KERN_WARNING PREFIX
"Blank _PRS EXT IRQ resource\n");
@@ -140,6 +143,12 @@ static acpi_status acpi_pci_link_check_possible(struct acpi_resource *resource,
link->irq.triggering = p->triggering;
link->irq.polarity = p->polarity;
link->irq.resource_type = ACPI_RESOURCE_TYPE_EXTENDED_IRQ;
+ if (p->resource_source.string_length) {
+ rs->index = p->resource_source.index;
+ rs->string_length = p->resource_source.string_length;
+ rs->string_ptr = kstrdup(p->resource_source.string_ptr,
+ GFP_KERNEL);
+ }
break;
}
default:
@@ -326,7 +335,8 @@ static int acpi_pci_link_set(struct acpi_pci_link *link, int irq)
resource->res.data.extended_irq.shareable = ACPI_SHARED;
resource->res.data.extended_irq.interrupt_count = 1;
resource->res.data.extended_irq.interrupts[0] = irq;
- /* ignore resource_source, it's optional */
+ resource->res.data.extended_irq.resource_source =
+ link->irq.resource_source;
break;
default:
printk(KERN_ERR PREFIX "Invalid Resource_type %d\n", link->irq.resource_type);
@@ -612,7 +622,7 @@ static int acpi_pci_link_allocate(struct acpi_pci_link *link)
* failure: return -1
*/
int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering,
- int *polarity, char **name)
+ int *polarity, char **name, struct fwnode_handle **rs_fwnode)
{
int result;
struct acpi_device *device;
@@ -656,6 +666,9 @@ int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering,
*polarity = link->irq.polarity;
if (name)
*name = acpi_device_bid(link->device);
+ if (rs_fwnode)
+ *rs_fwnode = acpi_get_irq_source_fwhandle(&link->irq.resource_source);
+
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Link %s is referenced\n",
acpi_device_bid(link->device)));
@@ -68,7 +68,7 @@
int acpi_irq_penalty_init(void);
int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering,
- int *polarity, char **name);
+ int *polarity, char **name, struct fwnode_handle **rs_fwnode);
int acpi_pci_link_free_irq(acpi_handle handle);
/* ACPI PCI Device Binding */
@@ -336,6 +336,16 @@ struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags,
const struct irq_domain_ops *ops,
void *host_data);
+#ifdef CONFIG_ACPI_GENERIC_GSI
+struct fwnode_handle *acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source);
+#else
+static inline
+struct fwnode_handle *acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source)
+{
+ return NULL;
+}
+#endif
+
#ifdef CONFIG_X86_IO_APIC
extern int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity);
#else
The ResourceSource field of an Extended Interrupt Descriptor was ignored when the driver is parsing _PRS method of PNP0C0F PCI Interrupt Link devices, which means PCI INTx would be always registered under the GSI domain. This patch introduces stacked IRQ domain support to PCI Interrupt Link devices for ACPI. With this support, we can populate the ResourceSource field in _PRS method of PCI Interrupt Link devices to refer to a device object that describes an interrupt controller as the following examples: Device (IXIU) { ... } Device(LINKA) { Name(_HID, EISAID("PNP0C0F")) Name(_PRS, ResourceTemplate(){ Interrupt(ResourceProducer, Level, ActiveHigh, Exclusive, 0, "\\SB.IXIU") { 60 } }) ... } Signed-off-by: Chen Baozi <chenbaozi@phytium.com.cn> --- v3->v4: Include "internal.h" in drivers/acpi/irq.c to avoid the warning because of lacking prototype for 'acpi_register_irq' drivers/acpi/internal.h | 12 ++++++++++++ drivers/acpi/irq.c | 36 ++++++++++++++++++++++-------------- drivers/acpi/pci_irq.c | 8 ++++++-- drivers/acpi/pci_link.c | 17 +++++++++++++++-- include/acpi/acpi_drivers.h | 2 +- include/linux/acpi.h | 10 ++++++++++ 6 files changed, 66 insertions(+), 19 deletions(-)