Skip to content

Commit e6308b6

Browse files
Thomas Bogendoerferpaulburton
Thomas Bogendoerfer
authored andcommitted
MIPS: SGI-IP27: abstract chipset irq from bridge
Bridge ASIC is widely used in different SGI systems, but the connected chipset is either HUB, HEART or BEDROCK. This commit switches to irq domain hierarchy for hub and bridge interrupts to get bridge setup out of hub interrupt code. Signed-off-by: Thomas Bogendoerfer <[email protected]> [[email protected]: Resolve conflict with commit 69a07a4 ("MIPS: SGI-IP27: rework HUB interrupts").] Signed-off-by: Paul Burton <[email protected]> Cc: Ralf Baechle <[email protected]> Cc: James Hogan <[email protected]> Cc: [email protected] Cc: [email protected]
1 parent a57140e commit e6308b6

File tree

5 files changed

+265
-119
lines changed

5 files changed

+265
-119
lines changed

arch/mips/Kconfig

+1
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,7 @@ config SGI_IP27
675675
select SYS_HAS_EARLY_PRINTK
676676
select HAVE_PCI
677677
select IRQ_MIPS_CPU
678+
select IRQ_DOMAIN_HIERARCHY
678679
select NR_CPUS_DEFAULT_64
679680
select PCI_DRIVERS_GENERIC
680681
select PCI_XTALK_BRIDGE

arch/mips/include/asm/pci/bridge.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,7 @@ struct bridge_controller {
805805
struct bridge_regs *base;
806806
unsigned long baddr;
807807
unsigned long intr_addr;
808+
struct irq_domain *domain;
808809
unsigned int pci_int[8];
809810
nasid_t nasid;
810811
};
@@ -819,6 +820,4 @@ struct bridge_controller {
819820
#define bridge_clr(bc, reg, val) \
820821
__raw_writel(__raw_readl(&bc->base->reg) & ~(val), &bc->base->reg)
821822

822-
extern int request_bridge_irq(struct bridge_controller *bc, int pin);
823-
824823
#endif /* _ASM_PCI_BRIDGE_H */

arch/mips/include/asm/sn/irq_alloc.h

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef __ASM_SN_IRQ_ALLOC_H
3+
#define __ASM_SN_IRQ_ALLOC_H
4+
5+
struct irq_alloc_info {
6+
void *ctrl;
7+
nasid_t nasid;
8+
int pin;
9+
};
10+
11+
#endif /* __ASM_SN_IRQ_ALLOC_H */

arch/mips/pci/pci-xtalk-bridge.c

+168-10
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
#include <asm/pci/bridge.h>
1616
#include <asm/paccess.h>
17-
#include <asm/sn/intr.h>
17+
#include <asm/sn/irq_alloc.h>
1818

1919
/*
2020
* Most of the IOC3 PCI config register aren't present
@@ -310,6 +310,135 @@ static struct pci_ops bridge_pci_ops = {
310310
.write = pci_write_config,
311311
};
312312

313+
struct bridge_irq_chip_data {
314+
struct bridge_controller *bc;
315+
nasid_t nasid;
316+
};
317+
318+
static int bridge_set_affinity(struct irq_data *d, const struct cpumask *mask,
319+
bool force)
320+
{
321+
#ifdef CONFIG_NUMA
322+
struct bridge_irq_chip_data *data = d->chip_data;
323+
int bit = d->parent_data->hwirq;
324+
int pin = d->hwirq;
325+
nasid_t nasid;
326+
int ret, cpu;
327+
328+
ret = irq_chip_set_affinity_parent(d, mask, force);
329+
if (ret >= 0) {
330+
cpu = cpumask_first_and(mask, cpu_online_mask);
331+
nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu));
332+
bridge_write(data->bc, b_int_addr[pin].addr,
333+
(((data->bc->intr_addr >> 30) & 0x30000) |
334+
bit | (nasid << 8)));
335+
bridge_read(data->bc, b_wid_tflush);
336+
}
337+
return ret;
338+
#else
339+
return irq_chip_set_affinity_parent(d, mask, force);
340+
#endif
341+
}
342+
343+
struct irq_chip bridge_irq_chip = {
344+
.name = "BRIDGE",
345+
.irq_mask = irq_chip_mask_parent,
346+
.irq_unmask = irq_chip_unmask_parent,
347+
.irq_set_affinity = bridge_set_affinity
348+
};
349+
350+
static int bridge_domain_alloc(struct irq_domain *domain, unsigned int virq,
351+
unsigned int nr_irqs, void *arg)
352+
{
353+
struct bridge_irq_chip_data *data;
354+
struct irq_alloc_info *info = arg;
355+
int ret;
356+
357+
if (nr_irqs > 1 || !info)
358+
return -EINVAL;
359+
360+
data = kzalloc(sizeof(*data), GFP_KERNEL);
361+
if (!data)
362+
return -ENOMEM;
363+
364+
ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
365+
if (ret >= 0) {
366+
data->bc = info->ctrl;
367+
data->nasid = info->nasid;
368+
irq_domain_set_info(domain, virq, info->pin, &bridge_irq_chip,
369+
data, handle_level_irq, NULL, NULL);
370+
} else {
371+
kfree(data);
372+
}
373+
374+
return ret;
375+
}
376+
377+
static void bridge_domain_free(struct irq_domain *domain, unsigned int virq,
378+
unsigned int nr_irqs)
379+
{
380+
struct irq_data *irqd = irq_domain_get_irq_data(domain, virq);
381+
382+
if (nr_irqs)
383+
return;
384+
385+
kfree(irqd->chip_data);
386+
irq_domain_free_irqs_top(domain, virq, nr_irqs);
387+
}
388+
389+
static int bridge_domain_activate(struct irq_domain *domain,
390+
struct irq_data *irqd, bool reserve)
391+
{
392+
struct bridge_irq_chip_data *data = irqd->chip_data;
393+
struct bridge_controller *bc = data->bc;
394+
int bit = irqd->parent_data->hwirq;
395+
int pin = irqd->hwirq;
396+
u32 device;
397+
398+
bridge_write(bc, b_int_addr[pin].addr,
399+
(((bc->intr_addr >> 30) & 0x30000) |
400+
bit | (data->nasid << 8)));
401+
bridge_set(bc, b_int_enable, (1 << pin));
402+
bridge_set(bc, b_int_enable, 0x7ffffe00); /* more stuff in int_enable */
403+
404+
/*
405+
* Enable sending of an interrupt clear packt to the hub on a high to
406+
* low transition of the interrupt pin.
407+
*
408+
* IRIX sets additional bits in the address which are documented as
409+
* reserved in the bridge docs.
410+
*/
411+
bridge_set(bc, b_int_mode, (1UL << pin));
412+
413+
/*
414+
* We assume the bridge to have a 1:1 mapping between devices
415+
* (slots) and intr pins.
416+
*/
417+
device = bridge_read(bc, b_int_device);
418+
device &= ~(7 << (pin*3));
419+
device |= (pin << (pin*3));
420+
bridge_write(bc, b_int_device, device);
421+
422+
bridge_read(bc, b_wid_tflush);
423+
return 0;
424+
}
425+
426+
static void bridge_domain_deactivate(struct irq_domain *domain,
427+
struct irq_data *irqd)
428+
{
429+
struct bridge_irq_chip_data *data = irqd->chip_data;
430+
431+
bridge_clr(data->bc, b_int_enable, (1 << irqd->hwirq));
432+
bridge_read(data->bc, b_wid_tflush);
433+
}
434+
435+
static const struct irq_domain_ops bridge_domain_ops = {
436+
.alloc = bridge_domain_alloc,
437+
.free = bridge_domain_free,
438+
.activate = bridge_domain_activate,
439+
.deactivate = bridge_domain_deactivate
440+
};
441+
313442
/*
314443
* All observed requests have pin == 1. We could have a global here, that
315444
* gets incremented and returned every time - unfortunately, pci_map_irq
@@ -322,11 +451,16 @@ static struct pci_ops bridge_pci_ops = {
322451
static int bridge_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
323452
{
324453
struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus);
454+
struct irq_alloc_info info;
325455
int irq;
326456

327457
irq = bc->pci_int[slot];
328458
if (irq == -1) {
329-
irq = request_bridge_irq(bc, slot);
459+
info.ctrl = bc;
460+
info.nasid = bc->nasid;
461+
info.pin = slot;
462+
463+
irq = irq_domain_alloc_irqs(bc->domain, 1, bc->nasid, &info);
330464
if (irq < 0)
331465
return irq;
332466

@@ -337,18 +471,34 @@ static int bridge_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
337471

338472
static int bridge_probe(struct platform_device *pdev)
339473
{
474+
struct xtalk_bridge_platform_data *bd = dev_get_platdata(&pdev->dev);
340475
struct device *dev = &pdev->dev;
341476
struct bridge_controller *bc;
342477
struct pci_host_bridge *host;
478+
struct irq_domain *domain, *parent;
479+
struct fwnode_handle *fn;
343480
int slot;
344481
int err;
345-
struct xtalk_bridge_platform_data *bd = dev_get_platdata(&pdev->dev);
482+
483+
parent = irq_get_default_host();
484+
if (!parent)
485+
return -ENODEV;
486+
fn = irq_domain_alloc_named_fwnode("BRIDGE");
487+
if (!fn)
488+
return -ENOMEM;
489+
domain = irq_domain_create_hierarchy(parent, 0, 8, fn,
490+
&bridge_domain_ops, NULL);
491+
irq_domain_free_fwnode(fn);
492+
if (!domain)
493+
return -ENOMEM;
346494

347495
pci_set_flags(PCI_PROBE_ONLY);
348496

349497
host = devm_pci_alloc_host_bridge(dev, sizeof(*bc));
350-
if (!host)
351-
return -ENOMEM;
498+
if (!host) {
499+
err = -ENOMEM;
500+
goto err_remove_domain;
501+
}
352502

353503
bc = pci_host_bridge_priv(host);
354504

@@ -357,15 +507,15 @@ static int bridge_probe(struct platform_device *pdev)
357507
bc->busn.end = 0xff;
358508
bc->busn.flags = IORESOURCE_BUS;
359509

510+
bc->domain = domain;
511+
360512
pci_add_resource_offset(&host->windows, &bd->mem, bd->mem_offset);
361513
pci_add_resource_offset(&host->windows, &bd->io, bd->io_offset);
362514
pci_add_resource(&host->windows, &bc->busn);
363515

364516
err = devm_request_pci_bus_resources(dev, &host->windows);
365-
if (err < 0) {
366-
pci_free_resource_list(&host->windows);
367-
return err;
368-
}
517+
if (err < 0)
518+
goto err_free_resource;
369519

370520
bc->nasid = bd->nasid;
371521

@@ -419,20 +569,28 @@ static int bridge_probe(struct platform_device *pdev)
419569

420570
err = pci_scan_root_bus_bridge(host);
421571
if (err < 0)
422-
return err;
572+
goto err_free_resource;
423573

424574
pci_bus_claim_resources(host->bus);
425575
pci_bus_add_devices(host->bus);
426576

427577
platform_set_drvdata(pdev, host->bus);
428578

429579
return 0;
580+
581+
err_free_resource:
582+
pci_free_resource_list(&host->windows);
583+
err_remove_domain:
584+
irq_domain_remove(domain);
585+
return err;
430586
}
431587

432588
static int bridge_remove(struct platform_device *pdev)
433589
{
434590
struct pci_bus *bus = platform_get_drvdata(pdev);
591+
struct bridge_controller *bc = BRIDGE_CONTROLLER(bus);
435592

593+
irq_domain_remove(bc->domain);
436594
pci_lock_rescan_remove();
437595
pci_stop_root_bus(bus);
438596
pci_remove_root_bus(bus);

0 commit comments

Comments
 (0)