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> 16*10d569eaSBin 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 1069c7dea60SBin Meng ret = fdt_find_string(blob, node, "intel,pirq-config", "pci"); 1079c7dea60SBin Meng if (!ret) { 108b46c2088SBin Meng priv->config = PIRQ_VIA_PCI; 1099c7dea60SBin Meng } else { 1109c7dea60SBin Meng ret = fdt_find_string(blob, node, "intel,pirq-config", "ibase"); 1119c7dea60SBin Meng if (!ret) 112b46c2088SBin Meng priv->config = PIRQ_VIA_IBASE; 1139c7dea60SBin Meng else 1149c7dea60SBin Meng return -EINVAL; 1159c7dea60SBin Meng } 1169c7dea60SBin Meng 1179e3ff9c2SSimon Glass ret = fdtdec_get_int(blob, node, "intel,pirq-link", -1); 1189e3ff9c2SSimon Glass if (ret == -1) 1199c7dea60SBin Meng return ret; 120b46c2088SBin Meng priv->link_base = ret; 1219c7dea60SBin Meng 122b46c2088SBin Meng priv->irq_mask = fdtdec_get_int(blob, node, 1239c7dea60SBin Meng "intel,pirq-mask", PIRQ_BITMAP); 1249c7dea60SBin Meng 12507ac84eaSBin Meng if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) { 12607ac84eaSBin Meng /* Reserve IRQ9 for SCI */ 12707ac84eaSBin Meng priv->irq_mask &= ~(1 << 9); 12807ac84eaSBin Meng } 12907ac84eaSBin Meng 130b46c2088SBin Meng if (priv->config == PIRQ_VIA_IBASE) { 1319c7dea60SBin Meng int ibase_off; 1329c7dea60SBin Meng 1339c7dea60SBin Meng ibase_off = fdtdec_get_int(blob, node, "intel,ibase-offset", 0); 1349c7dea60SBin Meng if (!ibase_off) 1359c7dea60SBin Meng return -EINVAL; 1369c7dea60SBin Meng 1379c7dea60SBin Meng /* 1389c7dea60SBin Meng * Here we assume that the IBASE register has already been 1399c7dea60SBin Meng * properly configured by U-Boot before. 1409c7dea60SBin Meng * 1419c7dea60SBin Meng * By 'valid' we mean: 1429c7dea60SBin Meng * 1) a valid memory space carved within system memory space 1439c7dea60SBin Meng * assigned to IBASE register block. 1449c7dea60SBin Meng * 2) memory range decoding is enabled. 1459c7dea60SBin Meng * Hence we don't do any santify test here. 1469c7dea60SBin Meng */ 147248c4faaSBin Meng dm_pci_read_config32(dev->parent, ibase_off, &priv->ibase); 148b46c2088SBin Meng priv->ibase &= ~0xf; 1499c7dea60SBin Meng } 1509c7dea60SBin Meng 151d4e61f50SBin Meng priv->actl_8bit = fdtdec_get_bool(blob, node, "intel,actl-8bit"); 152d4e61f50SBin Meng priv->actl_addr = fdtdec_get_int(blob, node, "intel,actl-addr", 0); 153d4e61f50SBin Meng 1549c7dea60SBin Meng cell = fdt_getprop(blob, node, "intel,pirq-routing", &len); 1559e3ff9c2SSimon Glass if (!cell || len % sizeof(struct pirq_routing)) 1569c7dea60SBin Meng return -EINVAL; 1579c7dea60SBin Meng count = len / sizeof(struct pirq_routing); 1589c7dea60SBin Meng 1599e3ff9c2SSimon Glass rt = calloc(1, sizeof(struct irq_routing_table)); 1609c7dea60SBin Meng if (!rt) 1619c7dea60SBin Meng return -ENOMEM; 1629c7dea60SBin Meng 1639c7dea60SBin Meng /* Populate the PIRQ table fields */ 1649c7dea60SBin Meng rt->signature = PIRQ_SIGNATURE; 1659c7dea60SBin Meng rt->version = PIRQ_VERSION; 166b46c2088SBin Meng rt->rtr_bus = PCI_BUS(priv->bdf); 167b46c2088SBin Meng rt->rtr_devfn = (PCI_DEV(priv->bdf) << 3) | PCI_FUNC(priv->bdf); 1689c7dea60SBin Meng rt->rtr_vendor = PCI_VENDOR_ID_INTEL; 1699c7dea60SBin Meng rt->rtr_device = PCI_DEVICE_ID_INTEL_ICH7_31; 1709c7dea60SBin Meng 171df81749dSBin Meng slot_base = rt->slots; 1729c7dea60SBin Meng 1739c7dea60SBin Meng /* Now fill in the irq_info entries in the PIRQ table */ 1749e3ff9c2SSimon Glass for (i = 0; i < count; 1759e3ff9c2SSimon Glass i++, cell += sizeof(struct pirq_routing) / sizeof(u32)) { 1769c7dea60SBin Meng struct pirq_routing pr; 1779c7dea60SBin Meng 1789c7dea60SBin Meng pr.bdf = fdt_addr_to_cpu(cell[0]); 1799c7dea60SBin Meng pr.pin = fdt_addr_to_cpu(cell[1]); 1809c7dea60SBin Meng pr.pirq = fdt_addr_to_cpu(cell[2]); 1819c7dea60SBin Meng 1829c7dea60SBin Meng debug("irq_info %d: b.d.f %x.%x.%x INT%c PIRQ%c\n", 1839c7dea60SBin Meng i, PCI_BUS(pr.bdf), PCI_DEV(pr.bdf), 1849c7dea60SBin Meng PCI_FUNC(pr.bdf), 'A' + pr.pin - 1, 1859c7dea60SBin Meng 'A' + pr.pirq); 186df81749dSBin Meng 187df81749dSBin Meng slot = check_dup_entry(slot_base, irq_entries, 188df81749dSBin Meng PCI_BUS(pr.bdf), PCI_DEV(pr.bdf)); 189df81749dSBin Meng if (slot) { 190df81749dSBin Meng debug("found entry for bus %d device %d, ", 191df81749dSBin Meng PCI_BUS(pr.bdf), PCI_DEV(pr.bdf)); 192df81749dSBin Meng 193df81749dSBin Meng if (slot->irq[pr.pin - 1].link) { 194df81749dSBin Meng debug("skipping\n"); 195df81749dSBin Meng 196df81749dSBin Meng /* 197df81749dSBin Meng * Sanity test on the routed PIRQ pin 198df81749dSBin Meng * 199df81749dSBin Meng * If they don't match, show a warning to tell 200df81749dSBin Meng * there might be something wrong with the PIRQ 201df81749dSBin Meng * routing information in the device tree. 202df81749dSBin Meng */ 203df81749dSBin Meng if (slot->irq[pr.pin - 1].link != 204b46c2088SBin Meng LINK_N2V(pr.pirq, priv->link_base)) 205df81749dSBin Meng debug("WARNING: Inconsistent PIRQ routing information\n"); 206df81749dSBin Meng continue; 2079e3ff9c2SSimon Glass } 208df81749dSBin Meng } else { 2099e3ff9c2SSimon Glass slot = slot_base + irq_entries++; 2109e3ff9c2SSimon Glass } 211df81749dSBin Meng debug("writing INT%c\n", 'A' + pr.pin - 1); 212b46c2088SBin Meng fill_irq_info(priv, slot, PCI_BUS(pr.bdf), PCI_DEV(pr.bdf), 213b46c2088SBin Meng pr.pin, pr.pirq); 2149c7dea60SBin Meng } 2159c7dea60SBin Meng 2169c7dea60SBin Meng rt->size = irq_entries * sizeof(struct irq_info) + 32; 2179c7dea60SBin Meng 218*10d569eaSBin Meng /* Fix up the table checksum */ 219*10d569eaSBin Meng rt->checksum = table_compute_checksum(rt, rt->size); 220*10d569eaSBin Meng 2219c7dea60SBin Meng pirq_routing_table = rt; 2229c7dea60SBin Meng 2239c7dea60SBin Meng return 0; 2249c7dea60SBin Meng } 2259c7dea60SBin Meng 226d4e61f50SBin Meng static void irq_enable_sci(struct udevice *dev) 227d4e61f50SBin Meng { 228d4e61f50SBin Meng struct irq_router *priv = dev_get_priv(dev); 229d4e61f50SBin Meng 230d4e61f50SBin Meng if (priv->actl_8bit) { 231d4e61f50SBin Meng /* Bit7 must be turned on to enable ACPI */ 232d4e61f50SBin Meng dm_pci_write_config8(dev->parent, priv->actl_addr, 0x80); 233d4e61f50SBin Meng } else { 234d4e61f50SBin Meng /* Write 0 to enable SCI on IRQ9 */ 235d4e61f50SBin Meng if (priv->config == PIRQ_VIA_PCI) 236d4e61f50SBin Meng dm_pci_write_config32(dev->parent, priv->actl_addr, 0); 237d4e61f50SBin Meng else 238d4e61f50SBin Meng writel(0, priv->ibase + priv->actl_addr); 239d4e61f50SBin Meng } 240d4e61f50SBin Meng } 241d4e61f50SBin Meng 242d3b884b2SSimon Glass int irq_router_common_init(struct udevice *dev) 243e76187a3SSimon Glass { 2447e4be120SSimon Glass int ret; 2457e4be120SSimon Glass 246b565d66dSSimon Glass ret = create_pirq_routing_table(dev); 2477e4be120SSimon Glass if (ret) { 2489c7dea60SBin Meng debug("Failed to create pirq routing table\n"); 2497e4be120SSimon Glass return ret; 2507e4be120SSimon Glass } 2519c7dea60SBin Meng /* Route PIRQ */ 252b46c2088SBin Meng pirq_route_irqs(dev, pirq_routing_table->slots, 2539c7dea60SBin Meng get_irq_slot_count(pirq_routing_table)); 2547e4be120SSimon Glass 255d4e61f50SBin Meng if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) 256d4e61f50SBin Meng irq_enable_sci(dev); 257d4e61f50SBin Meng 2587e4be120SSimon Glass return 0; 2599c7dea60SBin Meng } 2609c7dea60SBin Meng 261d3b884b2SSimon Glass int irq_router_probe(struct udevice *dev) 262d3b884b2SSimon Glass { 263d3b884b2SSimon Glass return irq_router_common_init(dev); 264d3b884b2SSimon Glass } 265d3b884b2SSimon Glass 2669c7dea60SBin Meng u32 write_pirq_routing_table(u32 addr) 2679c7dea60SBin Meng { 26867b24970SBin Meng if (!pirq_routing_table) 26967b24970SBin Meng return addr; 27067b24970SBin Meng 2719c7dea60SBin Meng return copy_pirq_routing_table(addr, pirq_routing_table); 2729c7dea60SBin Meng } 273e76187a3SSimon Glass 274e76187a3SSimon Glass static const struct udevice_id irq_router_ids[] = { 275e76187a3SSimon Glass { .compatible = "intel,irq-router" }, 276e76187a3SSimon Glass { } 277e76187a3SSimon Glass }; 278e76187a3SSimon Glass 279e76187a3SSimon Glass U_BOOT_DRIVER(irq_router_drv) = { 280e76187a3SSimon Glass .name = "intel_irq", 281e76187a3SSimon Glass .id = UCLASS_IRQ, 282e76187a3SSimon Glass .of_match = irq_router_ids, 283e76187a3SSimon Glass .probe = irq_router_probe, 284b46c2088SBin Meng .priv_auto_alloc_size = sizeof(struct irq_router), 285e76187a3SSimon Glass }; 286e76187a3SSimon Glass 287e76187a3SSimon Glass UCLASS_DRIVER(irq) = { 288e76187a3SSimon Glass .id = UCLASS_IRQ, 289e76187a3SSimon Glass .name = "irq", 290e76187a3SSimon Glass }; 291