[prev in list] [next in list] [prev in thread] [next in thread] 

List:       linaro-acpi
Subject:    [Linaro-acpi] [internal RFC 6/6] irqchip / GICv3 / ACPI: Add GICR support via GICC structures
From:       Hanjun Guo <hanjun.guo () linaro ! org>
Date:       2015-08-17 13:23:34
Message-ID: 1439817814-3873-7-git-send-email-hanjun.guo () linaro ! org
[Download RAW message or body]

On systems supporting GICv3 and above, in MADT GICC structures, the
field of GICR Base Address holds the 64-bit physical address of the
associated Redistributor if the GIC Redistributors are not in the
always-on power domain, so instead of init GICR regions via GIC
redistributor structure(s), init it with GICR base address in GICC
structures in that case.

As GICR base in MADT GICC is another way to indicate the GIC version
is 3 or 4, add its support to find out the GIC versions.

Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
---
 drivers/acpi/irq.c           |  26 +++++++++
 drivers/irqchip/irq-gic-v3.c | 122 ++++++++++++++++++++++++++++++++++---------
 2 files changed, 124 insertions(+), 24 deletions(-)

diff --git a/drivers/acpi/irq.c b/drivers/acpi/irq.c
index cb23cb2..bc963f7 100644
--- a/drivers/acpi/irq.c
+++ b/drivers/acpi/irq.c
@@ -37,6 +37,27 @@ acpi_match_gic_redist(struct acpi_subtable_header *header,
 	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;
+
+	gicc = (struct acpi_madt_generic_interrupt *)header;
+
+	if (BAD_MADT_GICC_ENTRY(gicc, end))
+	return -EINVAL;
+
+	/*
+	 * If GICC is enabled but has no valid gicr base address, then it
+	 * means GICR base is not presented via GICC
+	 */
+	if ((gicc->flags & ACPI_MADT_ENABLED) && !gicc->gicr_base_address)
+	return -ENODEV;
+
+	return 0;
+}
+
 static bool __init
 acpi_gic_redist_is_present(void)
 {
@@ -44,6 +65,11 @@ acpi_gic_redist_is_present(void)
 
 	count  =  acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR,
 					acpi_match_gic_redist, 0);
+	if (count > 0)
+		return true;
+
+	count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
+				gic_acpi_parse_madt_gicc, 0);
 	return count > 0;
 }
 
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 51547c9..5596846 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -39,6 +39,7 @@
 struct redist_region {
 	void __iomem		*redist_base;
 	phys_addr_t		phys_base;
+	bool			single_redist;
 };
 
 struct gic_chip_data {
@@ -429,6 +430,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 {
@@ -906,22 +910,35 @@ static phys_addr_t dist_phys_base __initdata;
 
 static int __init
 gic_acpi_match_redist(struct acpi_subtable_header *header,
-		     const unsigned long end)
+		      const unsigned long end)
 {
 	struct acpi_madt_generic_redistributor *redist;
 
-	if (BAD_MADT_ENTRY(header, end))
+	redist = (struct acpi_madt_generic_redistributor *)header;
+	if (BAD_MADT_ENTRY(redist, end) || !redist->base_address)
 		return -EINVAL;
 
-	redist = (struct acpi_madt_generic_redistributor *)header;
-	if (!redist->base_address)
+	return 0;
+}
+
+static int __init
+gic_acpi_match_gicc(struct acpi_subtable_header *header,
+		    const unsigned long end)
+{
+	struct acpi_madt_generic_interrupt *gicc;
+
+	gicc = (struct acpi_madt_generic_interrupt *)header;
+	if (BAD_MADT_GICC_ENTRY(gicc, end))
 		return -EINVAL;
 
+	if ((gicc->flags & ACPI_MADT_ENABLED) && !gicc->gicr_base_address)
+		return -ENODEV;
+
 	return 0;
 }
 
 static int __init
-gic_acpi_register_redist(phys_addr_t phys_base, u64 size)
+gic_acpi_register_redist(phys_addr_t phys_base, u64 size, bool flag)
 {
 	void __iomem *redist_base;
 
@@ -933,6 +950,7 @@ gic_acpi_register_redist(phys_addr_t phys_base, u64 size)
 
 	redist_regs[nr_redist_regions].phys_base = phys_base;
 	redist_regs[nr_redist_regions].redist_base = redist_base;
+	redist_regs[nr_redist_regions].single_redist = flag;
 	nr_redist_regions++;
 	return 0;
 }
@@ -944,7 +962,29 @@ gic_acpi_parse_madt_redist(struct acpi_subtable_header *header,
 	struct acpi_madt_generic_redistributor *redist;
 
 	redist = (struct acpi_madt_generic_redistributor *)header;
-	return gic_acpi_register_redist(redist->base_address, redist->length);
+	return gic_acpi_register_redist(redist->base_address, redist->length,
+					false);
+}
+
+static int __init
+gic_acpi_parse_madt_gicc(struct acpi_subtable_header *header,
+			const unsigned long end)
+{
+	struct acpi_madt_generic_interrupt *gicc;
+	void __iomem *redist_base;
+	u64 typer;
+	u32 size;
+
+	gicc = (struct acpi_madt_generic_interrupt *)header;
+	redist_base = ioremap(gicc->gicr_base_address, SZ_64K * 2);
+	if (!redist_base)
+		return -ENOMEM;
+
+	typer = readq_relaxed(redist_base + GICR_TYPER);
+	/* don't map reserved page as it's buggy to access it */
+	size = (typer & GICR_TYPER_VLPIS) ? SZ_64K * 3 : SZ_64K * 2;
+	iounmap(redist_base);
+	return gic_acpi_register_redist(gicc->gicr_base_address, size, true);
 }
 
 static int __init
@@ -988,6 +1028,54 @@ static int gic_acpi_gsi_desc_populate(struct acpi_gsi_descriptor *data,
 }
 
 static int __init
+gic_acpi_count_gicr_regions(struct acpi_table_header *table, int *count)
+{
+	/* Count how many redistributor regions we have */
+	*count = acpi_parse_entries(ACPI_SIG_MADT,
+				sizeof(struct acpi_table_madt),
+				gic_acpi_match_redist, table,
+				ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR, 0);
+	if (*count > 0)
+		return 0;
+
+	*count = acpi_parse_entries(ACPI_SIG_MADT,
+				sizeof(struct acpi_table_madt),
+				gic_acpi_match_gicc, table,
+				ACPI_MADT_TYPE_GENERIC_INTERRUPT, 0);
+
+	if (*count > 0)
+		return 0;
+
+	pr_err("No valid GICR entries\n");
+	return -ENODEV;
+}
+
+static int __init
+gic_acpi_collect_gicr_base(struct acpi_table_header *table)
+{
+	int count;
+
+	/* Collect redistributor base addresses in GICR entries */
+	count = acpi_parse_entries(ACPI_SIG_MADT,
+			sizeof(struct acpi_table_madt),
+			gic_acpi_parse_madt_redist, table,
+			ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR, 0);
+	if (count > 0)
+		return 0;
+
+	/* Collect redistributor base addresses in GICC entries */
+	count = acpi_parse_entries(ACPI_SIG_MADT,
+			sizeof(struct acpi_table_madt),
+			gic_acpi_parse_madt_gicc, table,
+			ACPI_MADT_TYPE_GENERIC_INTERRUPT, 0);
+	if (count > 0)
+		return 0;
+
+	pr_info("No valid GICR entries exist\n");
+	return -ENODEV;
+}
+
+static int __init
 gic_acpi_init(struct acpi_table_header *table)
 {
 	int count, i, err = 0;
@@ -1018,16 +1106,9 @@ gic_acpi_init(struct acpi_table_header *table)
 		goto out_dist_unmap;
 	}
 
-	/* Count how many redistributor regions we have */
-	count = acpi_parse_entries(ACPI_SIG_MADT,
-				sizeof(struct acpi_table_madt),
-				gic_acpi_match_redist, table,
-				ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR, 0);
-	if (count <= 0) {
-		pr_err("No valid GICR entries\n");
-		err = -EINVAL;
+	err = gic_acpi_count_gicr_regions(table, &count);
+	if (err)
 		goto out_dist_unmap;
-	}
 
 	redist_regs = kzalloc(sizeof(*redist_regs) * count, GFP_KERNEL);
 	if (!redist_regs) {
@@ -1035,16 +1116,9 @@ gic_acpi_init(struct acpi_table_header *table)
 		goto out_dist_unmap;
 	}
 
-	/* Collect redistributor base addresses */
-	count = acpi_parse_entries(ACPI_SIG_MADT,
-			sizeof(struct acpi_table_madt),
-			gic_acpi_parse_madt_redist, table,
-			ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR, 0);
-	if (count <= 0) {
-		pr_info("No valid GICR entries exist\n");
-		err = -EINVAL;
+	err = gic_acpi_collect_gicr_base(table);
+	if (err)
 		goto out_redist_unmap;
-	}
 
 	err = gic_init_bases(dist_base, redist_regs, nr_redist_regions, 0,
 			     (void *)dist_phys_base);
-- 
1.9.1

_______________________________________________
Linaro-acpi mailing list
Linaro-acpi@lists.linaro.org
https://lists.linaro.org/mailman/listinfo/linaro-acpi

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic