1 /* 2 * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <errno.h> 9 #include <fdtdec.h> 10 #include <malloc.h> 11 #include <asm/io.h> 12 #include <asm/irq.h> 13 #include <asm/pci.h> 14 #include <asm/pirq_routing.h> 15 16 DECLARE_GLOBAL_DATA_PTR; 17 18 static struct irq_router irq_router; 19 static struct irq_routing_table *pirq_routing_table; 20 21 bool pirq_check_irq_routed(int link, u8 irq) 22 { 23 u8 pirq; 24 int base = irq_router.link_base; 25 26 if (irq_router.config == PIRQ_VIA_PCI) 27 pirq = x86_pci_read_config8(irq_router.bdf, 28 LINK_N2V(link, base)); 29 else 30 pirq = readb(irq_router.ibase + LINK_N2V(link, base)); 31 32 pirq &= 0xf; 33 34 /* IRQ# 0/1/2/8/13 are reserved */ 35 if (pirq < 3 || pirq == 8 || pirq == 13) 36 return false; 37 38 return pirq == irq ? true : false; 39 } 40 41 int pirq_translate_link(int link) 42 { 43 return LINK_V2N(link, irq_router.link_base); 44 } 45 46 void pirq_assign_irq(int link, u8 irq) 47 { 48 int base = irq_router.link_base; 49 50 /* IRQ# 0/1/2/8/13 are reserved */ 51 if (irq < 3 || irq == 8 || irq == 13) 52 return; 53 54 if (irq_router.config == PIRQ_VIA_PCI) 55 x86_pci_write_config8(irq_router.bdf, 56 LINK_N2V(link, base), irq); 57 else 58 writeb(irq, irq_router.ibase + LINK_N2V(link, base)); 59 } 60 61 static struct irq_info *check_dup_entry(struct irq_info *slot_base, 62 int entry_num, int bus, int device) 63 { 64 struct irq_info *slot = slot_base; 65 int i; 66 67 for (i = 0; i < entry_num; i++) { 68 if (slot->bus == bus && slot->devfn == (device << 3)) 69 break; 70 slot++; 71 } 72 73 return (i == entry_num) ? NULL : slot; 74 } 75 76 static inline void fill_irq_info(struct irq_info *slot, int bus, int device, 77 int pin, int pirq) 78 { 79 slot->bus = bus; 80 slot->devfn = (device << 3) | 0; 81 slot->irq[pin - 1].link = LINK_N2V(pirq, irq_router.link_base); 82 slot->irq[pin - 1].bitmap = irq_router.irq_mask; 83 } 84 85 __weak void cpu_irq_init(void) 86 { 87 return; 88 } 89 90 static int create_pirq_routing_table(void) 91 { 92 const void *blob = gd->fdt_blob; 93 struct fdt_pci_addr addr; 94 int node; 95 int len, count; 96 const u32 *cell; 97 struct irq_routing_table *rt; 98 struct irq_info *slot, *slot_base; 99 int irq_entries = 0; 100 int i; 101 int ret; 102 103 node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_IRQ_ROUTER); 104 if (node < 0) { 105 debug("%s: Cannot find irq router node\n", __func__); 106 return -EINVAL; 107 } 108 109 ret = fdtdec_get_pci_addr(blob, node, FDT_PCI_SPACE_CONFIG, 110 "reg", &addr); 111 if (ret) 112 return ret; 113 114 /* extract the bdf from fdt_pci_addr */ 115 irq_router.bdf = addr.phys_hi & 0xffff00; 116 117 ret = fdt_find_string(blob, node, "intel,pirq-config", "pci"); 118 if (!ret) { 119 irq_router.config = PIRQ_VIA_PCI; 120 } else { 121 ret = fdt_find_string(blob, node, "intel,pirq-config", "ibase"); 122 if (!ret) 123 irq_router.config = PIRQ_VIA_IBASE; 124 else 125 return -EINVAL; 126 } 127 128 ret = fdtdec_get_int_array(blob, node, "intel,pirq-link", 129 &irq_router.link_base, 1); 130 if (ret) 131 return ret; 132 133 irq_router.irq_mask = fdtdec_get_int(blob, node, 134 "intel,pirq-mask", PIRQ_BITMAP); 135 136 if (irq_router.config == PIRQ_VIA_IBASE) { 137 int ibase_off; 138 139 ibase_off = fdtdec_get_int(blob, node, "intel,ibase-offset", 0); 140 if (!ibase_off) 141 return -EINVAL; 142 143 /* 144 * Here we assume that the IBASE register has already been 145 * properly configured by U-Boot before. 146 * 147 * By 'valid' we mean: 148 * 1) a valid memory space carved within system memory space 149 * assigned to IBASE register block. 150 * 2) memory range decoding is enabled. 151 * Hence we don't do any santify test here. 152 */ 153 irq_router.ibase = x86_pci_read_config32(irq_router.bdf, 154 ibase_off); 155 irq_router.ibase &= ~0xf; 156 } 157 158 cell = fdt_getprop(blob, node, "intel,pirq-routing", &len); 159 if (!cell) 160 return -EINVAL; 161 162 if ((len % sizeof(struct pirq_routing)) == 0) 163 count = len / sizeof(struct pirq_routing); 164 else 165 return -EINVAL; 166 167 rt = malloc(sizeof(struct irq_routing_table)); 168 if (!rt) 169 return -ENOMEM; 170 memset((char *)rt, 0, sizeof(struct irq_routing_table)); 171 172 /* Populate the PIRQ table fields */ 173 rt->signature = PIRQ_SIGNATURE; 174 rt->version = PIRQ_VERSION; 175 rt->rtr_bus = PCI_BUS(irq_router.bdf); 176 rt->rtr_devfn = (PCI_DEV(irq_router.bdf) << 3) | 177 PCI_FUNC(irq_router.bdf); 178 rt->rtr_vendor = PCI_VENDOR_ID_INTEL; 179 rt->rtr_device = PCI_DEVICE_ID_INTEL_ICH7_31; 180 181 slot_base = rt->slots; 182 183 /* Now fill in the irq_info entries in the PIRQ table */ 184 for (i = 0; i < count; i++) { 185 struct pirq_routing pr; 186 187 pr.bdf = fdt_addr_to_cpu(cell[0]); 188 pr.pin = fdt_addr_to_cpu(cell[1]); 189 pr.pirq = fdt_addr_to_cpu(cell[2]); 190 191 debug("irq_info %d: b.d.f %x.%x.%x INT%c PIRQ%c\n", 192 i, PCI_BUS(pr.bdf), PCI_DEV(pr.bdf), 193 PCI_FUNC(pr.bdf), 'A' + pr.pin - 1, 194 'A' + pr.pirq); 195 196 slot = check_dup_entry(slot_base, irq_entries, 197 PCI_BUS(pr.bdf), PCI_DEV(pr.bdf)); 198 if (slot) { 199 debug("found entry for bus %d device %d, ", 200 PCI_BUS(pr.bdf), PCI_DEV(pr.bdf)); 201 202 if (slot->irq[pr.pin - 1].link) { 203 debug("skipping\n"); 204 205 /* 206 * Sanity test on the routed PIRQ pin 207 * 208 * If they don't match, show a warning to tell 209 * there might be something wrong with the PIRQ 210 * routing information in the device tree. 211 */ 212 if (slot->irq[pr.pin - 1].link != 213 LINK_N2V(pr.pirq, irq_router.link_base)) 214 debug("WARNING: Inconsistent PIRQ routing information\n"); 215 216 cell += sizeof(struct pirq_routing) / 217 sizeof(u32); 218 continue; 219 } else { 220 debug("writing INT%c\n", 'A' + pr.pin - 1); 221 fill_irq_info(slot, PCI_BUS(pr.bdf), 222 PCI_DEV(pr.bdf), pr.pin, pr.pirq); 223 cell += sizeof(struct pirq_routing) / 224 sizeof(u32); 225 continue; 226 } 227 } 228 229 slot = slot_base + irq_entries; 230 fill_irq_info(slot, PCI_BUS(pr.bdf), PCI_DEV(pr.bdf), 231 pr.pin, pr.pirq); 232 irq_entries++; 233 cell += sizeof(struct pirq_routing) / sizeof(u32); 234 } 235 236 rt->size = irq_entries * sizeof(struct irq_info) + 32; 237 238 pirq_routing_table = rt; 239 240 return 0; 241 } 242 243 void pirq_init(void) 244 { 245 cpu_irq_init(); 246 247 if (create_pirq_routing_table()) { 248 debug("Failed to create pirq routing table\n"); 249 } else { 250 /* Route PIRQ */ 251 pirq_route_irqs(pirq_routing_table->slots, 252 get_irq_slot_count(pirq_routing_table)); 253 } 254 } 255 256 u32 write_pirq_routing_table(u32 addr) 257 { 258 if (!pirq_routing_table) 259 return addr; 260 261 return copy_pirq_routing_table(addr, pirq_routing_table); 262 } 263