amd-xgbe: Set RSS enablement based on hardware features
[firefly-linux-kernel-4.4.55.git] / drivers / irqchip / irq-gic-v3.c
index aa17ae805a703c80fe6584c019e18617741caaab..1a146ccee7017d4b344763c5f5d2cf45af1d9e4f 100644 (file)
 #include "irq-gic-common.h"
 #include "irqchip.h"
 
+struct redist_region {
+       void __iomem            *redist_base;
+       phys_addr_t             phys_base;
+};
+
 struct gic_chip_data {
        void __iomem            *dist_base;
-       void __iomem            **redist_base;
-       void __iomem * __percpu *rdist;
+       struct redist_region    *redist_regions;
+       struct rdists           rdists;
        struct irq_domain       *domain;
        u64                     redist_stride;
-       u32                     redist_regions;
+       u32                     nr_redist_regions;
        unsigned int            irq_nr;
 };
 
 static struct gic_chip_data gic_data __read_mostly;
 
-#define gic_data_rdist()               (this_cpu_ptr(gic_data.rdist))
-#define gic_data_rdist_rd_base()       (*gic_data_rdist())
+#define gic_data_rdist()               (this_cpu_ptr(gic_data.rdists.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)
 
 /* Our default, arbitrary priority value. Linux only uses one anyway. */
@@ -71,9 +76,6 @@ static inline void __iomem *gic_dist_base(struct irq_data *d)
        if (d->hwirq <= 1023)           /* SPI -> dist_base */
                return gic_data.dist_base;
 
-       if (d->hwirq >= 8192)
-               BUG();          /* LPI Detected!!! */
-
        return NULL;
 }
 
@@ -271,11 +273,11 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
        do {
                irqnr = gic_read_iar();
 
-               if (likely(irqnr > 15 && irqnr < 1020)) {
+               if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) {
                        int err;
                        err = handle_domain_irq(gic_data.domain, irqnr, regs);
                        if (err) {
-                               WARN_ONCE(true, "Unexpected SPI received!\n");
+                               WARN_ONCE(true, "Unexpected interrupt received!\n");
                                gic_write_eoir(irqnr);
                        }
                        continue;
@@ -333,8 +335,8 @@ static int gic_populate_rdist(void)
               MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 |
               MPIDR_AFFINITY_LEVEL(mpidr, 0));
 
-       for (i = 0; i < gic_data.redist_regions; i++) {
-               void __iomem *ptr = gic_data.redist_base[i];
+       for (i = 0; i < gic_data.nr_redist_regions; i++) {
+               void __iomem *ptr = gic_data.redist_regions[i].redist_base;
                u32 reg;
 
                reg = readl_relaxed(ptr + GICR_PIDR2) & GIC_PIDR2_ARCH_MASK;
@@ -347,10 +349,13 @@ static int gic_populate_rdist(void)
                do {
                        typer = readq_relaxed(ptr + GICR_TYPER);
                        if ((typer >> 32) == aff) {
+                               u64 offset = ptr - gic_data.redist_regions[i].redist_base;
                                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(),
-                                       (unsigned long long)mpidr, ptr);
+                                       (unsigned long long)mpidr,
+                                       i, &gic_data_rdist()->phys_base);
                                return 0;
                        }
 
@@ -385,6 +390,11 @@ static void gic_cpu_sys_reg_init(void)
        gic_write_grpen1(1);
 }
 
+static int gic_dist_supports_lpis(void)
+{
+       return !!(readl_relaxed(gic_data.dist_base + GICD_TYPER) & GICD_TYPER_LPIS);
+}
+
 static void gic_cpu_init(void)
 {
        void __iomem *rbase;
@@ -399,6 +409,10 @@ static void gic_cpu_init(void)
 
        gic_cpu_config(rbase, gic_redist_wait_for_rwp);
 
+       /* Give LPIs a spin */
+       if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis())
+               its_cpu_init();
+
        /* initialise system registers */
        gic_cpu_sys_reg_init();
 }
@@ -585,26 +599,43 @@ static struct irq_chip gic_chip = {
        .irq_set_affinity       = gic_set_affinity,
 };
 
+#define GIC_ID_NR              (1U << gic_data.rdists.id_bits)
+
 static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
                              irq_hw_number_t hw)
 {
        /* SGIs are private to the core kernel */
        if (hw < 16)
                return -EPERM;
+       /* Nothing here */
+       if (hw >= gic_data.irq_nr && hw < 8192)
+               return -EPERM;
+       /* Off limits */
+       if (hw >= GIC_ID_NR)
+               return -EPERM;
+
        /* PPIs */
        if (hw < 32) {
                irq_set_percpu_devid(irq);
-               irq_set_chip_and_handler(irq, &gic_chip,
-                                        handle_percpu_devid_irq);
+               irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
+                                   handle_percpu_devid_irq, NULL, NULL);
                set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
        }
        /* SPIs */
        if (hw >= 32 && hw < gic_data.irq_nr) {
-               irq_set_chip_and_handler(irq, &gic_chip,
-                                        handle_fasteoi_irq);
+               irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
+                                   handle_fasteoi_irq, NULL, NULL);
                set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
        }
-       irq_set_chip_data(irq, d->host_data);
+       /* LPIs */
+       if (hw >= 8192 && hw < GIC_ID_NR) {
+               if (!gic_dist_supports_lpis())
+                       return -EPERM;
+               irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
+                                   handle_fasteoi_irq, NULL, NULL);
+               set_irq_flags(irq, IRQF_VALID);
+       }
+
        return 0;
 }
 
@@ -625,6 +656,9 @@ static int gic_irq_domain_xlate(struct irq_domain *d,
        case 1:                 /* PPI */
                *out_hwirq = intspec[1] + 16;
                break;
+       case GIC_IRQ_TYPE_LPI:  /* LPI */
+               *out_hwirq = intspec[1];
+               break;
        default:
                return -EINVAL;
        }
@@ -633,17 +667,50 @@ static int gic_irq_domain_xlate(struct irq_domain *d,
        return 0;
 }
 
+static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                               unsigned int nr_irqs, void *arg)
+{
+       int i, ret;
+       irq_hw_number_t hwirq;
+       unsigned int type = IRQ_TYPE_NONE;
+       struct of_phandle_args *irq_data = arg;
+
+       ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args,
+                                  irq_data->args_count, &hwirq, &type);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < nr_irqs; i++)
+               gic_irq_domain_map(domain, virq + i, hwirq + i);
+
+       return 0;
+}
+
+static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq,
+                               unsigned int nr_irqs)
+{
+       int i;
+
+       for (i = 0; i < nr_irqs; i++) {
+               struct irq_data *d = irq_domain_get_irq_data(domain, virq + i);
+               irq_set_handler(virq + i, NULL);
+               irq_domain_reset_irq_data(d);
+       }
+}
+
 static const struct irq_domain_ops gic_irq_domain_ops = {
-       .map = gic_irq_domain_map,
        .xlate = gic_irq_domain_xlate,
+       .alloc = gic_irq_domain_alloc,
+       .free = gic_irq_domain_free,
 };
 
 static int __init gic_of_init(struct device_node *node, struct device_node *parent)
 {
        void __iomem *dist_base;
-       void __iomem **redist_base;
+       struct redist_region *rdist_regs;
        u64 redist_stride;
-       u32 redist_regions;
+       u32 nr_redist_regions;
+       u32 typer;
        u32 reg;
        int gic_irqs;
        int err;
@@ -664,54 +731,63 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
                goto out_unmap_dist;
        }
 
-       if (of_property_read_u32(node, "#redistributor-regions", &redist_regions))
-               redist_regions = 1;
+       if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions))
+               nr_redist_regions = 1;
 
-       redist_base = kzalloc(sizeof(*redist_base) * redist_regions, GFP_KERNEL);
-       if (!redist_base) {
+       rdist_regs = kzalloc(sizeof(*rdist_regs) * nr_redist_regions, GFP_KERNEL);
+       if (!rdist_regs) {
                err = -ENOMEM;
                goto out_unmap_dist;
        }
 
-       for (i = 0; i < redist_regions; i++) {
-               redist_base[i] = of_iomap(node, 1 + i);
-               if (!redist_base[i]) {
+       for (i = 0; i < nr_redist_regions; i++) {
+               struct resource res;
+               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",
                               node->full_name, i);
                        err = -ENODEV;
                        goto out_unmap_rdist;
                }
+               rdist_regs[i].phys_base = res.start;
        }
 
        if (of_property_read_u64(node, "redistributor-stride", &redist_stride))
                redist_stride = 0;
 
        gic_data.dist_base = dist_base;
-       gic_data.redist_base = redist_base;
-       gic_data.redist_regions = redist_regions;
+       gic_data.redist_regions = rdist_regs;
+       gic_data.nr_redist_regions = nr_redist_regions;
        gic_data.redist_stride = redist_stride;
 
        /*
         * Find out how many interrupts are supported.
         * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)
         */
-       gic_irqs = readl_relaxed(gic_data.dist_base + GICD_TYPER) & 0x1f;
-       gic_irqs = (gic_irqs + 1) * 32;
+       typer = readl_relaxed(gic_data.dist_base + GICD_TYPER);
+       gic_data.rdists.id_bits = GICD_TYPER_ID_BITS(typer);
+       gic_irqs = GICD_TYPER_IRQS(typer);
        if (gic_irqs > 1020)
                gic_irqs = 1020;
        gic_data.irq_nr = gic_irqs;
 
        gic_data.domain = irq_domain_add_tree(node, &gic_irq_domain_ops,
                                              &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;
                goto out_free;
        }
 
        set_handle_irq(gic_handle_irq);
 
+       if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis())
+               its_init(node, &gic_data.rdists, gic_data.domain);
+
        gic_smp_init();
        gic_dist_init();
        gic_cpu_init();
@@ -722,12 +798,12 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
 out_free:
        if (gic_data.domain)
                irq_domain_remove(gic_data.domain);
-       free_percpu(gic_data.rdist);
+       free_percpu(gic_data.rdists.rdist);
 out_unmap_rdist:
-       for (i = 0; i < redist_regions; i++)
-               if (redist_base[i])
-                       iounmap(redist_base[i]);
-       kfree(redist_base);
+       for (i = 0; i < nr_redist_regions; i++)
+               if (rdist_regs[i].redist_base)
+                       iounmap(rdist_regs[i].redist_base);
+       kfree(rdist_regs);
 out_unmap_dist:
        iounmap(dist_base);
        return err;