19c7dea60SBin Meng /* 29c7dea60SBin Meng * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> 39c7dea60SBin Meng * 49c7dea60SBin Meng * SPDX-License-Identifier: GPL-2.0+ 59c7dea60SBin Meng */ 69c7dea60SBin Meng 79c7dea60SBin Meng #include <common.h> 8e76187a3SSimon Glass #include <dm.h> 99c7dea60SBin Meng #include <errno.h> 109c7dea60SBin Meng #include <fdtdec.h> 119c7dea60SBin Meng #include <malloc.h> 129c7dea60SBin Meng #include <asm/io.h> 139c7dea60SBin Meng #include <asm/irq.h> 149c7dea60SBin Meng #include <asm/pci.h> 159c7dea60SBin Meng #include <asm/pirq_routing.h> 1610d569eaSBin Meng #include <asm/tables.h> 179c7dea60SBin Meng 189c7dea60SBin Meng DECLARE_GLOBAL_DATA_PTR; 199c7dea60SBin Meng 209c7dea60SBin Meng static struct irq_routing_table *pirq_routing_table; 219c7dea60SBin Meng 22b46c2088SBin Meng bool pirq_check_irq_routed(struct udevice *dev, int link, u8 irq) 239c7dea60SBin Meng { 24b46c2088SBin Meng struct irq_router *priv = dev_get_priv(dev); 259c7dea60SBin Meng u8 pirq; 26b46c2088SBin Meng int base = priv->link_base; 279c7dea60SBin Meng 28b46c2088SBin Meng if (priv->config == PIRQ_VIA_PCI) 29248c4faaSBin Meng dm_pci_read_config8(dev->parent, LINK_N2V(link, base), &pirq); 309c7dea60SBin Meng else 31b46c2088SBin Meng pirq = readb(priv->ibase + LINK_N2V(link, base)); 329c7dea60SBin Meng 339c7dea60SBin Meng pirq &= 0xf; 349c7dea60SBin Meng 359c7dea60SBin Meng /* IRQ# 0/1/2/8/13 are reserved */ 369c7dea60SBin Meng if (pirq < 3 || pirq == 8 || pirq == 13) 379c7dea60SBin Meng return false; 389c7dea60SBin Meng 399c7dea60SBin Meng return pirq == irq ? true : false; 409c7dea60SBin Meng } 419c7dea60SBin Meng 42b46c2088SBin Meng int pirq_translate_link(struct udevice *dev, int link) 439c7dea60SBin Meng { 44b46c2088SBin Meng struct irq_router *priv = dev_get_priv(dev); 45b46c2088SBin Meng 46b46c2088SBin Meng return LINK_V2N(link, priv->link_base); 479c7dea60SBin Meng } 489c7dea60SBin Meng 49b46c2088SBin Meng void pirq_assign_irq(struct udevice *dev, int link, u8 irq) 509c7dea60SBin Meng { 51b46c2088SBin Meng struct irq_router *priv = dev_get_priv(dev); 52b46c2088SBin Meng int base = priv->link_base; 539c7dea60SBin Meng 549c7dea60SBin Meng /* IRQ# 0/1/2/8/13 are reserved */ 559c7dea60SBin Meng if (irq < 3 || irq == 8 || irq == 13) 569c7dea60SBin Meng return; 579c7dea60SBin Meng 58b46c2088SBin Meng if (priv->config == PIRQ_VIA_PCI) 59248c4faaSBin Meng dm_pci_write_config8(dev->parent, LINK_N2V(link, base), irq); 609c7dea60SBin Meng else 61b46c2088SBin Meng writeb(irq, priv->ibase + LINK_N2V(link, base)); 629c7dea60SBin Meng } 639c7dea60SBin Meng 64df81749dSBin Meng static struct irq_info *check_dup_entry(struct irq_info *slot_base, 65df81749dSBin Meng int entry_num, int bus, int device) 669c7dea60SBin Meng { 67df81749dSBin Meng struct irq_info *slot = slot_base; 68df81749dSBin Meng int i; 699c7dea60SBin Meng 70df81749dSBin Meng for (i = 0; i < entry_num; i++) { 71df81749dSBin Meng if (slot->bus == bus && slot->devfn == (device << 3)) 72df81749dSBin Meng break; 73df81749dSBin Meng slot++; 74df81749dSBin Meng } 75df81749dSBin Meng 76df81749dSBin Meng return (i == entry_num) ? NULL : slot; 77df81749dSBin Meng } 78df81749dSBin Meng 79b46c2088SBin Meng static inline void fill_irq_info(struct irq_router *priv, struct irq_info *slot, 80b46c2088SBin Meng int bus, int device, int pin, int pirq) 81df81749dSBin Meng { 829c7dea60SBin Meng slot->bus = bus; 838c38e4d0SBin Meng slot->devfn = (device << 3) | 0; 84b46c2088SBin Meng slot->irq[pin - 1].link = LINK_N2V(pirq, priv->link_base); 85b46c2088SBin Meng slot->irq[pin - 1].bitmap = priv->irq_mask; 869c7dea60SBin Meng } 879c7dea60SBin Meng 88b565d66dSSimon Glass static int create_pirq_routing_table(struct udevice *dev) 899c7dea60SBin Meng { 90b46c2088SBin Meng struct irq_router *priv = dev_get_priv(dev); 919c7dea60SBin Meng const void *blob = gd->fdt_blob; 929c7dea60SBin Meng int node; 939c7dea60SBin Meng int len, count; 949c7dea60SBin Meng const u32 *cell; 959c7dea60SBin Meng struct irq_routing_table *rt; 96df81749dSBin Meng struct irq_info *slot, *slot_base; 979c7dea60SBin Meng int irq_entries = 0; 989c7dea60SBin Meng int i; 999c7dea60SBin Meng int ret; 1009c7dea60SBin Meng 101b565d66dSSimon Glass node = dev->of_offset; 1029c7dea60SBin Meng 1039c7dea60SBin Meng /* extract the bdf from fdt_pci_addr */ 104b46c2088SBin Meng priv->bdf = dm_pci_get_bdf(dev->parent); 1059c7dea60SBin Meng 106b02e4044SSimon Glass ret = fdt_stringlist_search(blob, node, "intel,pirq-config", "pci"); 1079c7dea60SBin Meng if (!ret) { 108b46c2088SBin Meng priv->config = PIRQ_VIA_PCI; 1099c7dea60SBin Meng } else { 110b02e4044SSimon Glass ret = fdt_stringlist_search(blob, node, "intel,pirq-config", 111b02e4044SSimon Glass "ibase"); 1129c7dea60SBin Meng if (!ret) 113b46c2088SBin Meng priv->config = PIRQ_VIA_IBASE; 1149c7dea60SBin Meng else 1159c7dea60SBin Meng return -EINVAL; 1169c7dea60SBin Meng } 1179c7dea60SBin Meng 1189e3ff9c2SSimon Glass ret = fdtdec_get_int(blob, node, "intel,pirq-link", -1); 1199e3ff9c2SSimon Glass if (ret == -1) 1209c7dea60SBin Meng return ret; 121b46c2088SBin Meng priv->link_base = ret; 1229c7dea60SBin Meng 123b46c2088SBin Meng priv->irq_mask = fdtdec_get_int(blob, node, 1249c7dea60SBin Meng "intel,pirq-mask", PIRQ_BITMAP); 1259c7dea60SBin Meng 12607ac84eaSBin Meng if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) { 12707ac84eaSBin Meng /* Reserve IRQ9 for SCI */ 12807ac84eaSBin Meng priv->irq_mask &= ~(1 << 9); 12907ac84eaSBin Meng } 13007ac84eaSBin Meng 131b46c2088SBin Meng if (priv->config == PIRQ_VIA_IBASE) { 1329c7dea60SBin Meng int ibase_off; 1339c7dea60SBin Meng 1349c7dea60SBin Meng ibase_off = fdtdec_get_int(blob, node, "intel,ibase-offset", 0); 1359c7dea60SBin Meng if (!ibase_off) 1369c7dea60SBin Meng return -EINVAL; 1379c7dea60SBin Meng 1389c7dea60SBin Meng /* 1399c7dea60SBin Meng * Here we assume that the IBASE register has already been 1409c7dea60SBin Meng * properly configured by U-Boot before. 1419c7dea60SBin Meng * 1429c7dea60SBin Meng * By 'valid' we mean: 1439c7dea60SBin Meng * 1) a valid memory space carved within system memory space 1449c7dea60SBin Meng * assigned to IBASE register block. 1459c7dea60SBin Meng * 2) memory range decoding is enabled. 1469c7dea60SBin Meng * Hence we don't do any santify test here. 1479c7dea60SBin Meng */ 148248c4faaSBin Meng dm_pci_read_config32(dev->parent, ibase_off, &priv->ibase); 149b46c2088SBin Meng priv->ibase &= ~0xf; 1509c7dea60SBin Meng } 1519c7dea60SBin Meng 152d4e61f50SBin Meng priv->actl_8bit = fdtdec_get_bool(blob, node, "intel,actl-8bit"); 153d4e61f50SBin Meng priv->actl_addr = fdtdec_get_int(blob, node, "intel,actl-addr", 0); 154d4e61f50SBin Meng 1559c7dea60SBin Meng cell = fdt_getprop(blob, node, "intel,pirq-routing", &len); 1569e3ff9c2SSimon Glass if (!cell || len % sizeof(struct pirq_routing)) 1579c7dea60SBin Meng return -EINVAL; 1589c7dea60SBin Meng count = len / sizeof(struct pirq_routing); 1599c7dea60SBin Meng 1609e3ff9c2SSimon Glass rt = calloc(1, sizeof(struct irq_routing_table)); 1619c7dea60SBin Meng if (!rt) 1629c7dea60SBin Meng return -ENOMEM; 1639c7dea60SBin Meng 1649c7dea60SBin Meng /* Populate the PIRQ table fields */ 1659c7dea60SBin Meng rt->signature = PIRQ_SIGNATURE; 1669c7dea60SBin Meng rt->version = PIRQ_VERSION; 167b46c2088SBin Meng rt->rtr_bus = PCI_BUS(priv->bdf); 168b46c2088SBin Meng rt->rtr_devfn = (PCI_DEV(priv->bdf) << 3) | PCI_FUNC(priv->bdf); 1699c7dea60SBin Meng rt->rtr_vendor = PCI_VENDOR_ID_INTEL; 1709c7dea60SBin Meng rt->rtr_device = PCI_DEVICE_ID_INTEL_ICH7_31; 1719c7dea60SBin Meng 172df81749dSBin Meng slot_base = rt->slots; 1739c7dea60SBin Meng 1749c7dea60SBin Meng /* Now fill in the irq_info entries in the PIRQ table */ 1759e3ff9c2SSimon Glass for (i = 0; i < count; 1769e3ff9c2SSimon Glass i++, cell += sizeof(struct pirq_routing) / sizeof(u32)) { 1779c7dea60SBin Meng struct pirq_routing pr; 1789c7dea60SBin Meng 1799c7dea60SBin Meng pr.bdf = fdt_addr_to_cpu(cell[0]); 1809c7dea60SBin Meng pr.pin = fdt_addr_to_cpu(cell[1]); 1819c7dea60SBin Meng pr.pirq = fdt_addr_to_cpu(cell[2]); 1829c7dea60SBin Meng 1839c7dea60SBin Meng debug("irq_info %d: b.d.f %x.%x.%x INT%c PIRQ%c\n", 1849c7dea60SBin Meng i, PCI_BUS(pr.bdf), PCI_DEV(pr.bdf), 1859c7dea60SBin Meng PCI_FUNC(pr.bdf), 'A' + pr.pin - 1, 1869c7dea60SBin Meng 'A' + pr.pirq); 187df81749dSBin Meng 188df81749dSBin Meng slot = check_dup_entry(slot_base, irq_entries, 189df81749dSBin Meng PCI_BUS(pr.bdf), PCI_DEV(pr.bdf)); 190df81749dSBin Meng if (slot) { 191df81749dSBin Meng debug("found entry for bus %d device %d, ", 192df81749dSBin Meng PCI_BUS(pr.bdf), PCI_DEV(pr.bdf)); 193df81749dSBin Meng 194df81749dSBin Meng if (slot->irq[pr.pin - 1].link) { 195df81749dSBin Meng debug("skipping\n"); 196df81749dSBin Meng 197df81749dSBin Meng /* 198df81749dSBin Meng * Sanity test on the routed PIRQ pin 199df81749dSBin Meng * 200df81749dSBin Meng * If they don't match, show a warning to tell 201df81749dSBin Meng * there might be something wrong with the PIRQ 202df81749dSBin Meng * routing information in the device tree. 203df81749dSBin Meng */ 204df81749dSBin Meng if (slot->irq[pr.pin - 1].link != 205b46c2088SBin Meng LINK_N2V(pr.pirq, priv->link_base)) 206df81749dSBin Meng debug("WARNING: Inconsistent PIRQ routing information\n"); 207df81749dSBin Meng continue; 2089e3ff9c2SSimon Glass } 209df81749dSBin Meng } else { 2109e3ff9c2SSimon Glass slot = slot_base + irq_entries++; 2119e3ff9c2SSimon Glass } 212df81749dSBin Meng debug("writing INT%c\n", 'A' + pr.pin - 1); 213b46c2088SBin Meng fill_irq_info(priv, slot, PCI_BUS(pr.bdf), PCI_DEV(pr.bdf), 214b46c2088SBin Meng pr.pin, pr.pirq); 2159c7dea60SBin Meng } 2169c7dea60SBin Meng 2179c7dea60SBin Meng rt->size = irq_entries * sizeof(struct irq_info) + 32; 2189c7dea60SBin Meng 21910d569eaSBin Meng /* Fix up the table checksum */ 22010d569eaSBin Meng rt->checksum = table_compute_checksum(rt, rt->size); 22110d569eaSBin Meng 2229c7dea60SBin Meng pirq_routing_table = rt; 2239c7dea60SBin Meng 2249c7dea60SBin Meng return 0; 2259c7dea60SBin Meng } 2269c7dea60SBin Meng 227d4e61f50SBin Meng static void irq_enable_sci(struct udevice *dev) 228d4e61f50SBin Meng { 229d4e61f50SBin Meng struct irq_router *priv = dev_get_priv(dev); 230d4e61f50SBin Meng 231d4e61f50SBin Meng if (priv->actl_8bit) { 232d4e61f50SBin Meng /* Bit7 must be turned on to enable ACPI */ 233d4e61f50SBin Meng dm_pci_write_config8(dev->parent, priv->actl_addr, 0x80); 234d4e61f50SBin Meng } else { 235d4e61f50SBin Meng /* Write 0 to enable SCI on IRQ9 */ 236d4e61f50SBin Meng if (priv->config == PIRQ_VIA_PCI) 237d4e61f50SBin Meng dm_pci_write_config32(dev->parent, priv->actl_addr, 0); 238d4e61f50SBin Meng else 239d4e61f50SBin Meng writel(0, priv->ibase + priv->actl_addr); 240d4e61f50SBin Meng } 241d4e61f50SBin Meng } 242d4e61f50SBin Meng 243d3b884b2SSimon Glass int irq_router_common_init(struct udevice *dev) 244e76187a3SSimon Glass { 2457e4be120SSimon Glass int ret; 2467e4be120SSimon Glass 247b565d66dSSimon Glass ret = create_pirq_routing_table(dev); 2487e4be120SSimon Glass if (ret) { 2499c7dea60SBin Meng debug("Failed to create pirq routing table\n"); 2507e4be120SSimon Glass return ret; 2517e4be120SSimon Glass } 2529c7dea60SBin Meng /* Route PIRQ */ 253b46c2088SBin Meng pirq_route_irqs(dev, pirq_routing_table->slots, 2549c7dea60SBin Meng get_irq_slot_count(pirq_routing_table)); 2557e4be120SSimon Glass 256d4e61f50SBin Meng if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) 257d4e61f50SBin Meng irq_enable_sci(dev); 258d4e61f50SBin Meng 2597e4be120SSimon Glass return 0; 2609c7dea60SBin Meng } 2619c7dea60SBin Meng 262d3b884b2SSimon Glass int irq_router_probe(struct udevice *dev) 263d3b884b2SSimon Glass { 264d3b884b2SSimon Glass return irq_router_common_init(dev); 265d3b884b2SSimon Glass } 266d3b884b2SSimon Glass 267*42fd8c19SSimon Glass ulong write_pirq_routing_table(ulong addr) 2689c7dea60SBin Meng { 26967b24970SBin Meng if (!pirq_routing_table) 27067b24970SBin Meng return addr; 27167b24970SBin Meng 2729c7dea60SBin Meng return copy_pirq_routing_table(addr, pirq_routing_table); 2739c7dea60SBin Meng } 274e76187a3SSimon Glass 275e76187a3SSimon Glass static const struct udevice_id irq_router_ids[] = { 276e76187a3SSimon Glass { .compatible = "intel,irq-router" }, 277e76187a3SSimon Glass { } 278e76187a3SSimon Glass }; 279e76187a3SSimon Glass 280e76187a3SSimon Glass U_BOOT_DRIVER(irq_router_drv) = { 281e76187a3SSimon Glass .name = "intel_irq", 282e76187a3SSimon Glass .id = UCLASS_IRQ, 283e76187a3SSimon Glass .of_match = irq_router_ids, 284e76187a3SSimon Glass .probe = irq_router_probe, 285b46c2088SBin Meng .priv_auto_alloc_size = sizeof(struct irq_router), 286e76187a3SSimon Glass }; 287e76187a3SSimon Glass 288e76187a3SSimon Glass UCLASS_DRIVER(irq) = { 289e76187a3SSimon Glass .id = UCLASS_IRQ, 290e76187a3SSimon Glass .name = "irq", 291e76187a3SSimon Glass }; 292