parisc: Fix CPU affinity for Lasi, WAX and Dino chips
authorHelge Deller <deller@gmx.de>
Sun, 27 Mar 2022 13:46:26 +0000 (15:46 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 20 Apr 2022 07:08:28 +0000 (09:08 +0200)
[ Upstream commit 939fc856676c266c3bc347c1c1661872a3725c0f ]

Add the missing logic to allow Lasi, WAX and Dino to set the
CPU affinity. This fixes IRQ migration to other CPUs when a
CPU is shutdown which currently holds the IRQs for one of those
chips.

Signed-off-by: Helge Deller <deller@gmx.de>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/parisc/dino.c
drivers/parisc/gsc.c
drivers/parisc/gsc.h
drivers/parisc/lasi.c
drivers/parisc/wax.c

index c11515bdac832ad65dfad7e921a4c39f8dfc7380..4cc84c3b7602fa0364657955b03be4b17ab24527 100644 (file)
@@ -144,9 +144,8 @@ struct dino_device
 {
        struct pci_hba_data     hba;    /* 'C' inheritance - must be first */
        spinlock_t              dinosaur_pen;
-       unsigned long           txn_addr; /* EIR addr to generate interrupt */ 
-       u32                     txn_data; /* EIR data assign to each dino */ 
        u32                     imr;      /* IRQ's which are enabled */ 
+       struct gsc_irq          gsc_irq;
        int                     global_irq[DINO_LOCAL_IRQS]; /* map IMR bit to global irq */
 #ifdef DINO_DEBUG
        unsigned int            dino_irr0; /* save most recent IRQ line stat */
@@ -343,14 +342,43 @@ static void dino_unmask_irq(struct irq_data *d)
        if (tmp & DINO_MASK_IRQ(local_irq)) {
                DBG(KERN_WARNING "%s(): IRQ asserted! (ILR 0x%x)\n",
                                __func__, tmp);
-               gsc_writel(dino_dev->txn_data, dino_dev->txn_addr);
+               gsc_writel(dino_dev->gsc_irq.txn_data, dino_dev->gsc_irq.txn_addr);
        }
 }
 
+#ifdef CONFIG_SMP
+static int dino_set_affinity_irq(struct irq_data *d, const struct cpumask *dest,
+                               bool force)
+{
+       struct dino_device *dino_dev = irq_data_get_irq_chip_data(d);
+       struct cpumask tmask;
+       int cpu_irq;
+       u32 eim;
+
+       if (!cpumask_and(&tmask, dest, cpu_online_mask))
+               return -EINVAL;
+
+       cpu_irq = cpu_check_affinity(d, &tmask);
+       if (cpu_irq < 0)
+               return cpu_irq;
+
+       dino_dev->gsc_irq.txn_addr = txn_affinity_addr(d->irq, cpu_irq);
+       eim = ((u32) dino_dev->gsc_irq.txn_addr) | dino_dev->gsc_irq.txn_data;
+       __raw_writel(eim, dino_dev->hba.base_addr+DINO_IAR0);
+
+       irq_data_update_effective_affinity(d, &tmask);
+
+       return IRQ_SET_MASK_OK;
+}
+#endif
+
 static struct irq_chip dino_interrupt_type = {
        .name           = "GSC-PCI",
        .irq_unmask     = dino_unmask_irq,
        .irq_mask       = dino_mask_irq,
+#ifdef CONFIG_SMP
+       .irq_set_affinity = dino_set_affinity_irq,
+#endif
 };
 
 
@@ -811,7 +839,6 @@ static int __init dino_common_init(struct parisc_device *dev,
 {
        int status;
        u32 eim;
-       struct gsc_irq gsc_irq;
        struct resource *res;
 
        pcibios_register_hba(&dino_dev->hba);
@@ -826,10 +853,8 @@ static int __init dino_common_init(struct parisc_device *dev,
        **   still only has 11 IRQ input lines - just map some of them
        **   to a different processor.
        */
-       dev->irq = gsc_alloc_irq(&gsc_irq);
-       dino_dev->txn_addr = gsc_irq.txn_addr;
-       dino_dev->txn_data = gsc_irq.txn_data;
-       eim = ((u32) gsc_irq.txn_addr) | gsc_irq.txn_data;
+       dev->irq = gsc_alloc_irq(&dino_dev->gsc_irq);
+       eim = ((u32) dino_dev->gsc_irq.txn_addr) | dino_dev->gsc_irq.txn_data;
 
        /* 
        ** Dino needs a PA "IRQ" to get a processor's attention.
index 1bab5a2cd359ffefbcff50b2e02f6e06e2f9ad86..a0cae6194591ddca7ca646b470e3c5c107d0a7a6 100644 (file)
@@ -139,10 +139,41 @@ static void gsc_asic_unmask_irq(struct irq_data *d)
         */
 }
 
+#ifdef CONFIG_SMP
+static int gsc_set_affinity_irq(struct irq_data *d, const struct cpumask *dest,
+                               bool force)
+{
+       struct gsc_asic *gsc_dev = irq_data_get_irq_chip_data(d);
+       struct cpumask tmask;
+       int cpu_irq;
+
+       if (!cpumask_and(&tmask, dest, cpu_online_mask))
+               return -EINVAL;
+
+       cpu_irq = cpu_check_affinity(d, &tmask);
+       if (cpu_irq < 0)
+               return cpu_irq;
+
+       gsc_dev->gsc_irq.txn_addr = txn_affinity_addr(d->irq, cpu_irq);
+       gsc_dev->eim = ((u32) gsc_dev->gsc_irq.txn_addr) | gsc_dev->gsc_irq.txn_data;
+
+       /* switch IRQ's for devices below LASI/WAX to other CPU */
+       gsc_writel(gsc_dev->eim, gsc_dev->hpa + OFFSET_IAR);
+
+       irq_data_update_effective_affinity(d, &tmask);
+
+       return IRQ_SET_MASK_OK;
+}
+#endif
+
+
 static struct irq_chip gsc_asic_interrupt_type = {
        .name           =       "GSC-ASIC",
        .irq_unmask     =       gsc_asic_unmask_irq,
        .irq_mask       =       gsc_asic_mask_irq,
+#ifdef CONFIG_SMP
+       .irq_set_affinity =     gsc_set_affinity_irq,
+#endif
 };
 
 int gsc_assign_irq(struct irq_chip *type, void *data)
index b9d7bfb68e24d057e89e726ce0852250e90d4a85..9a364a4d09a5197d74fd92187de31e3af29547f7 100644 (file)
@@ -32,6 +32,7 @@ struct gsc_asic {
        int version;
        int type;
        int eim;
+       struct gsc_irq gsc_irq;
        int global_irq[32];
 };
 
index 4c9225431500456c9788a48d2863adc20bcab716..07ac0b8ee4fe317953ebbafc4294b5859eaf124f 100644 (file)
@@ -167,7 +167,6 @@ static int __init lasi_init_chip(struct parisc_device *dev)
 {
        extern void (*chassis_power_off)(void);
        struct gsc_asic *lasi;
-       struct gsc_irq gsc_irq;
        int ret;
 
        lasi = kzalloc(sizeof(*lasi), GFP_KERNEL);
@@ -189,7 +188,7 @@ static int __init lasi_init_chip(struct parisc_device *dev)
        lasi_init_irq(lasi);
 
        /* the IRQ lasi should use */
-       dev->irq = gsc_alloc_irq(&gsc_irq);
+       dev->irq = gsc_alloc_irq(&lasi->gsc_irq);
        if (dev->irq < 0) {
                printk(KERN_ERR "%s(): cannot get GSC irq\n",
                                __func__);
@@ -197,9 +196,9 @@ static int __init lasi_init_chip(struct parisc_device *dev)
                return -EBUSY;
        }
 
-       lasi->eim = ((u32) gsc_irq.txn_addr) | gsc_irq.txn_data;
+       lasi->eim = ((u32) lasi->gsc_irq.txn_addr) | lasi->gsc_irq.txn_data;
 
-       ret = request_irq(gsc_irq.irq, gsc_asic_intr, 0, "lasi", lasi);
+       ret = request_irq(lasi->gsc_irq.irq, gsc_asic_intr, 0, "lasi", lasi);
        if (ret < 0) {
                kfree(lasi);
                return ret;
index 6a3e40702b3bfc6b2287c0b50056c251477ef625..5c42bfa83398d53396dbac63e5322b5e962ae21e 100644 (file)
@@ -72,7 +72,6 @@ static int __init wax_init_chip(struct parisc_device *dev)
 {
        struct gsc_asic *wax;
        struct parisc_device *parent;
-       struct gsc_irq gsc_irq;
        int ret;
 
        wax = kzalloc(sizeof(*wax), GFP_KERNEL);
@@ -89,7 +88,7 @@ static int __init wax_init_chip(struct parisc_device *dev)
        wax_init_irq(wax);
 
        /* the IRQ wax should use */
-       dev->irq = gsc_claim_irq(&gsc_irq, WAX_GSC_IRQ);
+       dev->irq = gsc_claim_irq(&wax->gsc_irq, WAX_GSC_IRQ);
        if (dev->irq < 0) {
                printk(KERN_ERR "%s(): cannot get GSC irq\n",
                                __func__);
@@ -97,9 +96,9 @@ static int __init wax_init_chip(struct parisc_device *dev)
                return -EBUSY;
        }
 
-       wax->eim = ((u32) gsc_irq.txn_addr) | gsc_irq.txn_data;
+       wax->eim = ((u32) wax->gsc_irq.txn_addr) | wax->gsc_irq.txn_data;
 
-       ret = request_irq(gsc_irq.irq, gsc_asic_intr, 0, "wax", wax);
+       ret = request_irq(wax->gsc_irq.irq, gsc_asic_intr, 0, "wax", wax);
        if (ret < 0) {
                kfree(wax);
                return ret;