blob: 81eda3d93a5dca78b3342a75c312af0ddfb128e3 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/pci/setup-irq.c
3 *
4 * Extruded from code written by
5 * Dave Rusling (david.rusling@reo.mts.dec.com)
6 * David Mosberger (davidm@cs.arizona.edu)
7 * David Miller (davem@redhat.com)
8 *
9 * Support routines for initializing a PCI subsystem.
10 */
11
12
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include <linux/kernel.h>
14#include <linux/pci.h>
15#include <linux/errno.h>
16#include <linux/ioport.h>
17#include <linux/cache.h>
Matthew Minter47a650f2017-06-28 15:14:02 -050018#include "pci.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070019
Thierry Reding8885b7b2012-09-17 13:22:54 +020020void __weak pcibios_update_irq(struct pci_dev *dev, int irq)
21{
22 dev_dbg(&dev->dev, "assigning IRQ %02d\n", irq);
23 pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
24}
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
Matthew Minter47a650f2017-06-28 15:14:02 -050026void pci_assign_irq(struct pci_dev *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -070027{
Matthew Minter47a650f2017-06-28 15:14:02 -050028 u8 pin;
29 u8 slot = -1;
Andreas Block691cd0c2007-02-05 16:36:07 -080030 int irq = 0;
Matthew Minter47a650f2017-06-28 15:14:02 -050031 struct pci_host_bridge *hbrg = pci_find_host_bridge(dev->bus);
32
33 if (!(hbrg->map_irq)) {
34 dev_dbg(&dev->dev, "runtime IRQ mapping not provided by arch\n");
35 return;
36 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
38 /* If this device is not on the primary bus, we need to figure out
39 which interrupt pin it will come in on. We know which slot it
40 will come in on 'cos that slot is where the bridge is. Each
41 time the interrupt line passes through a PCI-PCI bridge we must
42 apply the swizzle function. */
43
44 pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
Andreas Block691cd0c2007-02-05 16:36:07 -080045 /* Cope with illegal. */
46 if (pin > 4)
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 pin = 1;
48
Matthew Minter47a650f2017-06-28 15:14:02 -050049 if (pin) {
Andreas Block691cd0c2007-02-05 16:36:07 -080050 /* Follow the chain of bridges, swizzling as we go. */
Matthew Minter47a650f2017-06-28 15:14:02 -050051 if (hbrg->swizzle_irq)
52 slot = (*(hbrg->swizzle_irq))(dev, &pin);
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
Matthew Minter47a650f2017-06-28 15:14:02 -050054 /*
55 * If a swizzling function is not used map_irq must
56 * ignore slot
57 */
58 irq = (*(hbrg->map_irq))(dev, slot, pin);
Andreas Block691cd0c2007-02-05 16:36:07 -080059 if (irq == -1)
60 irq = 0;
61 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 dev->irq = irq;
63
Matthew Minter47a650f2017-06-28 15:14:02 -050064 dev_dbg(&dev->dev, "assign IRQ: got %d\n", dev->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
66 /* Always tell the device, so the driver knows what is
67 the real IRQ to use; the device does not use it. */
68 pcibios_update_irq(dev, irq);
69}
70
Ryan Desfosses3c78bc62014-04-18 20:13:49 -040071void pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *),
72 int (*map_irq)(const struct pci_dev *, u8, u8))
Linus Torvalds1da177e2005-04-16 15:20:36 -070073{
Matthew Minter47a650f2017-06-28 15:14:02 -050074 /*
75 * Implement pci_fixup_irqs() through pci_assign_irq().
76 * This code should be remove eventually, it is a wrapper
77 * around pci_assign_irq() interface to keep current
78 * pci_fixup_irqs() behaviour unchanged on architecture
79 * code still relying on its interface.
80 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 struct pci_dev *dev = NULL;
Matthew Minter47a650f2017-06-28 15:14:02 -050082 struct pci_host_bridge *hbrg = NULL;
Ryan Desfosses3c78bc62014-04-18 20:13:49 -040083
Matthew Minter47a650f2017-06-28 15:14:02 -050084 for_each_pci_dev(dev) {
85 hbrg = pci_find_host_bridge(dev->bus);
86 hbrg->swizzle_irq = swizzle;
87 hbrg->map_irq = map_irq;
88 pci_assign_irq(dev);
89 hbrg->swizzle_irq = NULL;
90 hbrg->map_irq = NULL;
91 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070092}
Ray Juie6b29de2015-04-08 11:21:33 -070093EXPORT_SYMBOL_GPL(pci_fixup_irqs);