/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "chip.h" /* PCH I/O APIC redirection entries */ #define PCH_REDIR_ETR 120 /** * Set miscellaneous static southbridge features. * * @param dev PCI device with I/O APIC control registers */ static void pch_enable_ioapic(struct device *dev) { /* affirm full set of redirection table entries ("write once") */ ioapic_set_max_vectors(VIO_APIC_VADDR, PCH_REDIR_ETR); register_new_ioapic_gsi0((void *)IO_APIC_ADDR); } /* interrupt router lookup for internal devices */ struct dnv_ir_lut { /* (dev << 3) | fn */ u8 devfn; u8 ir; }; #define DEVFN(dev, fn) ((dev << 3) | (fn)) static const struct dnv_ir_lut dnv_ir_lut[] = { {.devfn = DEVFN(0x05, 0), .ir = 3}, /* RCEC */ {.devfn = DEVFN(0x06, 0), .ir = 4}, /* Virtual RP to QAT */ {.devfn = DEVFN(0x09, 0), .ir = 7}, /* PCIe RP0 */ {.devfn = DEVFN(0x0a, 0), .ir = 7}, /* PCIe RP1 */ {.devfn = DEVFN(0x0b, 0), .ir = 7}, /* PCIe RP2 */ {.devfn = DEVFN(0x0c, 0), .ir = 7}, /* PCIe RP3 */ {.devfn = DEVFN(0x0e, 0), .ir = 8}, /* PCIe RP4 */ {.devfn = DEVFN(0x0f, 0), .ir = 8}, /* PCIe RP5 */ {.devfn = DEVFN(0x10, 0), .ir = 8}, /* PCIe RP6 */ {.devfn = DEVFN(0x11, 0), .ir = 8}, /* PCIe RP7 */ {.devfn = DEVFN(0x12, 0), .ir = 10}, /* SMBus - Host */ {.devfn = DEVFN(0x13, 0), .ir = 6}, /* AHCI0 */ {.devfn = DEVFN(0x14, 0), .ir = 11}, /* AHCI1 */ {.devfn = DEVFN(0x15, 0), .ir = 9}, /* USB */ {.devfn = DEVFN(0x16, 0), .ir = 1}, /* Virtual RP to LAN0 */ {.devfn = DEVFN(0x17, 0), .ir = 2}, /* Virtual RP to LAN1 */ {.devfn = DEVFN(0x18, 0), .ir = 5}, /* ME HECI1 */ {.devfn = DEVFN(0x18, 1), .ir = 5}, /* ME HECI1 */ {.devfn = DEVFN(0x18, 2), .ir = 5}, /* ME PTIO-IDER */ {.devfn = DEVFN(0x18, 3), .ir = 5}, /* ME PTIO-KT */ {.devfn = DEVFN(0x18, 4), .ir = 5}, /* ME HECI3 */ {.devfn = DEVFN(0x1a, 0), .ir = 10}, /* HSUART0 */ {.devfn = DEVFN(0x1a, 1), .ir = 10}, /* HSUART1 */ {.devfn = DEVFN(0x1a, 2), .ir = 10}, /* HSUART2 */ {.devfn = DEVFN(0x1b, 0), .ir = 12}, /* IE HECI1 */ {.devfn = DEVFN(0x1b, 1), .ir = 12}, /* IE HECI1 */ {.devfn = DEVFN(0x1b, 2), .ir = 12}, /* IE PTIO-IDER */ {.devfn = DEVFN(0x1b, 3), .ir = 12}, /* IE PTIO-KT */ {.devfn = DEVFN(0x1b, 4), .ir = 12}, /* IE HECI3 */ {.devfn = DEVFN(0x1c, 0), .ir = 12}, /* SDHCI */ {.devfn = DEVFN(0x1f, 0), .ir = 0}, /* LPC */ {.devfn = DEVFN(0x1f, 1), .ir = 0}, /* PS2B */ {.devfn = DEVFN(0x1f, 4), .ir = 0}, /* SMBus - Legacy */ {.devfn = DEVFN(0x1f, 7), .ir = 0}, /* Trace Hub */ }; /* * Only 6 of the 8 root ports have swizzling, return '1' if this bdf is one of * them, '0' otherwise */ static int is_dnv_swizzled_rp(uint16_t bdf) { switch (bdf) { case DEVFN(10, 0): case DEVFN(11, 0): case DEVFN(12, 0): case DEVFN(15, 0): case DEVFN(16, 0): case DEVFN(17, 0): return 1; } return 0; } /* * Figure out which upstream interrupt pin a downstream device gets swizzled to * * config - pointer to chip_info containing routing info * devfn - device/function of root port to check swizzling for * pin - interrupt pin 1-4 = A-D * * Return new pin mapping, 0 if invalid pin */ static int dnv_get_swizzled_pin(config_t *config, u8 devfn, u8 pin) { if (pin < 1 || pin > 4) return 0; devfn >>= 3; if (devfn < 13) devfn -= 9; else devfn -= 14; return ((pin - 1 + devfn) % 4) + 1; } /* * Figure out which upstream interrupt pin a downstream device gets swizzled to * * config - pointer to chip_info containing routing info * devfn - device/function of root port to check swizzling for * pin - interrupt pin 1-4 = A-D * * Return new pin mapping, 0 if invalid pin */ static int dnv_get_ir(config_t *config, u8 devfn, u8 pin) { int i = 0; int line = 0xff; u16 ir = 0xffff; /* The only valid pin values are 1-4 for A-D */ if (pin < 1 || pin > 4) { printk(BIOS_WARNING, "%s: pin %d is invalid\n", __func__, pin); goto dnv_get_ir_done; } for (i = 0; i < ARRAY_SIZE(dnv_ir_lut); i++) { if (dnv_ir_lut[i].devfn == devfn) break; } if (i == ARRAY_SIZE(dnv_ir_lut)) { printk(BIOS_WARNING, "%s: no entry\n", __func__); goto dnv_get_ir_done; } switch (dnv_ir_lut[i].ir) { case 0: ir = config->ir00_routing; break; case 1: ir = config->ir01_routing; break; case 2: ir = config->ir02_routing; break; case 3: ir = config->ir03_routing; break; case 4: ir = config->ir04_routing; break; case 5: ir = config->ir05_routing; break; case 6: ir = config->ir06_routing; break; case 7: ir = config->ir07_routing; break; case 8: ir = config->ir08_routing; break; case 9: ir = config->ir09_routing; break; case 10: ir = config->ir10_routing; break; case 11: ir = config->ir11_routing; break; case 12: ir = config->ir12_routing; break; default: printk(BIOS_ERR, "%s: invalid ir %d for entry %d\n", __func__, dnv_ir_lut[i].ir, i); goto dnv_get_ir_done; } ir >>= (pin - 1) * 4; ir &= 0xf; switch (ir) { case 0: line = config->pirqa_routing; break; case 1: line = config->pirqb_routing; break; case 2: line = config->pirqc_routing; break; case 3: line = config->pirqd_routing; break; case 4: line = config->pirqe_routing; break; case 5: line = config->pirqf_routing; break; case 6: line = config->pirqg_routing; break; case 7: line = config->pirqh_routing; break; default: printk(BIOS_ERR, "%s: invalid ir pirq %d for entry %d\n", __func__, ir, i); break; } dnv_get_ir_done: return line; } /* * PCI devices have the INT_LINE (0x3C) and INT_PIN (0x3D) registers which * report interrupt routing information to operating systems and drivers. The * INT_PIN register is generally read only and reports which interrupt pin * A - D it uses. The INT_LINE register is configurable and reports which IRQ * (generally the PIC IRQs 1 - 15) it will use. This needs to take interrupt * pin swizzling on devices that are downstream on a PCI bridge into account. */ static u8 dnv_get_int_line(struct device *irq_dev) { config_t *config; struct device *targ_dev = NULL; uint16_t parent_bdf = 0; int8_t original_int_pin = 0, new_int_pin = 0, swiz_int_pin = 0; uint8_t int_line = 0xff; if (!is_enabled_pci(irq_dev)) { printk(BIOS_ERR, "%s for non pci device?\n", __func__); goto dnv_get_int_line_done; } /* * Get the INT_PIN swizzled up to the root port if necessary * using the existing coreboot pci_device code */ original_int_pin = pci_read_config8(irq_dev, PCI_INTERRUPT_PIN); new_int_pin = get_pci_irq_pins(irq_dev, &targ_dev); if (targ_dev == NULL || new_int_pin < 1) goto dnv_get_int_line_done; printk(BIOS_DEBUG, "%s: irq_dev %s, targ_dev %s:\n", __func__, dev_path(irq_dev), dev_path(targ_dev)); printk(BIOS_DEBUG, "%s: std swizzle %s from %c to %c\n", __func__, dev_path(targ_dev), '@' + original_int_pin, '@' + new_int_pin); /* Swizzle this device if needed */ config = targ_dev->chip_info; parent_bdf = targ_dev->path.pci.devfn | targ_dev->upstream->secondary << 8; if (is_dnv_swizzled_rp(parent_bdf) && irq_dev != targ_dev) { swiz_int_pin = dnv_get_swizzled_pin(config, parent_bdf, new_int_pin); printk(BIOS_DEBUG, "%s: dnv swizzle %s from %c to %c\n", __func__, dev_path(targ_dev), '@' + new_int_pin, '@' + swiz_int_pin); } else { swiz_int_pin = new_int_pin; } /* Look up the routing for the pin */ int_line = dnv_get_ir(config, parent_bdf, swiz_int_pin); dnv_get_int_line_done: printk(BIOS_DEBUG, "\tINT_LINE\t\t: %d\n", int_line); return int_line; } /* PIRQ[n]_ROUT[3:0] - PIRQ Routing Control * 0x00 - 0000 = Reserved * 0x01 - 0001 = Reserved * 0x02 - 0010 = Reserved * 0x03 - 0011 = IRQ3 * 0x04 - 0100 = IRQ4 * 0x05 - 0101 = IRQ5 * 0x06 - 0110 = IRQ6 * 0x07 - 0111 = IRQ7 * 0x08 - 1000 = Reserved * 0x09 - 1001 = IRQ9 * 0x0A - 1010 = IRQ10 * 0x0B - 1011 = IRQ11 * 0x0C - 1100 = IRQ12 * 0x0D - 1101 = Reserved * 0x0E - 1110 = IRQ14 * 0x0F - 1111 = IRQ15 * PIRQ[n]_ROUT[7] - PIRQ Routing Control * 0x80 - The PIRQ is not routed. */ static void pch_pirq_init(struct device *dev) { struct device *irq_dev; /* Get the chip configuration */ config_t *config = config_of(dev); /* Initialize PIRQ Routings */ write8((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIRQA_ROUT), config->pirqa_routing); write8((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIRQB_ROUT), config->pirqb_routing); write8((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIRQC_ROUT), config->pirqc_routing); write8((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIRQD_ROUT), config->pirqd_routing); write8((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIRQE_ROUT), config->pirqe_routing); write8((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIRQF_ROUT), config->pirqf_routing); write8((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIRQG_ROUT), config->pirqg_routing); write8((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIRQH_ROUT), config->pirqh_routing); /* Initialize device's Interrupt Routings */ write16((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIR00), config->ir00_routing); write16((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIR01), config->ir01_routing); write16((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIR02), config->ir02_routing); write16((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIR03), config->ir03_routing); write16((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIR04), config->ir04_routing); write16((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIR05), config->ir05_routing); write16((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIR06), config->ir06_routing); write16((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIR07), config->ir07_routing); write16((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIR08), config->ir08_routing); write16((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIR09), config->ir09_routing); write16((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIR10), config->ir10_routing); write16((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIR11), config->ir11_routing); write16((void *)PCH_PCR_ADDRESS(PID_ITSS, PCR_ITSS_PIR12), config->ir12_routing); /* Initialize device's Interrupt Polarity Control */ write32((void *)PCH_PCR_ADDRESS(PID_ITSS, PCH_PCR_ITSS_IPC0), config->ipc0); write32((void *)PCH_PCR_ADDRESS(PID_ITSS, PCH_PCR_ITSS_IPC1), config->ipc1); write32((void *)PCH_PCR_ADDRESS(PID_ITSS, PCH_PCR_ITSS_IPC2), config->ipc2); write32((void *)PCH_PCR_ADDRESS(PID_ITSS, PCH_PCR_ITSS_IPC3), config->ipc3); for (irq_dev = all_devices; irq_dev; irq_dev = irq_dev->next) { int devfn = irq_dev->path.pci.devfn; u8 int_pin = 0, int_line = 0; if (!is_enabled_pci(irq_dev)) continue; int_pin = pci_read_config8(irq_dev, PCI_INTERRUPT_PIN); int_line = dnv_get_int_line(irq_dev); printk(BIOS_DEBUG, "%s: %02x:%02x.%d pin %d int line %d\n", __func__, irq_dev->upstream->secondary, devfn >> 3, devfn & 0x7, int_pin, int_line); pci_write_config8(irq_dev, PCI_INTERRUPT_LINE, int_line); } } static void pci_p2sb_read_resources(struct device *dev) { struct resource *res; /* Add MMIO resource * Use 0xda as an unused index for PCR BAR. */ res = new_resource(dev, 0xda); res->base = DEFAULT_PCR_BASE; res->size = 16 * 1024 * 1024; /* 16MB PCR config space */ res->flags = IORESOURCE_MEM | IORESOURCE_FIXED | IORESOURCE_STORED | IORESOURCE_ASSIGNED; /* Add MMIO resource * Use 0xdb as an unused index for IOAPIC. */ res = new_resource(dev, 0xdb); /* IOAPIC */ res->base = IO_APIC_ADDR; res->size = 0x00001000; res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; } static void pch_enable_serial_irqs(struct device *dev) { /* Set packet length and toggle silent mode bit for one frame. */ pci_write_config8(dev, SERIRQ_CNTL, (1 << 7) | (1 << 6) | ((21 - 17) << 2) | (0 << 0)); #if !CONFIG(SERIRQ_CONTINUOUS_MODE) pci_write_config8(dev, SERIRQ_CNTL, (1 << 7) | (0 << 6) | ((21 - 17) << 2) | (0 << 0)); #endif } static void lpc_init(struct device *dev) { printk(BIOS_DEBUG, "pch: %s\n", __func__); /* Get the base address */ /* Set the value for PCI command register. */ pci_write_config16(dev, PCI_COMMAND, PCI_COMMAND_SPECIAL | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO); /* Serial IRQ initialization. */ pch_enable_serial_irqs(dev); /* IO APIC initialization. */ pch_enable_ioapic(dev); /* Setup the PIRQ. */ pch_pirq_init(dev); } static void pch_lpc_add_mmio_resources(struct device *dev) { /* TODO */ } static void pch_lpc_add_io_resources(struct device *dev) { struct resource *res; u8 io_index = 0; /* Add an extra subtractive resource for both memory and I/O. */ res = new_resource(dev, IOINDEX_SUBTRACTIVE(io_index++, 0)); res->base = 0; res->size = 0x1000; res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; res = new_resource(dev, IOINDEX_SUBTRACTIVE(io_index++, 0)); res->base = 0xff000000; res->size = 0x01000000; /* 16 MB for flash */ res->flags = IORESOURCE_MEM | IORESOURCE_SUBTRACTIVE | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; } static void lpc_read_resources(struct device *dev) { /* Get the normal PCI resources of this device. */ pci_dev_read_resources(dev); /* Add non-standard MMIO resources. */ pch_lpc_add_mmio_resources(dev); /* Add IO resources. */ pch_lpc_add_io_resources(dev); /* Add MMIO resource for IOAPIC. */ pci_p2sb_read_resources(dev); } static void pch_decode_init(struct device *dev) { /* TODO */ } static void lpc_enable_resources(struct device *dev) { pch_decode_init(dev); pci_dev_enable_resources(dev); } /* Set bit in Function Disable register to hide this device */ static void pch_hide_devfn(uint32_t devfn) { /* TODO */ } void southcluster_enable_dev(struct device *dev) { u16 reg16; if (!dev->enabled) { printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev)); /* Ensure memory, io, and bus master are all disabled */ reg16 = pci_read_config16(dev, PCI_COMMAND); reg16 &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO); pci_write_config16(dev, PCI_COMMAND, reg16); /* Hide this device if possible */ pch_hide_devfn(dev->path.pci.devfn); } else { /* Enable SERR */ pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_SERR); } } static struct device_operations device_ops = { .read_resources = lpc_read_resources, .set_resources = pci_dev_set_resources, #if CONFIG(HAVE_ACPI_TABLES) .write_acpi_tables = southcluster_write_acpi_tables, #endif .enable_resources = lpc_enable_resources, .init = lpc_init, .enable = southcluster_enable_dev, .scan_bus = scan_static_bus, .ops_pci = &soc_pci_ops, }; static const struct pci_driver lpc_driver __pci_driver = { .ops = &device_ops, .vendor = PCI_VID_INTEL, .device = PCI_DID_INTEL_DNV_LPC, }; static void finalize_chipset(void *unused) { apm_control(APM_CNT_FINALIZE); } BOOT_STATE_INIT_ENTRY(BS_OS_RESUME, BS_ON_ENTRY, finalize_chipset, NULL); BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_EXIT, finalize_chipset, NULL);