|
@@ -40,6 +40,7 @@
|
|
|
struct redist_region {
|
|
|
void __iomem *redist_base;
|
|
|
phys_addr_t phys_base;
|
|
|
+ bool single_redist;
|
|
|
};
|
|
|
|
|
|
struct gic_chip_data {
|
|
@@ -436,6 +437,9 @@ static int gic_populate_rdist(void)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+ if (gic_data.redist_regions[i].single_redist)
|
|
|
+ break;
|
|
|
+
|
|
|
if (gic_data.redist_stride) {
|
|
|
ptr += gic_data.redist_stride;
|
|
|
} else {
|
|
@@ -964,8 +968,21 @@ out_unmap_dist:
|
|
|
IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);
|
|
|
|
|
|
#ifdef CONFIG_ACPI
|
|
|
+static void __iomem *dist_base;
|
|
|
static struct redist_region *redist_regs __initdata;
|
|
|
static u32 nr_redist_regions __initdata;
|
|
|
+static bool single_redist;
|
|
|
+
|
|
|
+static void __init
|
|
|
+gic_acpi_register_redist(phys_addr_t phys_base, void __iomem *redist_base)
|
|
|
+{
|
|
|
+ static int count = 0;
|
|
|
+
|
|
|
+ redist_regs[count].phys_base = phys_base;
|
|
|
+ redist_regs[count].redist_base = redist_base;
|
|
|
+ redist_regs[count].single_redist = single_redist;
|
|
|
+ count++;
|
|
|
+}
|
|
|
|
|
|
static int __init
|
|
|
gic_acpi_parse_madt_redist(struct acpi_subtable_header *header,
|
|
@@ -974,7 +991,6 @@ gic_acpi_parse_madt_redist(struct acpi_subtable_header *header,
|
|
|
struct acpi_madt_generic_redistributor *redist =
|
|
|
(struct acpi_madt_generic_redistributor *)header;
|
|
|
void __iomem *redist_base;
|
|
|
- static int count = 0;
|
|
|
|
|
|
redist_base = ioremap(redist->base_address, redist->length);
|
|
|
if (!redist_base) {
|
|
@@ -982,12 +998,49 @@ gic_acpi_parse_madt_redist(struct acpi_subtable_header *header,
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
|
|
|
- redist_regs[count].phys_base = redist->base_address;
|
|
|
- redist_regs[count].redist_base = redist_base;
|
|
|
- count++;
|
|
|
+ gic_acpi_register_redist(redist->base_address, redist_base);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int __init
|
|
|
+gic_acpi_parse_madt_gicc(struct acpi_subtable_header *header,
|
|
|
+ const unsigned long end)
|
|
|
+{
|
|
|
+ struct acpi_madt_generic_interrupt *gicc =
|
|
|
+ (struct acpi_madt_generic_interrupt *)header;
|
|
|
+ u32 reg = readl_relaxed(dist_base + GICD_PIDR2) & GIC_PIDR2_ARCH_MASK;
|
|
|
+ u32 size = reg == GIC_PIDR2_ARCH_GICv4 ? SZ_64K * 4 : SZ_64K * 2;
|
|
|
+ void __iomem *redist_base;
|
|
|
+
|
|
|
+ redist_base = ioremap(gicc->gicr_base_address, size);
|
|
|
+ if (!redist_base)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ gic_acpi_register_redist(gicc->gicr_base_address, redist_base);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int __init gic_acpi_collect_gicr_base(void)
|
|
|
+{
|
|
|
+ acpi_tbl_entry_handler redist_parser;
|
|
|
+ enum acpi_madt_type type;
|
|
|
+
|
|
|
+ if (single_redist) {
|
|
|
+ type = ACPI_MADT_TYPE_GENERIC_INTERRUPT;
|
|
|
+ redist_parser = gic_acpi_parse_madt_gicc;
|
|
|
+ } else {
|
|
|
+ type = ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR;
|
|
|
+ redist_parser = gic_acpi_parse_madt_redist;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Collect redistributor base addresses in GICR entries */
|
|
|
+ if (acpi_table_parse_madt(type, redist_parser, 0) > 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ pr_info("No valid GICR entries exist\n");
|
|
|
+ return -ENODEV;
|
|
|
+}
|
|
|
+
|
|
|
static int __init gic_acpi_match_gicr(struct acpi_subtable_header *header,
|
|
|
const unsigned long end)
|
|
|
{
|
|
@@ -995,6 +1048,46 @@ static int __init gic_acpi_match_gicr(struct acpi_subtable_header *header,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int __init gic_acpi_match_gicc(struct acpi_subtable_header *header,
|
|
|
+ const unsigned long end)
|
|
|
+{
|
|
|
+ struct acpi_madt_generic_interrupt *gicc =
|
|
|
+ (struct acpi_madt_generic_interrupt *)header;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If GICC is enabled and has valid gicr base address, then it means
|
|
|
+ * GICR base is presented via GICC
|
|
|
+ */
|
|
|
+ if ((gicc->flags & ACPI_MADT_ENABLED) && gicc->gicr_base_address)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return -ENODEV;
|
|
|
+}
|
|
|
+
|
|
|
+static int __init gic_acpi_count_gicr_regions(void)
|
|
|
+{
|
|
|
+ int count;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Count how many redistributor regions we have. It is not allowed
|
|
|
+ * to mix redistributor description, GICR and GICC subtables have to be
|
|
|
+ * mutually exclusive.
|
|
|
+ */
|
|
|
+ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR,
|
|
|
+ gic_acpi_match_gicr, 0);
|
|
|
+ if (count > 0) {
|
|
|
+ single_redist = false;
|
|
|
+ return count;
|
|
|
+ }
|
|
|
+
|
|
|
+ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
|
|
|
+ gic_acpi_match_gicc, 0);
|
|
|
+ if (count > 0)
|
|
|
+ single_redist = true;
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
static bool __init acpi_validate_gic_table(struct acpi_subtable_header *header,
|
|
|
struct acpi_probe_entry *ape)
|
|
|
{
|
|
@@ -1006,8 +1099,7 @@ static bool __init acpi_validate_gic_table(struct acpi_subtable_header *header,
|
|
|
return false;
|
|
|
|
|
|
/* We need to do that exercise anyway, the sooner the better */
|
|
|
- count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR,
|
|
|
- gic_acpi_match_gicr, 0);
|
|
|
+ count = gic_acpi_count_gicr_regions();
|
|
|
if (count <= 0)
|
|
|
return false;
|
|
|
|
|
@@ -1022,8 +1114,7 @@ gic_acpi_init(struct acpi_subtable_header *header, const unsigned long end)
|
|
|
{
|
|
|
struct acpi_madt_generic_distributor *dist;
|
|
|
struct fwnode_handle *domain_handle;
|
|
|
- void __iomem *dist_base;
|
|
|
- int i, err, count;
|
|
|
+ int i, err;
|
|
|
|
|
|
/* Get distributor base address */
|
|
|
dist = (struct acpi_madt_generic_distributor *)header;
|
|
@@ -1046,12 +1137,9 @@ gic_acpi_init(struct acpi_subtable_header *header, const unsigned long end)
|
|
|
goto out_dist_unmap;
|
|
|
}
|
|
|
|
|
|
- count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR,
|
|
|
- gic_acpi_parse_madt_redist, 0);
|
|
|
- if (count <= 0) {
|
|
|
- err = -ENODEV;
|
|
|
+ err = gic_acpi_collect_gicr_base();
|
|
|
+ if (err)
|
|
|
goto out_redist_unmap;
|
|
|
- }
|
|
|
|
|
|
domain_handle = irq_domain_alloc_fwnode(dist_base);
|
|
|
if (!domain_handle) {
|