Commit 7a96c6b22efbd84e195836e192a3ce478cd6e14c

Authored by Benjamin Herrenschmidt
1 parent bb7f20b1c6

powerpc: Fix MSI support on U4 bridge PCIe slot

On machines using the Apple U4 bridge (AKA IBM CPC945) PCIe interface such
as the latest generation G5 machines x16 slot or the x16 slot of the
PowerStation, MSIs are currently broken (and will oops when enabling).

This fixes the oops and implements proper support for those. Instead of
using the PCIe <-> HT bridge conversion, on such slots we need to use
a bunch of magic registers in the bridge as the MSI target, encoding
the interrupt number in the low bits of the address itself

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

Showing 2 changed files with 49 additions and 8 deletions Side-by-side Diff

arch/powerpc/sysdev/mpic_msi.c
... ... @@ -39,7 +39,12 @@
39 39  
40 40 pr_debug("mpic: found U3, guessing msi allocator setup\n");
41 41  
42   - /* Reserve source numbers we know are reserved in the HW */
  42 + /* Reserve source numbers we know are reserved in the HW.
  43 + *
  44 + * This is a bit of a mix of U3 and U4 reserves but that's going
  45 + * to work fine, we have plenty enugh numbers left so let's just
  46 + * mark anything we don't like reserved.
  47 + */
43 48 for (i = 0; i < 8; i++)
44 49 msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, i);
45 50  
... ... @@ -48,6 +53,10 @@
48 53  
49 54 for (i = 100; i < 105; i++)
50 55 msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, i);
  56 +
  57 + for (i = 124; i < mpic->irq_count; i++)
  58 + msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, i);
  59 +
51 60  
52 61 np = NULL;
53 62 while ((np = of_find_all_nodes(np))) {
arch/powerpc/sysdev/mpic_u3msi.c
... ... @@ -64,12 +64,12 @@
64 64 return addr;
65 65 }
66 66  
67   -static u64 find_ht_magic_addr(struct pci_dev *pdev)
  67 +static u64 find_ht_magic_addr(struct pci_dev *pdev, unsigned int hwirq)
68 68 {
69 69 struct pci_bus *bus;
70 70 unsigned int pos;
71 71  
72   - for (bus = pdev->bus; bus; bus = bus->parent) {
  72 + for (bus = pdev->bus; bus && bus->self; bus = bus->parent) {
73 73 pos = pci_find_ht_capability(bus->self, HT_CAPTYPE_MSI_MAPPING);
74 74 if (pos)
75 75 return read_ht_magic_addr(bus->self, pos);
76 76  
... ... @@ -78,13 +78,41 @@
78 78 return 0;
79 79 }
80 80  
  81 +static u64 find_u4_magic_addr(struct pci_dev *pdev, unsigned int hwirq)
  82 +{
  83 + struct pci_controller *hose = pci_bus_to_host(pdev->bus);
  84 +
  85 + /* U4 PCIe MSIs need to write to the special register in
  86 + * the bridge that generates interrupts. There should be
  87 + * theorically a register at 0xf8005000 where you just write
  88 + * the MSI number and that triggers the right interrupt, but
  89 + * unfortunately, this is busted in HW, the bridge endian swaps
  90 + * the value and hits the wrong nibble in the register.
  91 + *
  92 + * So instead we use another register set which is used normally
  93 + * for converting HT interrupts to MPIC interrupts, which decodes
  94 + * the interrupt number as part of the low address bits
  95 + *
  96 + * This will not work if we ever use more than one legacy MSI in
  97 + * a block but we never do. For one MSI or multiple MSI-X where
  98 + * each interrupt address can be specified separately, it works
  99 + * just fine.
  100 + */
  101 + if (of_device_is_compatible(hose->dn, "u4-pcie") ||
  102 + of_device_is_compatible(hose->dn, "U4-pcie"))
  103 + return 0xf8004000 | (hwirq << 4);
  104 +
  105 + return 0;
  106 +}
  107 +
81 108 static int u3msi_msi_check_device(struct pci_dev *pdev, int nvec, int type)
82 109 {
83 110 if (type == PCI_CAP_ID_MSIX)
84 111 pr_debug("u3msi: MSI-X untested, trying anyway.\n");
85 112  
86 113 /* If we can't find a magic address then MSI ain't gonna work */
87   - if (find_ht_magic_addr(pdev) == 0) {
  114 + if (find_ht_magic_addr(pdev, 0) == 0 &&
  115 + find_u4_magic_addr(pdev, 0) == 0) {
88 116 pr_debug("u3msi: no magic address found for %s\n",
89 117 pci_name(pdev));
90 118 return -ENXIO;
... ... @@ -118,10 +146,6 @@
118 146 u64 addr;
119 147 int hwirq;
120 148  
121   - addr = find_ht_magic_addr(pdev);
122   - msg.address_lo = addr & 0xFFFFFFFF;
123   - msg.address_hi = addr >> 32;
124   -
125 149 list_for_each_entry(entry, &pdev->msi_list, list) {
126 150 hwirq = msi_bitmap_alloc_hwirqs(&msi_mpic->msi_bitmap, 1);
127 151 if (hwirq < 0) {
... ... @@ -129,6 +153,12 @@
129 153 return hwirq;
130 154 }
131 155  
  156 + addr = find_ht_magic_addr(pdev, hwirq);
  157 + if (addr == 0)
  158 + addr = find_u4_magic_addr(pdev, hwirq);
  159 + msg.address_lo = addr & 0xFFFFFFFF;
  160 + msg.address_hi = addr >> 32;
  161 +
132 162 virq = irq_create_mapping(msi_mpic->irqhost, hwirq);
133 163 if (virq == NO_IRQ) {
134 164 pr_debug("u3msi: failed mapping hwirq 0x%x\n", hwirq);
... ... @@ -143,6 +173,8 @@
143 173 pr_debug("u3msi: allocated virq 0x%x (hw 0x%x) addr 0x%lx\n",
144 174 virq, hwirq, (unsigned long)addr);
145 175  
  176 + printk("u3msi: allocated virq 0x%x (hw 0x%x) addr 0x%lx\n",
  177 + virq, hwirq, (unsigned long)addr);
146 178 msg.data = hwirq;
147 179 write_msi_msg(virq, &msg);
148 180