Skip to content

Commit

Permalink
irqchip/gic-v3: Add ACPI support for GICv3/4 initialization
Browse files Browse the repository at this point in the history
With the refator of gic_of_init(), GICv3/4 can be initialized
by gic_init_bases() with gic distributor base address and gic
redistributor region(s).

So get the redistributor region base addresses from MADT GIC
redistributor subtable, and the distributor base address from
GICD subtable to init GICv3 irqchip in ACPI way.

Note: GIC redistributor base address may also be provided in
GICC structures on systems supporting GICv3 and above if the GIC
Redistributors are not in the always-on power domain, this
patch didn't implement such feature yet.

Acked-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Tomasz Nowicki <tn@semihalf.com>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
  • Loading branch information
Tomasz Nowicki authored and Marc Zyngier committed Mar 9, 2016
1 parent db57d74 commit ffa7d61
Showing 1 changed file with 137 additions and 0 deletions.
137 changes: 137 additions & 0 deletions drivers/irqchip/irq-gic-v3.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <linux/acpi.h>
#include <linux/cpu.h>
#include <linux/cpu_pm.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
Expand Down Expand Up @@ -764,6 +766,15 @@ static int gic_irq_domain_translate(struct irq_domain *d,
return 0;
}

if (is_fwnode_irqchip(fwspec->fwnode)) {
if(fwspec->param_count != 2)
return -EINVAL;

*hwirq = fwspec->param[0];
*type = fwspec->param[1];
return 0;
}

return -EINVAL;
}

Expand Down Expand Up @@ -951,3 +962,129 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
}

IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);

#ifdef CONFIG_ACPI
static struct redist_region *redist_regs __initdata;
static u32 nr_redist_regions __initdata;

static int __init
gic_acpi_parse_madt_redist(struct acpi_subtable_header *header,
const unsigned long end)
{
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) {
pr_err("Couldn't map GICR region @%llx\n", redist->base_address);
return -ENOMEM;
}

redist_regs[count].phys_base = redist->base_address;
redist_regs[count].redist_base = redist_base;
count++;
return 0;
}

static int __init gic_acpi_match_gicr(struct acpi_subtable_header *header,
const unsigned long end)
{
/* Subtable presence means that redist exists, that's it */
return 0;
}

static bool __init acpi_validate_gic_table(struct acpi_subtable_header *header,
struct acpi_probe_entry *ape)
{
struct acpi_madt_generic_distributor *dist;
int count;

dist = (struct acpi_madt_generic_distributor *)header;
if (dist->version != ape->driver_data)
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);
if (count <= 0)
return false;

nr_redist_regions = count;
return true;
}

#define ACPI_GICV3_DIST_MEM_SIZE (SZ_64K)

static int __init
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;

/* Get distributor base address */
dist = (struct acpi_madt_generic_distributor *)header;
dist_base = ioremap(dist->base_address, ACPI_GICV3_DIST_MEM_SIZE);
if (!dist_base) {
pr_err("Unable to map GICD registers\n");
return -ENOMEM;
}

err = gic_validate_dist_version(dist_base);
if (err) {
pr_err("No distributor detected at @%p, giving up", dist_base);
goto out_dist_unmap;
}

redist_regs = kzalloc(sizeof(*redist_regs) * nr_redist_regions,
GFP_KERNEL);
if (!redist_regs) {
err = -ENOMEM;
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;
goto out_redist_unmap;
}

domain_handle = irq_domain_alloc_fwnode(dist_base);
if (!domain_handle) {
err = -ENOMEM;
goto out_redist_unmap;
}

err = gic_init_bases(dist_base, redist_regs, nr_redist_regions, 0,
domain_handle);
if (err)
goto out_fwhandle_free;

acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
return 0;

out_fwhandle_free:
irq_domain_free_fwnode(domain_handle);
out_redist_unmap:
for (i = 0; i < nr_redist_regions; i++)
if (redist_regs[i].redist_base)
iounmap(redist_regs[i].redist_base);
kfree(redist_regs);
out_dist_unmap:
iounmap(dist_base);
return err;
}
IRQCHIP_ACPI_DECLARE(gic_v3, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
acpi_validate_gic_table, ACPI_MADT_GIC_VERSION_V3,
gic_acpi_init);
IRQCHIP_ACPI_DECLARE(gic_v4, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
acpi_validate_gic_table, ACPI_MADT_GIC_VERSION_V4,
gic_acpi_init);
IRQCHIP_ACPI_DECLARE(gic_v3_or_v4, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
acpi_validate_gic_table, ACPI_MADT_GIC_VERSION_NONE,
gic_acpi_init);
#endif

0 comments on commit ffa7d61

Please sign in to comment.