Skip to content

Commit

Permalink
acpi/irq: Add stacked IRQ domain support to PCI interrupt link
Browse files Browse the repository at this point in the history
Some PCIe designs require software to do extra acknowledgements for
legacy INTx interrupts. If the driver is written only for device tree,
things are simple. In that case, a new driver can be written under
driver/pci/controller/ with a DT node of PCIe host written like:

  pcie {
    ...
    interrupt-map = <0 0 0  1  &pcie_intc 0>,
                    <0 0 0  2  &pcie_intc 1>,
                    <0 0 0  3  &pcie_intc 2>,
                    <0 0 0  4  &pcie_intc 3>;

    pcie_intc: legacy-interrupt-controller {
      interrupt-controller;
      #interrupt-cells = <1>;
      interrupt-parent = <&gic>;
      interrupts = <0 226 4>;
    };
  };

Similar designs can be found on Aardvark, MediaTek Gen2 and Socionext
UniPhier PCIe controller at the moment. Essentially, those designs are
supported by inserting an extra interrupt controller between PCIe host
and GIC and parse the topology in a DT-based PCI controller driver.
As we turn to ACPI, All the PCIe hosts are described the same ID of
"PNP0A03" and share driver/acpi/pci_root.c. It comes to be a problem
to make this kind of PCI INTx work under ACPI.

Therefore, we introduce an stacked IRQ domain support to PCI interrupt
link for ACPI. With this support, we can populate the ResourceSource
to refer to a device object that describes an interrupt controller.
That would allow us to refer to a dedicated driver which implements
the logic needed to manage the interrupt state. With this patch,
those PCI interrupt links can be supported by describing the INTx
in ACPI table as the following example:

  Device (IXIU) {
    ...
  }

  Device(LINKA) {
    Name(_HID, EISAID("PNP0C0F"))
    Name(_PRS, ResourceTemplate(){
      Interrupt(ResourceProducer, Level, ActiveHigh, Exclusive, 0, "\\SB.IXIU")
        { 60 }
    })
    ...
  }

  Device(PCI0) {
    ...
    Name(_PRT, Package{
      Package{ 0x0000FFFF, 0, LINKA, 0 }
      ...
    })
  }

Signed-off-by: Chen Baozi <chenbaozi@phytium.com.cn>
  • Loading branch information
chenbaozi-ft authored and intel-lab-lkp committed Nov 17, 2020
1 parent 4e4b3ac commit 76f0a22
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 6 deletions.
22 changes: 21 additions & 1 deletion drivers/acpi/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,25 @@ void acpi_unregister_gsi(u32 gsi)
}
EXPORT_SYMBOL_GPL(acpi_unregister_gsi);

int acpi_register_irq(struct device *dev, u32 irq, int trigger,
int polarity, struct fwnode_handle *domain_id)
{
struct irq_fwspec fwspec;

if (WARN_ON(!domain_id)) {
pr_warn("GSI: No registered irqchip, giving up\n");
return -EINVAL;
}

fwspec.fwnode = domain_id;
fwspec.param[0] = irq;
fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
fwspec.param_count = 2;

return irq_create_fwspec_mapping(&fwspec);
}
EXPORT_SYMBOL_GPL(acpi_register_irq);

/**
* acpi_get_irq_source_fwhandle() - Retrieve fwhandle from IRQ resource source.
* @source: acpi_resource_source to use for the lookup.
Expand All @@ -92,7 +111,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;
Expand All @@ -115,6 +134,7 @@ acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source)
acpi_bus_put_acpi_device(device);
return result;
}
EXPORT_SYMBOL_GPL(acpi_get_irq_source_fwhandle);

/*
* Context for the resource walk used to lookup IRQ resources.
Expand Down
6 changes: 4 additions & 2 deletions drivers/acpi/pci_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
char *link = NULL;
char link_desc[16];
int rc;
struct fwnode_handle *irq_domain;

pin = dev->pin;
if (!pin) {
Expand Down Expand Up @@ -438,7 +439,8 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
gsi = acpi_pci_link_allocate_irq(entry->link,
entry->index,
&triggering, &polarity,
&link);
&link,
&irq_domain);
else
gsi = entry->index;
} else
Expand All @@ -462,7 +464,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, irq_domain);
if (rc < 0) {
dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n",
pin_name(pin));
Expand Down
17 changes: 15 additions & 2 deletions drivers/acpi/pci_link.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down Expand Up @@ -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");
Expand All @@ -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 = kmalloc(rs->string_length, GFP_KERNEL);
strcpy(rs->string_ptr, p->resource_source.string_ptr);
}
break;
}
default:
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 **irq_domain)
{
int result;
struct acpi_device *device;
Expand Down Expand Up @@ -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 (irq_domain)
*irq_domain = 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)));
Expand Down
2 changes: 1 addition & 1 deletion include/acpi/acpi_drivers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 **irq_domain);
int acpi_pci_link_free_irq(acpi_handle handle);

/* ACPI PCI Device Binding (pci_bind.c) */
Expand Down
4 changes: 4 additions & 0 deletions include/linux/acpi.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,8 @@ extern int sbf_port;
extern unsigned long acpi_realmode_flags;

int acpi_register_gsi (struct device *dev, u32 gsi, int triggering, int polarity);
int acpi_register_irq(struct device *dev, u32 gsi, int trigger,
int polarity, struct fwnode_handle *domain_id);
int acpi_gsi_to_irq (u32 gsi, unsigned int *irq);
int acpi_isa_irq_to_gsi (unsigned isa_irq, u32 *gsi);

Expand All @@ -336,6 +338,8 @@ struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags,
const struct irq_domain_ops *ops,
void *host_data);

struct fwnode_handle *acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source);

#ifdef CONFIG_X86_IO_APIC
extern int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity);
#else
Expand Down

0 comments on commit 76f0a22

Please sign in to comment.