Skip to content

Commit

Permalink
parisc: Fix CPU affinity for Lasi, WAX and Dino chips
Browse files Browse the repository at this point in the history
[ Upstream commit 939fc85 ]

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>
  • Loading branch information
hdeller authored and gregkh committed Apr 13, 2022
1 parent 30dd4af commit f77f482
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 16 deletions.
41 changes: 33 additions & 8 deletions drivers/parisc/dino.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,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 */
Expand Down Expand Up @@ -339,14 +338,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
};


Expand Down Expand Up @@ -806,7 +834,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);
Expand All @@ -821,10 +848,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.
Expand Down
31 changes: 31 additions & 0 deletions drivers/parisc/gsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,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)
Expand Down
1 change: 1 addition & 0 deletions drivers/parisc/gsc.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct gsc_asic {
int version;
int type;
int eim;
struct gsc_irq gsc_irq;
int global_irq[32];
};

Expand Down
7 changes: 3 additions & 4 deletions drivers/parisc/lasi.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,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);
Expand All @@ -185,17 +184,17 @@ 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__);
kfree(lasi);
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;
Expand Down
7 changes: 3 additions & 4 deletions drivers/parisc/wax.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,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);
Expand All @@ -85,17 +84,17 @@ 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__);
kfree(wax);
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;
Expand Down

0 comments on commit f77f482

Please sign in to comment.