Commit 86be8993 authored by Lorenzo Pieralisi's avatar Lorenzo Pieralisi Committed by Matt Turner

alpha/PCI: Fix noname IRQ level detection

The conversion of the alpha architecture PCI host bridge legacy IRQ
mapping/swizzling to the new PCI host bridge map/swizzle hooks carried
out through:

commit 0e4c2eeb ("alpha/PCI: Replace pci_fixup_irqs() call with
host bridge IRQ mapping hooks")

implies that IRQ for devices are now allocated through pci_assign_irq()
function in pci_device_probe() that is called when a driver matching a
device is found in order to probe the device through the device driver.

Alpha noname platforms required IRQ level programming to be executed
in sio_fixup_irq_levels(), that is called in noname_init_pci(), a
platform hook called within a subsys_initcall.

In noname_init_pci(), present IRQs are detected through
sio_collect_irq_levels() that check the struct pci_dev->irq number
to detect if an IRQ has been allocated for the device.

By the time sio_collect_irq_levels() is called, some devices may still
have not a matching driver loaded to match them (eg loadable module)
therefore their IRQ allocation is still pending - which means that
sio_collect_irq_levels() does not programme the correct IRQ level for
those devices, causing their IRQ handling to be broken when the device
driver is actually loaded and the device is probed.

Fix the issue by adding code in the noname map_irq() function
(noname_map_irq()) that, whilst mapping/swizzling the IRQ line, it also
ensures that the correct IRQ level programming is executed at platform
level, fixing the issue.

Fixes: 0e4c2eeb ("alpha/PCI: Replace pci_fixup_irqs() call with
host bridge IRQ mapping hooks")
Reported-by: default avatarMikulas Patocka <mpatocka@redhat.com>
Signed-off-by: default avatarLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: stable@vger.kernel.org # 4.14
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Cc: Mikulas Patocka <mpatocka@redhat.com>
Cc: Meelis Roos <mroos@linux.ee>
Signed-off-by: default avatarMatt Turner <mattst88@gmail.com>
parent 0d83620f
...@@ -102,6 +102,15 @@ sio_pci_route(void) ...@@ -102,6 +102,15 @@ sio_pci_route(void)
alpha_mv.sys.sio.route_tab); alpha_mv.sys.sio.route_tab);
} }
static bool sio_pci_dev_irq_needs_level(const struct pci_dev *dev)
{
if ((dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) &&
(dev->class >> 8 != PCI_CLASS_BRIDGE_PCMCIA))
return false;
return true;
}
static unsigned int __init static unsigned int __init
sio_collect_irq_levels(void) sio_collect_irq_levels(void)
{ {
...@@ -110,8 +119,7 @@ sio_collect_irq_levels(void) ...@@ -110,8 +119,7 @@ sio_collect_irq_levels(void)
/* Iterate through the devices, collecting IRQ levels. */ /* Iterate through the devices, collecting IRQ levels. */
for_each_pci_dev(dev) { for_each_pci_dev(dev) {
if ((dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) && if (!sio_pci_dev_irq_needs_level(dev))
(dev->class >> 8 != PCI_CLASS_BRIDGE_PCMCIA))
continue; continue;
if (dev->irq) if (dev->irq)
...@@ -120,8 +128,7 @@ sio_collect_irq_levels(void) ...@@ -120,8 +128,7 @@ sio_collect_irq_levels(void)
return level_bits; return level_bits;
} }
static void __init static void __sio_fixup_irq_levels(unsigned int level_bits, bool reset)
sio_fixup_irq_levels(unsigned int level_bits)
{ {
unsigned int old_level_bits; unsigned int old_level_bits;
...@@ -139,12 +146,21 @@ sio_fixup_irq_levels(unsigned int level_bits) ...@@ -139,12 +146,21 @@ sio_fixup_irq_levels(unsigned int level_bits)
*/ */
old_level_bits = inb(0x4d0) | (inb(0x4d1) << 8); old_level_bits = inb(0x4d0) | (inb(0x4d1) << 8);
level_bits |= (old_level_bits & 0x71ff); if (reset)
old_level_bits &= 0x71ff;
level_bits |= old_level_bits;
outb((level_bits >> 0) & 0xff, 0x4d0); outb((level_bits >> 0) & 0xff, 0x4d0);
outb((level_bits >> 8) & 0xff, 0x4d1); outb((level_bits >> 8) & 0xff, 0x4d1);
} }
static inline void
sio_fixup_irq_levels(unsigned int level_bits)
{
__sio_fixup_irq_levels(level_bits, true);
}
static inline int static inline int
noname_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) noname_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{ {
...@@ -181,7 +197,14 @@ noname_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) ...@@ -181,7 +197,14 @@ noname_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
const long min_idsel = 6, max_idsel = 14, irqs_per_slot = 5; const long min_idsel = 6, max_idsel = 14, irqs_per_slot = 5;
int irq = COMMON_TABLE_LOOKUP, tmp; int irq = COMMON_TABLE_LOOKUP, tmp;
tmp = __kernel_extbl(alpha_mv.sys.sio.route_tab, irq); tmp = __kernel_extbl(alpha_mv.sys.sio.route_tab, irq);
return irq >= 0 ? tmp : -1;
irq = irq >= 0 ? tmp : -1;
/* Fixup IRQ level if an actual IRQ mapping is detected */
if (sio_pci_dev_irq_needs_level(dev) && irq >= 0)
__sio_fixup_irq_levels(1 << irq, false);
return irq;
} }
static inline int static inline int
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment