irqchip: GICv3: rework redistributor structure

The basic GICv3 driver has almost no use for the redistributor
(other than the basic per-CPU interrupts), but the ITS needs
a lot more from them.

As such, rework the set of data structures. The behaviour of the
GICv3 driver is otherwise unaffected.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Link: https://lkml.kernel.org/r/1416839720-18400-4-git-send-email-marc.zyngier@arm.com
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
This commit is contained in:
Marc Zyngier 2014-11-24 14:35:10 +00:00 committed by Jason Cooper
parent 443acc4f37
commit f5c1434c21
2 changed files with 59 additions and 29 deletions

View File

@ -34,20 +34,25 @@
#include "irq-gic-common.h" #include "irq-gic-common.h"
#include "irqchip.h" #include "irqchip.h"
struct redist_region {
void __iomem *redist_base;
phys_addr_t phys_base;
};
struct gic_chip_data { struct gic_chip_data {
void __iomem *dist_base; void __iomem *dist_base;
void __iomem **redist_base; struct redist_region *redist_regions;
void __iomem * __percpu *rdist; struct rdists rdists;
struct irq_domain *domain; struct irq_domain *domain;
u64 redist_stride; u64 redist_stride;
u32 redist_regions; u32 nr_redist_regions;
unsigned int irq_nr; unsigned int irq_nr;
}; };
static struct gic_chip_data gic_data __read_mostly; static struct gic_chip_data gic_data __read_mostly;
#define gic_data_rdist() (this_cpu_ptr(gic_data.rdist)) #define gic_data_rdist() (this_cpu_ptr(gic_data.rdists.rdist))
#define gic_data_rdist_rd_base() (*gic_data_rdist()) #define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base)
#define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K) #define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K)
/* Our default, arbitrary priority value. Linux only uses one anyway. */ /* Our default, arbitrary priority value. Linux only uses one anyway. */
@ -333,8 +338,8 @@ static int gic_populate_rdist(void)
MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 | MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 |
MPIDR_AFFINITY_LEVEL(mpidr, 0)); MPIDR_AFFINITY_LEVEL(mpidr, 0));
for (i = 0; i < gic_data.redist_regions; i++) { for (i = 0; i < gic_data.nr_redist_regions; i++) {
void __iomem *ptr = gic_data.redist_base[i]; void __iomem *ptr = gic_data.redist_regions[i].redist_base;
u32 reg; u32 reg;
reg = readl_relaxed(ptr + GICR_PIDR2) & GIC_PIDR2_ARCH_MASK; reg = readl_relaxed(ptr + GICR_PIDR2) & GIC_PIDR2_ARCH_MASK;
@ -347,10 +352,13 @@ static int gic_populate_rdist(void)
do { do {
typer = readq_relaxed(ptr + GICR_TYPER); typer = readq_relaxed(ptr + GICR_TYPER);
if ((typer >> 32) == aff) { if ((typer >> 32) == aff) {
u64 offset = ptr - gic_data.redist_regions[i].redist_base;
gic_data_rdist_rd_base() = ptr; gic_data_rdist_rd_base() = ptr;
pr_info("CPU%d: found redistributor %llx @%p\n", gic_data_rdist()->phys_base = gic_data.redist_regions[i].phys_base + offset;
pr_info("CPU%d: found redistributor %llx region %d:%pa\n",
smp_processor_id(), smp_processor_id(),
(unsigned long long)mpidr, ptr); (unsigned long long)mpidr,
i, &gic_data_rdist()->phys_base);
return 0; return 0;
} }
@ -673,9 +681,10 @@ static const struct irq_domain_ops gic_irq_domain_ops = {
static int __init gic_of_init(struct device_node *node, struct device_node *parent) static int __init gic_of_init(struct device_node *node, struct device_node *parent)
{ {
void __iomem *dist_base; void __iomem *dist_base;
void __iomem **redist_base; struct redist_region *rdist_regs;
u64 redist_stride; u64 redist_stride;
u32 redist_regions; u32 nr_redist_regions;
u32 typer;
u32 reg; u32 reg;
int gic_irqs; int gic_irqs;
int err; int err;
@ -696,48 +705,54 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
goto out_unmap_dist; goto out_unmap_dist;
} }
if (of_property_read_u32(node, "#redistributor-regions", &redist_regions)) if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions))
redist_regions = 1; nr_redist_regions = 1;
redist_base = kzalloc(sizeof(*redist_base) * redist_regions, GFP_KERNEL); rdist_regs = kzalloc(sizeof(*rdist_regs) * nr_redist_regions, GFP_KERNEL);
if (!redist_base) { if (!rdist_regs) {
err = -ENOMEM; err = -ENOMEM;
goto out_unmap_dist; goto out_unmap_dist;
} }
for (i = 0; i < redist_regions; i++) { for (i = 0; i < nr_redist_regions; i++) {
redist_base[i] = of_iomap(node, 1 + i); struct resource res;
if (!redist_base[i]) { int ret;
ret = of_address_to_resource(node, 1 + i, &res);
rdist_regs[i].redist_base = of_iomap(node, 1 + i);
if (ret || !rdist_regs[i].redist_base) {
pr_err("%s: couldn't map region %d\n", pr_err("%s: couldn't map region %d\n",
node->full_name, i); node->full_name, i);
err = -ENODEV; err = -ENODEV;
goto out_unmap_rdist; goto out_unmap_rdist;
} }
rdist_regs[i].phys_base = res.start;
} }
if (of_property_read_u64(node, "redistributor-stride", &redist_stride)) if (of_property_read_u64(node, "redistributor-stride", &redist_stride))
redist_stride = 0; redist_stride = 0;
gic_data.dist_base = dist_base; gic_data.dist_base = dist_base;
gic_data.redist_base = redist_base; gic_data.redist_regions = rdist_regs;
gic_data.redist_regions = redist_regions; gic_data.nr_redist_regions = nr_redist_regions;
gic_data.redist_stride = redist_stride; gic_data.redist_stride = redist_stride;
/* /*
* Find out how many interrupts are supported. * Find out how many interrupts are supported.
* The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI) * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)
*/ */
gic_irqs = readl_relaxed(gic_data.dist_base + GICD_TYPER) & 0x1f; typer = readl_relaxed(gic_data.dist_base + GICD_TYPER);
gic_irqs = (gic_irqs + 1) * 32; gic_data.rdists.id_bits = GICD_TYPER_ID_BITS(typer);
gic_irqs = GICD_TYPER_IRQS(typer);
if (gic_irqs > 1020) if (gic_irqs > 1020)
gic_irqs = 1020; gic_irqs = 1020;
gic_data.irq_nr = gic_irqs; gic_data.irq_nr = gic_irqs;
gic_data.domain = irq_domain_add_tree(node, &gic_irq_domain_ops, gic_data.domain = irq_domain_add_tree(node, &gic_irq_domain_ops,
&gic_data); &gic_data);
gic_data.rdist = alloc_percpu(typeof(*gic_data.rdist)); gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist));
if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdist)) { if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) {
err = -ENOMEM; err = -ENOMEM;
goto out_free; goto out_free;
} }
@ -754,12 +769,12 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
out_free: out_free:
if (gic_data.domain) if (gic_data.domain)
irq_domain_remove(gic_data.domain); irq_domain_remove(gic_data.domain);
free_percpu(gic_data.rdist); free_percpu(gic_data.rdists.rdist);
out_unmap_rdist: out_unmap_rdist:
for (i = 0; i < redist_regions; i++) for (i = 0; i < nr_redist_regions; i++)
if (redist_base[i]) if (rdist_regs[i].redist_base)
iounmap(redist_base[i]); iounmap(rdist_regs[i].redist_base);
kfree(redist_base); kfree(rdist_regs);
out_unmap_dist: out_unmap_dist:
iounmap(dist_base); iounmap(dist_base);
return err; return err;

View File

@ -49,6 +49,10 @@
#define GICD_CTLR_ENABLE_G1A (1U << 1) #define GICD_CTLR_ENABLE_G1A (1U << 1)
#define GICD_CTLR_ENABLE_G1 (1U << 0) #define GICD_CTLR_ENABLE_G1 (1U << 0)
#define GICD_TYPER_ID_BITS(typer) ((((typer) >> 19) & 0x1f) + 1)
#define GICD_TYPER_IRQS(typer) ((((typer) & 0x1f) + 1) * 32)
#define GICD_TYPER_LPIS (1U << 17)
#define GICD_IROUTER_SPI_MODE_ONE (0U << 31) #define GICD_IROUTER_SPI_MODE_ONE (0U << 31)
#define GICD_IROUTER_SPI_MODE_ANY (1U << 31) #define GICD_IROUTER_SPI_MODE_ANY (1U << 31)
@ -189,6 +193,17 @@
#include <linux/stringify.h> #include <linux/stringify.h>
struct rdists {
struct {
void __iomem *rd_base;
struct page *pend_page;
phys_addr_t phys_base;
} __percpu *rdist;
struct page *prop_page;
int id_bits;
u64 flags;
};
static inline void gic_write_eoir(u64 irq) static inline void gic_write_eoir(u64 irq)
{ {
asm volatile("msr_s " __stringify(ICC_EOIR1_EL1) ", %0" : : "r" (irq)); asm volatile("msr_s " __stringify(ICC_EOIR1_EL1) ", %0" : : "r" (irq));