From c200e8c7cdebed98860a771888efbf998c5912b3 Mon Sep 17 00:00:00 2001 From: Angel Pons Date: Fri, 23 Oct 2020 21:37:21 +0200 Subject: soc/intel/broadwell: Move PCH code into pch subdir Change-Id: Icb57eb89b4f225298e43ae27970dc1e27fb6e222 Signed-off-by: Angel Pons Reviewed-on: https://review.coreboot.org/c/coreboot/+/46706 Reviewed-by: Arthur Heymans Tested-by: build bot (Jenkins) --- src/soc/intel/broadwell/Makefile.inc | 36 +- src/soc/intel/broadwell/adsp.c | 144 ---- src/soc/intel/broadwell/bootblock/pch.c | 115 --- src/soc/intel/broadwell/ehci.c | 57 -- src/soc/intel/broadwell/elog.c | 124 --- src/soc/intel/broadwell/fadt.c | 89 -- src/soc/intel/broadwell/gpio.c | 133 --- src/soc/intel/broadwell/hda.c | 155 ---- src/soc/intel/broadwell/iobp.c | 139 ---- src/soc/intel/broadwell/lpc.c | 677 --------------- src/soc/intel/broadwell/me.c | 1057 ------------------------ src/soc/intel/broadwell/me_status.c | 325 -------- src/soc/intel/broadwell/pch.c | 209 ----- src/soc/intel/broadwell/pch/Makefile.inc | 38 + src/soc/intel/broadwell/pch/adsp.c | 144 ++++ src/soc/intel/broadwell/pch/bootblock.c | 115 +++ src/soc/intel/broadwell/pch/early_pch.c | 77 ++ src/soc/intel/broadwell/pch/ehci.c | 57 ++ src/soc/intel/broadwell/pch/elog.c | 124 +++ src/soc/intel/broadwell/pch/fadt.c | 89 ++ src/soc/intel/broadwell/pch/gpio.c | 133 +++ src/soc/intel/broadwell/pch/hda.c | 155 ++++ src/soc/intel/broadwell/pch/iobp.c | 139 ++++ src/soc/intel/broadwell/pch/lpc.c | 677 +++++++++++++++ src/soc/intel/broadwell/pch/me.c | 1057 ++++++++++++++++++++++++ src/soc/intel/broadwell/pch/me_status.c | 325 ++++++++ src/soc/intel/broadwell/pch/pch.c | 209 +++++ src/soc/intel/broadwell/pch/pcie.c | 650 +++++++++++++++ src/soc/intel/broadwell/pch/pmutil.c | 455 ++++++++++ src/soc/intel/broadwell/pch/power_state.c | 111 +++ src/soc/intel/broadwell/pch/sata.c | 286 +++++++ src/soc/intel/broadwell/pch/serialio.c | 295 +++++++ src/soc/intel/broadwell/pch/smbus.c | 94 +++ src/soc/intel/broadwell/pch/smi.c | 56 ++ src/soc/intel/broadwell/pch/smihandler.c | 569 +++++++++++++ src/soc/intel/broadwell/pch/uart.c | 69 ++ src/soc/intel/broadwell/pch/usb_debug.c | 17 + src/soc/intel/broadwell/pch/xhci.c | 219 +++++ src/soc/intel/broadwell/pcie.c | 650 --------------- src/soc/intel/broadwell/pmutil.c | 455 ---------- src/soc/intel/broadwell/romstage/Makefile.inc | 3 - src/soc/intel/broadwell/romstage/pch.c | 77 -- src/soc/intel/broadwell/romstage/power_state.c | 111 --- src/soc/intel/broadwell/romstage/uart.c | 69 -- src/soc/intel/broadwell/sata.c | 286 ------- src/soc/intel/broadwell/serialio.c | 295 ------- src/soc/intel/broadwell/smbus.c | 94 --- src/soc/intel/broadwell/smi.c | 56 -- src/soc/intel/broadwell/smihandler.c | 569 ------------- src/soc/intel/broadwell/usb_debug.c | 17 - src/soc/intel/broadwell/xhci.c | 219 ----- 51 files changed, 6162 insertions(+), 6159 deletions(-) delete mode 100644 src/soc/intel/broadwell/adsp.c delete mode 100644 src/soc/intel/broadwell/bootblock/pch.c delete mode 100644 src/soc/intel/broadwell/ehci.c delete mode 100644 src/soc/intel/broadwell/elog.c delete mode 100644 src/soc/intel/broadwell/fadt.c delete mode 100644 src/soc/intel/broadwell/gpio.c delete mode 100644 src/soc/intel/broadwell/hda.c delete mode 100644 src/soc/intel/broadwell/iobp.c delete mode 100644 src/soc/intel/broadwell/lpc.c delete mode 100644 src/soc/intel/broadwell/me.c delete mode 100644 src/soc/intel/broadwell/me_status.c delete mode 100644 src/soc/intel/broadwell/pch.c create mode 100644 src/soc/intel/broadwell/pch/Makefile.inc create mode 100644 src/soc/intel/broadwell/pch/adsp.c create mode 100644 src/soc/intel/broadwell/pch/bootblock.c create mode 100644 src/soc/intel/broadwell/pch/early_pch.c create mode 100644 src/soc/intel/broadwell/pch/ehci.c create mode 100644 src/soc/intel/broadwell/pch/elog.c create mode 100644 src/soc/intel/broadwell/pch/fadt.c create mode 100644 src/soc/intel/broadwell/pch/gpio.c create mode 100644 src/soc/intel/broadwell/pch/hda.c create mode 100644 src/soc/intel/broadwell/pch/iobp.c create mode 100644 src/soc/intel/broadwell/pch/lpc.c create mode 100644 src/soc/intel/broadwell/pch/me.c create mode 100644 src/soc/intel/broadwell/pch/me_status.c create mode 100644 src/soc/intel/broadwell/pch/pch.c create mode 100644 src/soc/intel/broadwell/pch/pcie.c create mode 100644 src/soc/intel/broadwell/pch/pmutil.c create mode 100644 src/soc/intel/broadwell/pch/power_state.c create mode 100644 src/soc/intel/broadwell/pch/sata.c create mode 100644 src/soc/intel/broadwell/pch/serialio.c create mode 100644 src/soc/intel/broadwell/pch/smbus.c create mode 100644 src/soc/intel/broadwell/pch/smi.c create mode 100644 src/soc/intel/broadwell/pch/smihandler.c create mode 100644 src/soc/intel/broadwell/pch/uart.c create mode 100644 src/soc/intel/broadwell/pch/usb_debug.c create mode 100644 src/soc/intel/broadwell/pch/xhci.c delete mode 100644 src/soc/intel/broadwell/pcie.c delete mode 100644 src/soc/intel/broadwell/pmutil.c delete mode 100644 src/soc/intel/broadwell/romstage/pch.c delete mode 100644 src/soc/intel/broadwell/romstage/power_state.c delete mode 100644 src/soc/intel/broadwell/romstage/uart.c delete mode 100644 src/soc/intel/broadwell/sata.c delete mode 100644 src/soc/intel/broadwell/serialio.c delete mode 100644 src/soc/intel/broadwell/smbus.c delete mode 100644 src/soc/intel/broadwell/smi.c delete mode 100644 src/soc/intel/broadwell/smihandler.c delete mode 100644 src/soc/intel/broadwell/usb_debug.c delete mode 100644 src/soc/intel/broadwell/xhci.c (limited to 'src') diff --git a/src/soc/intel/broadwell/Makefile.inc b/src/soc/intel/broadwell/Makefile.inc index e24b949fb5..75ef33fa02 100644 --- a/src/soc/intel/broadwell/Makefile.inc +++ b/src/soc/intel/broadwell/Makefile.inc @@ -9,52 +9,28 @@ subdirs-y += ../../../cpu/intel/microcode subdirs-y += ../../../cpu/intel/turbo subdirs-y += ../../../cpu/intel/common +subdirs-y += pch + bootblock-y += bootblock/cpu.c -bootblock-y += bootblock/pch.c bootblock-y += bootblock/systemagent.c bootblock-y += ../../../cpu/intel/car/bootblock.c bootblock-y += ../../../cpu/intel/car/non-evict/cache_as_ram.S bootblock-y += ../../../cpu/x86/early_reset.S ramstage-y += acpi.c -ramstage-y += adsp.c ramstage-y += cpu.c ramstage-y += cpu_info.c smm-y += cpu_info.c -ramstage-$(CONFIG_ELOG) += elog.c ramstage-y += finalize.c -ramstage-y += gpio.c -romstage-y += gpio.c -smm-y += gpio.c -ramstage-y += hda.c ramstage-y += gma.c -ramstage-y += iobp.c -romstage-y += iobp.c -ramstage-y += fadt.c -ramstage-y += lpc.c -ramstage-y += me.c -ramstage-y += me_status.c -romstage-y += me_status.c ramstage-y += memmap.c romstage-y += memmap.c postcar-y += memmap.c ramstage-y += minihd.c -ramstage-y += pch.c -romstage-y += pch.c -ramstage-y += pcie.c ramstage-y += pei_data.c romstage-y += pei_data.c -ramstage-y += pmutil.c -romstage-y += pmutil.c -smm-y += pmutil.c -verstage-y += pmutil.c ramstage-y += ramstage.c ramstage-$(CONFIG_HAVE_REFCODE_BLOB) += refcode.c -ramstage-y += sata.c -ramstage-y += serialio.c -ramstage-y += smbus.c -ramstage-y += smi.c -smm-y += smihandler.c ramstage-y += smmrelocate.c ramstage-y += systemagent.c bootblock-y += tsc_freq.c @@ -63,17 +39,9 @@ romstage-y += tsc_freq.c smm-y += tsc_freq.c postcar-y += tsc_freq.c verstage-y += tsc_freq.c -bootblock-y += usb_debug.c -romstage-y += usb_debug.c -ramstage-y += usb_debug.c -ramstage-y += ehci.c -ramstage-y += xhci.c -smm-y += xhci.c postcar-y += ../../../cpu/intel/car/non-evict/exit_car.S -ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/hda_verb.c - cpu_microcode_bins += 3rdparty/blobs/soc/intel/broadwell/microcode.bin CPPFLAGS_common += -Isrc/soc/intel/broadwell/include diff --git a/src/soc/intel/broadwell/adsp.c b/src/soc/intel/broadwell/adsp.c deleted file mode 100644 index 06dd38bd8a..0000000000 --- a/src/soc/intel/broadwell/adsp.c +++ /dev/null @@ -1,144 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void adsp_init(struct device *dev) -{ - const struct soc_intel_broadwell_pch_config *config = config_of(dev); - struct resource *bar0, *bar1; - u32 tmp32; - - /* Ensure memory and bus master are enabled */ - pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); - - /* Find BAR0 and BAR1 */ - bar0 = find_resource(dev, PCI_BASE_ADDRESS_0); - if (!bar0) - return; - bar1 = find_resource(dev, PCI_BASE_ADDRESS_1); - if (!bar1) - return; - - /* - * Set LTR value in DSP shim LTR control register to 3ms - * SNOOP_REQ[13]=1b SNOOP_SCALE[12:10]=100b (1ms) SNOOP_VAL[9:0]=3h - */ - tmp32 = pch_is_wpt() ? ADSP_SHIM_BASE_WPT : ADSP_SHIM_BASE_LPT; - write32(res2mmio(bar0, tmp32 + ADSP_SHIM_LTRC, 0), - ADSP_SHIM_LTRC_VALUE); - - /* Program VDRTCTL2 D19:F0:A8[31:0] = 0x00000fff */ - pci_write_config32(dev, ADSP_PCI_VDRTCTL2, ADSP_VDRTCTL2_VALUE); - - /* Program ADSP IOBP VDLDAT1 to 0x040100 */ - pch_iobp_write(ADSP_IOBP_VDLDAT1, ADSP_VDLDAT1_VALUE); - - /* Set D3 Power Gating Enable in D19:F0:A0 based on PCH type */ - tmp32 = pci_read_config32(dev, ADSP_PCI_VDRTCTL0); - if (pch_is_wpt()) { - if (config->adsp_d3_pg_enable) { - tmp32 &= ~ADSP_VDRTCTL0_D3PGD_WPT; - if (config->adsp_sram_pg_enable) - tmp32 &= ~ADSP_VDRTCTL0_D3SRAMPGD_WPT; - else - tmp32 |= ADSP_VDRTCTL0_D3SRAMPGD_WPT; - } else { - tmp32 |= ADSP_VDRTCTL0_D3PGD_WPT; - } - } else { - if (config->adsp_d3_pg_enable) { - tmp32 &= ~ADSP_VDRTCTL0_D3PGD_LPT; - if (config->adsp_sram_pg_enable) - tmp32 &= ~ADSP_VDRTCTL0_D3SRAMPGD_LPT; - else - tmp32 |= ADSP_VDRTCTL0_D3SRAMPGD_LPT; - } else { - tmp32 |= ADSP_VDRTCTL0_D3PGD_LPT; - } - } - pci_write_config32(dev, ADSP_PCI_VDRTCTL0, tmp32); - - /* Set PSF Snoop to SA, RCBA+0x3350[10]=1b */ - RCBA32_OR(0x3350, (1 << 10)); - - /* Set DSP IOBP PMCTL 0x1e0=0x3f */ - pch_iobp_write(ADSP_IOBP_PMCTL, ADSP_PMCTL_VALUE); - - if (config->sio_acpi_mode) { - /* Configure for ACPI mode */ - struct global_nvs *gnvs; - - printk(BIOS_INFO, "ADSP: Enable ACPI Mode IRQ3\n"); - - /* Find ACPI NVS to update BARs */ - gnvs = acpi_get_gnvs(); - if (!gnvs) - return; - - /* Save BAR0 and BAR1 to ACPI NVS */ - gnvs->dev.bar0[SIO_NVS_ADSP] = (u32)bar0->base; - gnvs->dev.bar1[SIO_NVS_ADSP] = (u32)bar1->base; - gnvs->dev.enable[SIO_NVS_ADSP] = 1; - - /* Set PCI Config Disable Bit */ - pch_iobp_update(ADSP_IOBP_PCICFGCTL, ~0, ADSP_PCICFGCTL_PCICD); - - /* Set interrupt de-assert/assert opcode override to IRQ3 */ - pch_iobp_write(ADSP_IOBP_VDLDAT2, ADSP_IOBP_ACPI_IRQ3); - - /* Enable IRQ3 in RCBA */ - RCBA32_OR(ACPIIRQEN, ADSP_ACPI_IRQEN); - - /* Set ACPI Interrupt Enable Bit */ - pch_iobp_update(ADSP_IOBP_PCICFGCTL, ~ADSP_PCICFGCTL_SPCBAD, - ADSP_PCICFGCTL_ACPIIE); - - /* Put ADSP in D3hot */ - tmp32 = read32(res2mmio(bar1, PCH_PCS, 0)); - tmp32 |= PCH_PCS_PS_D3HOT; - write32(res2mmio(bar1, PCH_PCS, 0), tmp32); - } else { - printk(BIOS_INFO, "ADSP: Enable PCI Mode IRQ23\n"); - - /* Configure for PCI mode */ - pci_write_config8(dev, PCI_INTERRUPT_LINE, ADSP_PCI_IRQ); - - /* Clear ACPI Interrupt Enable Bit */ - pch_iobp_update(ADSP_IOBP_PCICFGCTL, - ~(ADSP_PCICFGCTL_SPCBAD | ADSP_PCICFGCTL_ACPIIE), 0); - } -} - -static struct device_operations adsp_ops = { - .read_resources = pci_dev_read_resources, - .set_resources = pci_dev_set_resources, - .enable_resources = pci_dev_enable_resources, - .init = adsp_init, - .ops_pci = &pci_dev_ops_pci, -}; - -static const unsigned short pci_device_ids[] = { - 0x9c36, /* LynxPoint */ - 0x9cb6, /* WildcatPoint */ - 0 -}; - -static const struct pci_driver pch_adsp __pci_driver = { - .ops = &adsp_ops, - .vendor = PCI_VENDOR_ID_INTEL, - .devices = pci_device_ids, -}; diff --git a/src/soc/intel/broadwell/bootblock/pch.c b/src/soc/intel/broadwell/bootblock/pch.c deleted file mode 100644 index 7f6d0d52d9..0000000000 --- a/src/soc/intel/broadwell/bootblock/pch.c +++ /dev/null @@ -1,115 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void map_rcba(void) -{ - pci_write_config32(PCH_DEV_LPC, RCBA, RCBA_BASE_ADDRESS | 1); -} - -static void enable_port80_on_lpc(void) -{ - /* Enable port 80 POST on LPC. The chipset does this by default, - * but it doesn't appear to hurt anything. */ - u32 gcs = RCBA32(GCS); - gcs = gcs & ~0x4; - RCBA32(GCS) = gcs; -} - -static void set_spi_speed(void) -{ - u32 fdod; - u8 ssfc; - - /* Observe SPI Descriptor Component Section 0 */ - SPIBAR32(SPIBAR_FDOC) = 0x1000; - - /* Extract the Write/Erase SPI Frequency from descriptor */ - fdod = SPIBAR32(SPIBAR_FDOD); - fdod >>= 24; - fdod &= 7; - - /* Set Software Sequence frequency to match */ - ssfc = SPIBAR8(SPIBAR_SSFC + 2); - ssfc &= ~7; - ssfc |= fdod; - SPIBAR8(SPIBAR_SSFC + 2) = ssfc; -} - -static void pch_enable_bars(void) -{ - /* Set up southbridge BARs */ - pci_write_config32(PCH_DEV_LPC, RCBA, RCBA_BASE_ADDRESS | 1); - - pci_write_config32(PCH_DEV_LPC, PMBASE, ACPI_BASE_ADDRESS | 1); - - pci_write_config8(PCH_DEV_LPC, ACPI_CNTL, ACPI_EN); - - pci_write_config32(PCH_DEV_LPC, GPIO_BASE, GPIO_BASE_ADDRESS | 1); - - /* Enable GPIO functionality. */ - pci_write_config8(PCH_DEV_LPC, GPIO_CNTL, GPIO_EN); -} - -static void pch_early_lpc(void) -{ - pch_enable_bars(); - - /* Set COM1/COM2 decode range */ - pci_write_config16(PCH_DEV_LPC, LPC_IO_DEC, 0x0010); - - /* Enable SuperIO + MC + COM1 + PS/2 Keyboard/Mouse */ - u16 lpc_config = CNF1_LPC_EN | CNF2_LPC_EN | GAMEL_LPC_EN | - COMA_LPC_EN | KBC_LPC_EN | MC_LPC_EN; - pci_write_config16(PCH_DEV_LPC, LPC_EN, lpc_config); - - /* Enable IOAPIC */ - RCBA16(OIC) = 0x0100; - - /* Read back for posted write */ - (void)RCBA16(OIC); - - /* Set HPET address and enable it */ - RCBA32_AND_OR(HPTC, ~3, 1 << 7); - - /* - * Reading the register back guarantees that the write is - * done before we use the configured base address below. - */ - (void)RCBA32(HPTC); - - /* Enable HPET to start counter */ - setbits32((void *)HPET_BASE_ADDRESS + 0x10, 1 << 0); - - /* Disable reset */ - RCBA32_OR(GCS, 1 << 5); - - /* TCO timer halt */ - u16 reg16 = inb(ACPI_BASE_ADDRESS + TCO1_CNT); - reg16 |= TCO_TMR_HLT; - outb(reg16, ACPI_BASE_ADDRESS + TCO1_CNT); - - /* Enable upper 128 bytes of CMOS */ - RCBA32_OR(RC, 1 << 2); - - /* Disable unused device (always) */ - RCBA32_OR(FD, PCH_DISABLE_ALWAYS); -} - -void bootblock_early_southbridge_init(void) -{ - map_rcba(); - enable_spi_prefetching_and_caching(); - enable_port80_on_lpc(); - set_spi_speed(); - pch_early_lpc(); -} diff --git a/src/soc/intel/broadwell/ehci.c b/src/soc/intel/broadwell/ehci.c deleted file mode 100644 index 5b977dd3c6..0000000000 --- a/src/soc/intel/broadwell/ehci.c +++ /dev/null @@ -1,57 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include - -static void usb_ehci_set_subsystem(struct device *dev, unsigned int vendor, - unsigned int device) -{ - u8 access_cntl; - - access_cntl = pci_read_config8(dev, 0x80); - - /* Enable writes to protected registers. */ - pci_write_config8(dev, 0x80, access_cntl | 1); - - pci_dev_set_subsystem(dev, vendor, device); - - /* Restore protection. */ - pci_write_config8(dev, 0x80, access_cntl); -} - -static void ehci_enable(struct device *dev) -{ - if (CONFIG(USBDEBUG)) - dev->enabled = 1; - else - pch_disable_devfn(dev); -} - -static struct pci_operations ehci_ops_pci = { - .set_subsystem = &usb_ehci_set_subsystem, -}; - -static struct device_operations usb_ehci_ops = { - .read_resources = pci_ehci_read_resources, - .set_resources = pci_dev_set_resources, - .enable_resources = pci_dev_enable_resources, - .ops_pci = &ehci_ops_pci, - .enable = ehci_enable, -}; - -static const unsigned short pci_device_ids[] = { - 0x9c26, /* LynxPoint-LP */ - 0x9ca6, /* WildcatPoint */ - 0 -}; - -static const struct pci_driver pch_usb_ehci __pci_driver = { - .ops = &usb_ehci_ops, - .vendor = PCI_VENDOR_ID_INTEL, - .devices = pci_device_ids, -}; diff --git a/src/soc/intel/broadwell/elog.c b/src/soc/intel/broadwell/elog.c deleted file mode 100644 index 9271e27872..0000000000 --- a/src/soc/intel/broadwell/elog.c +++ /dev/null @@ -1,124 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include - -static void pch_log_gpio_gpe(u32 gpe0_sts, u32 gpe0_en, int start) -{ - int i; - - gpe0_sts &= gpe0_en; - - for (i = 0; i <= 31; i++) { - if (gpe0_sts & (1 << i)) - elog_add_event_wake(ELOG_WAKE_SOURCE_GPE, i + start); - } -} - -static void pch_log_wake_source(struct chipset_power_state *ps) -{ - /* Power Button */ - if (ps->pm1_sts & PWRBTN_STS) - elog_add_event_wake(ELOG_WAKE_SOURCE_PWRBTN, 0); - - /* RTC */ - if (ps->pm1_sts & RTC_STS) - elog_add_event_wake(ELOG_WAKE_SOURCE_RTC, 0); - - /* PCI Express (TODO: determine wake device) */ - if (ps->pm1_sts & PCIEXPWAK_STS) - elog_add_event_wake(ELOG_WAKE_SOURCE_PCIE, 0); - - /* PME (TODO: determine wake device) */ - if (ps->gpe0_sts[GPE_STD] & PME_STS) - elog_add_event_wake(ELOG_WAKE_SOURCE_PME, 0); - - /* Internal PME (TODO: determine wake device) */ - if (ps->gpe0_sts[GPE_STD] & PME_B0_STS) - elog_add_event_wake(ELOG_WAKE_SOURCE_PME_INTERNAL, 0); - - /* SMBUS Wake */ - if (ps->gpe0_sts[GPE_STD] & SMB_WAK_STS) - elog_add_event_wake(ELOG_WAKE_SOURCE_SMBUS, 0); - - /* GPIO27 */ - if (ps->gpe0_sts[GPE_STD] & GP27_STS) - elog_add_event_wake(ELOG_WAKE_SOURCE_GPE, 27); - - /* Log GPIO events in set 1-3 */ - pch_log_gpio_gpe(ps->gpe0_sts[GPE_31_0], ps->gpe0_en[GPE_31_0], 0); - pch_log_gpio_gpe(ps->gpe0_sts[GPE_63_32], ps->gpe0_en[GPE_63_32], 32); - pch_log_gpio_gpe(ps->gpe0_sts[GPE_94_64], ps->gpe0_en[GPE_94_64], 64); -} - -static void pch_log_power_and_resets(struct chipset_power_state *ps) -{ - /* Thermal Trip Status */ - if (ps->gen_pmcon2 & THERMTRIP_STS) - elog_add_event(ELOG_TYPE_THERM_TRIP); - - /* PWR_FLR Power Failure */ - if (ps->gen_pmcon2 & PWROK_FLR) - elog_add_event(ELOG_TYPE_POWER_FAIL); - - /* SUS Well Power Failure */ - if (ps->gen_pmcon3 & SUS_PWR_FLR) - elog_add_event(ELOG_TYPE_SUS_POWER_FAIL); - - /* SYS_PWROK Failure */ - if (ps->gen_pmcon2 & SYSPWR_FLR) - elog_add_event(ELOG_TYPE_SYS_PWROK_FAIL); - - /* PWROK Failure */ - if (ps->gen_pmcon2 & PWROK_FLR) - elog_add_event(ELOG_TYPE_PWROK_FAIL); - - /* TCO Timeout */ - if (ps->prev_sleep_state != ACPI_S3 && - ps->tco2_sts & TCO2_STS_SECOND_TO) - elog_add_event(ELOG_TYPE_TCO_RESET); - - /* Power Button Override */ - if (ps->pm1_sts & PRBTNOR_STS) - elog_add_event(ELOG_TYPE_POWER_BUTTON_OVERRIDE); - - /* RTC reset */ - if (ps->gen_pmcon3 & RTC_BATTERY_DEAD) - elog_add_event(ELOG_TYPE_RTC_RESET); - - /* System Reset Status (reset button pushed) */ - if (ps->gen_pmcon2 & SYSTEM_RESET_STS) - elog_add_event(ELOG_TYPE_RESET_BUTTON); - - /* General Reset Status */ - if (ps->gen_pmcon3 & GEN_RST_STS) - elog_add_event(ELOG_TYPE_SYSTEM_RESET); - - /* ACPI Wake Event */ - if (ps->prev_sleep_state != ACPI_S0) - elog_add_event_byte(ELOG_TYPE_ACPI_WAKE, ps->prev_sleep_state); -} - -static void pch_log_state(void *unused) -{ - struct chipset_power_state *ps = cbmem_find(CBMEM_ID_POWER_STATE); - - if (ps == NULL) { - printk(BIOS_ERR, "Not logging power state information. " - "Power state not found in cbmem.\n"); - return; - } - - /* Power and Reset */ - pch_log_power_and_resets(ps); - - /* Wake Sources */ - pch_log_wake_source(ps); -} - -BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_ENTRY, pch_log_state, NULL); diff --git a/src/soc/intel/broadwell/fadt.c b/src/soc/intel/broadwell/fadt.c deleted file mode 100644 index 8fbd0c45ad..0000000000 --- a/src/soc/intel/broadwell/fadt.c +++ /dev/null @@ -1,89 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include "chip.h" - -void acpi_fill_fadt(acpi_fadt_t *fadt) -{ - const uint16_t pmbase = ACPI_BASE_ADDRESS; - - fadt->sci_int = acpi_sci_irq(); - - if (permanent_smi_handler()) { - fadt->smi_cmd = APM_CNT; - fadt->acpi_enable = APM_CNT_ACPI_ENABLE; - fadt->acpi_disable = APM_CNT_ACPI_DISABLE; - } - - fadt->pm1a_evt_blk = pmbase + PM1_STS; - fadt->pm1a_cnt_blk = pmbase + PM1_CNT; - fadt->pm2_cnt_blk = pmbase + PM2_CNT; - fadt->pm_tmr_blk = pmbase + PM1_TMR; - fadt->gpe0_blk = pmbase + GPE0_STS(0); - - fadt->pm1_evt_len = 4; - fadt->pm1_cnt_len = 2; - fadt->pm2_cnt_len = 1; - fadt->pm_tmr_len = 4; - fadt->gpe0_blk_len = 32; - fadt->p_lvl2_lat = 1; - fadt->p_lvl3_lat = 87; - fadt->duty_offset = 1; - fadt->duty_width = 0; - fadt->day_alrm = 0xd; - fadt->mon_alrm = 0x00; - fadt->century = 0x00; - fadt->iapc_boot_arch = ACPI_FADT_LEGACY_DEVICES | ACPI_FADT_8042; - - fadt->flags |= ACPI_FADT_WBINVD | - ACPI_FADT_C1_SUPPORTED | - ACPI_FADT_C2_MP_SUPPORTED | - ACPI_FADT_SLEEP_BUTTON | - ACPI_FADT_SEALED_CASE | - ACPI_FADT_S4_RTC_WAKE | - ACPI_FADT_PLATFORM_CLOCK; - - fadt->x_pm1a_evt_blk.space_id = ACPI_ADDRESS_SPACE_IO; - fadt->x_pm1a_evt_blk.bit_width = fadt->pm1_evt_len * 8; - fadt->x_pm1a_evt_blk.bit_offset = 0; - fadt->x_pm1a_evt_blk.access_size = ACPI_ACCESS_SIZE_WORD_ACCESS; - fadt->x_pm1a_evt_blk.addrl = pmbase + PM1_STS; - fadt->x_pm1a_evt_blk.addrh = 0x0; - - fadt->x_pm1a_cnt_blk.space_id = ACPI_ADDRESS_SPACE_IO; - fadt->x_pm1a_cnt_blk.bit_width = fadt->pm1_cnt_len * 8; - fadt->x_pm1a_cnt_blk.bit_offset = 0; - fadt->x_pm1a_cnt_blk.access_size = ACPI_ACCESS_SIZE_WORD_ACCESS; - fadt->x_pm1a_cnt_blk.addrl = pmbase + PM1_CNT; - fadt->x_pm1a_cnt_blk.addrh = 0x0; - - fadt->x_pm2_cnt_blk.space_id = ACPI_ADDRESS_SPACE_IO; - fadt->x_pm2_cnt_blk.bit_width = fadt->pm2_cnt_len * 8; - fadt->x_pm2_cnt_blk.bit_offset = 0; - fadt->x_pm2_cnt_blk.access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS; - fadt->x_pm2_cnt_blk.addrl = pmbase + PM2_CNT; - fadt->x_pm2_cnt_blk.addrh = 0x0; - - fadt->x_pm_tmr_blk.space_id = ACPI_ADDRESS_SPACE_IO; - fadt->x_pm_tmr_blk.bit_width = fadt->pm_tmr_len * 8; - fadt->x_pm_tmr_blk.bit_offset = 0; - fadt->x_pm_tmr_blk.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS; - fadt->x_pm_tmr_blk.addrl = pmbase + PM1_TMR; - fadt->x_pm_tmr_blk.addrh = 0x0; - - /* - * Windows 10 requires x_gpe0_blk to be set starting with FADT revision 5. - * The bit_width field intentionally overflows here. - * The OSPM can instead use the values in `fadt->gpe0_blk{,_len}`, which - * seems to work fine on Linux 5.0 and Windows 10. - */ - fadt->x_gpe0_blk.space_id = ACPI_ADDRESS_SPACE_IO; - fadt->x_gpe0_blk.bit_width = fadt->gpe0_blk_len * 8; - fadt->x_gpe0_blk.bit_offset = 0; - fadt->x_gpe0_blk.access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS; - fadt->x_gpe0_blk.addrl = fadt->gpe0_blk; - fadt->x_gpe0_blk.addrh = 0x0; -} diff --git a/src/soc/intel/broadwell/gpio.c b/src/soc/intel/broadwell/gpio.c deleted file mode 100644 index ff1f019ce0..0000000000 --- a/src/soc/intel/broadwell/gpio.c +++ /dev/null @@ -1,133 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * This function will return a number that indicates which PIRQ - * this GPIO maps to. If this is not a PIRQ capable GPIO then - * it will return -1. The GPIO to PIRQ mapping is not linear. - */ -static int gpio_to_pirq(int gpio) -{ - switch (gpio) { - case 8: return 0; /* PIRQI */ - case 9: return 1; /* PIRQJ */ - case 10: return 2; /* PIRQK */ - case 13: return 3; /* PIRQL */ - case 14: return 4; /* PIRQM */ - case 45: return 5; /* PIRQN */ - case 46: return 6; /* PIRQO */ - case 47: return 7; /* PIRQP */ - case 48: return 8; /* PIRQQ */ - case 49: return 9; /* PIRQR */ - case 50: return 10; /* PIRQS */ - case 51: return 11; /* PIRQT */ - case 52: return 12; /* PIRQU */ - case 53: return 13; /* PIRQV */ - case 54: return 14; /* PIRQW */ - case 55: return 15; /* PIRQX */ - default: return -1; - }; -} - -void init_gpios(const struct gpio_config config[]) -{ - const struct gpio_config *entry; - u32 owner[3] = {0}; - u32 route[3] = {0}; - u32 irqen[3] = {0}; - u32 reset[3] = {0}; - u32 blink = 0; - u16 pirq2apic = 0; - int set, bit, gpio = 0; - - for (entry = config; entry->conf0 != GPIO_LIST_END; entry++, gpio++) { - if (gpio > MAX_GPIO_NUMBER) - break; - - /* Setup Configuration registers 1 and 2 */ - outl(entry->conf0, GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio)); - outl(entry->conf1, GPIO_BASE_ADDRESS + GPIO_CONFIG1(gpio)); - - /* Determine set and bit based on GPIO number */ - set = gpio >> 5; - bit = gpio % 32; - - /* Apply settings to set specific bits */ - owner[set] |= entry->owner << bit; - route[set] |= entry->route << bit; - irqen[set] |= entry->irqen << bit; - reset[set] |= entry->reset << bit; - - if (set == 0) - blink |= entry->blink << bit; - - /* PIRQ to IO-APIC map */ - if (entry->pirq == GPIO_PIRQ_APIC_ROUTE) { - set = gpio_to_pirq(gpio); - if (set >= 0) - pirq2apic |= 1 << set; - } - } - - for (set = 0; set <= 2; set++) { - outl(owner[set], GPIO_BASE_ADDRESS + GPIO_OWNER(set)); - outl(route[set], GPIO_BASE_ADDRESS + GPIO_ROUTE(set)); - outl(irqen[set], GPIO_BASE_ADDRESS + GPIO_IRQ_IE(set)); - outl(reset[set], GPIO_BASE_ADDRESS + GPIO_RESET(set)); - } - - outl(blink, GPIO_BASE_ADDRESS + GPIO_BLINK); - outl(pirq2apic, GPIO_BASE_ADDRESS + GPIO_PIRQ_APIC_EN); -} - -int get_gpio(int gpio_num) -{ - if (gpio_num > MAX_GPIO_NUMBER) - return 0; - - return !!(inl(GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio_num)) & GPI_LEVEL); -} - -/* - * get a number comprised of multiple GPIO values. gpio_num_array points to - * the array of gpio pin numbers to scan, terminated by -1. - */ -unsigned int get_gpios(const int *gpio_num_array) -{ - int gpio; - unsigned int bitmask = 1; - unsigned int vector = 0; - - while (bitmask && - ((gpio = *gpio_num_array++) != -1)) { - if (get_gpio(gpio)) - vector |= bitmask; - bitmask <<= 1; - } - return vector; -} - -void set_gpio(int gpio_num, int value) -{ - u32 conf0; - - if (gpio_num > MAX_GPIO_NUMBER) - return; - - conf0 = inl(GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio_num)); - conf0 &= ~GPO_LEVEL_MASK; - conf0 |= value << GPO_LEVEL_SHIFT; - outl(conf0, GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio_num)); -} - -int gpio_is_native(int gpio_num) -{ - return !(inl(GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio_num)) & 1); -} diff --git a/src/soc/intel/broadwell/hda.c b/src/soc/intel/broadwell/hda.c deleted file mode 100644 index 04390d1342..0000000000 --- a/src/soc/intel/broadwell/hda.c +++ /dev/null @@ -1,155 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void codecs_init(u8 *base, u32 codec_mask) -{ - int i; - - /* Can support up to 4 codecs */ - for (i = 3; i >= 0; i--) { - if (codec_mask & (1 << i)) - hda_codec_init(base, i, - cim_verb_data_size, - cim_verb_data); - } - - if (pc_beep_verbs_size) - hda_codec_write(base, pc_beep_verbs_size, pc_beep_verbs); -} - -static void hda_pch_init(struct device *dev, u8 *base) -{ - u8 reg8; - u16 reg16; - u32 reg32; - - if (RCBA32(0x2030) & (1 << 31)) { - reg32 = pci_read_config32(dev, 0x120); - reg32 &= 0xf8ffff01; - reg32 |= (1 << 25); - reg32 |= RCBA32(0x2030) & 0xfe; - pci_write_config32(dev, 0x120, reg32); - } else - printk(BIOS_DEBUG, "HDA: V1CTL disabled.\n"); - - reg32 = pci_read_config32(dev, 0x114); - reg32 &= ~0xfe; - pci_write_config32(dev, 0x114, reg32); - - // Set VCi enable bit - if (pci_read_config32(dev, 0x120) & ((1 << 24) | - (1 << 25) | (1 << 26))) { - reg32 = pci_read_config32(dev, 0x120); - reg32 &= ~(1 << 31); - pci_write_config32(dev, 0x120, reg32); - } - - /* Additional programming steps */ - reg32 = pci_read_config32(dev, 0xc4); - reg32 |= (1 << 24); - pci_write_config32(dev, 0xc4, reg32); - - reg8 = pci_read_config8(dev, 0x40); // Audio Control - reg8 |= 1; // Select HDA mode - pci_write_config8(dev, 0x40, reg8); - - reg8 = pci_read_config8(dev, 0x4d); // Docking Status - reg8 &= ~(1 << 7); // Docking not supported - pci_write_config8(dev, 0x4d, reg8); - - reg16 = read32(base + 0x0012); - reg16 |= (1 << 0); - write32(base + 0x0012, reg16); - - /* disable Auto Voltage Detector */ - reg8 = pci_read_config8(dev, 0x42); - reg8 |= (1 << 2); - pci_write_config8(dev, 0x42, reg8); -} - -static void hda_init(struct device *dev) -{ - u8 *base; - struct resource *res; - u32 codec_mask; - - /* Find base address */ - res = find_resource(dev, PCI_BASE_ADDRESS_0); - if (!res) - return; - - base = res2mmio(res, 0, 0); - printk(BIOS_DEBUG, "HDA: base = %p\n", base); - - /* Set Bus Master */ - pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER); - - hda_pch_init(dev, base); - - codec_mask = hda_codec_detect(base); - - if (codec_mask) { - printk(BIOS_DEBUG, "HDA: codec_mask = %02x\n", codec_mask); - codecs_init(base, codec_mask); - } -} - -static void hda_enable(struct device *dev) -{ - u16 reg16; - u8 reg8; - - reg8 = pci_read_config8(dev, 0x43); - reg8 |= 0x6f; - pci_write_config8(dev, 0x43, reg8); - - if (!dev->enabled) { - /* Route I/O buffers to ADSP function */ - reg8 = pci_read_config8(dev, 0x42); - reg8 |= (1 << 7) | (1 << 6); - pci_write_config8(dev, 0x42, reg8); - - printk(BIOS_INFO, "HDA disabled, I/O buffers routed to ADSP\n"); - - /* 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); - - /* Disable this device */ - pch_disable_devfn(dev); - } -} - -static struct device_operations hda_ops = { - .read_resources = &pci_dev_read_resources, - .set_resources = &pci_dev_set_resources, - .enable_resources = &pci_dev_enable_resources, - .init = &hda_init, - .enable = &hda_enable, - .ops_pci = &pci_dev_ops_pci, -}; - -static const unsigned short pci_device_ids[] = { - 0x9c20, /* LynxPoint-LP */ - 0x9ca0, /* WildcatPoint */ - 0 -}; - -static const struct pci_driver pch_hda __pci_driver = { - .ops = &hda_ops, - .vendor = PCI_VENDOR_ID_INTEL, - .devices = pci_device_ids, -}; diff --git a/src/soc/intel/broadwell/iobp.c b/src/soc/intel/broadwell/iobp.c deleted file mode 100644 index deb4156198..0000000000 --- a/src/soc/intel/broadwell/iobp.c +++ /dev/null @@ -1,139 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include - -#define IOBP_RETRY 1000 - -static inline int iobp_poll(void) -{ - unsigned int try; - - for (try = IOBP_RETRY; try > 0; try--) { - u16 status = RCBA16(IOBPS); - if ((status & IOBPS_READY) == 0) - return 1; - udelay(10); - } - - printk(BIOS_ERR, "IOBP: timeout waiting for transaction to complete\n"); - return 0; -} - -u32 pch_iobp_read(u32 address) -{ - u16 status; - - if (!iobp_poll()) - return 0; - - /* Set the address */ - RCBA32(IOBPIRI) = address; - - /* READ OPCODE */ - status = RCBA16(IOBPS); - status &= ~IOBPS_MASK; - status |= IOBPS_READ; - RCBA16(IOBPS) = status; - - /* Undocumented magic */ - RCBA16(IOBPU) = IOBPU_MAGIC; - - /* Set ready bit */ - status = RCBA16(IOBPS); - status |= IOBPS_READY; - RCBA16(IOBPS) = status; - - if (!iobp_poll()) - return 0; - - /* Check for successful transaction */ - status = RCBA16(IOBPS); - if (status & IOBPS_TX_MASK) { - printk(BIOS_ERR, "IOBP: read 0x%08x failed\n", address); - return 0; - } - - /* Read IOBP data */ - return RCBA32(IOBPD); -} - -void pch_iobp_write(u32 address, u32 data) -{ - u16 status; - - if (!iobp_poll()) - return; - - /* Set the address */ - RCBA32(IOBPIRI) = address; - - /* WRITE OPCODE */ - status = RCBA16(IOBPS); - status &= ~IOBPS_MASK; - status |= IOBPS_WRITE; - RCBA16(IOBPS) = status; - - RCBA32(IOBPD) = data; - - /* Undocumented magic */ - RCBA16(IOBPU) = IOBPU_MAGIC; - - /* Set ready bit */ - status = RCBA16(IOBPS); - status |= IOBPS_READY; - RCBA16(IOBPS) = status; - - if (!iobp_poll()) - return; - - /* Check for successful transaction */ - status = RCBA16(IOBPS); - if (status & IOBPS_TX_MASK) { - printk(BIOS_ERR, "IOBP: write 0x%08x failed\n", address); - return; - } - - printk(BIOS_SPEW, "IOBP: set 0x%08x to 0x%08x\n", address, data); -} - -void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue) -{ - u32 data = pch_iobp_read(address); - - /* Update the data */ - data &= andvalue; - data |= orvalue; - - pch_iobp_write(address, data); -} - -void pch_iobp_exec(u32 addr, u16 op_code, u8 route_id, u32 *data, u8 *resp) -{ - if (!data || !resp) - return; - - *resp = -1; - if (!iobp_poll()) - return; - - /* RCBA2330[31:0] = Address */ - RCBA32(IOBPIRI) = addr; - /* RCBA2338[15:8] = opcode */ - RCBA16(IOBPS) = (RCBA16(IOBPS) & 0x00ff) | op_code; - /* RCBA233A[15:8] = 0xf0 RCBA233A[7:0] = Route ID */ - RCBA16(IOBPU) = IOBPU_MAGIC | route_id; - - if (op_code == IOBP_PCICFG_WRITE) - RCBA32(IOBPD) = *data; - /* Set RCBA2338[0] to trigger IOBP transaction*/ - RCBA16(IOBPS) = RCBA16(IOBPS) | 0x1; - - if (!iobp_poll()) - return; - - *resp = (RCBA16(IOBPS) & IOBPS_TX_MASK) >> 1; - *data = RCBA32(IOBPD); -} diff --git a/src/soc/intel/broadwell/lpc.c b/src/soc/intel/broadwell/lpc.c deleted file mode 100644 index 2111913a0e..0000000000 --- a/src/soc/intel/broadwell/lpc.c +++ /dev/null @@ -1,677 +0,0 @@ -/* 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void pch_enable_ioapic(struct device *dev) -{ - u32 reg32; - - /* Assign unique bus/dev/fn for I/O APIC */ - pci_write_config16(dev, LPC_IBDF, - PCH_IOAPIC_PCI_BUS << 8 | PCH_IOAPIC_PCI_SLOT << 3); - - set_ioapic_id(VIO_APIC_VADDR, 0x02); - - /* affirm full set of redirection table entries ("write once") */ - reg32 = io_apic_read(VIO_APIC_VADDR, 0x01); - - /* PCH-LP has 39 redirection entries */ - reg32 &= ~0x00ff0000; - reg32 |= 0x00270000; - - io_apic_write(VIO_APIC_VADDR, 0x01, reg32); - - /* - * Select Boot Configuration register (0x03) and - * use Processor System Bus (0x01) to deliver interrupts. - */ - io_apic_write(VIO_APIC_VADDR, 0x03, 0x01); -} - -static void enable_hpet(struct device *dev) -{ - size_t i; - - /* Assign unique bus/dev/fn for each HPET */ - for (i = 0; i < 8; ++i) - pci_write_config16(dev, LPC_HnBDF(i), - PCH_HPET_PCI_BUS << 8 | PCH_HPET_PCI_SLOT << 3 | i); -} - -/* 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; - - const uint8_t pirq = 0x80; - - pci_write_config8(dev, PIRQA_ROUT, pirq); - pci_write_config8(dev, PIRQB_ROUT, pirq); - pci_write_config8(dev, PIRQC_ROUT, pirq); - pci_write_config8(dev, PIRQD_ROUT, pirq); - - pci_write_config8(dev, PIRQE_ROUT, pirq); - pci_write_config8(dev, PIRQF_ROUT, pirq); - pci_write_config8(dev, PIRQG_ROUT, pirq); - pci_write_config8(dev, PIRQH_ROUT, pirq); - - for (irq_dev = all_devices; irq_dev; irq_dev = irq_dev->next) { - u8 int_pin = 0, int_line = 0; - - if (!irq_dev->enabled || irq_dev->path.type != DEVICE_PATH_PCI) - continue; - - int_pin = pci_read_config8(irq_dev, PCI_INTERRUPT_PIN); - - switch (int_pin) { - case 1: /* INTA# */ - case 2: /* INTB# */ - case 3: /* INTC# */ - case 4: /* INTD# */ - int_line = pirq; - break; - } - - if (!int_line) - continue; - - pci_write_config8(irq_dev, PCI_INTERRUPT_LINE, int_line); - } -} - -static void pch_power_options(struct device *dev) -{ - u16 reg16; - const char *state; - /* Get the chip configuration */ - const struct soc_intel_broadwell_pch_config *config = config_of(dev); - int pwr_on = CONFIG_MAINBOARD_POWER_FAILURE_STATE; - - /* Which state do we want to goto after g3 (power restored)? - * 0 == S0 Full On - * 1 == S5 Soft Off - * - * If the option is not existent (Laptops), use Kconfig setting. - */ - get_option(&pwr_on, "power_on_after_fail"); - - reg16 = pci_read_config16(dev, GEN_PMCON_3); - reg16 &= 0xfffe; - switch (pwr_on) { - case MAINBOARD_POWER_OFF: - reg16 |= 1; - state = "off"; - break; - case MAINBOARD_POWER_ON: - reg16 &= ~1; - state = "on"; - break; - case MAINBOARD_POWER_KEEP: - reg16 &= ~1; - state = "state keep"; - break; - default: - state = "undefined"; - } - pci_write_config16(dev, GEN_PMCON_3, reg16); - printk(BIOS_INFO, "Set power %s after power failure.\n", state); - - /* GPE setup based on device tree configuration */ - enable_all_gpe(config->gpe0_en_1, config->gpe0_en_2, - config->gpe0_en_3, config->gpe0_en_4); - - /* SMI setup based on device tree configuration */ - enable_alt_smi(config->alt_gp_smi_en); -} - -static void pch_misc_init(struct device *dev) -{ - u8 reg8; - u16 reg16; - u32 reg32; - - reg16 = pci_read_config16(dev, GEN_PMCON_3); - - reg16 &= ~(3 << 4); /* SLP_S4# Assertion Stretch 4s */ - reg16 |= (1 << 3); /* SLP_S4# Assertion Stretch Enable */ - - reg16 &= ~(1 << 10); - reg16 |= (1 << 11); /* SLP_S3# Min Assertion Width 50ms */ - - reg16 |= (1 << 12); /* Disable SLP stretch after SUS well */ - - pci_write_config16(dev, GEN_PMCON_3, reg16); - - /* Prepare sleep mode */ - reg32 = inl(ACPI_BASE_ADDRESS + PM1_CNT); - reg32 &= ~SLP_TYP; - reg32 |= SCI_EN; - outl(reg32, ACPI_BASE_ADDRESS + PM1_CNT); - - /* Set up NMI on errors */ - reg8 = inb(0x61); - reg8 &= ~0xf0; /* Higher nibble must be 0 */ - reg8 |= (1 << 2); /* PCI SERR# disable for now */ - outb(reg8, 0x61); - - /* Disable NMI sources */ - reg8 = inb(0x70); - reg8 |= (1 << 7); /* Can't mask NMI from PCI-E and NMI_NOW */ - outb(reg8, 0x70); - - /* Indicate DRAM init done for MRC */ - pci_or_config8(dev, GEN_PMCON_2, 1 << 7); - - /* Enable BIOS updates outside of SMM */ - pci_and_config8(dev, BIOS_CNTL, ~(1 << 5)); - - /* Clear status bits to prevent unexpected wake */ - RCBA32_OR(0x3310, 0x2f); - - RCBA32_AND_OR(0x3f02, ~0xf, 0); - - /* Enable PCIe Releaxed Order */ - RCBA32_OR(0x2314, (1 << 31) | (1 << 7)), - RCBA32_OR(0x1114, (1 << 15) | (1 << 14)), - - /* Setup SERIRQ, enable continuous mode */ - reg8 = pci_read_config8(dev, SERIRQ_CNTL); - reg8 |= 1 << 7; - - if (CONFIG(SERIRQ_CONTINUOUS_MODE)) - reg8 |= 1 << 6; - - pci_write_config8(dev, SERIRQ_CNTL, reg8); -} - -/* Magic register settings for power management */ -static void pch_pm_init_magic(struct device *dev) -{ - pci_write_config8(dev, 0xa9, 0x46); - - RCBA32_AND_OR(0x232c, ~1, 0); - - RCBA32_OR(0x1100, 0x0000c13f); - - RCBA32_AND_OR(0x2320, ~0x60, 0x10); - - RCBA32(0x3314) = 0x00012fff; - - RCBA32_AND_OR(0x3318, ~0x000f0330, 0x0dcf0400); - - RCBA32(0x3324) = 0x04000000; - RCBA32(0x3368) = 0x00041400; - RCBA32(0x3388) = 0x3f8ddbff; - RCBA32(0x33ac) = 0x00007001; - RCBA32(0x33b0) = 0x00181900; - RCBA32(0x33c0) = 0x00060A00; - RCBA32(0x33d0) = 0x06200840; - RCBA32(0x3a28) = 0x01010101; - RCBA32(0x3a2c) = 0x040c0404; - RCBA32(0x3a9c) = 0x9000000a; - RCBA32(0x2b1c) = 0x03808033; - RCBA32(0x2b34) = 0x80000009; - RCBA32(0x3348) = 0x022ddfff; - RCBA32(0x334c) = 0x00000001; - RCBA32(0x3358) = 0x0001c000; - RCBA32(0x3380) = 0x3f8ddbff; - RCBA32(0x3384) = 0x0001c7e1; - RCBA32(0x338c) = 0x0001c7e1; - RCBA32(0x3398) = 0x0001c000; - RCBA32(0x33a8) = 0x00181900; - RCBA32(0x33dc) = 0x00080000; - RCBA32(0x33e0) = 0x00000001; - RCBA32(0x3a20) = 0x0000040c; - RCBA32(0x3a24) = 0x01010101; - RCBA32(0x3a30) = 0x01010101; - - pci_update_config32(dev, 0xac, ~0x00200000, 0); - - RCBA32_OR(0x0410, 0x00000003); - RCBA32_OR(0x2618, 0x08000000); - RCBA32_OR(0x2300, 0x00000002); - RCBA32_OR(0x2600, 0x00000008); - - RCBA32(0x33b4) = 0x00007001; - RCBA32(0x3350) = 0x022ddfff; - RCBA32(0x3354) = 0x00000001; - - /* Power Optimizer */ - RCBA32_OR(0x33d4, 0x08000000); - RCBA32_OR(0x33c8, 0x00000080); - - RCBA32(0x2b10) = 0x0000883c; - RCBA32(0x2b14) = 0x1e0a4616; - RCBA32(0x2b24) = 0x40000005; - RCBA32(0x2b20) = 0x0005db01; - RCBA32(0x3a80) = 0x05145005; - RCBA32(0x3a84) = 0x00001005; - - RCBA32_OR(0x33d4, 0x2fff2fb1); - RCBA32_OR(0x33c8, 0x00008000); -} - -static void pch_enable_mphy(void) -{ - u32 gpio71_native = gpio_is_native(71); - u32 data_and = 0xffffffff; - u32 data_or = (1 << 14) | (1 << 13) | (1 << 12); - - if (gpio71_native) { - data_or |= (1 << 0); - if (pch_is_wpt()) { - data_and &= ~((1 << 7) | (1 << 6) | (1 << 3)); - data_or |= (1 << 5) | (1 << 4); - - if (pch_is_wpt_ulx()) { - /* Check if SATA and USB3 MPHY are enabled */ - u32 strap19 = pch_read_soft_strap(19); - strap19 &= ((1 << 31) | (1 << 30)); - strap19 >>= 30; - if (strap19 == 3) { - data_or |= (1 << 3); - printk(BIOS_DEBUG, "Enable ULX MPHY PG " - "control in single domain\n"); - } else if (strap19 == 0) { - printk(BIOS_DEBUG, "Enable ULX MPHY PG " - "control in split domains\n"); - } else { - printk(BIOS_DEBUG, "Invalid PCH Soft " - "Strap 19 configuration\n"); - } - } else { - data_or |= (1 << 3); - } - } - } - - pch_iobp_update(0xCF000000, data_and, data_or); -} - -static void pch_init_deep_sx(struct device *dev) -{ - const struct soc_intel_broadwell_pch_config *config = config_of(dev); - - if (config->deep_sx_enable_ac) { - RCBA32_OR(DEEP_S3_POL, DEEP_S3_EN_AC); - RCBA32_OR(DEEP_S5_POL, DEEP_S5_EN_AC); - } - - if (config->deep_sx_enable_dc) { - RCBA32_OR(DEEP_S3_POL, DEEP_S3_EN_DC); - RCBA32_OR(DEEP_S5_POL, DEEP_S5_EN_DC); - } - - if (config->deep_sx_enable_ac || config->deep_sx_enable_dc) - RCBA32_OR(DEEP_SX_CONFIG, - DEEP_SX_WAKE_PIN_EN | DEEP_SX_GP27_PIN_EN); -} - -/* Power Management init */ -static void pch_pm_init(struct device *dev) -{ - printk(BIOS_DEBUG, "PCH PM init\n"); - - pch_init_deep_sx(dev); - - pch_enable_mphy(); - - pch_pm_init_magic(dev); - - if (pch_is_wpt()) { - RCBA32_OR(0x33e0, (1 << 4) | (1 << 1)); - RCBA32_OR(0x2b1c, (1 << 22) | (1 << 14) | (1 << 13)); - RCBA32(0x33e4) = 0x16bf0002; - RCBA32_OR(0x33e4, 0x1); - } - - pch_iobp_update(0xCA000000, ~0UL, 0x00000009); - - /* Set RCBA 0x2b1c[29]=1 if DSP disabled */ - if (RCBA32(FD) & PCH_DISABLE_ADSPD) - RCBA32_OR(0x2b1c, (1 << 29)); - -} - -static void pch_cg_init(struct device *dev) -{ - u32 reg32; - u16 reg16; - struct device *igd_dev = pcidev_path_on_root(SA_DEVFN_IGD); - - /* DMI */ - RCBA32_OR(0x2234, 0xf); - - reg16 = pci_read_config16(dev, GEN_PMCON_1); - reg16 &= ~(1 << 10); /* Disable BIOS_PCI_EXP_EN for native PME */ - if (pch_is_wpt()) - reg16 &= ~(1 << 11); - else - reg16 |= (1 << 11); - reg16 |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 12); - reg16 |= (1 << 2); // PCI CLKRUN# Enable - pci_write_config16(dev, GEN_PMCON_1, reg16); - - /* - * RCBA + 0x2614[27:25,14:13,10,8] = 101,11,1,1 - * RCBA + 0x2614[23:16] = 0x20 - * RCBA + 0x2614[30:28] = 0x0 - * RCBA + 0x2614[26] = 1 (IF 0:2.0@0x08 >= 0x0b) - */ - RCBA32_AND_OR(0x2614, ~0x64ff0000, 0x0a206500); - - /* Check for 0:2.0@0x08 >= 0x0b */ - if (pch_is_wpt() || pci_read_config8(igd_dev, 0x8) >= 0x0b) - RCBA32_OR(0x2614, (1 << 26)); - - RCBA32_OR(0x900, 0x0000031f); - - reg32 = RCBA32(CG); - if (RCBA32(0x3454) & (1 << 4)) - reg32 &= ~(1 << 29); // LPC Dynamic - else - reg32 |= (1 << 29); // LPC Dynamic - reg32 |= (1 << 31); // LP LPC - reg32 |= (1 << 30); // LP BLA - if (RCBA32(0x3454) & (1 << 4)) - reg32 &= ~(1 << 29); - else - reg32 |= (1 << 29); - reg32 |= (1 << 28); // GPIO Dynamic - reg32 |= (1 << 27); // HPET Dynamic - reg32 |= (1 << 26); // Generic Platform Event Clock - if (RCBA32(BUC) & PCH_DISABLE_GBE) - reg32 |= (1 << 23); // GbE Static - if (RCBA32(FD) & PCH_DISABLE_HD_AUDIO) - reg32 |= (1 << 21); // HDA Static - reg32 |= (1 << 22); // HDA Dynamic - RCBA32(CG) = reg32; - - /* PCH-LP LPC */ - if (pch_is_wpt()) - RCBA32_AND_OR(0x3434, ~0x1f, 0x17); - else - RCBA32_OR(0x3434, 0x7); - - /* SPI */ - RCBA32_OR(0x38c0, 0x3c07); - - pch_iobp_update(0xCE00C000, ~1UL, 0x00000000); -} - -static void pch_set_acpi_mode(void) -{ - if (!acpi_is_wakeup_s3()) { - apm_control(APM_CNT_ACPI_DISABLE); - } -} - -static void lpc_init(struct device *dev) -{ - /* Legacy initialization */ - isa_dma_init(); - sb_rtc_init(); - pch_misc_init(dev); - - /* Interrupt configuration */ - pch_enable_ioapic(dev); - pch_pirq_init(dev); - setup_i8259(); - i8259_configure_irq_trigger(9, 1); - enable_hpet(dev); - - /* Initialize power management */ - pch_power_options(dev); - pch_pm_init(dev); - pch_cg_init(dev); - - pch_set_acpi_mode(); -} - -static void pch_lpc_add_mmio_resources(struct device *dev) -{ - u32 reg; - struct resource *res; - const u32 default_decode_base = IO_APIC_ADDR; - - /* - * Just report all resources from IO-APIC base to 4GiB. Don't mark - * them reserved as that may upset the OS if this range is marked - * as reserved in the e820. - */ - res = new_resource(dev, OIC); - res->base = default_decode_base; - res->size = 0 - default_decode_base; - res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; - - /* RCBA */ - if (default_decode_base > RCBA_BASE_ADDRESS) { - res = new_resource(dev, RCBA); - res->base = RCBA_BASE_ADDRESS; - res->size = 16 * 1024; - res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | - IORESOURCE_FIXED | IORESOURCE_RESERVE; - } - - /* Check LPC Memory Decode register. */ - reg = pci_read_config32(dev, LGMR); - if (reg & 1) { - reg &= ~0xffff; - if (reg < default_decode_base) { - res = new_resource(dev, LGMR); - res->base = reg; - res->size = 16 * 1024; - res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | - IORESOURCE_FIXED | IORESOURCE_RESERVE; - } - } -} - -/* Default IO range claimed by the LPC device. The upper bound is exclusive. */ -#define LPC_DEFAULT_IO_RANGE_LOWER 0 -#define LPC_DEFAULT_IO_RANGE_UPPER 0x1000 - -static inline int pch_io_range_in_default(int base, int size) -{ - /* Does it start above the range? */ - if (base >= LPC_DEFAULT_IO_RANGE_UPPER) - return 0; - - /* Is it entirely contained? */ - if (base >= LPC_DEFAULT_IO_RANGE_LOWER && - (base + size) < LPC_DEFAULT_IO_RANGE_UPPER) - return 1; - - /* This will return not in range for partial overlaps. */ - return 0; -} - -/* - * Note: this function assumes there is no overlap with the default LPC device's - * claimed range: LPC_DEFAULT_IO_RANGE_LOWER -> LPC_DEFAULT_IO_RANGE_UPPER. - */ -static void pch_lpc_add_io_resource(struct device *dev, u16 base, u16 size, - int index) -{ - struct resource *res; - - if (pch_io_range_in_default(base, size)) - return; - - res = new_resource(dev, index); - res->base = base; - res->size = size; - res->flags = IORESOURCE_IO | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; -} - -static void pch_lpc_add_gen_io_resources(struct device *dev, int reg_value, - int index) -{ - /* - * Check if the register is enabled. If so and the base exceeds the - * device's default claim range add the resource. - */ - if (reg_value & 1) { - u16 base = reg_value & 0xfffc; - u16 size = (0x3 | ((reg_value >> 16) & 0xfc)) + 1; - pch_lpc_add_io_resource(dev, base, size, index); - } -} - -static void pch_lpc_add_io_resources(struct device *dev) -{ - struct resource *res; - const struct soc_intel_broadwell_pch_config *config = config_of(dev); - - /* Add the default claimed IO range for the LPC device. */ - res = new_resource(dev, 0); - res->base = LPC_DEFAULT_IO_RANGE_LOWER; - res->size = LPC_DEFAULT_IO_RANGE_UPPER - LPC_DEFAULT_IO_RANGE_LOWER; - res->flags = IORESOURCE_IO | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; - - /* GPIOBASE */ - pch_lpc_add_io_resource(dev, GPIO_BASE_ADDRESS, - GPIO_BASE_SIZE, GPIO_BASE); - - /* PMBASE */ - pch_lpc_add_io_resource(dev, ACPI_BASE_ADDRESS, ACPI_BASE_SIZE, PMBASE); - - /* LPC Generic IO Decode range. */ - pch_lpc_add_gen_io_resources(dev, config->gen1_dec, LPC_GEN1_DEC); - pch_lpc_add_gen_io_resources(dev, config->gen2_dec, LPC_GEN2_DEC); - pch_lpc_add_gen_io_resources(dev, config->gen3_dec, LPC_GEN3_DEC); - pch_lpc_add_gen_io_resources(dev, config->gen4_dec, LPC_GEN4_DEC); -} - -static void pch_lpc_read_resources(struct device *dev) -{ - struct global_nvs *gnvs; - - /* 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); - - /* Allocate ACPI NVS in CBMEM */ - gnvs = cbmem_add(CBMEM_ID_ACPI_GNVS, sizeof(struct global_nvs)); - if (!acpi_is_wakeup_s3() && gnvs) - memset(gnvs, 0, sizeof(struct global_nvs)); -} - -static void southcluster_inject_dsdt(const struct device *device) -{ - struct global_nvs *gnvs; - - gnvs = cbmem_find(CBMEM_ID_ACPI_GNVS); - if (!gnvs) { - gnvs = cbmem_add(CBMEM_ID_ACPI_GNVS, sizeof(*gnvs)); - if (gnvs) - memset(gnvs, 0, sizeof(*gnvs)); - } - - if (gnvs) { - acpi_create_gnvs(gnvs); - /* And tell SMI about it */ - apm_control(APM_CNT_GNVS_UPDATE); - - /* Add it to DSDT. */ - acpigen_write_scope("\\"); - acpigen_write_name_dword("NVSA", (u32) gnvs); - acpigen_pop_len(); - } -} - -static unsigned long broadwell_write_acpi_tables(const struct device *device, - unsigned long current, - struct acpi_rsdp *rsdp) -{ - if (CONFIG(INTEL_PCH_UART_CONSOLE)) - current = acpi_write_dbg2_pci_uart(rsdp, current, - (CONFIG_INTEL_PCH_UART_CONSOLE_NUMBER == 1) ? - PCH_DEV_UART1 : PCH_DEV_UART0, - ACPI_ACCESS_SIZE_BYTE_ACCESS); - return acpi_write_hpet(device, current, rsdp); -} - -static struct device_operations device_ops = { - .read_resources = &pch_lpc_read_resources, - .set_resources = &pci_dev_set_resources, - .enable_resources = &pci_dev_enable_resources, - .acpi_inject_dsdt = southcluster_inject_dsdt, - .write_acpi_tables = broadwell_write_acpi_tables, - .init = &lpc_init, - .scan_bus = &scan_static_bus, - .ops_pci = &pci_dev_ops_pci, -}; - -static const unsigned short pci_device_ids[] = { - PCH_LPT_LP_SAMPLE, - PCH_LPT_LP_PREMIUM, - PCH_LPT_LP_MAINSTREAM, - PCH_LPT_LP_VALUE, - PCH_WPT_HSW_U_SAMPLE, - PCH_WPT_BDW_U_SAMPLE, - PCH_WPT_BDW_U_PREMIUM, - PCH_WPT_BDW_U_BASE, - PCH_WPT_BDW_Y_SAMPLE, - PCH_WPT_BDW_Y_PREMIUM, - PCH_WPT_BDW_Y_BASE, - PCH_WPT_BDW_H, - 0 -}; - -static const struct pci_driver pch_lpc __pci_driver = { - .ops = &device_ops, - .vendor = PCI_VENDOR_ID_INTEL, - .devices = pci_device_ids, -}; diff --git a/src/soc/intel/broadwell/me.c b/src/soc/intel/broadwell/me.c deleted file mode 100644 index 40a81d8810..0000000000 --- a/src/soc/intel/broadwell/me.c +++ /dev/null @@ -1,1057 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -/* - * This is a ramstage driver for the Intel Management Engine found in the - * southbridge. It handles the required boot-time messages over the - * MMIO-based Management Engine Interface to tell the ME that the BIOS is - * finished with POST. Additional messages are defined for debug but are - * not used unless the console loglevel is high enough. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if CONFIG(CHROMEOS) -#include -#include -#endif - -/* Path that the BIOS should take based on ME state */ -static const char *me_bios_path_values[] = { - [ME_NORMAL_BIOS_PATH] = "Normal", - [ME_S3WAKE_BIOS_PATH] = "S3 Wake", - [ME_ERROR_BIOS_PATH] = "Error", - [ME_RECOVERY_BIOS_PATH] = "Recovery", - [ME_DISABLE_BIOS_PATH] = "Disable", - [ME_FIRMWARE_UPDATE_BIOS_PATH] = "Firmware Update", -}; - -/* MMIO base address for MEI interface */ -static u8 *mei_base_address; - -static void mei_dump(void *ptr, int dword, int offset, const char *type) -{ - struct mei_csr *csr; - - if (!CONFIG(DEBUG_INTEL_ME)) - return; - - printk(BIOS_SPEW, "%-9s[%02x] : ", type, offset); - - switch (offset) { - case MEI_H_CSR: - case MEI_ME_CSR_HA: - csr = ptr; - if (!csr) { - printk(BIOS_SPEW, "ERROR: 0x%08x\n", dword); - break; - } - printk(BIOS_SPEW, "cbd=%u cbrp=%02u cbwp=%02u ready=%u " - "reset=%u ig=%u is=%u ie=%u\n", csr->buffer_depth, - csr->buffer_read_ptr, csr->buffer_write_ptr, - csr->ready, csr->reset, csr->interrupt_generate, - csr->interrupt_status, csr->interrupt_enable); - break; - case MEI_ME_CB_RW: - case MEI_H_CB_WW: - printk(BIOS_SPEW, "CB: 0x%08x\n", dword); - break; - default: - printk(BIOS_SPEW, "0x%08x\n", offset); - break; - } -} - -/* - * ME/MEI access helpers using memcpy to avoid aliasing. - */ - -static inline void mei_read_dword_ptr(void *ptr, int offset) -{ - u32 dword = read32(mei_base_address + offset); - memcpy(ptr, &dword, sizeof(dword)); - mei_dump(ptr, dword, offset, "READ"); -} - -static inline void mei_write_dword_ptr(void *ptr, int offset) -{ - u32 dword = 0; - memcpy(&dword, ptr, sizeof(dword)); - write32(mei_base_address + offset, dword); - mei_dump(ptr, dword, offset, "WRITE"); -} - -static inline void pci_read_dword_ptr(struct device *dev, void *ptr, int offset) -{ - u32 dword = pci_read_config32(dev, offset); - memcpy(ptr, &dword, sizeof(dword)); - mei_dump(ptr, dword, offset, "PCI READ"); -} - -static inline void read_host_csr(struct mei_csr *csr) -{ - mei_read_dword_ptr(csr, MEI_H_CSR); -} - -static inline void write_host_csr(struct mei_csr *csr) -{ - mei_write_dword_ptr(csr, MEI_H_CSR); -} - -static inline void read_me_csr(struct mei_csr *csr) -{ - mei_read_dword_ptr(csr, MEI_ME_CSR_HA); -} - -static inline void write_cb(u32 dword) -{ - write32(mei_base_address + MEI_H_CB_WW, dword); - mei_dump(NULL, dword, MEI_H_CB_WW, "WRITE"); -} - -static inline u32 read_cb(void) -{ - u32 dword = read32(mei_base_address + MEI_ME_CB_RW); - mei_dump(NULL, dword, MEI_ME_CB_RW, "READ"); - return dword; -} - -/* Wait for ME ready bit to be asserted */ -static int mei_wait_for_me_ready(void) -{ - struct mei_csr me; - unsigned int try = ME_RETRY; - - while (try--) { - read_me_csr(&me); - if (me.ready) - return 0; - udelay(ME_DELAY); - } - - printk(BIOS_ERR, "ME: failed to become ready\n"); - return -1; -} - -static void mei_reset(void) -{ - struct mei_csr host; - - if (mei_wait_for_me_ready() < 0) - return; - - /* Reset host and ME circular buffers for next message */ - read_host_csr(&host); - host.reset = 1; - host.interrupt_generate = 1; - write_host_csr(&host); - - if (mei_wait_for_me_ready() < 0) - return; - - /* Re-init and indicate host is ready */ - read_host_csr(&host); - host.interrupt_generate = 1; - host.ready = 1; - host.reset = 0; - write_host_csr(&host); -} - -static int mei_send_packet(struct mei_header *mei, void *req_data) -{ - struct mei_csr host; - unsigned int ndata, n; - u32 *data; - - /* Number of dwords to write */ - ndata = mei->length >> 2; - - /* Pad non-dword aligned request message length */ - if (mei->length & 3) - ndata++; - if (!ndata) { - printk(BIOS_DEBUG, "ME: request has no data\n"); - return -1; - } - ndata++; /* Add MEI header */ - - /* - * Make sure there is still room left in the circular buffer. - * Reset the buffer pointers if the requested message will not fit. - */ - read_host_csr(&host); - if ((host.buffer_depth - host.buffer_write_ptr) < ndata) { - printk(BIOS_ERR, "ME: circular buffer full, resetting...\n"); - mei_reset(); - read_host_csr(&host); - } - - /* Ensure the requested length will fit in the circular buffer. */ - if ((host.buffer_depth - host.buffer_write_ptr) < ndata) { - printk(BIOS_ERR, "ME: message (%u) too large for buffer (%u)\n", - ndata + 2, host.buffer_depth); - return -1; - } - - /* Write MEI header */ - mei_write_dword_ptr(mei, MEI_H_CB_WW); - ndata--; - - /* Write message data */ - data = req_data; - for (n = 0; n < ndata; ++n) - write_cb(*data++); - - /* Generate interrupt to the ME */ - read_host_csr(&host); - host.interrupt_generate = 1; - write_host_csr(&host); - - /* Make sure ME is ready after sending request data */ - return mei_wait_for_me_ready(); -} - -static int mei_send_data(u8 me_address, u8 host_address, - void *req_data, int req_bytes) -{ - struct mei_header header = { - .client_address = me_address, - .host_address = host_address, - }; - struct mei_csr host; - int current = 0; - u8 *req_ptr = req_data; - - while (!header.is_complete) { - int remain = req_bytes - current; - int buf_len; - - read_host_csr(&host); - buf_len = host.buffer_depth - host.buffer_write_ptr; - - if (buf_len > remain) { - /* Send all remaining data as final message */ - header.length = req_bytes - current; - header.is_complete = 1; - } else { - /* Send as much data as the buffer can hold */ - header.length = buf_len; - } - - mei_send_packet(&header, req_ptr); - - req_ptr += header.length; - current += header.length; - } - - return 0; -} - -static int mei_send_header(u8 me_address, u8 host_address, - void *header, int header_len, int complete) -{ - struct mei_header mei = { - .client_address = me_address, - .host_address = host_address, - .length = header_len, - .is_complete = complete, - }; - return mei_send_packet(&mei, header); -} - -static int mei_recv_msg(void *header, int header_bytes, - void *rsp_data, int rsp_bytes) -{ - struct mei_header mei_rsp; - struct mei_csr me, host; - unsigned int ndata, n; - unsigned int expected; - u32 *data; - - /* Total number of dwords to read from circular buffer */ - expected = (rsp_bytes + sizeof(mei_rsp) + header_bytes) >> 2; - if (rsp_bytes & 3) - expected++; - - if (mei_wait_for_me_ready() < 0) - return -1; - - /* - * The interrupt status bit does not appear to indicate that the - * message has actually been received. Instead we wait until the - * expected number of dwords are present in the circular buffer. - */ - for (n = ME_RETRY; n; --n) { - read_me_csr(&me); - if ((me.buffer_write_ptr - me.buffer_read_ptr) >= expected) - break; - udelay(ME_DELAY); - } - if (!n) { - printk(BIOS_ERR, "ME: timeout waiting for data: expected " - "%u, available %u\n", expected, - me.buffer_write_ptr - me.buffer_read_ptr); - return -1; - } - - /* Read and verify MEI response header from the ME */ - mei_read_dword_ptr(&mei_rsp, MEI_ME_CB_RW); - if (!mei_rsp.is_complete) { - printk(BIOS_ERR, "ME: response is not complete\n"); - return -1; - } - - /* Handle non-dword responses and expect at least the header */ - ndata = mei_rsp.length >> 2; - if (mei_rsp.length & 3) - ndata++; - if (ndata != (expected - 1)) { - printk(BIOS_ERR, "ME: response is missing data %d != %d\n", - ndata, (expected - 1)); - return -1; - } - - /* Read response header from the ME */ - data = header; - for (n = 0; n < (header_bytes >> 2); ++n) - *data++ = read_cb(); - ndata -= header_bytes >> 2; - - /* Make sure caller passed a buffer with enough space */ - if (ndata != (rsp_bytes >> 2)) { - printk(BIOS_ERR, "ME: not enough room in response buffer: " - "%u != %u\n", ndata, rsp_bytes >> 2); - return -1; - } - - /* Read response data from the circular buffer */ - data = rsp_data; - for (n = 0; n < ndata; ++n) - *data++ = read_cb(); - - /* Tell the ME that we have consumed the response */ - read_host_csr(&host); - host.interrupt_status = 1; - host.interrupt_generate = 1; - write_host_csr(&host); - - return mei_wait_for_me_ready(); -} - -static inline int mei_sendrecv_mkhi(struct mkhi_header *mkhi, - void *req_data, int req_bytes, - void *rsp_data, int rsp_bytes) -{ - struct mkhi_header mkhi_rsp; - - /* Send header */ - if (mei_send_header(MEI_ADDRESS_MKHI, MEI_HOST_ADDRESS, - mkhi, sizeof(*mkhi), req_bytes ? 0 : 1) < 0) - return -1; - - /* Send data if available */ - if (req_bytes && mei_send_data(MEI_ADDRESS_MKHI, MEI_HOST_ADDRESS, - req_data, req_bytes) < 0) - return -1; - - /* Return now if no response expected */ - if (!rsp_bytes) - return 0; - - /* Read header and data */ - if (mei_recv_msg(&mkhi_rsp, sizeof(mkhi_rsp), - rsp_data, rsp_bytes) < 0) - return -1; - - if (!mkhi_rsp.is_response || - mkhi->group_id != mkhi_rsp.group_id || - mkhi->command != mkhi_rsp.command) { - printk(BIOS_ERR, "ME: invalid response, group %u ?= %u," - "command %u ?= %u, is_response %u\n", mkhi->group_id, - mkhi_rsp.group_id, mkhi->command, mkhi_rsp.command, - mkhi_rsp.is_response); - return -1; - } - - return 0; -} - -static inline int mei_sendrecv_icc(struct icc_header *icc, - void *req_data, int req_bytes, - void *rsp_data, int rsp_bytes) -{ - struct icc_header icc_rsp; - - /* Send header */ - if (mei_send_header(MEI_ADDRESS_ICC, MEI_HOST_ADDRESS, - icc, sizeof(*icc), req_bytes ? 0 : 1) < 0) - return -1; - - /* Send data if available */ - if (req_bytes && mei_send_data(MEI_ADDRESS_ICC, MEI_HOST_ADDRESS, - req_data, req_bytes) < 0) - return -1; - - /* Read header and data, if needed */ - if (rsp_bytes && mei_recv_msg(&icc_rsp, sizeof(icc_rsp), - rsp_data, rsp_bytes) < 0) - return -1; - - return 0; -} - -/* - * mbp give up routine. This path is taken if hfs.mpb_rdy is 0 or the read - * state machine on the BIOS end doesn't match the ME's state machine. - */ -static void intel_me_mbp_give_up(struct device *dev) -{ - struct mei_csr csr; - - pci_write_config32(dev, PCI_ME_H_GS2, PCI_ME_MBP_GIVE_UP); - - read_host_csr(&csr); - csr.reset = 1; - csr.interrupt_generate = 1; - write_host_csr(&csr); -} - -/* - * mbp clear routine. This will wait for the ME to indicate that - * the MBP has been read and cleared. - */ -static void intel_me_mbp_clear(struct device *dev) -{ - int count; - struct me_hfs2 hfs2; - - /* Wait for the mbp_cleared indicator */ - for (count = ME_RETRY; count > 0; --count) { - pci_read_dword_ptr(dev, &hfs2, PCI_ME_HFS2); - if (hfs2.mbp_cleared) - break; - udelay(ME_DELAY); - } - - if (count == 0) { - printk(BIOS_WARNING, "ME: Timeout waiting for mbp_cleared\n"); - intel_me_mbp_give_up(dev); - } else { - printk(BIOS_INFO, "ME: MBP cleared\n"); - } -} - -static void me_print_fw_version(mbp_fw_version_name *vers_name) -{ - if (!vers_name) { - printk(BIOS_ERR, "ME: mbp missing version report\n"); - return; - } - - printk(BIOS_DEBUG, "ME: found version %d.%d.%d.%d\n", - vers_name->major_version, vers_name->minor_version, - vers_name->hotfix_version, vers_name->build_version); -} - -static inline void print_cap(const char *name, int state) -{ - printk(BIOS_DEBUG, "ME Capability: %-41s : %sabled\n", - name, state ? " en" : "dis"); -} - -/* Get ME Firmware Capabilities */ -static int mkhi_get_fwcaps(mbp_mefwcaps *cap) -{ - u32 rule_id = 0; - struct me_fwcaps cap_msg; - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_FWCAPS, - .command = MKHI_FWCAPS_GET_RULE, - }; - - /* Send request and wait for response */ - if (mei_sendrecv_mkhi(&mkhi, &rule_id, sizeof(u32), - &cap_msg, sizeof(cap_msg)) < 0) { - printk(BIOS_ERR, "ME: GET FWCAPS message failed\n"); - return -1; - } - *cap = cap_msg.caps_sku; - return 0; -} - -/* Get ME Firmware Capabilities */ -static void me_print_fwcaps(mbp_mefwcaps *cap) -{ - mbp_mefwcaps local_caps; - if (!cap) { - cap = &local_caps; - printk(BIOS_ERR, "ME: mbp missing fwcaps report\n"); - if (mkhi_get_fwcaps(cap)) - return; - } - - print_cap("Full Network manageability", cap->full_net); - print_cap("Regular Network manageability", cap->std_net); - print_cap("Manageability", cap->manageability); - print_cap("IntelR Anti-Theft (AT)", cap->intel_at); - print_cap("IntelR Capability Licensing Service (CLS)", cap->intel_cls); - print_cap("IntelR Power Sharing Technology (MPC)", cap->intel_mpc); - print_cap("ICC Over Clocking", cap->icc_over_clocking); - print_cap("Protected Audio Video Path (PAVP)", cap->pavp); - print_cap("IPV6", cap->ipv6); - print_cap("KVM Remote Control (KVM)", cap->kvm); - print_cap("Outbreak Containment Heuristic (OCH)", cap->och); - print_cap("Virtual LAN (VLAN)", cap->vlan); - print_cap("TLS", cap->tls); - print_cap("Wireless LAN (WLAN)", cap->wlan); -} - -/* Send END OF POST message to the ME */ -static int mkhi_end_of_post(void) -{ - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_GEN, - .command = MKHI_END_OF_POST, - }; - u32 eop_ack; - - /* Send request and wait for response */ - if (mei_sendrecv_mkhi(&mkhi, NULL, 0, &eop_ack, sizeof(eop_ack)) < 0) { - printk(BIOS_ERR, "ME: END OF POST message failed\n"); - return -1; - } - - printk(BIOS_INFO, "ME: END OF POST message successful (%d)\n", eop_ack); - return 0; -} - -/* Send END OF POST message to the ME */ -static int mkhi_end_of_post_noack(void) -{ - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_GEN, - .command = MKHI_END_OF_POST_NOACK, - }; - - /* Send request, do not wait for response */ - if (mei_sendrecv_mkhi(&mkhi, NULL, 0, NULL, 0) < 0) { - printk(BIOS_ERR, "ME: END OF POST NOACK message failed\n"); - return -1; - } - - printk(BIOS_INFO, "ME: END OF POST NOACK message successful\n"); - return 0; -} - -/* Send HMRFPO LOCK message to the ME */ -static int mkhi_hmrfpo_lock(void) -{ - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_HMRFPO, - .command = MKHI_HMRFPO_LOCK, - }; - u32 ack; - - /* Send request and wait for response */ - if (mei_sendrecv_mkhi(&mkhi, NULL, 0, &ack, sizeof(ack)) < 0) { - printk(BIOS_ERR, "ME: HMRFPO LOCK message failed\n"); - return -1; - } - - printk(BIOS_INFO, "ME: HMRFPO LOCK message successful (%d)\n", ack); - return 0; -} - -/* Send HMRFPO LOCK message to the ME, do not wait for response */ -static int mkhi_hmrfpo_lock_noack(void) -{ - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_HMRFPO, - .command = MKHI_HMRFPO_LOCK_NOACK, - }; - - /* Send request, do not wait for response */ - if (mei_sendrecv_mkhi(&mkhi, NULL, 0, NULL, 0) < 0) { - printk(BIOS_ERR, "ME: HMRFPO LOCK NOACK message failed\n"); - return -1; - } - - printk(BIOS_INFO, "ME: HMRFPO LOCK NOACK message successful\n"); - return 0; -} - -static void intel_me_finalize(struct device *dev) -{ - u16 reg16; - - /* S3 path will have hidden this device already */ - if (!mei_base_address || mei_base_address == (u8 *) 0xfffffff0) - return; - - /* Make sure IO is 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 the PCI device */ - RCBA32_OR(FD2, PCH_DISABLE_MEI1); - RCBA32(FD2); -} - -static int me_icc_set_clock_enables(u32 mask) -{ - struct icc_clock_enables_msg clk = { - .clock_enables = 0, /* Turn off specified clocks */ - .clock_mask = mask, - .no_response = 1, /* Do not expect response */ - }; - struct icc_header icc = { - .api_version = ICC_API_VERSION_LYNXPOINT, - .icc_command = ICC_SET_CLOCK_ENABLES, - .length = sizeof(clk), - }; - - /* Send request and wait for response */ - if (mei_sendrecv_icc(&icc, &clk, sizeof(clk), NULL, 0) < 0) { - printk(BIOS_ERR, "ME: ICC SET CLOCK ENABLES message failed\n"); - return -1; - } - printk(BIOS_INFO, "ME: ICC SET CLOCK ENABLES 0x%08x\n", mask); - return 0; -} - -/* Determine the path that we should take based on ME status */ -static me_bios_path intel_me_path(struct device *dev) -{ - me_bios_path path = ME_DISABLE_BIOS_PATH; - struct me_hfs hfs; - struct me_hfs2 hfs2; - - /* Check and dump status */ - intel_me_status(); - - pci_read_dword_ptr(dev, &hfs, PCI_ME_HFS); - pci_read_dword_ptr(dev, &hfs2, PCI_ME_HFS2); - - /* Check Current Working State */ - switch (hfs.working_state) { - case ME_HFS_CWS_NORMAL: - path = ME_NORMAL_BIOS_PATH; - break; - case ME_HFS_CWS_REC: - path = ME_RECOVERY_BIOS_PATH; - break; - default: - path = ME_DISABLE_BIOS_PATH; - break; - } - - /* Check Current Operation Mode */ - switch (hfs.operation_mode) { - case ME_HFS_MODE_NORMAL: - break; - case ME_HFS_MODE_DEBUG: - case ME_HFS_MODE_DIS: - case ME_HFS_MODE_OVER_JMPR: - case ME_HFS_MODE_OVER_MEI: - default: - path = ME_DISABLE_BIOS_PATH; - break; - } - - /* Check for any error code and valid firmware and MBP */ - if (hfs.error_code || hfs.fpt_bad) - path = ME_ERROR_BIOS_PATH; - - /* Check if the MBP is ready */ - if (!hfs2.mbp_rdy) { - printk(BIOS_CRIT, "%s: mbp is not ready!\n", - __func__); - path = ME_ERROR_BIOS_PATH; - } - - if (CONFIG(ELOG) && path != ME_NORMAL_BIOS_PATH) { - struct elog_event_data_me_extended data = { - .current_working_state = hfs.working_state, - .operation_state = hfs.operation_state, - .operation_mode = hfs.operation_mode, - .error_code = hfs.error_code, - .progress_code = hfs2.progress_code, - .current_pmevent = hfs2.current_pmevent, - .current_state = hfs2.current_state, - }; - elog_add_event_byte(ELOG_TYPE_MANAGEMENT_ENGINE, path); - elog_add_event_raw(ELOG_TYPE_MANAGEMENT_ENGINE_EXT, - &data, sizeof(data)); - } - - return path; -} - -/* Prepare ME for MEI messages */ -static int intel_mei_setup(struct device *dev) -{ - struct resource *res; - struct mei_csr host; - - /* Find the MMIO base for the ME interface */ - res = find_resource(dev, PCI_BASE_ADDRESS_0); - if (!res || res->base == 0 || res->size == 0) { - printk(BIOS_DEBUG, "ME: MEI resource not present!\n"); - return -1; - } - mei_base_address = res2mmio(res, 0, 0); - - /* Ensure Memory and Bus Master bits are set */ - pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); - - /* Clean up status for next message */ - read_host_csr(&host); - host.interrupt_generate = 1; - host.ready = 1; - host.reset = 0; - write_host_csr(&host); - - return 0; -} - -/* Read the Extend register hash of ME firmware */ -static int intel_me_extend_valid(struct device *dev) -{ - struct me_heres status; - u32 extend[8] = {0}; - int i, count = 0; - - pci_read_dword_ptr(dev, &status, PCI_ME_HERES); - if (!status.extend_feature_present) { - printk(BIOS_ERR, "ME: Extend Feature not present\n"); - return -1; - } - - if (!status.extend_reg_valid) { - printk(BIOS_ERR, "ME: Extend Register not valid\n"); - return -1; - } - - switch (status.extend_reg_algorithm) { - case PCI_ME_EXT_SHA1: - count = 5; - printk(BIOS_DEBUG, "ME: Extend SHA-1: "); - break; - case PCI_ME_EXT_SHA256: - count = 8; - printk(BIOS_DEBUG, "ME: Extend SHA-256: "); - break; - default: - printk(BIOS_ERR, "ME: Extend Algorithm %d unknown\n", - status.extend_reg_algorithm); - return -1; - } - - for (i = 0; i < count; ++i) { - extend[i] = pci_read_config32(dev, PCI_ME_HER(i)); - printk(BIOS_DEBUG, "%08x", extend[i]); - } - printk(BIOS_DEBUG, "\n"); - -#if CONFIG(CHROMEOS) - /* Save hash in NVS for the OS to verify */ - chromeos_set_me_hash(extend, count); -#endif - - return 0; -} - -static void intel_me_print_mbp(me_bios_payload *mbp_data) -{ - me_print_fw_version(mbp_data->fw_version_name); - - if (CONFIG(DEBUG_INTEL_ME)) - me_print_fwcaps(mbp_data->fw_capabilities); - - if (mbp_data->plat_time) { - printk(BIOS_DEBUG, "ME: Wake Event to ME Reset: %u ms\n", - mbp_data->plat_time->wake_event_mrst_time_ms); - printk(BIOS_DEBUG, "ME: ME Reset to Platform Reset: %u ms\n", - mbp_data->plat_time->mrst_pltrst_time_ms); - printk(BIOS_DEBUG, "ME: Platform Reset to CPU Reset: %u ms\n", - mbp_data->plat_time->pltrst_cpurst_time_ms); - } -} - -static u32 me_to_host_words_pending(void) -{ - struct mei_csr me; - read_me_csr(&me); - if (!me.ready) - return 0; - return (me.buffer_write_ptr - me.buffer_read_ptr) & - (me.buffer_depth - 1); -} - -struct mbp_payload { - mbp_header header; - u32 data[0]; -}; - -/* - * Read and print ME MBP data - * - * Return -1 to indicate a problem (give up) - * Return 0 to indicate success (send LOCK+EOP) - * Return 1 to indicate success (send LOCK+EOP with NOACK) - */ -static int intel_me_read_mbp(me_bios_payload *mbp_data, struct device *dev) -{ - mbp_header mbp_hdr; - u32 me2host_pending; - struct mei_csr host; - struct me_hfs2 hfs2; - struct mbp_payload *mbp; - int i; - int ret = 0; - - pci_read_dword_ptr(dev, &hfs2, PCI_ME_HFS2); - - if (!hfs2.mbp_rdy) { - printk(BIOS_ERR, "ME: MBP not ready\n"); - intel_me_mbp_give_up(dev); - return -1; - } - - me2host_pending = me_to_host_words_pending(); - if (!me2host_pending) { - printk(BIOS_ERR, "ME: no mbp data!\n"); - intel_me_mbp_give_up(dev); - return -1; - } - - /* we know for sure that at least the header is there */ - mei_read_dword_ptr(&mbp_hdr, MEI_ME_CB_RW); - - if ((mbp_hdr.num_entries > (mbp_hdr.mbp_size / 2)) || - (me2host_pending < mbp_hdr.mbp_size)) { - printk(BIOS_ERR, "ME: mbp of %d entries, total size %d words" - " buffer contains %d words\n", - mbp_hdr.num_entries, mbp_hdr.mbp_size, - me2host_pending); - intel_me_mbp_give_up(dev); - return -1; - } - mbp = malloc(mbp_hdr.mbp_size * sizeof(u32)); - if (!mbp) { - intel_me_mbp_give_up(dev); - return -1; - } - - mbp->header = mbp_hdr; - me2host_pending--; - - i = 0; - while (i != me2host_pending) { - mei_read_dword_ptr(&mbp->data[i], MEI_ME_CB_RW); - i++; - } - - read_host_csr(&host); - - /* Check that read and write pointers are equal. */ - if (host.buffer_read_ptr != host.buffer_write_ptr) { - printk(BIOS_INFO, "ME: MBP Read/Write pointer mismatch\n"); - printk(BIOS_INFO, "ME: MBP Waiting for MBP cleared flag\n"); - - /* Tell ME that the host has finished reading the MBP. */ - host.interrupt_generate = 1; - host.reset = 0; - write_host_csr(&host); - - /* Wait for the mbp_cleared indicator. */ - intel_me_mbp_clear(dev); - } else { - /* Indicate NOACK messages should be used. */ - ret = 1; - } - - /* Dump out the MBP contents. */ - if (CONFIG(DEBUG_INTEL_ME)) { - printk(BIOS_INFO, "ME MBP: Header: items: %d, size dw: %d\n", - mbp->header.num_entries, mbp->header.mbp_size); - for (i = 0; i < mbp->header.mbp_size - 1; i++) - printk(BIOS_INFO, "ME MBP: %04x: 0x%08x\n", i, mbp->data[i]); - } - -#define ASSIGN_FIELD_PTR(field_, val_) \ - { \ - mbp_data->field_ = (typeof(mbp_data->field_))(void *)val_; \ - break; \ - } - - /* Setup the pointers in the me_bios_payload structure. */ - for (i = 0; i < mbp->header.mbp_size - 1;) { - mbp_item_header *item = (void *)&mbp->data[i]; - - switch (MBP_MAKE_IDENT(item->app_id, item->item_id)) { - case MBP_IDENT(KERNEL, FW_VER): - ASSIGN_FIELD_PTR(fw_version_name, &mbp->data[i+1]); - - case MBP_IDENT(ICC, PROFILE): - ASSIGN_FIELD_PTR(icc_profile, &mbp->data[i+1]); - - case MBP_IDENT(INTEL_AT, STATE): - ASSIGN_FIELD_PTR(at_state, &mbp->data[i+1]); - - case MBP_IDENT(KERNEL, FW_CAP): - ASSIGN_FIELD_PTR(fw_capabilities, &mbp->data[i+1]); - - case MBP_IDENT(KERNEL, ROM_BIST): - ASSIGN_FIELD_PTR(rom_bist_data, &mbp->data[i+1]); - - case MBP_IDENT(KERNEL, PLAT_KEY): - ASSIGN_FIELD_PTR(platform_key, &mbp->data[i+1]); - - case MBP_IDENT(KERNEL, FW_TYPE): - ASSIGN_FIELD_PTR(fw_plat_type, &mbp->data[i+1]); - - case MBP_IDENT(KERNEL, MFS_FAILURE): - ASSIGN_FIELD_PTR(mfsintegrity, &mbp->data[i+1]); - - case MBP_IDENT(KERNEL, PLAT_TIME): - ASSIGN_FIELD_PTR(plat_time, &mbp->data[i+1]); - - case MBP_IDENT(NFC, SUPPORT_DATA): - ASSIGN_FIELD_PTR(nfc_data, &mbp->data[i+1]); - } - i += item->length; - } - #undef ASSIGN_FIELD_PTR - - free(mbp); - return ret; -} - -/* Check whether ME is present and do basic init */ -static void intel_me_init(struct device *dev) -{ - const struct soc_intel_broadwell_pch_config *config = config_of(dev); - me_bios_path path = intel_me_path(dev); - me_bios_payload mbp_data; - int mbp_ret; - struct me_hfs hfs; - struct mei_csr csr; - - /* Do initial setup and determine the BIOS path */ - printk(BIOS_NOTICE, "ME: BIOS path: %s\n", me_bios_path_values[path]); - - if (path == ME_NORMAL_BIOS_PATH) { - /* Validate the extend register */ - intel_me_extend_valid(dev); -} - - memset(&mbp_data, 0, sizeof(mbp_data)); - - /* - * According to the ME9 BWG, BIOS is required to fetch MBP data in - * all boot flows except S3 Resume. - */ - - /* Prepare MEI MMIO interface */ - if (intel_mei_setup(dev) < 0) - return; - - /* Read ME MBP data */ - mbp_ret = intel_me_read_mbp(&mbp_data, dev); - if (mbp_ret < 0) - return; - intel_me_print_mbp(&mbp_data); - - /* Set clock enables according to devicetree */ - if (config->icc_clock_disable) - me_icc_set_clock_enables(config->icc_clock_disable); - - /* Make sure ME is in a mode that expects EOP */ - pci_read_dword_ptr(dev, &hfs, PCI_ME_HFS); - - /* Abort and leave device alone if not normal mode */ - if (hfs.fpt_bad || - hfs.working_state != ME_HFS_CWS_NORMAL || - hfs.operation_mode != ME_HFS_MODE_NORMAL) - return; - - if (mbp_ret) { - /* - * MBP Cleared wait is skipped, - * Do not expect ACK and reset when complete. - */ - - /* Send HMRFPO Lock command, no response */ - mkhi_hmrfpo_lock_noack(); - - /* Send END OF POST command, no response */ - mkhi_end_of_post_noack(); - - /* Assert reset and interrupt */ - read_host_csr(&csr); - csr.interrupt_generate = 1; - csr.reset = 1; - write_host_csr(&csr); - } else { - /* - * MBP Cleared wait was not skipped - */ - - /* Send HMRFPO LOCK command */ - mkhi_hmrfpo_lock(); - - /* Send EOP command so ME stops accepting other commands */ - mkhi_end_of_post(); - } -} - -static void intel_me_enable(struct device *dev) -{ - /* Avoid talking to the device in S3 path */ - if (acpi_is_wakeup_s3()) { - dev->enabled = 0; - pch_disable_devfn(dev); - } -} - -static struct device_operations device_ops = { - .read_resources = &pci_dev_read_resources, - .set_resources = &pci_dev_set_resources, - .enable_resources = &pci_dev_enable_resources, - .enable = &intel_me_enable, - .init = &intel_me_init, - .final = &intel_me_finalize, - .ops_pci = &pci_dev_ops_pci, -}; - -static const unsigned short pci_device_ids[] = { - 0x9c3a, /* Low Power */ - 0x9cba, /* WildcatPoint */ - 0 -}; - -static const struct pci_driver intel_me __pci_driver = { - .ops = &device_ops, - .vendor = PCI_VENDOR_ID_INTEL, - .devices = pci_device_ids, -}; diff --git a/src/soc/intel/broadwell/me_status.c b/src/soc/intel/broadwell/me_status.c deleted file mode 100644 index fa44c7c79d..0000000000 --- a/src/soc/intel/broadwell/me_status.c +++ /dev/null @@ -1,325 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include - -#define ARRAY_TO_ELEMENT(__array__, __index__, __default__) \ - (((__index__) < ARRAY_SIZE((__array__))) ? \ - (__array__)[(__index__)] : \ - (__default__)) - -static inline void me_read_dword_ptr(void *ptr, int offset) -{ - u32 dword = pci_read_config32(PCH_DEV_ME, offset); - memcpy(ptr, &dword, sizeof(dword)); -} - -/* HFS1[3:0] Current Working State Values */ -static const char *me_cws_values[] = { - [ME_HFS_CWS_RESET] = "Reset", - [ME_HFS_CWS_INIT] = "Initializing", - [ME_HFS_CWS_REC] = "Recovery", - [3] = "Unknown (3)", - [4] = "Unknown (4)", - [ME_HFS_CWS_NORMAL] = "Normal", - [ME_HFS_CWS_WAIT] = "Platform Disable Wait", - [ME_HFS_CWS_TRANS] = "OP State Transition", - [ME_HFS_CWS_INVALID] = "Invalid CPU Plugged In", - [9] = "Unknown (9)", - [10] = "Unknown (10)", - [11] = "Unknown (11)", - [12] = "Unknown (12)", - [13] = "Unknown (13)", - [14] = "Unknown (14)", - [15] = "Unknown (15)", -}; - -/* HFS1[8:6] Current Operation State Values */ -static const char *me_opstate_values[] = { - [ME_HFS_STATE_PREBOOT] = "Preboot", - [ME_HFS_STATE_M0_UMA] = "M0 with UMA", - [ME_HFS_STATE_M3] = "M3 without UMA", - [ME_HFS_STATE_M0] = "M0 without UMA", - [ME_HFS_STATE_BRINGUP] = "Bring up", - [ME_HFS_STATE_ERROR] = "M0 without UMA but with error" -}; - -/* HFS[19:16] Current Operation Mode Values */ -static const char *me_opmode_values[] = { - [ME_HFS_MODE_NORMAL] = "Normal", - [ME_HFS_MODE_DEBUG] = "Debug", - [ME_HFS_MODE_DIS] = "Soft Temporary Disable", - [ME_HFS_MODE_OVER_JMPR] = "Security Override via Jumper", - [ME_HFS_MODE_OVER_MEI] = "Security Override via MEI Message" -}; - -/* HFS[15:12] Error Code Values */ -static const char *me_error_values[] = { - [ME_HFS_ERROR_NONE] = "No Error", - [ME_HFS_ERROR_UNCAT] = "Uncategorized Failure", - [ME_HFS_ERROR_IMAGE] = "Image Failure", - [ME_HFS_ERROR_DEBUG] = "Debug Failure" -}; - -/* HFS2[31:28] ME Progress Code */ -static const char *me_progress_values[] = { - [ME_HFS2_PHASE_ROM] = "ROM Phase", - [ME_HFS2_PHASE_BUP] = "BUP Phase", - [ME_HFS2_PHASE_UKERNEL] = "uKernel Phase", - [ME_HFS2_PHASE_POLICY] = "Policy Module", - [ME_HFS2_PHASE_MODULE_LOAD] = "Module Loading", - [ME_HFS2_PHASE_UNKNOWN] = "Unknown", - [ME_HFS2_PHASE_HOST_COMM] = "Host Communication" -}; - -/* HFS2[27:24] Power Management Event */ -static const char *me_pmevent_values[] = { - [ME_HFS2_PMEVENT_CLEAN_MOFF_MX_WAKE] = - "Clean Moff->Mx wake", - [ME_HFS2_PMEVENT_MOFF_MX_WAKE_ERROR] = - "Moff->Mx wake after an error", - [ME_HFS2_PMEVENT_CLEAN_GLOBAL_RESET] = - "Clean global reset", - [ME_HFS2_PMEVENT_CLEAN_GLOBAL_RESET_ERROR] = - "Global reset after an error", - [ME_HFS2_PMEVENT_CLEAN_ME_RESET] = - "Clean Intel ME reset", - [ME_HFS2_PMEVENT_ME_RESET_EXCEPTION] = - "Intel ME reset due to exception", - [ME_HFS2_PMEVENT_PSEUDO_ME_RESET] = - "Pseudo-global reset", - [ME_HFS2_PMEVENT_S0MO_SXM3] = - "S0/M0->Sx/M3", - [ME_HFS2_PMEVENT_SXM3_S0M0] = - "Sx/M3->S0/M0", - [ME_HFS2_PMEVENT_NON_PWR_CYCLE_RESET] = - "Non-power cycle reset", - [ME_HFS2_PMEVENT_PWR_CYCLE_RESET_M3] = - "Power cycle reset through M3", - [ME_HFS2_PMEVENT_PWR_CYCLE_RESET_MOFF] = - "Power cycle reset through Moff", - [ME_HFS2_PMEVENT_SXMX_SXMOFF] = - "Sx/Mx->Sx/Moff" -}; - -/* Progress Code 0 states */ -static const char *me_progress_rom_values[] = { - [ME_HFS2_STATE_ROM_BEGIN] = "BEGIN", - [ME_HFS2_STATE_ROM_DISABLE] = "DISABLE" -}; - -/* Progress Code 1 states */ -static const char *me_progress_bup_values[] = { - [ME_HFS2_STATE_BUP_INIT] = - "Initialization starts", - [ME_HFS2_STATE_BUP_DIS_HOST_WAKE] = - "Disable the host wake event", - [ME_HFS2_STATE_BUP_FLOW_DET] = - "Flow determination start process", - [ME_HFS2_STATE_BUP_VSCC_ERR] = - "Error reading/matching the VSCC table in the descriptor", - [ME_HFS2_STATE_BUP_CHECK_STRAP] = - "Check to see if straps say ME DISABLED", - [ME_HFS2_STATE_BUP_PWR_OK_TIMEOUT] = - "Timeout waiting for PWROK", - [ME_HFS2_STATE_BUP_MANUF_OVRD_STRAP] = - "Possibly handle BUP manufacturing override strap", - [ME_HFS2_STATE_BUP_M3] = - "Bringup in M3", - [ME_HFS2_STATE_BUP_M0] = - "Bringup in M0", - [ME_HFS2_STATE_BUP_FLOW_DET_ERR] = - "Flow detection error", - [ME_HFS2_STATE_BUP_M3_CLK_ERR] = - "M3 clock switching error", - [ME_HFS2_STATE_BUP_CPU_RESET_DID_TIMEOUT_MEM_MISSING] = - "Host error - CPU reset timeout, DID timeout, memory missing", - [ME_HFS2_STATE_BUP_M3_KERN_LOAD] = - "M3 kernel load", - [ME_HFS2_STATE_BUP_T32_MISSING] = - "T34 missing - cannot program ICC", - [ME_HFS2_STATE_BUP_WAIT_DID] = - "Waiting for DID BIOS message", - [ME_HFS2_STATE_BUP_WAIT_DID_FAIL] = - "Waiting for DID BIOS message failure", - [ME_HFS2_STATE_BUP_DID_NO_FAIL] = - "DID reported no error", - [ME_HFS2_STATE_BUP_ENABLE_UMA] = - "Enabling UMA", - [ME_HFS2_STATE_BUP_ENABLE_UMA_ERR] = - "Enabling UMA error", - [ME_HFS2_STATE_BUP_SEND_DID_ACK] = - "Sending DID Ack to BIOS", - [ME_HFS2_STATE_BUP_SEND_DID_ACK_ERR] = - "Sending DID Ack to BIOS error", - [ME_HFS2_STATE_BUP_M0_CLK] = - "Switching clocks in M0", - [ME_HFS2_STATE_BUP_M0_CLK_ERR] = - "Switching clocks in M0 error", - [ME_HFS2_STATE_BUP_TEMP_DIS] = - "ME in temp disable", - [ME_HFS2_STATE_BUP_M0_KERN_LOAD] = - "M0 kernel load", -}; - -/* Progress Code 3 states */ -static const char *me_progress_policy_values[] = { - [ME_HFS2_STATE_POLICY_ENTRY] = "Entry into Policy Module", - [ME_HFS2_STATE_POLICY_RCVD_S3] = "Received S3 entry", - [ME_HFS2_STATE_POLICY_RCVD_S4] = "Received S4 entry", - [ME_HFS2_STATE_POLICY_RCVD_S5] = "Received S5 entry", - [ME_HFS2_STATE_POLICY_RCVD_UPD] = "Received UPD entry", - [ME_HFS2_STATE_POLICY_RCVD_PCR] = "Received PCR entry", - [ME_HFS2_STATE_POLICY_RCVD_NPCR] = "Received NPCR entry", - [ME_HFS2_STATE_POLICY_RCVD_HOST_WAKE] = "Received host wake", - [ME_HFS2_STATE_POLICY_RCVD_AC_DC] = "Received AC<>DC switch", - [ME_HFS2_STATE_POLICY_RCVD_DID] = "Received DRAM Init Done", - [ME_HFS2_STATE_POLICY_VSCC_NOT_FOUND] = - "VSCC Data not found for flash device", - [ME_HFS2_STATE_POLICY_VSCC_INVALID] = - "VSCC Table is not valid", - [ME_HFS2_STATE_POLICY_FPB_ERR] = - "Flash Partition Boundary is outside address space", - [ME_HFS2_STATE_POLICY_DESCRIPTOR_ERR] = - "ME cannot access the chipset descriptor region", - [ME_HFS2_STATE_POLICY_VSCC_NO_MATCH] = - "Required VSCC values for flash parts do not match", -}; - -void intel_me_status(void) -{ - if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL < BIOS_DEBUG) - return; - - struct me_hfs _hfs, *hfs = &_hfs; - struct me_hfs2 _hfs2, *hfs2 = &_hfs2; - - me_read_dword_ptr(hfs, PCI_ME_HFS); - me_read_dword_ptr(hfs2, PCI_ME_HFS2); - - /* Check Current States */ - printk(BIOS_DEBUG, "ME: FW Partition Table : %s\n", - hfs->fpt_bad ? "BAD" : "OK"); - printk(BIOS_DEBUG, "ME: Bringup Loader Failure : %s\n", - hfs->ft_bup_ld_flr ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: Firmware Init Complete : %s\n", - hfs->fw_init_complete ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: Manufacturing Mode : %s\n", - hfs->mfg_mode ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: Boot Options Present : %s\n", - hfs->boot_options_present ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: Update In Progress : %s\n", - hfs->update_in_progress ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: Current Working State : %s\n", - ARRAY_TO_ELEMENT(me_cws_values, - hfs->working_state, - "Unknown (OOB)")); - printk(BIOS_DEBUG, "ME: Current Operation State : %s\n", - ARRAY_TO_ELEMENT(me_opstate_values, - hfs->operation_state, - "Unknown (OOB)")); - printk(BIOS_DEBUG, "ME: Current Operation Mode : %s\n", - ARRAY_TO_ELEMENT(me_opmode_values, - hfs->operation_mode, - "Unknown (OOB)")); - printk(BIOS_DEBUG, "ME: Error Code : %s\n", - ARRAY_TO_ELEMENT(me_error_values, - hfs->error_code, - "Unknown (OOB)")); - printk(BIOS_DEBUG, "ME: Progress Phase : %s\n", - ARRAY_TO_ELEMENT(me_progress_values, - hfs2->progress_code, - "Unknown (OOB)")); - printk(BIOS_DEBUG, "ME: Power Management Event : %s\n", - ARRAY_TO_ELEMENT(me_pmevent_values, - hfs2->current_pmevent, - "Unknown (OOB)")); - - printk(BIOS_DEBUG, "ME: Progress Phase State : "); - switch (hfs2->progress_code) { - case ME_HFS2_PHASE_ROM: /* ROM Phase */ - printk(BIOS_DEBUG, "%s", - ARRAY_TO_ELEMENT(me_progress_rom_values, - hfs2->current_state, - "Unknown (OOB)")); - break; - - case ME_HFS2_PHASE_UKERNEL: /* uKernel Phase */ - printk(BIOS_DEBUG, "0x%02x", hfs2->current_state); - break; - - case ME_HFS2_PHASE_BUP: /* Bringup Phase */ - if (ARRAY_TO_ELEMENT(me_progress_bup_values, - hfs2->current_state, NULL)) - printk(BIOS_DEBUG, "%s", - ARRAY_TO_ELEMENT(me_progress_bup_values, - hfs2->current_state, - NULL)); - else - printk(BIOS_DEBUG, "0x%02x", hfs2->current_state); - break; - - case ME_HFS2_PHASE_POLICY: /* Policy Module Phase */ - if (ARRAY_TO_ELEMENT(me_progress_policy_values, - hfs2->current_state, NULL)) - printk(BIOS_DEBUG, "%s", - ARRAY_TO_ELEMENT(me_progress_policy_values, - hfs2->current_state, - NULL)); - else - printk(BIOS_DEBUG, "0x%02x", hfs2->current_state); - break; - - case ME_HFS2_PHASE_HOST_COMM: /* Host Communication Phase */ - if (!hfs2->current_state) - printk(BIOS_DEBUG, "Host communication established"); - else - printk(BIOS_DEBUG, "0x%02x", hfs2->current_state); - break; - - default: - printk(BIOS_DEBUG, "Unknown phase: 0x%02x state: 0x%02x", - hfs2->progress_code, hfs2->current_state); - } - printk(BIOS_DEBUG, "\n"); -} - -void intel_me_hsio_version(uint16_t *version, uint16_t *checksum) -{ - int count; - u32 hsiover; - struct me_hfs hfs; - - /* Query for HSIO version, overloads H_GS and HFS */ - pci_write_config32(PCH_DEV_ME, PCI_ME_H_GS, - ME_HSIO_MESSAGE | ME_HSIO_CMD_GETHSIOVER); - - /* Must wait for ME acknowledgement */ - for (count = ME_RETRY; count > 0; --count) { - me_read_dword_ptr(&hfs, PCI_ME_HFS); - if (hfs.bios_msg_ack) - break; - udelay(ME_DELAY); - } - if (!count) { - printk(BIOS_ERR, "ERROR: ME failed to respond\n"); - return; - } - - /* HSIO version should be in HFS_5 */ - hsiover = pci_read_config32(PCH_DEV_ME, PCI_ME_HFS5); - *version = hsiover >> 16; - *checksum = hsiover & 0xffff; - - printk(BIOS_DEBUG, "ME: HSIO Version : %d (CRC 0x%04x)\n", - *version, *checksum); - - /* Reset registers to normal behavior */ - pci_write_config32(PCH_DEV_ME, PCI_ME_H_GS, - ME_HSIO_MESSAGE | ME_HSIO_CMD_GETHSIOVER); -} diff --git a/src/soc/intel/broadwell/pch.c b/src/soc/intel/broadwell/pch.c deleted file mode 100644 index e0c5bb0c4d..0000000000 --- a/src/soc/intel/broadwell/pch.c +++ /dev/null @@ -1,209 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -u8 pch_revision(void) -{ - return pci_read_config8(PCH_DEV_LPC, PCI_REVISION_ID); -} - -u16 pch_type(void) -{ - return pci_read_config16(PCH_DEV_LPC, PCI_DEVICE_ID); -} - -/* Return 1 if PCH type is WildcatPoint */ -int pch_is_wpt(void) -{ - return ((pch_type() & 0xfff0) == 0x9cc0) ? 1 : 0; -} - -/* Return 1 if PCH type is WildcatPoint ULX */ -int pch_is_wpt_ulx(void) -{ - u16 lpcid = pch_type(); - - switch (lpcid) { - case PCH_WPT_BDW_Y_SAMPLE: - case PCH_WPT_BDW_Y_PREMIUM: - case PCH_WPT_BDW_Y_BASE: - return 1; - } - - return 0; -} - -u32 pch_read_soft_strap(int id) -{ - u32 fdoc; - - fdoc = SPIBAR32(SPIBAR_FDOC); - fdoc &= ~0x00007ffc; - SPIBAR32(SPIBAR_FDOC) = fdoc; - - fdoc |= 0x00004000; - fdoc |= id * 4; - SPIBAR32(SPIBAR_FDOC) = fdoc; - - return SPIBAR32(SPIBAR_FDOD); -} - -#ifndef __SIMPLE_DEVICE__ - -/* Put device in D3Hot Power State */ -static void pch_enable_d3hot(struct device *dev) -{ - pci_or_config32(dev, PCH_PCS, PCH_PCS_PS_D3HOT); -} - -/* RCBA function disable and posting read to flush the transaction */ -static void rcba_function_disable(u32 reg, u32 bit) -{ - RCBA32_OR(reg, bit); - RCBA32(reg); -} - -/* Set bit in Function Disable register to hide this device */ -void pch_disable_devfn(struct device *dev) -{ - switch (dev->path.pci.devfn) { - case PCH_DEVFN_ADSP: /* Audio DSP */ - rcba_function_disable(FD, PCH_DISABLE_ADSPD); - break; - case PCH_DEVFN_XHCI: /* XHCI */ - rcba_function_disable(FD, PCH_DISABLE_XHCI); - break; - case PCH_DEVFN_SDMA: /* DMA */ - pch_enable_d3hot(dev); - pch_iobp_update(SIO_IOBP_FUNCDIS0, ~0UL, SIO_IOBP_FUNCDIS_DIS); - break; - case PCH_DEVFN_I2C0: /* I2C0 */ - pch_enable_d3hot(dev); - pch_iobp_update(SIO_IOBP_FUNCDIS1, ~0UL, SIO_IOBP_FUNCDIS_DIS); - break; - case PCH_DEVFN_I2C1: /* I2C1 */ - pch_enable_d3hot(dev); - pch_iobp_update(SIO_IOBP_FUNCDIS2, ~0UL, SIO_IOBP_FUNCDIS_DIS); - break; - case PCH_DEVFN_SPI0: /* SPI0 */ - pch_enable_d3hot(dev); - pch_iobp_update(SIO_IOBP_FUNCDIS3, ~0UL, SIO_IOBP_FUNCDIS_DIS); - break; - case PCH_DEVFN_SPI1: /* SPI1 */ - pch_enable_d3hot(dev); - pch_iobp_update(SIO_IOBP_FUNCDIS4, ~0UL, SIO_IOBP_FUNCDIS_DIS); - break; - case PCH_DEVFN_UART0: /* UART0 */ - pch_enable_d3hot(dev); - pch_iobp_update(SIO_IOBP_FUNCDIS5, ~0UL, SIO_IOBP_FUNCDIS_DIS); - break; - case PCH_DEVFN_UART1: /* UART1 */ - pch_enable_d3hot(dev); - pch_iobp_update(SIO_IOBP_FUNCDIS6, ~0UL, SIO_IOBP_FUNCDIS_DIS); - break; - case PCH_DEVFN_ME: /* MEI #1 */ - rcba_function_disable(FD2, PCH_DISABLE_MEI1); - break; - case PCH_DEVFN_ME_2: /* MEI #2 */ - rcba_function_disable(FD2, PCH_DISABLE_MEI2); - break; - case PCH_DEVFN_ME_IDER: /* IDE-R */ - rcba_function_disable(FD2, PCH_DISABLE_IDER); - break; - case PCH_DEVFN_ME_KT: /* KT */ - rcba_function_disable(FD2, PCH_DISABLE_KT); - break; - case PCH_DEVFN_SDIO: /* SDIO */ - pch_enable_d3hot(dev); - pch_iobp_update(SIO_IOBP_FUNCDIS7, ~0UL, SIO_IOBP_FUNCDIS_DIS); - break; - case PCH_DEVFN_GBE: /* Gigabit Ethernet */ - rcba_function_disable(BUC, PCH_DISABLE_GBE); - break; - case PCH_DEVFN_HDA: /* HD Audio Controller */ - rcba_function_disable(FD, PCH_DISABLE_HD_AUDIO); - break; - case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 0): /* PCI Express Root Port 1 */ - case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 1): /* PCI Express Root Port 2 */ - case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 2): /* PCI Express Root Port 3 */ - case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 3): /* PCI Express Root Port 4 */ - case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 4): /* PCI Express Root Port 5 */ - case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 5): /* PCI Express Root Port 6 */ - case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 6): /* PCI Express Root Port 7 */ - case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 7): /* PCI Express Root Port 8 */ - rcba_function_disable(FD, - PCH_DISABLE_PCIE(PCI_FUNC(dev->path.pci.devfn))); - break; - case PCH_DEVFN_EHCI: /* EHCI #1 */ - rcba_function_disable(FD, PCH_DISABLE_EHCI1); - break; - case PCH_DEVFN_LPC: /* LPC */ - rcba_function_disable(FD, PCH_DISABLE_LPC); - break; - case PCH_DEVFN_SATA: /* SATA #1 */ - rcba_function_disable(FD, PCH_DISABLE_SATA1); - break; - case PCH_DEVFN_SMBUS: /* SMBUS */ - rcba_function_disable(FD, PCH_DISABLE_SMBUS); - break; - case PCH_DEVFN_SATA2: /* SATA #2 */ - rcba_function_disable(FD, PCH_DISABLE_SATA2); - break; - case PCH_DEVFN_THERMAL: /* Thermal Subsystem */ - rcba_function_disable(FD, PCH_DISABLE_THERMAL); - break; - } -} - -static void broadwell_pch_enable_dev(struct device *dev) -{ - u16 reg16; - - if (dev->path.type != DEVICE_PATH_PCI) - return; - - if (dev->ops && dev->ops->enable) - return; - - /* These devices need special enable/disable handling */ - switch (PCI_SLOT(dev->path.pci.devfn)) { - case PCH_DEV_SLOT_PCIE: - case PCH_DEV_SLOT_EHCI: - case PCH_DEV_SLOT_HDA: - return; - } - - 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); - - /* Disable this device if possible */ - pch_disable_devfn(dev); - } else { - /* Enable SERR */ - pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_SERR); - } -} - -struct chip_operations soc_intel_broadwell_pch_ops = { - CHIP_NAME("Intel Broadwell PCH") - .enable_dev = &broadwell_pch_enable_dev, -}; - -#endif diff --git a/src/soc/intel/broadwell/pch/Makefile.inc b/src/soc/intel/broadwell/pch/Makefile.inc new file mode 100644 index 0000000000..7e5c65aa76 --- /dev/null +++ b/src/soc/intel/broadwell/pch/Makefile.inc @@ -0,0 +1,38 @@ +bootblock-y += bootblock.c + +ramstage-y += adsp.c +romstage-y += early_pch.c +ramstage-$(CONFIG_ELOG) += elog.c +ramstage-y += gpio.c +romstage-y += gpio.c +smm-y += gpio.c +ramstage-y += hda.c +ramstage-y += iobp.c +romstage-y += iobp.c +ramstage-y += fadt.c +ramstage-y += lpc.c +ramstage-y += me.c +ramstage-y += me_status.c +romstage-y += me_status.c +ramstage-y += pch.c +romstage-y += pch.c +ramstage-y += pcie.c +ramstage-y += pmutil.c +romstage-y += pmutil.c +smm-y += pmutil.c +verstage-y += pmutil.c +romstage-y += power_state.c +ramstage-y += sata.c +ramstage-y += serialio.c +ramstage-y += smbus.c +ramstage-y += smi.c +smm-y += smihandler.c +romstage-$(CONFIG_DRIVERS_UART_8250MEM) += uart.c +bootblock-y += usb_debug.c +romstage-y += usb_debug.c +ramstage-y += usb_debug.c +ramstage-y += ehci.c +ramstage-y += xhci.c +smm-y += xhci.c + +ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/hda_verb.c diff --git a/src/soc/intel/broadwell/pch/adsp.c b/src/soc/intel/broadwell/pch/adsp.c new file mode 100644 index 0000000000..06dd38bd8a --- /dev/null +++ b/src/soc/intel/broadwell/pch/adsp.c @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void adsp_init(struct device *dev) +{ + const struct soc_intel_broadwell_pch_config *config = config_of(dev); + struct resource *bar0, *bar1; + u32 tmp32; + + /* Ensure memory and bus master are enabled */ + pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + + /* Find BAR0 and BAR1 */ + bar0 = find_resource(dev, PCI_BASE_ADDRESS_0); + if (!bar0) + return; + bar1 = find_resource(dev, PCI_BASE_ADDRESS_1); + if (!bar1) + return; + + /* + * Set LTR value in DSP shim LTR control register to 3ms + * SNOOP_REQ[13]=1b SNOOP_SCALE[12:10]=100b (1ms) SNOOP_VAL[9:0]=3h + */ + tmp32 = pch_is_wpt() ? ADSP_SHIM_BASE_WPT : ADSP_SHIM_BASE_LPT; + write32(res2mmio(bar0, tmp32 + ADSP_SHIM_LTRC, 0), + ADSP_SHIM_LTRC_VALUE); + + /* Program VDRTCTL2 D19:F0:A8[31:0] = 0x00000fff */ + pci_write_config32(dev, ADSP_PCI_VDRTCTL2, ADSP_VDRTCTL2_VALUE); + + /* Program ADSP IOBP VDLDAT1 to 0x040100 */ + pch_iobp_write(ADSP_IOBP_VDLDAT1, ADSP_VDLDAT1_VALUE); + + /* Set D3 Power Gating Enable in D19:F0:A0 based on PCH type */ + tmp32 = pci_read_config32(dev, ADSP_PCI_VDRTCTL0); + if (pch_is_wpt()) { + if (config->adsp_d3_pg_enable) { + tmp32 &= ~ADSP_VDRTCTL0_D3PGD_WPT; + if (config->adsp_sram_pg_enable) + tmp32 &= ~ADSP_VDRTCTL0_D3SRAMPGD_WPT; + else + tmp32 |= ADSP_VDRTCTL0_D3SRAMPGD_WPT; + } else { + tmp32 |= ADSP_VDRTCTL0_D3PGD_WPT; + } + } else { + if (config->adsp_d3_pg_enable) { + tmp32 &= ~ADSP_VDRTCTL0_D3PGD_LPT; + if (config->adsp_sram_pg_enable) + tmp32 &= ~ADSP_VDRTCTL0_D3SRAMPGD_LPT; + else + tmp32 |= ADSP_VDRTCTL0_D3SRAMPGD_LPT; + } else { + tmp32 |= ADSP_VDRTCTL0_D3PGD_LPT; + } + } + pci_write_config32(dev, ADSP_PCI_VDRTCTL0, tmp32); + + /* Set PSF Snoop to SA, RCBA+0x3350[10]=1b */ + RCBA32_OR(0x3350, (1 << 10)); + + /* Set DSP IOBP PMCTL 0x1e0=0x3f */ + pch_iobp_write(ADSP_IOBP_PMCTL, ADSP_PMCTL_VALUE); + + if (config->sio_acpi_mode) { + /* Configure for ACPI mode */ + struct global_nvs *gnvs; + + printk(BIOS_INFO, "ADSP: Enable ACPI Mode IRQ3\n"); + + /* Find ACPI NVS to update BARs */ + gnvs = acpi_get_gnvs(); + if (!gnvs) + return; + + /* Save BAR0 and BAR1 to ACPI NVS */ + gnvs->dev.bar0[SIO_NVS_ADSP] = (u32)bar0->base; + gnvs->dev.bar1[SIO_NVS_ADSP] = (u32)bar1->base; + gnvs->dev.enable[SIO_NVS_ADSP] = 1; + + /* Set PCI Config Disable Bit */ + pch_iobp_update(ADSP_IOBP_PCICFGCTL, ~0, ADSP_PCICFGCTL_PCICD); + + /* Set interrupt de-assert/assert opcode override to IRQ3 */ + pch_iobp_write(ADSP_IOBP_VDLDAT2, ADSP_IOBP_ACPI_IRQ3); + + /* Enable IRQ3 in RCBA */ + RCBA32_OR(ACPIIRQEN, ADSP_ACPI_IRQEN); + + /* Set ACPI Interrupt Enable Bit */ + pch_iobp_update(ADSP_IOBP_PCICFGCTL, ~ADSP_PCICFGCTL_SPCBAD, + ADSP_PCICFGCTL_ACPIIE); + + /* Put ADSP in D3hot */ + tmp32 = read32(res2mmio(bar1, PCH_PCS, 0)); + tmp32 |= PCH_PCS_PS_D3HOT; + write32(res2mmio(bar1, PCH_PCS, 0), tmp32); + } else { + printk(BIOS_INFO, "ADSP: Enable PCI Mode IRQ23\n"); + + /* Configure for PCI mode */ + pci_write_config8(dev, PCI_INTERRUPT_LINE, ADSP_PCI_IRQ); + + /* Clear ACPI Interrupt Enable Bit */ + pch_iobp_update(ADSP_IOBP_PCICFGCTL, + ~(ADSP_PCICFGCTL_SPCBAD | ADSP_PCICFGCTL_ACPIIE), 0); + } +} + +static struct device_operations adsp_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = adsp_init, + .ops_pci = &pci_dev_ops_pci, +}; + +static const unsigned short pci_device_ids[] = { + 0x9c36, /* LynxPoint */ + 0x9cb6, /* WildcatPoint */ + 0 +}; + +static const struct pci_driver pch_adsp __pci_driver = { + .ops = &adsp_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/soc/intel/broadwell/pch/bootblock.c b/src/soc/intel/broadwell/pch/bootblock.c new file mode 100644 index 0000000000..7f6d0d52d9 --- /dev/null +++ b/src/soc/intel/broadwell/pch/bootblock.c @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void map_rcba(void) +{ + pci_write_config32(PCH_DEV_LPC, RCBA, RCBA_BASE_ADDRESS | 1); +} + +static void enable_port80_on_lpc(void) +{ + /* Enable port 80 POST on LPC. The chipset does this by default, + * but it doesn't appear to hurt anything. */ + u32 gcs = RCBA32(GCS); + gcs = gcs & ~0x4; + RCBA32(GCS) = gcs; +} + +static void set_spi_speed(void) +{ + u32 fdod; + u8 ssfc; + + /* Observe SPI Descriptor Component Section 0 */ + SPIBAR32(SPIBAR_FDOC) = 0x1000; + + /* Extract the Write/Erase SPI Frequency from descriptor */ + fdod = SPIBAR32(SPIBAR_FDOD); + fdod >>= 24; + fdod &= 7; + + /* Set Software Sequence frequency to match */ + ssfc = SPIBAR8(SPIBAR_SSFC + 2); + ssfc &= ~7; + ssfc |= fdod; + SPIBAR8(SPIBAR_SSFC + 2) = ssfc; +} + +static void pch_enable_bars(void) +{ + /* Set up southbridge BARs */ + pci_write_config32(PCH_DEV_LPC, RCBA, RCBA_BASE_ADDRESS | 1); + + pci_write_config32(PCH_DEV_LPC, PMBASE, ACPI_BASE_ADDRESS | 1); + + pci_write_config8(PCH_DEV_LPC, ACPI_CNTL, ACPI_EN); + + pci_write_config32(PCH_DEV_LPC, GPIO_BASE, GPIO_BASE_ADDRESS | 1); + + /* Enable GPIO functionality. */ + pci_write_config8(PCH_DEV_LPC, GPIO_CNTL, GPIO_EN); +} + +static void pch_early_lpc(void) +{ + pch_enable_bars(); + + /* Set COM1/COM2 decode range */ + pci_write_config16(PCH_DEV_LPC, LPC_IO_DEC, 0x0010); + + /* Enable SuperIO + MC + COM1 + PS/2 Keyboard/Mouse */ + u16 lpc_config = CNF1_LPC_EN | CNF2_LPC_EN | GAMEL_LPC_EN | + COMA_LPC_EN | KBC_LPC_EN | MC_LPC_EN; + pci_write_config16(PCH_DEV_LPC, LPC_EN, lpc_config); + + /* Enable IOAPIC */ + RCBA16(OIC) = 0x0100; + + /* Read back for posted write */ + (void)RCBA16(OIC); + + /* Set HPET address and enable it */ + RCBA32_AND_OR(HPTC, ~3, 1 << 7); + + /* + * Reading the register back guarantees that the write is + * done before we use the configured base address below. + */ + (void)RCBA32(HPTC); + + /* Enable HPET to start counter */ + setbits32((void *)HPET_BASE_ADDRESS + 0x10, 1 << 0); + + /* Disable reset */ + RCBA32_OR(GCS, 1 << 5); + + /* TCO timer halt */ + u16 reg16 = inb(ACPI_BASE_ADDRESS + TCO1_CNT); + reg16 |= TCO_TMR_HLT; + outb(reg16, ACPI_BASE_ADDRESS + TCO1_CNT); + + /* Enable upper 128 bytes of CMOS */ + RCBA32_OR(RC, 1 << 2); + + /* Disable unused device (always) */ + RCBA32_OR(FD, PCH_DISABLE_ALWAYS); +} + +void bootblock_early_southbridge_init(void) +{ + map_rcba(); + enable_spi_prefetching_and_caching(); + enable_port80_on_lpc(); + set_spi_speed(); + pch_early_lpc(); +} diff --git a/src/soc/intel/broadwell/pch/early_pch.c b/src/soc/intel/broadwell/pch/early_pch.c new file mode 100644 index 0000000000..149dda1ca0 --- /dev/null +++ b/src/soc/intel/broadwell/pch/early_pch.c @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void pch_route_interrupts(void) +{ + /* + * GFX INTA -> PIRQA (MSI) + * D28IP_P1IP PCIE INTA -> PIRQA + * D29IP_E1P EHCI INTA -> PIRQD + * D20IP_XHCI XHCI INTA -> PIRQC (MSI) + * D31IP_SIP SATA INTA -> PIRQF (MSI) + * D31IP_SMIP SMBUS INTB -> PIRQG + * D31IP_TTIP THRT INTC -> PIRQA + * D27IP_ZIP HDA INTA -> PIRQG (MSI) + */ + + /* Device interrupt pin register (board specific) */ + RCBA32(D31IP) = (INTC << D31IP_TTIP) | (NOINT << D31IP_SIP2) | + (INTB << D31IP_SMIP) | (INTA << D31IP_SIP); + RCBA32(D29IP) = (INTA << D29IP_E1P); + RCBA32(D28IP) = (INTA << D28IP_P1IP) | (INTC << D28IP_P3IP) | + (INTB << D28IP_P4IP); + RCBA32(D27IP) = (INTA << D27IP_ZIP); + RCBA32(D26IP) = (INTA << D26IP_E2P); + RCBA32(D22IP) = (NOINT << D22IP_MEI1IP); + RCBA32(D20IP) = (INTA << D20IP_XHCI); + + /* Device interrupt route registers */ + RCBA32(D31IR) = DIR_ROUTE(PIRQG, PIRQC, PIRQB, PIRQA); /* LPC */ + RCBA32(D29IR) = DIR_ROUTE(PIRQD, PIRQD, PIRQD, PIRQD); /* EHCI */ + RCBA32(D28IR) = DIR_ROUTE(PIRQA, PIRQB, PIRQC, PIRQD); /* PCIE */ + RCBA32(D27IR) = DIR_ROUTE(PIRQG, PIRQG, PIRQG, PIRQG); /* HDA */ + RCBA32(D23IR) = DIR_ROUTE(PIRQH, PIRQH, PIRQH, PIRQH); /* SDIO */ + RCBA32(D22IR) = DIR_ROUTE(PIRQA, PIRQA, PIRQA, PIRQA); /* ME */ + RCBA32(D21IR) = DIR_ROUTE(PIRQE, PIRQF, PIRQF, PIRQF); /* SIO */ + RCBA32(D20IR) = DIR_ROUTE(PIRQC, PIRQC, PIRQC, PIRQC); /* XHCI */ +} + +static void pch_enable_lpc(void) +{ + /* Lookup device tree in romstage */ + const struct device *const dev = pcidev_on_root(0x1f, 0); + + const struct soc_intel_broadwell_pch_config *config = config_of(dev); + + pci_write_config32(PCH_DEV_LPC, LPC_GEN1_DEC, config->gen1_dec); + pci_write_config32(PCH_DEV_LPC, LPC_GEN2_DEC, config->gen2_dec); + pci_write_config32(PCH_DEV_LPC, LPC_GEN3_DEC, config->gen3_dec); + pci_write_config32(PCH_DEV_LPC, LPC_GEN4_DEC, config->gen4_dec); +} + +void pch_early_init(void) +{ + pch_route_interrupts(); + + pch_enable_lpc(); + + enable_smbus(); + + /* 8.14 Additional PCI Express Programming Steps, step #1 */ + pci_update_config32(_PCH_DEV(PCIE, 0), 0xf4, ~0x60, 0); + pci_update_config32(_PCH_DEV(PCIE, 0), 0xf4, ~0x80, 0x80); + pci_update_config32(_PCH_DEV(PCIE, 0), 0xe2, ~0x30, 0x30); +} diff --git a/src/soc/intel/broadwell/pch/ehci.c b/src/soc/intel/broadwell/pch/ehci.c new file mode 100644 index 0000000000..5b977dd3c6 --- /dev/null +++ b/src/soc/intel/broadwell/pch/ehci.c @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include + +static void usb_ehci_set_subsystem(struct device *dev, unsigned int vendor, + unsigned int device) +{ + u8 access_cntl; + + access_cntl = pci_read_config8(dev, 0x80); + + /* Enable writes to protected registers. */ + pci_write_config8(dev, 0x80, access_cntl | 1); + + pci_dev_set_subsystem(dev, vendor, device); + + /* Restore protection. */ + pci_write_config8(dev, 0x80, access_cntl); +} + +static void ehci_enable(struct device *dev) +{ + if (CONFIG(USBDEBUG)) + dev->enabled = 1; + else + pch_disable_devfn(dev); +} + +static struct pci_operations ehci_ops_pci = { + .set_subsystem = &usb_ehci_set_subsystem, +}; + +static struct device_operations usb_ehci_ops = { + .read_resources = pci_ehci_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .ops_pci = &ehci_ops_pci, + .enable = ehci_enable, +}; + +static const unsigned short pci_device_ids[] = { + 0x9c26, /* LynxPoint-LP */ + 0x9ca6, /* WildcatPoint */ + 0 +}; + +static const struct pci_driver pch_usb_ehci __pci_driver = { + .ops = &usb_ehci_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/soc/intel/broadwell/pch/elog.c b/src/soc/intel/broadwell/pch/elog.c new file mode 100644 index 0000000000..9271e27872 --- /dev/null +++ b/src/soc/intel/broadwell/pch/elog.c @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include + +static void pch_log_gpio_gpe(u32 gpe0_sts, u32 gpe0_en, int start) +{ + int i; + + gpe0_sts &= gpe0_en; + + for (i = 0; i <= 31; i++) { + if (gpe0_sts & (1 << i)) + elog_add_event_wake(ELOG_WAKE_SOURCE_GPE, i + start); + } +} + +static void pch_log_wake_source(struct chipset_power_state *ps) +{ + /* Power Button */ + if (ps->pm1_sts & PWRBTN_STS) + elog_add_event_wake(ELOG_WAKE_SOURCE_PWRBTN, 0); + + /* RTC */ + if (ps->pm1_sts & RTC_STS) + elog_add_event_wake(ELOG_WAKE_SOURCE_RTC, 0); + + /* PCI Express (TODO: determine wake device) */ + if (ps->pm1_sts & PCIEXPWAK_STS) + elog_add_event_wake(ELOG_WAKE_SOURCE_PCIE, 0); + + /* PME (TODO: determine wake device) */ + if (ps->gpe0_sts[GPE_STD] & PME_STS) + elog_add_event_wake(ELOG_WAKE_SOURCE_PME, 0); + + /* Internal PME (TODO: determine wake device) */ + if (ps->gpe0_sts[GPE_STD] & PME_B0_STS) + elog_add_event_wake(ELOG_WAKE_SOURCE_PME_INTERNAL, 0); + + /* SMBUS Wake */ + if (ps->gpe0_sts[GPE_STD] & SMB_WAK_STS) + elog_add_event_wake(ELOG_WAKE_SOURCE_SMBUS, 0); + + /* GPIO27 */ + if (ps->gpe0_sts[GPE_STD] & GP27_STS) + elog_add_event_wake(ELOG_WAKE_SOURCE_GPE, 27); + + /* Log GPIO events in set 1-3 */ + pch_log_gpio_gpe(ps->gpe0_sts[GPE_31_0], ps->gpe0_en[GPE_31_0], 0); + pch_log_gpio_gpe(ps->gpe0_sts[GPE_63_32], ps->gpe0_en[GPE_63_32], 32); + pch_log_gpio_gpe(ps->gpe0_sts[GPE_94_64], ps->gpe0_en[GPE_94_64], 64); +} + +static void pch_log_power_and_resets(struct chipset_power_state *ps) +{ + /* Thermal Trip Status */ + if (ps->gen_pmcon2 & THERMTRIP_STS) + elog_add_event(ELOG_TYPE_THERM_TRIP); + + /* PWR_FLR Power Failure */ + if (ps->gen_pmcon2 & PWROK_FLR) + elog_add_event(ELOG_TYPE_POWER_FAIL); + + /* SUS Well Power Failure */ + if (ps->gen_pmcon3 & SUS_PWR_FLR) + elog_add_event(ELOG_TYPE_SUS_POWER_FAIL); + + /* SYS_PWROK Failure */ + if (ps->gen_pmcon2 & SYSPWR_FLR) + elog_add_event(ELOG_TYPE_SYS_PWROK_FAIL); + + /* PWROK Failure */ + if (ps->gen_pmcon2 & PWROK_FLR) + elog_add_event(ELOG_TYPE_PWROK_FAIL); + + /* TCO Timeout */ + if (ps->prev_sleep_state != ACPI_S3 && + ps->tco2_sts & TCO2_STS_SECOND_TO) + elog_add_event(ELOG_TYPE_TCO_RESET); + + /* Power Button Override */ + if (ps->pm1_sts & PRBTNOR_STS) + elog_add_event(ELOG_TYPE_POWER_BUTTON_OVERRIDE); + + /* RTC reset */ + if (ps->gen_pmcon3 & RTC_BATTERY_DEAD) + elog_add_event(ELOG_TYPE_RTC_RESET); + + /* System Reset Status (reset button pushed) */ + if (ps->gen_pmcon2 & SYSTEM_RESET_STS) + elog_add_event(ELOG_TYPE_RESET_BUTTON); + + /* General Reset Status */ + if (ps->gen_pmcon3 & GEN_RST_STS) + elog_add_event(ELOG_TYPE_SYSTEM_RESET); + + /* ACPI Wake Event */ + if (ps->prev_sleep_state != ACPI_S0) + elog_add_event_byte(ELOG_TYPE_ACPI_WAKE, ps->prev_sleep_state); +} + +static void pch_log_state(void *unused) +{ + struct chipset_power_state *ps = cbmem_find(CBMEM_ID_POWER_STATE); + + if (ps == NULL) { + printk(BIOS_ERR, "Not logging power state information. " + "Power state not found in cbmem.\n"); + return; + } + + /* Power and Reset */ + pch_log_power_and_resets(ps); + + /* Wake Sources */ + pch_log_wake_source(ps); +} + +BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_ENTRY, pch_log_state, NULL); diff --git a/src/soc/intel/broadwell/pch/fadt.c b/src/soc/intel/broadwell/pch/fadt.c new file mode 100644 index 0000000000..8fbd0c45ad --- /dev/null +++ b/src/soc/intel/broadwell/pch/fadt.c @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include "chip.h" + +void acpi_fill_fadt(acpi_fadt_t *fadt) +{ + const uint16_t pmbase = ACPI_BASE_ADDRESS; + + fadt->sci_int = acpi_sci_irq(); + + if (permanent_smi_handler()) { + fadt->smi_cmd = APM_CNT; + fadt->acpi_enable = APM_CNT_ACPI_ENABLE; + fadt->acpi_disable = APM_CNT_ACPI_DISABLE; + } + + fadt->pm1a_evt_blk = pmbase + PM1_STS; + fadt->pm1a_cnt_blk = pmbase + PM1_CNT; + fadt->pm2_cnt_blk = pmbase + PM2_CNT; + fadt->pm_tmr_blk = pmbase + PM1_TMR; + fadt->gpe0_blk = pmbase + GPE0_STS(0); + + fadt->pm1_evt_len = 4; + fadt->pm1_cnt_len = 2; + fadt->pm2_cnt_len = 1; + fadt->pm_tmr_len = 4; + fadt->gpe0_blk_len = 32; + fadt->p_lvl2_lat = 1; + fadt->p_lvl3_lat = 87; + fadt->duty_offset = 1; + fadt->duty_width = 0; + fadt->day_alrm = 0xd; + fadt->mon_alrm = 0x00; + fadt->century = 0x00; + fadt->iapc_boot_arch = ACPI_FADT_LEGACY_DEVICES | ACPI_FADT_8042; + + fadt->flags |= ACPI_FADT_WBINVD | + ACPI_FADT_C1_SUPPORTED | + ACPI_FADT_C2_MP_SUPPORTED | + ACPI_FADT_SLEEP_BUTTON | + ACPI_FADT_SEALED_CASE | + ACPI_FADT_S4_RTC_WAKE | + ACPI_FADT_PLATFORM_CLOCK; + + fadt->x_pm1a_evt_blk.space_id = ACPI_ADDRESS_SPACE_IO; + fadt->x_pm1a_evt_blk.bit_width = fadt->pm1_evt_len * 8; + fadt->x_pm1a_evt_blk.bit_offset = 0; + fadt->x_pm1a_evt_blk.access_size = ACPI_ACCESS_SIZE_WORD_ACCESS; + fadt->x_pm1a_evt_blk.addrl = pmbase + PM1_STS; + fadt->x_pm1a_evt_blk.addrh = 0x0; + + fadt->x_pm1a_cnt_blk.space_id = ACPI_ADDRESS_SPACE_IO; + fadt->x_pm1a_cnt_blk.bit_width = fadt->pm1_cnt_len * 8; + fadt->x_pm1a_cnt_blk.bit_offset = 0; + fadt->x_pm1a_cnt_blk.access_size = ACPI_ACCESS_SIZE_WORD_ACCESS; + fadt->x_pm1a_cnt_blk.addrl = pmbase + PM1_CNT; + fadt->x_pm1a_cnt_blk.addrh = 0x0; + + fadt->x_pm2_cnt_blk.space_id = ACPI_ADDRESS_SPACE_IO; + fadt->x_pm2_cnt_blk.bit_width = fadt->pm2_cnt_len * 8; + fadt->x_pm2_cnt_blk.bit_offset = 0; + fadt->x_pm2_cnt_blk.access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS; + fadt->x_pm2_cnt_blk.addrl = pmbase + PM2_CNT; + fadt->x_pm2_cnt_blk.addrh = 0x0; + + fadt->x_pm_tmr_blk.space_id = ACPI_ADDRESS_SPACE_IO; + fadt->x_pm_tmr_blk.bit_width = fadt->pm_tmr_len * 8; + fadt->x_pm_tmr_blk.bit_offset = 0; + fadt->x_pm_tmr_blk.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS; + fadt->x_pm_tmr_blk.addrl = pmbase + PM1_TMR; + fadt->x_pm_tmr_blk.addrh = 0x0; + + /* + * Windows 10 requires x_gpe0_blk to be set starting with FADT revision 5. + * The bit_width field intentionally overflows here. + * The OSPM can instead use the values in `fadt->gpe0_blk{,_len}`, which + * seems to work fine on Linux 5.0 and Windows 10. + */ + fadt->x_gpe0_blk.space_id = ACPI_ADDRESS_SPACE_IO; + fadt->x_gpe0_blk.bit_width = fadt->gpe0_blk_len * 8; + fadt->x_gpe0_blk.bit_offset = 0; + fadt->x_gpe0_blk.access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS; + fadt->x_gpe0_blk.addrl = fadt->gpe0_blk; + fadt->x_gpe0_blk.addrh = 0x0; +} diff --git a/src/soc/intel/broadwell/pch/gpio.c b/src/soc/intel/broadwell/pch/gpio.c new file mode 100644 index 0000000000..ff1f019ce0 --- /dev/null +++ b/src/soc/intel/broadwell/pch/gpio.c @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * This function will return a number that indicates which PIRQ + * this GPIO maps to. If this is not a PIRQ capable GPIO then + * it will return -1. The GPIO to PIRQ mapping is not linear. + */ +static int gpio_to_pirq(int gpio) +{ + switch (gpio) { + case 8: return 0; /* PIRQI */ + case 9: return 1; /* PIRQJ */ + case 10: return 2; /* PIRQK */ + case 13: return 3; /* PIRQL */ + case 14: return 4; /* PIRQM */ + case 45: return 5; /* PIRQN */ + case 46: return 6; /* PIRQO */ + case 47: return 7; /* PIRQP */ + case 48: return 8; /* PIRQQ */ + case 49: return 9; /* PIRQR */ + case 50: return 10; /* PIRQS */ + case 51: return 11; /* PIRQT */ + case 52: return 12; /* PIRQU */ + case 53: return 13; /* PIRQV */ + case 54: return 14; /* PIRQW */ + case 55: return 15; /* PIRQX */ + default: return -1; + }; +} + +void init_gpios(const struct gpio_config config[]) +{ + const struct gpio_config *entry; + u32 owner[3] = {0}; + u32 route[3] = {0}; + u32 irqen[3] = {0}; + u32 reset[3] = {0}; + u32 blink = 0; + u16 pirq2apic = 0; + int set, bit, gpio = 0; + + for (entry = config; entry->conf0 != GPIO_LIST_END; entry++, gpio++) { + if (gpio > MAX_GPIO_NUMBER) + break; + + /* Setup Configuration registers 1 and 2 */ + outl(entry->conf0, GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio)); + outl(entry->conf1, GPIO_BASE_ADDRESS + GPIO_CONFIG1(gpio)); + + /* Determine set and bit based on GPIO number */ + set = gpio >> 5; + bit = gpio % 32; + + /* Apply settings to set specific bits */ + owner[set] |= entry->owner << bit; + route[set] |= entry->route << bit; + irqen[set] |= entry->irqen << bit; + reset[set] |= entry->reset << bit; + + if (set == 0) + blink |= entry->blink << bit; + + /* PIRQ to IO-APIC map */ + if (entry->pirq == GPIO_PIRQ_APIC_ROUTE) { + set = gpio_to_pirq(gpio); + if (set >= 0) + pirq2apic |= 1 << set; + } + } + + for (set = 0; set <= 2; set++) { + outl(owner[set], GPIO_BASE_ADDRESS + GPIO_OWNER(set)); + outl(route[set], GPIO_BASE_ADDRESS + GPIO_ROUTE(set)); + outl(irqen[set], GPIO_BASE_ADDRESS + GPIO_IRQ_IE(set)); + outl(reset[set], GPIO_BASE_ADDRESS + GPIO_RESET(set)); + } + + outl(blink, GPIO_BASE_ADDRESS + GPIO_BLINK); + outl(pirq2apic, GPIO_BASE_ADDRESS + GPIO_PIRQ_APIC_EN); +} + +int get_gpio(int gpio_num) +{ + if (gpio_num > MAX_GPIO_NUMBER) + return 0; + + return !!(inl(GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio_num)) & GPI_LEVEL); +} + +/* + * get a number comprised of multiple GPIO values. gpio_num_array points to + * the array of gpio pin numbers to scan, terminated by -1. + */ +unsigned int get_gpios(const int *gpio_num_array) +{ + int gpio; + unsigned int bitmask = 1; + unsigned int vector = 0; + + while (bitmask && + ((gpio = *gpio_num_array++) != -1)) { + if (get_gpio(gpio)) + vector |= bitmask; + bitmask <<= 1; + } + return vector; +} + +void set_gpio(int gpio_num, int value) +{ + u32 conf0; + + if (gpio_num > MAX_GPIO_NUMBER) + return; + + conf0 = inl(GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio_num)); + conf0 &= ~GPO_LEVEL_MASK; + conf0 |= value << GPO_LEVEL_SHIFT; + outl(conf0, GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio_num)); +} + +int gpio_is_native(int gpio_num) +{ + return !(inl(GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio_num)) & 1); +} diff --git a/src/soc/intel/broadwell/pch/hda.c b/src/soc/intel/broadwell/pch/hda.c new file mode 100644 index 0000000000..04390d1342 --- /dev/null +++ b/src/soc/intel/broadwell/pch/hda.c @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void codecs_init(u8 *base, u32 codec_mask) +{ + int i; + + /* Can support up to 4 codecs */ + for (i = 3; i >= 0; i--) { + if (codec_mask & (1 << i)) + hda_codec_init(base, i, + cim_verb_data_size, + cim_verb_data); + } + + if (pc_beep_verbs_size) + hda_codec_write(base, pc_beep_verbs_size, pc_beep_verbs); +} + +static void hda_pch_init(struct device *dev, u8 *base) +{ + u8 reg8; + u16 reg16; + u32 reg32; + + if (RCBA32(0x2030) & (1 << 31)) { + reg32 = pci_read_config32(dev, 0x120); + reg32 &= 0xf8ffff01; + reg32 |= (1 << 25); + reg32 |= RCBA32(0x2030) & 0xfe; + pci_write_config32(dev, 0x120, reg32); + } else + printk(BIOS_DEBUG, "HDA: V1CTL disabled.\n"); + + reg32 = pci_read_config32(dev, 0x114); + reg32 &= ~0xfe; + pci_write_config32(dev, 0x114, reg32); + + // Set VCi enable bit + if (pci_read_config32(dev, 0x120) & ((1 << 24) | + (1 << 25) | (1 << 26))) { + reg32 = pci_read_config32(dev, 0x120); + reg32 &= ~(1 << 31); + pci_write_config32(dev, 0x120, reg32); + } + + /* Additional programming steps */ + reg32 = pci_read_config32(dev, 0xc4); + reg32 |= (1 << 24); + pci_write_config32(dev, 0xc4, reg32); + + reg8 = pci_read_config8(dev, 0x40); // Audio Control + reg8 |= 1; // Select HDA mode + pci_write_config8(dev, 0x40, reg8); + + reg8 = pci_read_config8(dev, 0x4d); // Docking Status + reg8 &= ~(1 << 7); // Docking not supported + pci_write_config8(dev, 0x4d, reg8); + + reg16 = read32(base + 0x0012); + reg16 |= (1 << 0); + write32(base + 0x0012, reg16); + + /* disable Auto Voltage Detector */ + reg8 = pci_read_config8(dev, 0x42); + reg8 |= (1 << 2); + pci_write_config8(dev, 0x42, reg8); +} + +static void hda_init(struct device *dev) +{ + u8 *base; + struct resource *res; + u32 codec_mask; + + /* Find base address */ + res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (!res) + return; + + base = res2mmio(res, 0, 0); + printk(BIOS_DEBUG, "HDA: base = %p\n", base); + + /* Set Bus Master */ + pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER); + + hda_pch_init(dev, base); + + codec_mask = hda_codec_detect(base); + + if (codec_mask) { + printk(BIOS_DEBUG, "HDA: codec_mask = %02x\n", codec_mask); + codecs_init(base, codec_mask); + } +} + +static void hda_enable(struct device *dev) +{ + u16 reg16; + u8 reg8; + + reg8 = pci_read_config8(dev, 0x43); + reg8 |= 0x6f; + pci_write_config8(dev, 0x43, reg8); + + if (!dev->enabled) { + /* Route I/O buffers to ADSP function */ + reg8 = pci_read_config8(dev, 0x42); + reg8 |= (1 << 7) | (1 << 6); + pci_write_config8(dev, 0x42, reg8); + + printk(BIOS_INFO, "HDA disabled, I/O buffers routed to ADSP\n"); + + /* 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); + + /* Disable this device */ + pch_disable_devfn(dev); + } +} + +static struct device_operations hda_ops = { + .read_resources = &pci_dev_read_resources, + .set_resources = &pci_dev_set_resources, + .enable_resources = &pci_dev_enable_resources, + .init = &hda_init, + .enable = &hda_enable, + .ops_pci = &pci_dev_ops_pci, +}; + +static const unsigned short pci_device_ids[] = { + 0x9c20, /* LynxPoint-LP */ + 0x9ca0, /* WildcatPoint */ + 0 +}; + +static const struct pci_driver pch_hda __pci_driver = { + .ops = &hda_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/soc/intel/broadwell/pch/iobp.c b/src/soc/intel/broadwell/pch/iobp.c new file mode 100644 index 0000000000..deb4156198 --- /dev/null +++ b/src/soc/intel/broadwell/pch/iobp.c @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include + +#define IOBP_RETRY 1000 + +static inline int iobp_poll(void) +{ + unsigned int try; + + for (try = IOBP_RETRY; try > 0; try--) { + u16 status = RCBA16(IOBPS); + if ((status & IOBPS_READY) == 0) + return 1; + udelay(10); + } + + printk(BIOS_ERR, "IOBP: timeout waiting for transaction to complete\n"); + return 0; +} + +u32 pch_iobp_read(u32 address) +{ + u16 status; + + if (!iobp_poll()) + return 0; + + /* Set the address */ + RCBA32(IOBPIRI) = address; + + /* READ OPCODE */ + status = RCBA16(IOBPS); + status &= ~IOBPS_MASK; + status |= IOBPS_READ; + RCBA16(IOBPS) = status; + + /* Undocumented magic */ + RCBA16(IOBPU) = IOBPU_MAGIC; + + /* Set ready bit */ + status = RCBA16(IOBPS); + status |= IOBPS_READY; + RCBA16(IOBPS) = status; + + if (!iobp_poll()) + return 0; + + /* Check for successful transaction */ + status = RCBA16(IOBPS); + if (status & IOBPS_TX_MASK) { + printk(BIOS_ERR, "IOBP: read 0x%08x failed\n", address); + return 0; + } + + /* Read IOBP data */ + return RCBA32(IOBPD); +} + +void pch_iobp_write(u32 address, u32 data) +{ + u16 status; + + if (!iobp_poll()) + return; + + /* Set the address */ + RCBA32(IOBPIRI) = address; + + /* WRITE OPCODE */ + status = RCBA16(IOBPS); + status &= ~IOBPS_MASK; + status |= IOBPS_WRITE; + RCBA16(IOBPS) = status; + + RCBA32(IOBPD) = data; + + /* Undocumented magic */ + RCBA16(IOBPU) = IOBPU_MAGIC; + + /* Set ready bit */ + status = RCBA16(IOBPS); + status |= IOBPS_READY; + RCBA16(IOBPS) = status; + + if (!iobp_poll()) + return; + + /* Check for successful transaction */ + status = RCBA16(IOBPS); + if (status & IOBPS_TX_MASK) { + printk(BIOS_ERR, "IOBP: write 0x%08x failed\n", address); + return; + } + + printk(BIOS_SPEW, "IOBP: set 0x%08x to 0x%08x\n", address, data); +} + +void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue) +{ + u32 data = pch_iobp_read(address); + + /* Update the data */ + data &= andvalue; + data |= orvalue; + + pch_iobp_write(address, data); +} + +void pch_iobp_exec(u32 addr, u16 op_code, u8 route_id, u32 *data, u8 *resp) +{ + if (!data || !resp) + return; + + *resp = -1; + if (!iobp_poll()) + return; + + /* RCBA2330[31:0] = Address */ + RCBA32(IOBPIRI) = addr; + /* RCBA2338[15:8] = opcode */ + RCBA16(IOBPS) = (RCBA16(IOBPS) & 0x00ff) | op_code; + /* RCBA233A[15:8] = 0xf0 RCBA233A[7:0] = Route ID */ + RCBA16(IOBPU) = IOBPU_MAGIC | route_id; + + if (op_code == IOBP_PCICFG_WRITE) + RCBA32(IOBPD) = *data; + /* Set RCBA2338[0] to trigger IOBP transaction*/ + RCBA16(IOBPS) = RCBA16(IOBPS) | 0x1; + + if (!iobp_poll()) + return; + + *resp = (RCBA16(IOBPS) & IOBPS_TX_MASK) >> 1; + *data = RCBA32(IOBPD); +} diff --git a/src/soc/intel/broadwell/pch/lpc.c b/src/soc/intel/broadwell/pch/lpc.c new file mode 100644 index 0000000000..2111913a0e --- /dev/null +++ b/src/soc/intel/broadwell/pch/lpc.c @@ -0,0 +1,677 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void pch_enable_ioapic(struct device *dev) +{ + u32 reg32; + + /* Assign unique bus/dev/fn for I/O APIC */ + pci_write_config16(dev, LPC_IBDF, + PCH_IOAPIC_PCI_BUS << 8 | PCH_IOAPIC_PCI_SLOT << 3); + + set_ioapic_id(VIO_APIC_VADDR, 0x02); + + /* affirm full set of redirection table entries ("write once") */ + reg32 = io_apic_read(VIO_APIC_VADDR, 0x01); + + /* PCH-LP has 39 redirection entries */ + reg32 &= ~0x00ff0000; + reg32 |= 0x00270000; + + io_apic_write(VIO_APIC_VADDR, 0x01, reg32); + + /* + * Select Boot Configuration register (0x03) and + * use Processor System Bus (0x01) to deliver interrupts. + */ + io_apic_write(VIO_APIC_VADDR, 0x03, 0x01); +} + +static void enable_hpet(struct device *dev) +{ + size_t i; + + /* Assign unique bus/dev/fn for each HPET */ + for (i = 0; i < 8; ++i) + pci_write_config16(dev, LPC_HnBDF(i), + PCH_HPET_PCI_BUS << 8 | PCH_HPET_PCI_SLOT << 3 | i); +} + +/* 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; + + const uint8_t pirq = 0x80; + + pci_write_config8(dev, PIRQA_ROUT, pirq); + pci_write_config8(dev, PIRQB_ROUT, pirq); + pci_write_config8(dev, PIRQC_ROUT, pirq); + pci_write_config8(dev, PIRQD_ROUT, pirq); + + pci_write_config8(dev, PIRQE_ROUT, pirq); + pci_write_config8(dev, PIRQF_ROUT, pirq); + pci_write_config8(dev, PIRQG_ROUT, pirq); + pci_write_config8(dev, PIRQH_ROUT, pirq); + + for (irq_dev = all_devices; irq_dev; irq_dev = irq_dev->next) { + u8 int_pin = 0, int_line = 0; + + if (!irq_dev->enabled || irq_dev->path.type != DEVICE_PATH_PCI) + continue; + + int_pin = pci_read_config8(irq_dev, PCI_INTERRUPT_PIN); + + switch (int_pin) { + case 1: /* INTA# */ + case 2: /* INTB# */ + case 3: /* INTC# */ + case 4: /* INTD# */ + int_line = pirq; + break; + } + + if (!int_line) + continue; + + pci_write_config8(irq_dev, PCI_INTERRUPT_LINE, int_line); + } +} + +static void pch_power_options(struct device *dev) +{ + u16 reg16; + const char *state; + /* Get the chip configuration */ + const struct soc_intel_broadwell_pch_config *config = config_of(dev); + int pwr_on = CONFIG_MAINBOARD_POWER_FAILURE_STATE; + + /* Which state do we want to goto after g3 (power restored)? + * 0 == S0 Full On + * 1 == S5 Soft Off + * + * If the option is not existent (Laptops), use Kconfig setting. + */ + get_option(&pwr_on, "power_on_after_fail"); + + reg16 = pci_read_config16(dev, GEN_PMCON_3); + reg16 &= 0xfffe; + switch (pwr_on) { + case MAINBOARD_POWER_OFF: + reg16 |= 1; + state = "off"; + break; + case MAINBOARD_POWER_ON: + reg16 &= ~1; + state = "on"; + break; + case MAINBOARD_POWER_KEEP: + reg16 &= ~1; + state = "state keep"; + break; + default: + state = "undefined"; + } + pci_write_config16(dev, GEN_PMCON_3, reg16); + printk(BIOS_INFO, "Set power %s after power failure.\n", state); + + /* GPE setup based on device tree configuration */ + enable_all_gpe(config->gpe0_en_1, config->gpe0_en_2, + config->gpe0_en_3, config->gpe0_en_4); + + /* SMI setup based on device tree configuration */ + enable_alt_smi(config->alt_gp_smi_en); +} + +static void pch_misc_init(struct device *dev) +{ + u8 reg8; + u16 reg16; + u32 reg32; + + reg16 = pci_read_config16(dev, GEN_PMCON_3); + + reg16 &= ~(3 << 4); /* SLP_S4# Assertion Stretch 4s */ + reg16 |= (1 << 3); /* SLP_S4# Assertion Stretch Enable */ + + reg16 &= ~(1 << 10); + reg16 |= (1 << 11); /* SLP_S3# Min Assertion Width 50ms */ + + reg16 |= (1 << 12); /* Disable SLP stretch after SUS well */ + + pci_write_config16(dev, GEN_PMCON_3, reg16); + + /* Prepare sleep mode */ + reg32 = inl(ACPI_BASE_ADDRESS + PM1_CNT); + reg32 &= ~SLP_TYP; + reg32 |= SCI_EN; + outl(reg32, ACPI_BASE_ADDRESS + PM1_CNT); + + /* Set up NMI on errors */ + reg8 = inb(0x61); + reg8 &= ~0xf0; /* Higher nibble must be 0 */ + reg8 |= (1 << 2); /* PCI SERR# disable for now */ + outb(reg8, 0x61); + + /* Disable NMI sources */ + reg8 = inb(0x70); + reg8 |= (1 << 7); /* Can't mask NMI from PCI-E and NMI_NOW */ + outb(reg8, 0x70); + + /* Indicate DRAM init done for MRC */ + pci_or_config8(dev, GEN_PMCON_2, 1 << 7); + + /* Enable BIOS updates outside of SMM */ + pci_and_config8(dev, BIOS_CNTL, ~(1 << 5)); + + /* Clear status bits to prevent unexpected wake */ + RCBA32_OR(0x3310, 0x2f); + + RCBA32_AND_OR(0x3f02, ~0xf, 0); + + /* Enable PCIe Releaxed Order */ + RCBA32_OR(0x2314, (1 << 31) | (1 << 7)), + RCBA32_OR(0x1114, (1 << 15) | (1 << 14)), + + /* Setup SERIRQ, enable continuous mode */ + reg8 = pci_read_config8(dev, SERIRQ_CNTL); + reg8 |= 1 << 7; + + if (CONFIG(SERIRQ_CONTINUOUS_MODE)) + reg8 |= 1 << 6; + + pci_write_config8(dev, SERIRQ_CNTL, reg8); +} + +/* Magic register settings for power management */ +static void pch_pm_init_magic(struct device *dev) +{ + pci_write_config8(dev, 0xa9, 0x46); + + RCBA32_AND_OR(0x232c, ~1, 0); + + RCBA32_OR(0x1100, 0x0000c13f); + + RCBA32_AND_OR(0x2320, ~0x60, 0x10); + + RCBA32(0x3314) = 0x00012fff; + + RCBA32_AND_OR(0x3318, ~0x000f0330, 0x0dcf0400); + + RCBA32(0x3324) = 0x04000000; + RCBA32(0x3368) = 0x00041400; + RCBA32(0x3388) = 0x3f8ddbff; + RCBA32(0x33ac) = 0x00007001; + RCBA32(0x33b0) = 0x00181900; + RCBA32(0x33c0) = 0x00060A00; + RCBA32(0x33d0) = 0x06200840; + RCBA32(0x3a28) = 0x01010101; + RCBA32(0x3a2c) = 0x040c0404; + RCBA32(0x3a9c) = 0x9000000a; + RCBA32(0x2b1c) = 0x03808033; + RCBA32(0x2b34) = 0x80000009; + RCBA32(0x3348) = 0x022ddfff; + RCBA32(0x334c) = 0x00000001; + RCBA32(0x3358) = 0x0001c000; + RCBA32(0x3380) = 0x3f8ddbff; + RCBA32(0x3384) = 0x0001c7e1; + RCBA32(0x338c) = 0x0001c7e1; + RCBA32(0x3398) = 0x0001c000; + RCBA32(0x33a8) = 0x00181900; + RCBA32(0x33dc) = 0x00080000; + RCBA32(0x33e0) = 0x00000001; + RCBA32(0x3a20) = 0x0000040c; + RCBA32(0x3a24) = 0x01010101; + RCBA32(0x3a30) = 0x01010101; + + pci_update_config32(dev, 0xac, ~0x00200000, 0); + + RCBA32_OR(0x0410, 0x00000003); + RCBA32_OR(0x2618, 0x08000000); + RCBA32_OR(0x2300, 0x00000002); + RCBA32_OR(0x2600, 0x00000008); + + RCBA32(0x33b4) = 0x00007001; + RCBA32(0x3350) = 0x022ddfff; + RCBA32(0x3354) = 0x00000001; + + /* Power Optimizer */ + RCBA32_OR(0x33d4, 0x08000000); + RCBA32_OR(0x33c8, 0x00000080); + + RCBA32(0x2b10) = 0x0000883c; + RCBA32(0x2b14) = 0x1e0a4616; + RCBA32(0x2b24) = 0x40000005; + RCBA32(0x2b20) = 0x0005db01; + RCBA32(0x3a80) = 0x05145005; + RCBA32(0x3a84) = 0x00001005; + + RCBA32_OR(0x33d4, 0x2fff2fb1); + RCBA32_OR(0x33c8, 0x00008000); +} + +static void pch_enable_mphy(void) +{ + u32 gpio71_native = gpio_is_native(71); + u32 data_and = 0xffffffff; + u32 data_or = (1 << 14) | (1 << 13) | (1 << 12); + + if (gpio71_native) { + data_or |= (1 << 0); + if (pch_is_wpt()) { + data_and &= ~((1 << 7) | (1 << 6) | (1 << 3)); + data_or |= (1 << 5) | (1 << 4); + + if (pch_is_wpt_ulx()) { + /* Check if SATA and USB3 MPHY are enabled */ + u32 strap19 = pch_read_soft_strap(19); + strap19 &= ((1 << 31) | (1 << 30)); + strap19 >>= 30; + if (strap19 == 3) { + data_or |= (1 << 3); + printk(BIOS_DEBUG, "Enable ULX MPHY PG " + "control in single domain\n"); + } else if (strap19 == 0) { + printk(BIOS_DEBUG, "Enable ULX MPHY PG " + "control in split domains\n"); + } else { + printk(BIOS_DEBUG, "Invalid PCH Soft " + "Strap 19 configuration\n"); + } + } else { + data_or |= (1 << 3); + } + } + } + + pch_iobp_update(0xCF000000, data_and, data_or); +} + +static void pch_init_deep_sx(struct device *dev) +{ + const struct soc_intel_broadwell_pch_config *config = config_of(dev); + + if (config->deep_sx_enable_ac) { + RCBA32_OR(DEEP_S3_POL, DEEP_S3_EN_AC); + RCBA32_OR(DEEP_S5_POL, DEEP_S5_EN_AC); + } + + if (config->deep_sx_enable_dc) { + RCBA32_OR(DEEP_S3_POL, DEEP_S3_EN_DC); + RCBA32_OR(DEEP_S5_POL, DEEP_S5_EN_DC); + } + + if (config->deep_sx_enable_ac || config->deep_sx_enable_dc) + RCBA32_OR(DEEP_SX_CONFIG, + DEEP_SX_WAKE_PIN_EN | DEEP_SX_GP27_PIN_EN); +} + +/* Power Management init */ +static void pch_pm_init(struct device *dev) +{ + printk(BIOS_DEBUG, "PCH PM init\n"); + + pch_init_deep_sx(dev); + + pch_enable_mphy(); + + pch_pm_init_magic(dev); + + if (pch_is_wpt()) { + RCBA32_OR(0x33e0, (1 << 4) | (1 << 1)); + RCBA32_OR(0x2b1c, (1 << 22) | (1 << 14) | (1 << 13)); + RCBA32(0x33e4) = 0x16bf0002; + RCBA32_OR(0x33e4, 0x1); + } + + pch_iobp_update(0xCA000000, ~0UL, 0x00000009); + + /* Set RCBA 0x2b1c[29]=1 if DSP disabled */ + if (RCBA32(FD) & PCH_DISABLE_ADSPD) + RCBA32_OR(0x2b1c, (1 << 29)); + +} + +static void pch_cg_init(struct device *dev) +{ + u32 reg32; + u16 reg16; + struct device *igd_dev = pcidev_path_on_root(SA_DEVFN_IGD); + + /* DMI */ + RCBA32_OR(0x2234, 0xf); + + reg16 = pci_read_config16(dev, GEN_PMCON_1); + reg16 &= ~(1 << 10); /* Disable BIOS_PCI_EXP_EN for native PME */ + if (pch_is_wpt()) + reg16 &= ~(1 << 11); + else + reg16 |= (1 << 11); + reg16 |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 12); + reg16 |= (1 << 2); // PCI CLKRUN# Enable + pci_write_config16(dev, GEN_PMCON_1, reg16); + + /* + * RCBA + 0x2614[27:25,14:13,10,8] = 101,11,1,1 + * RCBA + 0x2614[23:16] = 0x20 + * RCBA + 0x2614[30:28] = 0x0 + * RCBA + 0x2614[26] = 1 (IF 0:2.0@0x08 >= 0x0b) + */ + RCBA32_AND_OR(0x2614, ~0x64ff0000, 0x0a206500); + + /* Check for 0:2.0@0x08 >= 0x0b */ + if (pch_is_wpt() || pci_read_config8(igd_dev, 0x8) >= 0x0b) + RCBA32_OR(0x2614, (1 << 26)); + + RCBA32_OR(0x900, 0x0000031f); + + reg32 = RCBA32(CG); + if (RCBA32(0x3454) & (1 << 4)) + reg32 &= ~(1 << 29); // LPC Dynamic + else + reg32 |= (1 << 29); // LPC Dynamic + reg32 |= (1 << 31); // LP LPC + reg32 |= (1 << 30); // LP BLA + if (RCBA32(0x3454) & (1 << 4)) + reg32 &= ~(1 << 29); + else + reg32 |= (1 << 29); + reg32 |= (1 << 28); // GPIO Dynamic + reg32 |= (1 << 27); // HPET Dynamic + reg32 |= (1 << 26); // Generic Platform Event Clock + if (RCBA32(BUC) & PCH_DISABLE_GBE) + reg32 |= (1 << 23); // GbE Static + if (RCBA32(FD) & PCH_DISABLE_HD_AUDIO) + reg32 |= (1 << 21); // HDA Static + reg32 |= (1 << 22); // HDA Dynamic + RCBA32(CG) = reg32; + + /* PCH-LP LPC */ + if (pch_is_wpt()) + RCBA32_AND_OR(0x3434, ~0x1f, 0x17); + else + RCBA32_OR(0x3434, 0x7); + + /* SPI */ + RCBA32_OR(0x38c0, 0x3c07); + + pch_iobp_update(0xCE00C000, ~1UL, 0x00000000); +} + +static void pch_set_acpi_mode(void) +{ + if (!acpi_is_wakeup_s3()) { + apm_control(APM_CNT_ACPI_DISABLE); + } +} + +static void lpc_init(struct device *dev) +{ + /* Legacy initialization */ + isa_dma_init(); + sb_rtc_init(); + pch_misc_init(dev); + + /* Interrupt configuration */ + pch_enable_ioapic(dev); + pch_pirq_init(dev); + setup_i8259(); + i8259_configure_irq_trigger(9, 1); + enable_hpet(dev); + + /* Initialize power management */ + pch_power_options(dev); + pch_pm_init(dev); + pch_cg_init(dev); + + pch_set_acpi_mode(); +} + +static void pch_lpc_add_mmio_resources(struct device *dev) +{ + u32 reg; + struct resource *res; + const u32 default_decode_base = IO_APIC_ADDR; + + /* + * Just report all resources from IO-APIC base to 4GiB. Don't mark + * them reserved as that may upset the OS if this range is marked + * as reserved in the e820. + */ + res = new_resource(dev, OIC); + res->base = default_decode_base; + res->size = 0 - default_decode_base; + res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; + + /* RCBA */ + if (default_decode_base > RCBA_BASE_ADDRESS) { + res = new_resource(dev, RCBA); + res->base = RCBA_BASE_ADDRESS; + res->size = 16 * 1024; + res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | + IORESOURCE_FIXED | IORESOURCE_RESERVE; + } + + /* Check LPC Memory Decode register. */ + reg = pci_read_config32(dev, LGMR); + if (reg & 1) { + reg &= ~0xffff; + if (reg < default_decode_base) { + res = new_resource(dev, LGMR); + res->base = reg; + res->size = 16 * 1024; + res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | + IORESOURCE_FIXED | IORESOURCE_RESERVE; + } + } +} + +/* Default IO range claimed by the LPC device. The upper bound is exclusive. */ +#define LPC_DEFAULT_IO_RANGE_LOWER 0 +#define LPC_DEFAULT_IO_RANGE_UPPER 0x1000 + +static inline int pch_io_range_in_default(int base, int size) +{ + /* Does it start above the range? */ + if (base >= LPC_DEFAULT_IO_RANGE_UPPER) + return 0; + + /* Is it entirely contained? */ + if (base >= LPC_DEFAULT_IO_RANGE_LOWER && + (base + size) < LPC_DEFAULT_IO_RANGE_UPPER) + return 1; + + /* This will return not in range for partial overlaps. */ + return 0; +} + +/* + * Note: this function assumes there is no overlap with the default LPC device's + * claimed range: LPC_DEFAULT_IO_RANGE_LOWER -> LPC_DEFAULT_IO_RANGE_UPPER. + */ +static void pch_lpc_add_io_resource(struct device *dev, u16 base, u16 size, + int index) +{ + struct resource *res; + + if (pch_io_range_in_default(base, size)) + return; + + res = new_resource(dev, index); + res->base = base; + res->size = size; + res->flags = IORESOURCE_IO | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; +} + +static void pch_lpc_add_gen_io_resources(struct device *dev, int reg_value, + int index) +{ + /* + * Check if the register is enabled. If so and the base exceeds the + * device's default claim range add the resource. + */ + if (reg_value & 1) { + u16 base = reg_value & 0xfffc; + u16 size = (0x3 | ((reg_value >> 16) & 0xfc)) + 1; + pch_lpc_add_io_resource(dev, base, size, index); + } +} + +static void pch_lpc_add_io_resources(struct device *dev) +{ + struct resource *res; + const struct soc_intel_broadwell_pch_config *config = config_of(dev); + + /* Add the default claimed IO range for the LPC device. */ + res = new_resource(dev, 0); + res->base = LPC_DEFAULT_IO_RANGE_LOWER; + res->size = LPC_DEFAULT_IO_RANGE_UPPER - LPC_DEFAULT_IO_RANGE_LOWER; + res->flags = IORESOURCE_IO | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; + + /* GPIOBASE */ + pch_lpc_add_io_resource(dev, GPIO_BASE_ADDRESS, + GPIO_BASE_SIZE, GPIO_BASE); + + /* PMBASE */ + pch_lpc_add_io_resource(dev, ACPI_BASE_ADDRESS, ACPI_BASE_SIZE, PMBASE); + + /* LPC Generic IO Decode range. */ + pch_lpc_add_gen_io_resources(dev, config->gen1_dec, LPC_GEN1_DEC); + pch_lpc_add_gen_io_resources(dev, config->gen2_dec, LPC_GEN2_DEC); + pch_lpc_add_gen_io_resources(dev, config->gen3_dec, LPC_GEN3_DEC); + pch_lpc_add_gen_io_resources(dev, config->gen4_dec, LPC_GEN4_DEC); +} + +static void pch_lpc_read_resources(struct device *dev) +{ + struct global_nvs *gnvs; + + /* 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); + + /* Allocate ACPI NVS in CBMEM */ + gnvs = cbmem_add(CBMEM_ID_ACPI_GNVS, sizeof(struct global_nvs)); + if (!acpi_is_wakeup_s3() && gnvs) + memset(gnvs, 0, sizeof(struct global_nvs)); +} + +static void southcluster_inject_dsdt(const struct device *device) +{ + struct global_nvs *gnvs; + + gnvs = cbmem_find(CBMEM_ID_ACPI_GNVS); + if (!gnvs) { + gnvs = cbmem_add(CBMEM_ID_ACPI_GNVS, sizeof(*gnvs)); + if (gnvs) + memset(gnvs, 0, sizeof(*gnvs)); + } + + if (gnvs) { + acpi_create_gnvs(gnvs); + /* And tell SMI about it */ + apm_control(APM_CNT_GNVS_UPDATE); + + /* Add it to DSDT. */ + acpigen_write_scope("\\"); + acpigen_write_name_dword("NVSA", (u32) gnvs); + acpigen_pop_len(); + } +} + +static unsigned long broadwell_write_acpi_tables(const struct device *device, + unsigned long current, + struct acpi_rsdp *rsdp) +{ + if (CONFIG(INTEL_PCH_UART_CONSOLE)) + current = acpi_write_dbg2_pci_uart(rsdp, current, + (CONFIG_INTEL_PCH_UART_CONSOLE_NUMBER == 1) ? + PCH_DEV_UART1 : PCH_DEV_UART0, + ACPI_ACCESS_SIZE_BYTE_ACCESS); + return acpi_write_hpet(device, current, rsdp); +} + +static struct device_operations device_ops = { + .read_resources = &pch_lpc_read_resources, + .set_resources = &pci_dev_set_resources, + .enable_resources = &pci_dev_enable_resources, + .acpi_inject_dsdt = southcluster_inject_dsdt, + .write_acpi_tables = broadwell_write_acpi_tables, + .init = &lpc_init, + .scan_bus = &scan_static_bus, + .ops_pci = &pci_dev_ops_pci, +}; + +static const unsigned short pci_device_ids[] = { + PCH_LPT_LP_SAMPLE, + PCH_LPT_LP_PREMIUM, + PCH_LPT_LP_MAINSTREAM, + PCH_LPT_LP_VALUE, + PCH_WPT_HSW_U_SAMPLE, + PCH_WPT_BDW_U_SAMPLE, + PCH_WPT_BDW_U_PREMIUM, + PCH_WPT_BDW_U_BASE, + PCH_WPT_BDW_Y_SAMPLE, + PCH_WPT_BDW_Y_PREMIUM, + PCH_WPT_BDW_Y_BASE, + PCH_WPT_BDW_H, + 0 +}; + +static const struct pci_driver pch_lpc __pci_driver = { + .ops = &device_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/soc/intel/broadwell/pch/me.c b/src/soc/intel/broadwell/pch/me.c new file mode 100644 index 0000000000..40a81d8810 --- /dev/null +++ b/src/soc/intel/broadwell/pch/me.c @@ -0,0 +1,1057 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * This is a ramstage driver for the Intel Management Engine found in the + * southbridge. It handles the required boot-time messages over the + * MMIO-based Management Engine Interface to tell the ME that the BIOS is + * finished with POST. Additional messages are defined for debug but are + * not used unless the console loglevel is high enough. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if CONFIG(CHROMEOS) +#include +#include +#endif + +/* Path that the BIOS should take based on ME state */ +static const char *me_bios_path_values[] = { + [ME_NORMAL_BIOS_PATH] = "Normal", + [ME_S3WAKE_BIOS_PATH] = "S3 Wake", + [ME_ERROR_BIOS_PATH] = "Error", + [ME_RECOVERY_BIOS_PATH] = "Recovery", + [ME_DISABLE_BIOS_PATH] = "Disable", + [ME_FIRMWARE_UPDATE_BIOS_PATH] = "Firmware Update", +}; + +/* MMIO base address for MEI interface */ +static u8 *mei_base_address; + +static void mei_dump(void *ptr, int dword, int offset, const char *type) +{ + struct mei_csr *csr; + + if (!CONFIG(DEBUG_INTEL_ME)) + return; + + printk(BIOS_SPEW, "%-9s[%02x] : ", type, offset); + + switch (offset) { + case MEI_H_CSR: + case MEI_ME_CSR_HA: + csr = ptr; + if (!csr) { + printk(BIOS_SPEW, "ERROR: 0x%08x\n", dword); + break; + } + printk(BIOS_SPEW, "cbd=%u cbrp=%02u cbwp=%02u ready=%u " + "reset=%u ig=%u is=%u ie=%u\n", csr->buffer_depth, + csr->buffer_read_ptr, csr->buffer_write_ptr, + csr->ready, csr->reset, csr->interrupt_generate, + csr->interrupt_status, csr->interrupt_enable); + break; + case MEI_ME_CB_RW: + case MEI_H_CB_WW: + printk(BIOS_SPEW, "CB: 0x%08x\n", dword); + break; + default: + printk(BIOS_SPEW, "0x%08x\n", offset); + break; + } +} + +/* + * ME/MEI access helpers using memcpy to avoid aliasing. + */ + +static inline void mei_read_dword_ptr(void *ptr, int offset) +{ + u32 dword = read32(mei_base_address + offset); + memcpy(ptr, &dword, sizeof(dword)); + mei_dump(ptr, dword, offset, "READ"); +} + +static inline void mei_write_dword_ptr(void *ptr, int offset) +{ + u32 dword = 0; + memcpy(&dword, ptr, sizeof(dword)); + write32(mei_base_address + offset, dword); + mei_dump(ptr, dword, offset, "WRITE"); +} + +static inline void pci_read_dword_ptr(struct device *dev, void *ptr, int offset) +{ + u32 dword = pci_read_config32(dev, offset); + memcpy(ptr, &dword, sizeof(dword)); + mei_dump(ptr, dword, offset, "PCI READ"); +} + +static inline void read_host_csr(struct mei_csr *csr) +{ + mei_read_dword_ptr(csr, MEI_H_CSR); +} + +static inline void write_host_csr(struct mei_csr *csr) +{ + mei_write_dword_ptr(csr, MEI_H_CSR); +} + +static inline void read_me_csr(struct mei_csr *csr) +{ + mei_read_dword_ptr(csr, MEI_ME_CSR_HA); +} + +static inline void write_cb(u32 dword) +{ + write32(mei_base_address + MEI_H_CB_WW, dword); + mei_dump(NULL, dword, MEI_H_CB_WW, "WRITE"); +} + +static inline u32 read_cb(void) +{ + u32 dword = read32(mei_base_address + MEI_ME_CB_RW); + mei_dump(NULL, dword, MEI_ME_CB_RW, "READ"); + return dword; +} + +/* Wait for ME ready bit to be asserted */ +static int mei_wait_for_me_ready(void) +{ + struct mei_csr me; + unsigned int try = ME_RETRY; + + while (try--) { + read_me_csr(&me); + if (me.ready) + return 0; + udelay(ME_DELAY); + } + + printk(BIOS_ERR, "ME: failed to become ready\n"); + return -1; +} + +static void mei_reset(void) +{ + struct mei_csr host; + + if (mei_wait_for_me_ready() < 0) + return; + + /* Reset host and ME circular buffers for next message */ + read_host_csr(&host); + host.reset = 1; + host.interrupt_generate = 1; + write_host_csr(&host); + + if (mei_wait_for_me_ready() < 0) + return; + + /* Re-init and indicate host is ready */ + read_host_csr(&host); + host.interrupt_generate = 1; + host.ready = 1; + host.reset = 0; + write_host_csr(&host); +} + +static int mei_send_packet(struct mei_header *mei, void *req_data) +{ + struct mei_csr host; + unsigned int ndata, n; + u32 *data; + + /* Number of dwords to write */ + ndata = mei->length >> 2; + + /* Pad non-dword aligned request message length */ + if (mei->length & 3) + ndata++; + if (!ndata) { + printk(BIOS_DEBUG, "ME: request has no data\n"); + return -1; + } + ndata++; /* Add MEI header */ + + /* + * Make sure there is still room left in the circular buffer. + * Reset the buffer pointers if the requested message will not fit. + */ + read_host_csr(&host); + if ((host.buffer_depth - host.buffer_write_ptr) < ndata) { + printk(BIOS_ERR, "ME: circular buffer full, resetting...\n"); + mei_reset(); + read_host_csr(&host); + } + + /* Ensure the requested length will fit in the circular buffer. */ + if ((host.buffer_depth - host.buffer_write_ptr) < ndata) { + printk(BIOS_ERR, "ME: message (%u) too large for buffer (%u)\n", + ndata + 2, host.buffer_depth); + return -1; + } + + /* Write MEI header */ + mei_write_dword_ptr(mei, MEI_H_CB_WW); + ndata--; + + /* Write message data */ + data = req_data; + for (n = 0; n < ndata; ++n) + write_cb(*data++); + + /* Generate interrupt to the ME */ + read_host_csr(&host); + host.interrupt_generate = 1; + write_host_csr(&host); + + /* Make sure ME is ready after sending request data */ + return mei_wait_for_me_ready(); +} + +static int mei_send_data(u8 me_address, u8 host_address, + void *req_data, int req_bytes) +{ + struct mei_header header = { + .client_address = me_address, + .host_address = host_address, + }; + struct mei_csr host; + int current = 0; + u8 *req_ptr = req_data; + + while (!header.is_complete) { + int remain = req_bytes - current; + int buf_len; + + read_host_csr(&host); + buf_len = host.buffer_depth - host.buffer_write_ptr; + + if (buf_len > remain) { + /* Send all remaining data as final message */ + header.length = req_bytes - current; + header.is_complete = 1; + } else { + /* Send as much data as the buffer can hold */ + header.length = buf_len; + } + + mei_send_packet(&header, req_ptr); + + req_ptr += header.length; + current += header.length; + } + + return 0; +} + +static int mei_send_header(u8 me_address, u8 host_address, + void *header, int header_len, int complete) +{ + struct mei_header mei = { + .client_address = me_address, + .host_address = host_address, + .length = header_len, + .is_complete = complete, + }; + return mei_send_packet(&mei, header); +} + +static int mei_recv_msg(void *header, int header_bytes, + void *rsp_data, int rsp_bytes) +{ + struct mei_header mei_rsp; + struct mei_csr me, host; + unsigned int ndata, n; + unsigned int expected; + u32 *data; + + /* Total number of dwords to read from circular buffer */ + expected = (rsp_bytes + sizeof(mei_rsp) + header_bytes) >> 2; + if (rsp_bytes & 3) + expected++; + + if (mei_wait_for_me_ready() < 0) + return -1; + + /* + * The interrupt status bit does not appear to indicate that the + * message has actually been received. Instead we wait until the + * expected number of dwords are present in the circular buffer. + */ + for (n = ME_RETRY; n; --n) { + read_me_csr(&me); + if ((me.buffer_write_ptr - me.buffer_read_ptr) >= expected) + break; + udelay(ME_DELAY); + } + if (!n) { + printk(BIOS_ERR, "ME: timeout waiting for data: expected " + "%u, available %u\n", expected, + me.buffer_write_ptr - me.buffer_read_ptr); + return -1; + } + + /* Read and verify MEI response header from the ME */ + mei_read_dword_ptr(&mei_rsp, MEI_ME_CB_RW); + if (!mei_rsp.is_complete) { + printk(BIOS_ERR, "ME: response is not complete\n"); + return -1; + } + + /* Handle non-dword responses and expect at least the header */ + ndata = mei_rsp.length >> 2; + if (mei_rsp.length & 3) + ndata++; + if (ndata != (expected - 1)) { + printk(BIOS_ERR, "ME: response is missing data %d != %d\n", + ndata, (expected - 1)); + return -1; + } + + /* Read response header from the ME */ + data = header; + for (n = 0; n < (header_bytes >> 2); ++n) + *data++ = read_cb(); + ndata -= header_bytes >> 2; + + /* Make sure caller passed a buffer with enough space */ + if (ndata != (rsp_bytes >> 2)) { + printk(BIOS_ERR, "ME: not enough room in response buffer: " + "%u != %u\n", ndata, rsp_bytes >> 2); + return -1; + } + + /* Read response data from the circular buffer */ + data = rsp_data; + for (n = 0; n < ndata; ++n) + *data++ = read_cb(); + + /* Tell the ME that we have consumed the response */ + read_host_csr(&host); + host.interrupt_status = 1; + host.interrupt_generate = 1; + write_host_csr(&host); + + return mei_wait_for_me_ready(); +} + +static inline int mei_sendrecv_mkhi(struct mkhi_header *mkhi, + void *req_data, int req_bytes, + void *rsp_data, int rsp_bytes) +{ + struct mkhi_header mkhi_rsp; + + /* Send header */ + if (mei_send_header(MEI_ADDRESS_MKHI, MEI_HOST_ADDRESS, + mkhi, sizeof(*mkhi), req_bytes ? 0 : 1) < 0) + return -1; + + /* Send data if available */ + if (req_bytes && mei_send_data(MEI_ADDRESS_MKHI, MEI_HOST_ADDRESS, + req_data, req_bytes) < 0) + return -1; + + /* Return now if no response expected */ + if (!rsp_bytes) + return 0; + + /* Read header and data */ + if (mei_recv_msg(&mkhi_rsp, sizeof(mkhi_rsp), + rsp_data, rsp_bytes) < 0) + return -1; + + if (!mkhi_rsp.is_response || + mkhi->group_id != mkhi_rsp.group_id || + mkhi->command != mkhi_rsp.command) { + printk(BIOS_ERR, "ME: invalid response, group %u ?= %u," + "command %u ?= %u, is_response %u\n", mkhi->group_id, + mkhi_rsp.group_id, mkhi->command, mkhi_rsp.command, + mkhi_rsp.is_response); + return -1; + } + + return 0; +} + +static inline int mei_sendrecv_icc(struct icc_header *icc, + void *req_data, int req_bytes, + void *rsp_data, int rsp_bytes) +{ + struct icc_header icc_rsp; + + /* Send header */ + if (mei_send_header(MEI_ADDRESS_ICC, MEI_HOST_ADDRESS, + icc, sizeof(*icc), req_bytes ? 0 : 1) < 0) + return -1; + + /* Send data if available */ + if (req_bytes && mei_send_data(MEI_ADDRESS_ICC, MEI_HOST_ADDRESS, + req_data, req_bytes) < 0) + return -1; + + /* Read header and data, if needed */ + if (rsp_bytes && mei_recv_msg(&icc_rsp, sizeof(icc_rsp), + rsp_data, rsp_bytes) < 0) + return -1; + + return 0; +} + +/* + * mbp give up routine. This path is taken if hfs.mpb_rdy is 0 or the read + * state machine on the BIOS end doesn't match the ME's state machine. + */ +static void intel_me_mbp_give_up(struct device *dev) +{ + struct mei_csr csr; + + pci_write_config32(dev, PCI_ME_H_GS2, PCI_ME_MBP_GIVE_UP); + + read_host_csr(&csr); + csr.reset = 1; + csr.interrupt_generate = 1; + write_host_csr(&csr); +} + +/* + * mbp clear routine. This will wait for the ME to indicate that + * the MBP has been read and cleared. + */ +static void intel_me_mbp_clear(struct device *dev) +{ + int count; + struct me_hfs2 hfs2; + + /* Wait for the mbp_cleared indicator */ + for (count = ME_RETRY; count > 0; --count) { + pci_read_dword_ptr(dev, &hfs2, PCI_ME_HFS2); + if (hfs2.mbp_cleared) + break; + udelay(ME_DELAY); + } + + if (count == 0) { + printk(BIOS_WARNING, "ME: Timeout waiting for mbp_cleared\n"); + intel_me_mbp_give_up(dev); + } else { + printk(BIOS_INFO, "ME: MBP cleared\n"); + } +} + +static void me_print_fw_version(mbp_fw_version_name *vers_name) +{ + if (!vers_name) { + printk(BIOS_ERR, "ME: mbp missing version report\n"); + return; + } + + printk(BIOS_DEBUG, "ME: found version %d.%d.%d.%d\n", + vers_name->major_version, vers_name->minor_version, + vers_name->hotfix_version, vers_name->build_version); +} + +static inline void print_cap(const char *name, int state) +{ + printk(BIOS_DEBUG, "ME Capability: %-41s : %sabled\n", + name, state ? " en" : "dis"); +} + +/* Get ME Firmware Capabilities */ +static int mkhi_get_fwcaps(mbp_mefwcaps *cap) +{ + u32 rule_id = 0; + struct me_fwcaps cap_msg; + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_FWCAPS, + .command = MKHI_FWCAPS_GET_RULE, + }; + + /* Send request and wait for response */ + if (mei_sendrecv_mkhi(&mkhi, &rule_id, sizeof(u32), + &cap_msg, sizeof(cap_msg)) < 0) { + printk(BIOS_ERR, "ME: GET FWCAPS message failed\n"); + return -1; + } + *cap = cap_msg.caps_sku; + return 0; +} + +/* Get ME Firmware Capabilities */ +static void me_print_fwcaps(mbp_mefwcaps *cap) +{ + mbp_mefwcaps local_caps; + if (!cap) { + cap = &local_caps; + printk(BIOS_ERR, "ME: mbp missing fwcaps report\n"); + if (mkhi_get_fwcaps(cap)) + return; + } + + print_cap("Full Network manageability", cap->full_net); + print_cap("Regular Network manageability", cap->std_net); + print_cap("Manageability", cap->manageability); + print_cap("IntelR Anti-Theft (AT)", cap->intel_at); + print_cap("IntelR Capability Licensing Service (CLS)", cap->intel_cls); + print_cap("IntelR Power Sharing Technology (MPC)", cap->intel_mpc); + print_cap("ICC Over Clocking", cap->icc_over_clocking); + print_cap("Protected Audio Video Path (PAVP)", cap->pavp); + print_cap("IPV6", cap->ipv6); + print_cap("KVM Remote Control (KVM)", cap->kvm); + print_cap("Outbreak Containment Heuristic (OCH)", cap->och); + print_cap("Virtual LAN (VLAN)", cap->vlan); + print_cap("TLS", cap->tls); + print_cap("Wireless LAN (WLAN)", cap->wlan); +} + +/* Send END OF POST message to the ME */ +static int mkhi_end_of_post(void) +{ + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_GEN, + .command = MKHI_END_OF_POST, + }; + u32 eop_ack; + + /* Send request and wait for response */ + if (mei_sendrecv_mkhi(&mkhi, NULL, 0, &eop_ack, sizeof(eop_ack)) < 0) { + printk(BIOS_ERR, "ME: END OF POST message failed\n"); + return -1; + } + + printk(BIOS_INFO, "ME: END OF POST message successful (%d)\n", eop_ack); + return 0; +} + +/* Send END OF POST message to the ME */ +static int mkhi_end_of_post_noack(void) +{ + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_GEN, + .command = MKHI_END_OF_POST_NOACK, + }; + + /* Send request, do not wait for response */ + if (mei_sendrecv_mkhi(&mkhi, NULL, 0, NULL, 0) < 0) { + printk(BIOS_ERR, "ME: END OF POST NOACK message failed\n"); + return -1; + } + + printk(BIOS_INFO, "ME: END OF POST NOACK message successful\n"); + return 0; +} + +/* Send HMRFPO LOCK message to the ME */ +static int mkhi_hmrfpo_lock(void) +{ + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_HMRFPO, + .command = MKHI_HMRFPO_LOCK, + }; + u32 ack; + + /* Send request and wait for response */ + if (mei_sendrecv_mkhi(&mkhi, NULL, 0, &ack, sizeof(ack)) < 0) { + printk(BIOS_ERR, "ME: HMRFPO LOCK message failed\n"); + return -1; + } + + printk(BIOS_INFO, "ME: HMRFPO LOCK message successful (%d)\n", ack); + return 0; +} + +/* Send HMRFPO LOCK message to the ME, do not wait for response */ +static int mkhi_hmrfpo_lock_noack(void) +{ + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_HMRFPO, + .command = MKHI_HMRFPO_LOCK_NOACK, + }; + + /* Send request, do not wait for response */ + if (mei_sendrecv_mkhi(&mkhi, NULL, 0, NULL, 0) < 0) { + printk(BIOS_ERR, "ME: HMRFPO LOCK NOACK message failed\n"); + return -1; + } + + printk(BIOS_INFO, "ME: HMRFPO LOCK NOACK message successful\n"); + return 0; +} + +static void intel_me_finalize(struct device *dev) +{ + u16 reg16; + + /* S3 path will have hidden this device already */ + if (!mei_base_address || mei_base_address == (u8 *) 0xfffffff0) + return; + + /* Make sure IO is 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 the PCI device */ + RCBA32_OR(FD2, PCH_DISABLE_MEI1); + RCBA32(FD2); +} + +static int me_icc_set_clock_enables(u32 mask) +{ + struct icc_clock_enables_msg clk = { + .clock_enables = 0, /* Turn off specified clocks */ + .clock_mask = mask, + .no_response = 1, /* Do not expect response */ + }; + struct icc_header icc = { + .api_version = ICC_API_VERSION_LYNXPOINT, + .icc_command = ICC_SET_CLOCK_ENABLES, + .length = sizeof(clk), + }; + + /* Send request and wait for response */ + if (mei_sendrecv_icc(&icc, &clk, sizeof(clk), NULL, 0) < 0) { + printk(BIOS_ERR, "ME: ICC SET CLOCK ENABLES message failed\n"); + return -1; + } + printk(BIOS_INFO, "ME: ICC SET CLOCK ENABLES 0x%08x\n", mask); + return 0; +} + +/* Determine the path that we should take based on ME status */ +static me_bios_path intel_me_path(struct device *dev) +{ + me_bios_path path = ME_DISABLE_BIOS_PATH; + struct me_hfs hfs; + struct me_hfs2 hfs2; + + /* Check and dump status */ + intel_me_status(); + + pci_read_dword_ptr(dev, &hfs, PCI_ME_HFS); + pci_read_dword_ptr(dev, &hfs2, PCI_ME_HFS2); + + /* Check Current Working State */ + switch (hfs.working_state) { + case ME_HFS_CWS_NORMAL: + path = ME_NORMAL_BIOS_PATH; + break; + case ME_HFS_CWS_REC: + path = ME_RECOVERY_BIOS_PATH; + break; + default: + path = ME_DISABLE_BIOS_PATH; + break; + } + + /* Check Current Operation Mode */ + switch (hfs.operation_mode) { + case ME_HFS_MODE_NORMAL: + break; + case ME_HFS_MODE_DEBUG: + case ME_HFS_MODE_DIS: + case ME_HFS_MODE_OVER_JMPR: + case ME_HFS_MODE_OVER_MEI: + default: + path = ME_DISABLE_BIOS_PATH; + break; + } + + /* Check for any error code and valid firmware and MBP */ + if (hfs.error_code || hfs.fpt_bad) + path = ME_ERROR_BIOS_PATH; + + /* Check if the MBP is ready */ + if (!hfs2.mbp_rdy) { + printk(BIOS_CRIT, "%s: mbp is not ready!\n", + __func__); + path = ME_ERROR_BIOS_PATH; + } + + if (CONFIG(ELOG) && path != ME_NORMAL_BIOS_PATH) { + struct elog_event_data_me_extended data = { + .current_working_state = hfs.working_state, + .operation_state = hfs.operation_state, + .operation_mode = hfs.operation_mode, + .error_code = hfs.error_code, + .progress_code = hfs2.progress_code, + .current_pmevent = hfs2.current_pmevent, + .current_state = hfs2.current_state, + }; + elog_add_event_byte(ELOG_TYPE_MANAGEMENT_ENGINE, path); + elog_add_event_raw(ELOG_TYPE_MANAGEMENT_ENGINE_EXT, + &data, sizeof(data)); + } + + return path; +} + +/* Prepare ME for MEI messages */ +static int intel_mei_setup(struct device *dev) +{ + struct resource *res; + struct mei_csr host; + + /* Find the MMIO base for the ME interface */ + res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (!res || res->base == 0 || res->size == 0) { + printk(BIOS_DEBUG, "ME: MEI resource not present!\n"); + return -1; + } + mei_base_address = res2mmio(res, 0, 0); + + /* Ensure Memory and Bus Master bits are set */ + pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + + /* Clean up status for next message */ + read_host_csr(&host); + host.interrupt_generate = 1; + host.ready = 1; + host.reset = 0; + write_host_csr(&host); + + return 0; +} + +/* Read the Extend register hash of ME firmware */ +static int intel_me_extend_valid(struct device *dev) +{ + struct me_heres status; + u32 extend[8] = {0}; + int i, count = 0; + + pci_read_dword_ptr(dev, &status, PCI_ME_HERES); + if (!status.extend_feature_present) { + printk(BIOS_ERR, "ME: Extend Feature not present\n"); + return -1; + } + + if (!status.extend_reg_valid) { + printk(BIOS_ERR, "ME: Extend Register not valid\n"); + return -1; + } + + switch (status.extend_reg_algorithm) { + case PCI_ME_EXT_SHA1: + count = 5; + printk(BIOS_DEBUG, "ME: Extend SHA-1: "); + break; + case PCI_ME_EXT_SHA256: + count = 8; + printk(BIOS_DEBUG, "ME: Extend SHA-256: "); + break; + default: + printk(BIOS_ERR, "ME: Extend Algorithm %d unknown\n", + status.extend_reg_algorithm); + return -1; + } + + for (i = 0; i < count; ++i) { + extend[i] = pci_read_config32(dev, PCI_ME_HER(i)); + printk(BIOS_DEBUG, "%08x", extend[i]); + } + printk(BIOS_DEBUG, "\n"); + +#if CONFIG(CHROMEOS) + /* Save hash in NVS for the OS to verify */ + chromeos_set_me_hash(extend, count); +#endif + + return 0; +} + +static void intel_me_print_mbp(me_bios_payload *mbp_data) +{ + me_print_fw_version(mbp_data->fw_version_name); + + if (CONFIG(DEBUG_INTEL_ME)) + me_print_fwcaps(mbp_data->fw_capabilities); + + if (mbp_data->plat_time) { + printk(BIOS_DEBUG, "ME: Wake Event to ME Reset: %u ms\n", + mbp_data->plat_time->wake_event_mrst_time_ms); + printk(BIOS_DEBUG, "ME: ME Reset to Platform Reset: %u ms\n", + mbp_data->plat_time->mrst_pltrst_time_ms); + printk(BIOS_DEBUG, "ME: Platform Reset to CPU Reset: %u ms\n", + mbp_data->plat_time->pltrst_cpurst_time_ms); + } +} + +static u32 me_to_host_words_pending(void) +{ + struct mei_csr me; + read_me_csr(&me); + if (!me.ready) + return 0; + return (me.buffer_write_ptr - me.buffer_read_ptr) & + (me.buffer_depth - 1); +} + +struct mbp_payload { + mbp_header header; + u32 data[0]; +}; + +/* + * Read and print ME MBP data + * + * Return -1 to indicate a problem (give up) + * Return 0 to indicate success (send LOCK+EOP) + * Return 1 to indicate success (send LOCK+EOP with NOACK) + */ +static int intel_me_read_mbp(me_bios_payload *mbp_data, struct device *dev) +{ + mbp_header mbp_hdr; + u32 me2host_pending; + struct mei_csr host; + struct me_hfs2 hfs2; + struct mbp_payload *mbp; + int i; + int ret = 0; + + pci_read_dword_ptr(dev, &hfs2, PCI_ME_HFS2); + + if (!hfs2.mbp_rdy) { + printk(BIOS_ERR, "ME: MBP not ready\n"); + intel_me_mbp_give_up(dev); + return -1; + } + + me2host_pending = me_to_host_words_pending(); + if (!me2host_pending) { + printk(BIOS_ERR, "ME: no mbp data!\n"); + intel_me_mbp_give_up(dev); + return -1; + } + + /* we know for sure that at least the header is there */ + mei_read_dword_ptr(&mbp_hdr, MEI_ME_CB_RW); + + if ((mbp_hdr.num_entries > (mbp_hdr.mbp_size / 2)) || + (me2host_pending < mbp_hdr.mbp_size)) { + printk(BIOS_ERR, "ME: mbp of %d entries, total size %d words" + " buffer contains %d words\n", + mbp_hdr.num_entries, mbp_hdr.mbp_size, + me2host_pending); + intel_me_mbp_give_up(dev); + return -1; + } + mbp = malloc(mbp_hdr.mbp_size * sizeof(u32)); + if (!mbp) { + intel_me_mbp_give_up(dev); + return -1; + } + + mbp->header = mbp_hdr; + me2host_pending--; + + i = 0; + while (i != me2host_pending) { + mei_read_dword_ptr(&mbp->data[i], MEI_ME_CB_RW); + i++; + } + + read_host_csr(&host); + + /* Check that read and write pointers are equal. */ + if (host.buffer_read_ptr != host.buffer_write_ptr) { + printk(BIOS_INFO, "ME: MBP Read/Write pointer mismatch\n"); + printk(BIOS_INFO, "ME: MBP Waiting for MBP cleared flag\n"); + + /* Tell ME that the host has finished reading the MBP. */ + host.interrupt_generate = 1; + host.reset = 0; + write_host_csr(&host); + + /* Wait for the mbp_cleared indicator. */ + intel_me_mbp_clear(dev); + } else { + /* Indicate NOACK messages should be used. */ + ret = 1; + } + + /* Dump out the MBP contents. */ + if (CONFIG(DEBUG_INTEL_ME)) { + printk(BIOS_INFO, "ME MBP: Header: items: %d, size dw: %d\n", + mbp->header.num_entries, mbp->header.mbp_size); + for (i = 0; i < mbp->header.mbp_size - 1; i++) + printk(BIOS_INFO, "ME MBP: %04x: 0x%08x\n", i, mbp->data[i]); + } + +#define ASSIGN_FIELD_PTR(field_, val_) \ + { \ + mbp_data->field_ = (typeof(mbp_data->field_))(void *)val_; \ + break; \ + } + + /* Setup the pointers in the me_bios_payload structure. */ + for (i = 0; i < mbp->header.mbp_size - 1;) { + mbp_item_header *item = (void *)&mbp->data[i]; + + switch (MBP_MAKE_IDENT(item->app_id, item->item_id)) { + case MBP_IDENT(KERNEL, FW_VER): + ASSIGN_FIELD_PTR(fw_version_name, &mbp->data[i+1]); + + case MBP_IDENT(ICC, PROFILE): + ASSIGN_FIELD_PTR(icc_profile, &mbp->data[i+1]); + + case MBP_IDENT(INTEL_AT, STATE): + ASSIGN_FIELD_PTR(at_state, &mbp->data[i+1]); + + case MBP_IDENT(KERNEL, FW_CAP): + ASSIGN_FIELD_PTR(fw_capabilities, &mbp->data[i+1]); + + case MBP_IDENT(KERNEL, ROM_BIST): + ASSIGN_FIELD_PTR(rom_bist_data, &mbp->data[i+1]); + + case MBP_IDENT(KERNEL, PLAT_KEY): + ASSIGN_FIELD_PTR(platform_key, &mbp->data[i+1]); + + case MBP_IDENT(KERNEL, FW_TYPE): + ASSIGN_FIELD_PTR(fw_plat_type, &mbp->data[i+1]); + + case MBP_IDENT(KERNEL, MFS_FAILURE): + ASSIGN_FIELD_PTR(mfsintegrity, &mbp->data[i+1]); + + case MBP_IDENT(KERNEL, PLAT_TIME): + ASSIGN_FIELD_PTR(plat_time, &mbp->data[i+1]); + + case MBP_IDENT(NFC, SUPPORT_DATA): + ASSIGN_FIELD_PTR(nfc_data, &mbp->data[i+1]); + } + i += item->length; + } + #undef ASSIGN_FIELD_PTR + + free(mbp); + return ret; +} + +/* Check whether ME is present and do basic init */ +static void intel_me_init(struct device *dev) +{ + const struct soc_intel_broadwell_pch_config *config = config_of(dev); + me_bios_path path = intel_me_path(dev); + me_bios_payload mbp_data; + int mbp_ret; + struct me_hfs hfs; + struct mei_csr csr; + + /* Do initial setup and determine the BIOS path */ + printk(BIOS_NOTICE, "ME: BIOS path: %s\n", me_bios_path_values[path]); + + if (path == ME_NORMAL_BIOS_PATH) { + /* Validate the extend register */ + intel_me_extend_valid(dev); +} + + memset(&mbp_data, 0, sizeof(mbp_data)); + + /* + * According to the ME9 BWG, BIOS is required to fetch MBP data in + * all boot flows except S3 Resume. + */ + + /* Prepare MEI MMIO interface */ + if (intel_mei_setup(dev) < 0) + return; + + /* Read ME MBP data */ + mbp_ret = intel_me_read_mbp(&mbp_data, dev); + if (mbp_ret < 0) + return; + intel_me_print_mbp(&mbp_data); + + /* Set clock enables according to devicetree */ + if (config->icc_clock_disable) + me_icc_set_clock_enables(config->icc_clock_disable); + + /* Make sure ME is in a mode that expects EOP */ + pci_read_dword_ptr(dev, &hfs, PCI_ME_HFS); + + /* Abort and leave device alone if not normal mode */ + if (hfs.fpt_bad || + hfs.working_state != ME_HFS_CWS_NORMAL || + hfs.operation_mode != ME_HFS_MODE_NORMAL) + return; + + if (mbp_ret) { + /* + * MBP Cleared wait is skipped, + * Do not expect ACK and reset when complete. + */ + + /* Send HMRFPO Lock command, no response */ + mkhi_hmrfpo_lock_noack(); + + /* Send END OF POST command, no response */ + mkhi_end_of_post_noack(); + + /* Assert reset and interrupt */ + read_host_csr(&csr); + csr.interrupt_generate = 1; + csr.reset = 1; + write_host_csr(&csr); + } else { + /* + * MBP Cleared wait was not skipped + */ + + /* Send HMRFPO LOCK command */ + mkhi_hmrfpo_lock(); + + /* Send EOP command so ME stops accepting other commands */ + mkhi_end_of_post(); + } +} + +static void intel_me_enable(struct device *dev) +{ + /* Avoid talking to the device in S3 path */ + if (acpi_is_wakeup_s3()) { + dev->enabled = 0; + pch_disable_devfn(dev); + } +} + +static struct device_operations device_ops = { + .read_resources = &pci_dev_read_resources, + .set_resources = &pci_dev_set_resources, + .enable_resources = &pci_dev_enable_resources, + .enable = &intel_me_enable, + .init = &intel_me_init, + .final = &intel_me_finalize, + .ops_pci = &pci_dev_ops_pci, +}; + +static const unsigned short pci_device_ids[] = { + 0x9c3a, /* Low Power */ + 0x9cba, /* WildcatPoint */ + 0 +}; + +static const struct pci_driver intel_me __pci_driver = { + .ops = &device_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/soc/intel/broadwell/pch/me_status.c b/src/soc/intel/broadwell/pch/me_status.c new file mode 100644 index 0000000000..fa44c7c79d --- /dev/null +++ b/src/soc/intel/broadwell/pch/me_status.c @@ -0,0 +1,325 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include + +#define ARRAY_TO_ELEMENT(__array__, __index__, __default__) \ + (((__index__) < ARRAY_SIZE((__array__))) ? \ + (__array__)[(__index__)] : \ + (__default__)) + +static inline void me_read_dword_ptr(void *ptr, int offset) +{ + u32 dword = pci_read_config32(PCH_DEV_ME, offset); + memcpy(ptr, &dword, sizeof(dword)); +} + +/* HFS1[3:0] Current Working State Values */ +static const char *me_cws_values[] = { + [ME_HFS_CWS_RESET] = "Reset", + [ME_HFS_CWS_INIT] = "Initializing", + [ME_HFS_CWS_REC] = "Recovery", + [3] = "Unknown (3)", + [4] = "Unknown (4)", + [ME_HFS_CWS_NORMAL] = "Normal", + [ME_HFS_CWS_WAIT] = "Platform Disable Wait", + [ME_HFS_CWS_TRANS] = "OP State Transition", + [ME_HFS_CWS_INVALID] = "Invalid CPU Plugged In", + [9] = "Unknown (9)", + [10] = "Unknown (10)", + [11] = "Unknown (11)", + [12] = "Unknown (12)", + [13] = "Unknown (13)", + [14] = "Unknown (14)", + [15] = "Unknown (15)", +}; + +/* HFS1[8:6] Current Operation State Values */ +static const char *me_opstate_values[] = { + [ME_HFS_STATE_PREBOOT] = "Preboot", + [ME_HFS_STATE_M0_UMA] = "M0 with UMA", + [ME_HFS_STATE_M3] = "M3 without UMA", + [ME_HFS_STATE_M0] = "M0 without UMA", + [ME_HFS_STATE_BRINGUP] = "Bring up", + [ME_HFS_STATE_ERROR] = "M0 without UMA but with error" +}; + +/* HFS[19:16] Current Operation Mode Values */ +static const char *me_opmode_values[] = { + [ME_HFS_MODE_NORMAL] = "Normal", + [ME_HFS_MODE_DEBUG] = "Debug", + [ME_HFS_MODE_DIS] = "Soft Temporary Disable", + [ME_HFS_MODE_OVER_JMPR] = "Security Override via Jumper", + [ME_HFS_MODE_OVER_MEI] = "Security Override via MEI Message" +}; + +/* HFS[15:12] Error Code Values */ +static const char *me_error_values[] = { + [ME_HFS_ERROR_NONE] = "No Error", + [ME_HFS_ERROR_UNCAT] = "Uncategorized Failure", + [ME_HFS_ERROR_IMAGE] = "Image Failure", + [ME_HFS_ERROR_DEBUG] = "Debug Failure" +}; + +/* HFS2[31:28] ME Progress Code */ +static const char *me_progress_values[] = { + [ME_HFS2_PHASE_ROM] = "ROM Phase", + [ME_HFS2_PHASE_BUP] = "BUP Phase", + [ME_HFS2_PHASE_UKERNEL] = "uKernel Phase", + [ME_HFS2_PHASE_POLICY] = "Policy Module", + [ME_HFS2_PHASE_MODULE_LOAD] = "Module Loading", + [ME_HFS2_PHASE_UNKNOWN] = "Unknown", + [ME_HFS2_PHASE_HOST_COMM] = "Host Communication" +}; + +/* HFS2[27:24] Power Management Event */ +static const char *me_pmevent_values[] = { + [ME_HFS2_PMEVENT_CLEAN_MOFF_MX_WAKE] = + "Clean Moff->Mx wake", + [ME_HFS2_PMEVENT_MOFF_MX_WAKE_ERROR] = + "Moff->Mx wake after an error", + [ME_HFS2_PMEVENT_CLEAN_GLOBAL_RESET] = + "Clean global reset", + [ME_HFS2_PMEVENT_CLEAN_GLOBAL_RESET_ERROR] = + "Global reset after an error", + [ME_HFS2_PMEVENT_CLEAN_ME_RESET] = + "Clean Intel ME reset", + [ME_HFS2_PMEVENT_ME_RESET_EXCEPTION] = + "Intel ME reset due to exception", + [ME_HFS2_PMEVENT_PSEUDO_ME_RESET] = + "Pseudo-global reset", + [ME_HFS2_PMEVENT_S0MO_SXM3] = + "S0/M0->Sx/M3", + [ME_HFS2_PMEVENT_SXM3_S0M0] = + "Sx/M3->S0/M0", + [ME_HFS2_PMEVENT_NON_PWR_CYCLE_RESET] = + "Non-power cycle reset", + [ME_HFS2_PMEVENT_PWR_CYCLE_RESET_M3] = + "Power cycle reset through M3", + [ME_HFS2_PMEVENT_PWR_CYCLE_RESET_MOFF] = + "Power cycle reset through Moff", + [ME_HFS2_PMEVENT_SXMX_SXMOFF] = + "Sx/Mx->Sx/Moff" +}; + +/* Progress Code 0 states */ +static const char *me_progress_rom_values[] = { + [ME_HFS2_STATE_ROM_BEGIN] = "BEGIN", + [ME_HFS2_STATE_ROM_DISABLE] = "DISABLE" +}; + +/* Progress Code 1 states */ +static const char *me_progress_bup_values[] = { + [ME_HFS2_STATE_BUP_INIT] = + "Initialization starts", + [ME_HFS2_STATE_BUP_DIS_HOST_WAKE] = + "Disable the host wake event", + [ME_HFS2_STATE_BUP_FLOW_DET] = + "Flow determination start process", + [ME_HFS2_STATE_BUP_VSCC_ERR] = + "Error reading/matching the VSCC table in the descriptor", + [ME_HFS2_STATE_BUP_CHECK_STRAP] = + "Check to see if straps say ME DISABLED", + [ME_HFS2_STATE_BUP_PWR_OK_TIMEOUT] = + "Timeout waiting for PWROK", + [ME_HFS2_STATE_BUP_MANUF_OVRD_STRAP] = + "Possibly handle BUP manufacturing override strap", + [ME_HFS2_STATE_BUP_M3] = + "Bringup in M3", + [ME_HFS2_STATE_BUP_M0] = + "Bringup in M0", + [ME_HFS2_STATE_BUP_FLOW_DET_ERR] = + "Flow detection error", + [ME_HFS2_STATE_BUP_M3_CLK_ERR] = + "M3 clock switching error", + [ME_HFS2_STATE_BUP_CPU_RESET_DID_TIMEOUT_MEM_MISSING] = + "Host error - CPU reset timeout, DID timeout, memory missing", + [ME_HFS2_STATE_BUP_M3_KERN_LOAD] = + "M3 kernel load", + [ME_HFS2_STATE_BUP_T32_MISSING] = + "T34 missing - cannot program ICC", + [ME_HFS2_STATE_BUP_WAIT_DID] = + "Waiting for DID BIOS message", + [ME_HFS2_STATE_BUP_WAIT_DID_FAIL] = + "Waiting for DID BIOS message failure", + [ME_HFS2_STATE_BUP_DID_NO_FAIL] = + "DID reported no error", + [ME_HFS2_STATE_BUP_ENABLE_UMA] = + "Enabling UMA", + [ME_HFS2_STATE_BUP_ENABLE_UMA_ERR] = + "Enabling UMA error", + [ME_HFS2_STATE_BUP_SEND_DID_ACK] = + "Sending DID Ack to BIOS", + [ME_HFS2_STATE_BUP_SEND_DID_ACK_ERR] = + "Sending DID Ack to BIOS error", + [ME_HFS2_STATE_BUP_M0_CLK] = + "Switching clocks in M0", + [ME_HFS2_STATE_BUP_M0_CLK_ERR] = + "Switching clocks in M0 error", + [ME_HFS2_STATE_BUP_TEMP_DIS] = + "ME in temp disable", + [ME_HFS2_STATE_BUP_M0_KERN_LOAD] = + "M0 kernel load", +}; + +/* Progress Code 3 states */ +static const char *me_progress_policy_values[] = { + [ME_HFS2_STATE_POLICY_ENTRY] = "Entry into Policy Module", + [ME_HFS2_STATE_POLICY_RCVD_S3] = "Received S3 entry", + [ME_HFS2_STATE_POLICY_RCVD_S4] = "Received S4 entry", + [ME_HFS2_STATE_POLICY_RCVD_S5] = "Received S5 entry", + [ME_HFS2_STATE_POLICY_RCVD_UPD] = "Received UPD entry", + [ME_HFS2_STATE_POLICY_RCVD_PCR] = "Received PCR entry", + [ME_HFS2_STATE_POLICY_RCVD_NPCR] = "Received NPCR entry", + [ME_HFS2_STATE_POLICY_RCVD_HOST_WAKE] = "Received host wake", + [ME_HFS2_STATE_POLICY_RCVD_AC_DC] = "Received AC<>DC switch", + [ME_HFS2_STATE_POLICY_RCVD_DID] = "Received DRAM Init Done", + [ME_HFS2_STATE_POLICY_VSCC_NOT_FOUND] = + "VSCC Data not found for flash device", + [ME_HFS2_STATE_POLICY_VSCC_INVALID] = + "VSCC Table is not valid", + [ME_HFS2_STATE_POLICY_FPB_ERR] = + "Flash Partition Boundary is outside address space", + [ME_HFS2_STATE_POLICY_DESCRIPTOR_ERR] = + "ME cannot access the chipset descriptor region", + [ME_HFS2_STATE_POLICY_VSCC_NO_MATCH] = + "Required VSCC values for flash parts do not match", +}; + +void intel_me_status(void) +{ + if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL < BIOS_DEBUG) + return; + + struct me_hfs _hfs, *hfs = &_hfs; + struct me_hfs2 _hfs2, *hfs2 = &_hfs2; + + me_read_dword_ptr(hfs, PCI_ME_HFS); + me_read_dword_ptr(hfs2, PCI_ME_HFS2); + + /* Check Current States */ + printk(BIOS_DEBUG, "ME: FW Partition Table : %s\n", + hfs->fpt_bad ? "BAD" : "OK"); + printk(BIOS_DEBUG, "ME: Bringup Loader Failure : %s\n", + hfs->ft_bup_ld_flr ? "YES" : "NO"); + printk(BIOS_DEBUG, "ME: Firmware Init Complete : %s\n", + hfs->fw_init_complete ? "YES" : "NO"); + printk(BIOS_DEBUG, "ME: Manufacturing Mode : %s\n", + hfs->mfg_mode ? "YES" : "NO"); + printk(BIOS_DEBUG, "ME: Boot Options Present : %s\n", + hfs->boot_options_present ? "YES" : "NO"); + printk(BIOS_DEBUG, "ME: Update In Progress : %s\n", + hfs->update_in_progress ? "YES" : "NO"); + printk(BIOS_DEBUG, "ME: Current Working State : %s\n", + ARRAY_TO_ELEMENT(me_cws_values, + hfs->working_state, + "Unknown (OOB)")); + printk(BIOS_DEBUG, "ME: Current Operation State : %s\n", + ARRAY_TO_ELEMENT(me_opstate_values, + hfs->operation_state, + "Unknown (OOB)")); + printk(BIOS_DEBUG, "ME: Current Operation Mode : %s\n", + ARRAY_TO_ELEMENT(me_opmode_values, + hfs->operation_mode, + "Unknown (OOB)")); + printk(BIOS_DEBUG, "ME: Error Code : %s\n", + ARRAY_TO_ELEMENT(me_error_values, + hfs->error_code, + "Unknown (OOB)")); + printk(BIOS_DEBUG, "ME: Progress Phase : %s\n", + ARRAY_TO_ELEMENT(me_progress_values, + hfs2->progress_code, + "Unknown (OOB)")); + printk(BIOS_DEBUG, "ME: Power Management Event : %s\n", + ARRAY_TO_ELEMENT(me_pmevent_values, + hfs2->current_pmevent, + "Unknown (OOB)")); + + printk(BIOS_DEBUG, "ME: Progress Phase State : "); + switch (hfs2->progress_code) { + case ME_HFS2_PHASE_ROM: /* ROM Phase */ + printk(BIOS_DEBUG, "%s", + ARRAY_TO_ELEMENT(me_progress_rom_values, + hfs2->current_state, + "Unknown (OOB)")); + break; + + case ME_HFS2_PHASE_UKERNEL: /* uKernel Phase */ + printk(BIOS_DEBUG, "0x%02x", hfs2->current_state); + break; + + case ME_HFS2_PHASE_BUP: /* Bringup Phase */ + if (ARRAY_TO_ELEMENT(me_progress_bup_values, + hfs2->current_state, NULL)) + printk(BIOS_DEBUG, "%s", + ARRAY_TO_ELEMENT(me_progress_bup_values, + hfs2->current_state, + NULL)); + else + printk(BIOS_DEBUG, "0x%02x", hfs2->current_state); + break; + + case ME_HFS2_PHASE_POLICY: /* Policy Module Phase */ + if (ARRAY_TO_ELEMENT(me_progress_policy_values, + hfs2->current_state, NULL)) + printk(BIOS_DEBUG, "%s", + ARRAY_TO_ELEMENT(me_progress_policy_values, + hfs2->current_state, + NULL)); + else + printk(BIOS_DEBUG, "0x%02x", hfs2->current_state); + break; + + case ME_HFS2_PHASE_HOST_COMM: /* Host Communication Phase */ + if (!hfs2->current_state) + printk(BIOS_DEBUG, "Host communication established"); + else + printk(BIOS_DEBUG, "0x%02x", hfs2->current_state); + break; + + default: + printk(BIOS_DEBUG, "Unknown phase: 0x%02x state: 0x%02x", + hfs2->progress_code, hfs2->current_state); + } + printk(BIOS_DEBUG, "\n"); +} + +void intel_me_hsio_version(uint16_t *version, uint16_t *checksum) +{ + int count; + u32 hsiover; + struct me_hfs hfs; + + /* Query for HSIO version, overloads H_GS and HFS */ + pci_write_config32(PCH_DEV_ME, PCI_ME_H_GS, + ME_HSIO_MESSAGE | ME_HSIO_CMD_GETHSIOVER); + + /* Must wait for ME acknowledgement */ + for (count = ME_RETRY; count > 0; --count) { + me_read_dword_ptr(&hfs, PCI_ME_HFS); + if (hfs.bios_msg_ack) + break; + udelay(ME_DELAY); + } + if (!count) { + printk(BIOS_ERR, "ERROR: ME failed to respond\n"); + return; + } + + /* HSIO version should be in HFS_5 */ + hsiover = pci_read_config32(PCH_DEV_ME, PCI_ME_HFS5); + *version = hsiover >> 16; + *checksum = hsiover & 0xffff; + + printk(BIOS_DEBUG, "ME: HSIO Version : %d (CRC 0x%04x)\n", + *version, *checksum); + + /* Reset registers to normal behavior */ + pci_write_config32(PCH_DEV_ME, PCI_ME_H_GS, + ME_HSIO_MESSAGE | ME_HSIO_CMD_GETHSIOVER); +} diff --git a/src/soc/intel/broadwell/pch/pch.c b/src/soc/intel/broadwell/pch/pch.c new file mode 100644 index 0000000000..e0c5bb0c4d --- /dev/null +++ b/src/soc/intel/broadwell/pch/pch.c @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +u8 pch_revision(void) +{ + return pci_read_config8(PCH_DEV_LPC, PCI_REVISION_ID); +} + +u16 pch_type(void) +{ + return pci_read_config16(PCH_DEV_LPC, PCI_DEVICE_ID); +} + +/* Return 1 if PCH type is WildcatPoint */ +int pch_is_wpt(void) +{ + return ((pch_type() & 0xfff0) == 0x9cc0) ? 1 : 0; +} + +/* Return 1 if PCH type is WildcatPoint ULX */ +int pch_is_wpt_ulx(void) +{ + u16 lpcid = pch_type(); + + switch (lpcid) { + case PCH_WPT_BDW_Y_SAMPLE: + case PCH_WPT_BDW_Y_PREMIUM: + case PCH_WPT_BDW_Y_BASE: + return 1; + } + + return 0; +} + +u32 pch_read_soft_strap(int id) +{ + u32 fdoc; + + fdoc = SPIBAR32(SPIBAR_FDOC); + fdoc &= ~0x00007ffc; + SPIBAR32(SPIBAR_FDOC) = fdoc; + + fdoc |= 0x00004000; + fdoc |= id * 4; + SPIBAR32(SPIBAR_FDOC) = fdoc; + + return SPIBAR32(SPIBAR_FDOD); +} + +#ifndef __SIMPLE_DEVICE__ + +/* Put device in D3Hot Power State */ +static void pch_enable_d3hot(struct device *dev) +{ + pci_or_config32(dev, PCH_PCS, PCH_PCS_PS_D3HOT); +} + +/* RCBA function disable and posting read to flush the transaction */ +static void rcba_function_disable(u32 reg, u32 bit) +{ + RCBA32_OR(reg, bit); + RCBA32(reg); +} + +/* Set bit in Function Disable register to hide this device */ +void pch_disable_devfn(struct device *dev) +{ + switch (dev->path.pci.devfn) { + case PCH_DEVFN_ADSP: /* Audio DSP */ + rcba_function_disable(FD, PCH_DISABLE_ADSPD); + break; + case PCH_DEVFN_XHCI: /* XHCI */ + rcba_function_disable(FD, PCH_DISABLE_XHCI); + break; + case PCH_DEVFN_SDMA: /* DMA */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS0, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCH_DEVFN_I2C0: /* I2C0 */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS1, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCH_DEVFN_I2C1: /* I2C1 */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS2, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCH_DEVFN_SPI0: /* SPI0 */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS3, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCH_DEVFN_SPI1: /* SPI1 */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS4, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCH_DEVFN_UART0: /* UART0 */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS5, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCH_DEVFN_UART1: /* UART1 */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS6, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCH_DEVFN_ME: /* MEI #1 */ + rcba_function_disable(FD2, PCH_DISABLE_MEI1); + break; + case PCH_DEVFN_ME_2: /* MEI #2 */ + rcba_function_disable(FD2, PCH_DISABLE_MEI2); + break; + case PCH_DEVFN_ME_IDER: /* IDE-R */ + rcba_function_disable(FD2, PCH_DISABLE_IDER); + break; + case PCH_DEVFN_ME_KT: /* KT */ + rcba_function_disable(FD2, PCH_DISABLE_KT); + break; + case PCH_DEVFN_SDIO: /* SDIO */ + pch_enable_d3hot(dev); + pch_iobp_update(SIO_IOBP_FUNCDIS7, ~0UL, SIO_IOBP_FUNCDIS_DIS); + break; + case PCH_DEVFN_GBE: /* Gigabit Ethernet */ + rcba_function_disable(BUC, PCH_DISABLE_GBE); + break; + case PCH_DEVFN_HDA: /* HD Audio Controller */ + rcba_function_disable(FD, PCH_DISABLE_HD_AUDIO); + break; + case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 0): /* PCI Express Root Port 1 */ + case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 1): /* PCI Express Root Port 2 */ + case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 2): /* PCI Express Root Port 3 */ + case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 3): /* PCI Express Root Port 4 */ + case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 4): /* PCI Express Root Port 5 */ + case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 5): /* PCI Express Root Port 6 */ + case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 6): /* PCI Express Root Port 7 */ + case PCI_DEVFN(PCH_DEV_SLOT_PCIE, 7): /* PCI Express Root Port 8 */ + rcba_function_disable(FD, + PCH_DISABLE_PCIE(PCI_FUNC(dev->path.pci.devfn))); + break; + case PCH_DEVFN_EHCI: /* EHCI #1 */ + rcba_function_disable(FD, PCH_DISABLE_EHCI1); + break; + case PCH_DEVFN_LPC: /* LPC */ + rcba_function_disable(FD, PCH_DISABLE_LPC); + break; + case PCH_DEVFN_SATA: /* SATA #1 */ + rcba_function_disable(FD, PCH_DISABLE_SATA1); + break; + case PCH_DEVFN_SMBUS: /* SMBUS */ + rcba_function_disable(FD, PCH_DISABLE_SMBUS); + break; + case PCH_DEVFN_SATA2: /* SATA #2 */ + rcba_function_disable(FD, PCH_DISABLE_SATA2); + break; + case PCH_DEVFN_THERMAL: /* Thermal Subsystem */ + rcba_function_disable(FD, PCH_DISABLE_THERMAL); + break; + } +} + +static void broadwell_pch_enable_dev(struct device *dev) +{ + u16 reg16; + + if (dev->path.type != DEVICE_PATH_PCI) + return; + + if (dev->ops && dev->ops->enable) + return; + + /* These devices need special enable/disable handling */ + switch (PCI_SLOT(dev->path.pci.devfn)) { + case PCH_DEV_SLOT_PCIE: + case PCH_DEV_SLOT_EHCI: + case PCH_DEV_SLOT_HDA: + return; + } + + 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); + + /* Disable this device if possible */ + pch_disable_devfn(dev); + } else { + /* Enable SERR */ + pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_SERR); + } +} + +struct chip_operations soc_intel_broadwell_pch_ops = { + CHIP_NAME("Intel Broadwell PCH") + .enable_dev = &broadwell_pch_enable_dev, +}; + +#endif diff --git a/src/soc/intel/broadwell/pch/pcie.c b/src/soc/intel/broadwell/pch/pcie.c new file mode 100644 index 0000000000..c98201e5ab --- /dev/null +++ b/src/soc/intel/broadwell/pch/pcie.c @@ -0,0 +1,650 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Low Power variant has 6 root ports. */ +#define MAX_NUM_ROOT_PORTS 6 + +struct root_port_config { + /* RPFN is a write-once register so keep a copy until it is written */ + u32 orig_rpfn; + u32 new_rpfn; + u32 pin_ownership; + u32 strpfusecfg1; + u32 strpfusecfg2; + u32 strpfusecfg3; + u32 b0d28f0_32c; + u32 b0d28f4_32c; + u32 b0d28f5_32c; + int coalesce; + int gbe_port; + int num_ports; + struct device *ports[MAX_NUM_ROOT_PORTS]; +}; + +static struct root_port_config rpc; + +static inline int root_port_is_first(struct device *dev) +{ + return PCI_FUNC(dev->path.pci.devfn) == 0; +} + +static inline int root_port_is_last(struct device *dev) +{ + return PCI_FUNC(dev->path.pci.devfn) == (rpc.num_ports - 1); +} + +/* Root ports are numbered 1..N in the documentation. */ +static inline int root_port_number(struct device *dev) +{ + return PCI_FUNC(dev->path.pci.devfn) + 1; +} + +static void root_port_config_update_gbe_port(void) +{ + /* Is the Gbe Port enabled? */ + if (!((rpc.strpfusecfg1 >> 19) & 1)) + return; + + switch ((rpc.strpfusecfg1 >> 16) & 0x7) { + case 0: + rpc.gbe_port = 3; + break; + case 1: + rpc.gbe_port = 4; + break; + case 2: + case 3: + case 4: + case 5: + /* Lanes 0-4 of Root Port 5. */ + rpc.gbe_port = 5; + break; + default: + printk(BIOS_DEBUG, "Invalid GbE Port Selection.\n"); + } +} + +static void pcie_iosf_port_grant_count(struct device *dev) +{ + u8 update_val; + u32 rpcd = (pci_read_config32(dev, 0xfc) >> 14) & 0x3; + + switch (rpcd) { + case 1: + case 3: + update_val = 0x02; + break; + case 2: + update_val = 0x22; + break; + default: + update_val = 0x00; + break; + } + + RCBA32(0x103C) = (RCBA32(0x103C) & (~0xff)) | update_val; +} + +static void root_port_init_config(struct device *dev) +{ + int rp; + u32 data = 0; + u8 resp, id; + + if (root_port_is_first(dev)) { + rpc.orig_rpfn = RCBA32(RPFN); + rpc.new_rpfn = rpc.orig_rpfn; + rpc.num_ports = MAX_NUM_ROOT_PORTS; + rpc.gbe_port = -1; + /* RP0 f5[3:0] = 0101b*/ + pci_update_config8(dev, 0xf5, ~0xa, 0x5); + + pcie_iosf_port_grant_count(dev); + + rpc.pin_ownership = pci_read_config32(dev, 0x410); + root_port_config_update_gbe_port(); + + pci_update_config8(dev, 0xe2, ~(3 << 4), (3 << 4)); + const struct soc_intel_broadwell_pch_config *config = config_of(dev); + rpc.coalesce = config->pcie_port_coalesce; + } + + rp = root_port_number(dev); + if (rp > rpc.num_ports) { + printk(BIOS_ERR, "Found Root Port %d, expecting %d\n", + rp, rpc.num_ports); + return; + } + + /* Read the fuse configuration and pin ownership. */ + switch (rp) { + case 1: + rpc.strpfusecfg1 = pci_read_config32(dev, 0xfc); + rpc.b0d28f0_32c = pci_read_config32(dev, 0x32c); + break; + case 5: + rpc.strpfusecfg2 = pci_read_config32(dev, 0xfc); + rpc.b0d28f4_32c = pci_read_config32(dev, 0x32c); + break; + case 6: + rpc.b0d28f5_32c = pci_read_config32(dev, 0x32c); + rpc.strpfusecfg3 = pci_read_config32(dev, 0xfc); + break; + default: + break; + } + + pci_update_config32(dev, 0x418, 0, 0x02000430); + + if (root_port_is_first(dev)) { + /* + * set RP0 PCICFG E2h[5:4] = 11b and E1h[6] = 1 + * before configuring ASPM + */ + id = 0xe0 + (u8)(RCBA32(RPFN) & 0x07); + pch_iobp_exec(0xE00000E0, IOBP_PCICFG_READ, id, &data, &resp); + data |= ((0x30 << 16) | (0x40 << 8)); + pch_iobp_exec(0xE00000E0, IOBP_PCICFG_WRITE, id, &data, &resp); + } + + /* Cache pci device. */ + rpc.ports[rp - 1] = dev; +} + +/* Update devicetree with new Root Port function number assignment */ +static void pch_pcie_device_set_func(int index, int pci_func) +{ + struct device *dev; + unsigned int new_devfn; + + dev = rpc.ports[index]; + + /* Set the new PCI function field for this Root Port. */ + rpc.new_rpfn &= ~RPFN_FNMASK(index); + rpc.new_rpfn |= RPFN_FNSET(index, pci_func); + + /* Determine the new devfn for this port */ + new_devfn = PCI_DEVFN(PCH_DEV_SLOT_PCIE, pci_func); + + if (dev && dev->path.pci.devfn != new_devfn) { + printk(BIOS_DEBUG, + "PCH: PCIe map %02x.%1x -> %02x.%1x\n", + PCI_SLOT(dev->path.pci.devfn), + PCI_FUNC(dev->path.pci.devfn), + PCI_SLOT(new_devfn), PCI_FUNC(new_devfn)); + + dev->path.pci.devfn = new_devfn; + } +} + +static void pcie_enable_clock_gating(void) +{ + int i; + int enabled_ports = 0; + int is_broadwell = !!(cpu_family_model() == BROADWELL_FAMILY_ULT); + + for (i = 0; i < rpc.num_ports; i++) { + struct device *dev; + int rp; + + dev = rpc.ports[i]; + if (!dev) + continue; + + rp = root_port_number(dev); + + if (!dev->enabled) { + /* Configure shared resource clock gating. */ + if (rp == 1 || rp == 5 || rp == 6) + pci_update_config8(dev, 0xe1, 0xc3, 0x3c); + + pci_update_config8(dev, 0xe2, ~(3 << 4), (3 << 4)); + pci_update_config32(dev, 0x420, ~(1 << 31), (1 << 31)); + + /* Per-Port CLKREQ# handling. */ + if (gpio_is_native(18 + rp - 1)) + pci_update_config32(dev, 0x420, ~0, (3 << 29)); + + /* Enable static clock gating. */ + if (rp == 1 && !rpc.ports[1]->enabled && + !rpc.ports[2]->enabled && !rpc.ports[3]->enabled) { + pci_update_config8(dev, 0xe2, ~1, 1); + pci_update_config8(dev, 0xe1, 0x7f, 0x80); + } else if (rp == 5 || rp == 6) { + pci_update_config8(dev, 0xe2, ~1, 1); + pci_update_config8(dev, 0xe1, 0x7f, 0x80); + } + continue; + } + + enabled_ports++; + + /* Enable dynamic clock gating. */ + pci_update_config8(dev, 0xe1, 0xfc, 0x03); + pci_update_config8(dev, 0xe2, ~(1 << 6), (1 << 6)); + pci_update_config8(dev, 0xe8, ~(3 << 2), (2 << 2)); + + /* Update PECR1 register. */ + pci_update_config8(dev, 0xe8, ~0, 3); + if (is_broadwell) { + pci_update_config32(dev, 0x324, ~((1 << 5) | (1 << 14)), + ((1 << 5) | (1 << 14))); + } else { + pci_update_config32(dev, 0x324, ~(1 << 5), (1 << 5)); + } + /* Per-Port CLKREQ# handling. */ + if (gpio_is_native(18 + rp - 1)) + /* + * In addition to D28Fx PCICFG 420h[30:29] = 11b, + * set 420h[17] = 0b and 420[0] = 1b for L1 SubState. + */ + pci_update_config32(dev, 0x420, ~0x20000, + (3 << 29) | 1); + + /* Configure shared resource clock gating. */ + if (rp == 1 || rp == 5 || rp == 6) + pci_update_config8(dev, 0xe1, 0xc3, 0x3c); + + /* CLKREQ# VR Idle Enable */ + RCBA32_OR(0x2b1c, (1 << (16 + i))); + } + + if (!enabled_ports) + pci_update_config8(rpc.ports[0], 0xe1, ~(1 << 6), (1 << 6)); +} + +static void root_port_commit_config(void) +{ + int i; + + /* If the first root port is disabled the coalesce ports. */ + if (!rpc.ports[0]->enabled) + rpc.coalesce = 1; + + /* Perform clock gating configuration. */ + pcie_enable_clock_gating(); + + for (i = 0; i < rpc.num_ports; i++) { + struct device *dev; + u32 reg32; + int n = 0; + + dev = rpc.ports[i]; + + if (dev == NULL) { + printk(BIOS_ERR, "Root Port %d device is NULL?\n", i+1); + continue; + } + + if (dev->enabled) + continue; + + printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev)); + + /* 8.2 Configuration of PCI Express Root Ports */ + pci_update_config32(dev, 0x338, ~(1 << 26), 1 << 26); + + do { + reg32 = pci_read_config32(dev, 0x328); + n++; + if (((reg32 & 0xff000000) == 0x01000000) || (n > 50)) + break; + udelay(100); + } while (1); + + if (n > 50) + printk(BIOS_DEBUG, "%s: Timeout waiting for 328h\n", + dev_path(dev)); + + pci_update_config32(dev, 0x408, ~(1 << 27), 1 << 27); + + /* Disable this device if possible */ + pch_disable_devfn(dev); + } + + if (rpc.coalesce) { + int current_func; + + /* For all Root Ports N enabled ports get assigned the lower + * PCI function number. The disabled ones get upper PCI + * function numbers. */ + current_func = 0; + for (i = 0; i < rpc.num_ports; i++) { + if (!rpc.ports[i]->enabled) + continue; + pch_pcie_device_set_func(i, current_func); + current_func++; + } + + /* Allocate the disabled devices' PCI function number. */ + for (i = 0; i < rpc.num_ports; i++) { + if (rpc.ports[i]->enabled) + continue; + pch_pcie_device_set_func(i, current_func); + current_func++; + } + } + + printk(BIOS_SPEW, "PCH: RPFN 0x%08x -> 0x%08x\n", + rpc.orig_rpfn, rpc.new_rpfn); + RCBA32(RPFN) = rpc.new_rpfn; +} + +static void root_port_mark_disable(struct device *dev) +{ + /* Mark device as disabled. */ + dev->enabled = 0; + /* Mark device to be hidden. */ + rpc.new_rpfn |= RPFN_HIDE(PCI_FUNC(dev->path.pci.devfn)); +} + +static void root_port_check_disable(struct device *dev) +{ + int rp; + + /* Device already disabled. */ + if (!dev->enabled) { + root_port_mark_disable(dev); + return; + } + + rp = root_port_number(dev); + + /* Is the GbE port mapped to this Root Port? */ + if (rp == rpc.gbe_port) { + root_port_mark_disable(dev); + return; + } + + /* Check Root Port Configuration. */ + switch (rp) { + case 2: + /* Root Port 2 is disabled for all lane configurations + * but config 00b (4x1 links). */ + if ((rpc.strpfusecfg1 >> 14) & 0x3) { + root_port_mark_disable(dev); + return; + } + break; + case 3: + /* Root Port 3 is disabled in config 11b (1x4 links). */ + if (((rpc.strpfusecfg1 >> 14) & 0x3) == 0x3) { + root_port_mark_disable(dev); + return; + } + break; + case 4: + /* Root Port 4 is disabled in configs 11b (1x4 links) + * and 10b (2x2 links). */ + if ((rpc.strpfusecfg1 >> 14) & 0x2) { + root_port_mark_disable(dev); + return; + } + break; + } + + /* Check Pin Ownership. */ + switch (rp) { + case 1: + /* Bit 0 is Root Port 1 ownership. */ + if ((rpc.pin_ownership & 0x1) == 0) { + root_port_mark_disable(dev); + return; + } + break; + case 2: + /* Bit 2 is Root Port 2 ownership. */ + if ((rpc.pin_ownership & 0x4) == 0) { + root_port_mark_disable(dev); + return; + } + break; + case 6: + /* Bits 7:4 are Root Port 6 pin-lane ownership. */ + if ((rpc.pin_ownership & 0xf0) == 0) { + root_port_mark_disable(dev); + return; + } + break; + } +} + +static void pcie_add_0x0202000_iobp(u32 reg) +{ + u32 reg32; + + reg32 = pch_iobp_read(reg); + reg32 += (0x2 << 16) | (0x2 << 8); + pch_iobp_write(reg, reg32); +} + +static void pch_pcie_early(struct device *dev) +{ + const struct soc_intel_broadwell_pch_config *config = config_of(dev); + int do_aspm = 0; + int rp = root_port_number(dev); + + switch (rp) { + case 1: + case 2: + case 3: + case 4: + /* + * Bits 31:28 of b0d28f0 0x32c register correspond to + * Root Ports 4:1. + */ + do_aspm = !!(rpc.b0d28f0_32c & (1 << (28 + rp - 1))); + break; + case 5: + /* + * Bit 28 of b0d28f4 0x32c register correspond to + * Root Ports 4:1. + */ + do_aspm = !!(rpc.b0d28f4_32c & (1 << 28)); + break; + case 6: + /* + * Bit 28 of b0d28f5 0x32c register correspond to + * Root Ports 4:1. + */ + do_aspm = !!(rpc.b0d28f5_32c & (1 << 28)); + break; + } + + /* Allow ASPM to be forced on in devicetree */ + if ((config->pcie_port_force_aspm & (1 << (rp - 1)))) + do_aspm = 1; + + printk(BIOS_DEBUG, "PCIe Root Port %d ASPM is %sabled\n", + rp, do_aspm ? "en" : "dis"); + + if (do_aspm) { + /* Set ASPM bits in MPC2 register. */ + pci_update_config32(dev, 0xd4, ~(0x3 << 2), (1 << 4) | (0x2 << 2)); + + /* Set unique clock exit latency in MPC register. */ + pci_update_config32(dev, 0xd8, ~(0x7 << 18), (0x7 << 18)); + + switch (rp) { + case 1: + pcie_add_0x0202000_iobp(0xe9002440); + break; + case 2: + pcie_add_0x0202000_iobp(0xe9002640); + break; + case 3: + pcie_add_0x0202000_iobp(0xe9000840); + break; + case 4: + pcie_add_0x0202000_iobp(0xe9000a40); + break; + case 5: + pcie_add_0x0202000_iobp(0xe9000c40); + pcie_add_0x0202000_iobp(0xe9000e40); + pcie_add_0x0202000_iobp(0xe9001040); + pcie_add_0x0202000_iobp(0xe9001240); + break; + case 6: + /* Update IOBP based on lane ownership. */ + if (rpc.pin_ownership & (1 << 4)) + pcie_add_0x0202000_iobp(0xea002040); + if (rpc.pin_ownership & (1 << 5)) + pcie_add_0x0202000_iobp(0xea002240); + if (rpc.pin_ownership & (1 << 6)) + pcie_add_0x0202000_iobp(0xea002440); + if (rpc.pin_ownership & (1 << 7)) + pcie_add_0x0202000_iobp(0xea002640); + break; + } + + pci_update_config32(dev, 0x338, ~(1 << 26), 0); + } + + /* Enable LTR in Root Port. Disable OBFF. */ + pci_update_config32(dev, 0x64, ~(1 << 11) & ~(3 << 18), (1 << 11)); + pci_update_config32(dev, 0x68, ~(1 << 10), (1 << 10)); + + pci_update_config32(dev, 0x318, ~(0xffff << 16), (0x1414 << 16)); + + /* Set L1 exit latency in LCAP register. */ + if (!do_aspm && (pci_read_config8(dev, 0xf5) & 0x1)) + pci_update_config32(dev, 0x4c, ~(0x7 << 15), (0x4 << 15)); + else + pci_update_config32(dev, 0x4c, ~(0x7 << 15), (0x2 << 15)); + + pci_update_config32(dev, 0x314, 0, 0x743a361b); + + /* Set Common Clock Exit Latency in MPC register. */ + pci_update_config32(dev, 0xd8, ~(0x7 << 15), (0x3 << 15)); + + pci_update_config32(dev, 0x33c, ~0x00ffffff, 0x854d74); + + /* Set Invalid Receive Range Check Enable in MPC register. */ + pci_update_config32(dev, 0xd8, ~0, (1 << 25)); + + pci_update_config8(dev, 0xf5, 0x0f, 0); + + /* Set AER Extended Cap ID to 01h and Next Cap Pointer to 200h. */ + if (CONFIG(PCIEXP_AER)) + pci_update_config32(dev, 0x100, ~(1 << 29) & ~0xfffff, + (1 << 29) | 0x10001); + else + pci_update_config32(dev, 0x100, ~(1 << 29) & ~0xfffff, + (1 << 29)); + + /* Set L1 Sub-State Cap ID to 1Eh and Next Cap Pointer to None. */ + if (CONFIG(PCIEXP_L1_SUB_STATE)) + pci_update_config32(dev, 0x200, ~0xfffff, 0x001e); + else + pci_update_config32(dev, 0x200, ~0xfffff, 0); + + pci_update_config32(dev, 0x320, ~(3 << 20) & ~(7 << 6), + (1 << 20) | (3 << 6)); + /* Enable Relaxed Order from Root Port. */ + pci_update_config32(dev, 0x320, ~(3 << 23), (3 << 23)); + + if (rp == 1 || rp == 5 || rp == 6) + pci_update_config8(dev, 0xf7, ~0xc, 0); + + /* Set EOI forwarding disable. */ + pci_update_config32(dev, 0xd4, ~0, (1 << 1)); + + /* Read and write back write-once capability registers. */ + pci_update_config32(dev, 0x34, ~0, 0); + pci_update_config32(dev, 0x40, ~0, 0); + pci_update_config32(dev, 0x80, ~0, 0); + pci_update_config32(dev, 0x90, ~0, 0); +} + +static void pch_pcie_init(struct device *dev) +{ + printk(BIOS_DEBUG, "Initializing PCH PCIe bridge.\n"); + + /* Enable SERR */ + pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_SERR); + + /* Enable Bus Master */ + pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER); + + /* Set Cache Line Size to 0x10 */ + pci_write_config8(dev, 0x0c, 0x10); + + pci_and_config16(dev, PCI_BRIDGE_CONTROL, ~PCI_BRIDGE_CTL_PARITY); + + /* Clear errors in status registers */ + pci_update_config16(dev, 0x06, ~0, 0); + pci_update_config16(dev, 0x1e, ~0, 0); +} + +static void pch_pcie_enable(struct device *dev) +{ + /* Add this device to the root port config structure. */ + root_port_init_config(dev); + + /* Check to see if this Root Port should be disabled. */ + root_port_check_disable(dev); + + /* Power Management init before enumeration */ + if (dev->enabled) + pch_pcie_early(dev); + + /* + * When processing the last PCIe root port we can now + * update the Root Port Function Number and Hide register. + */ + if (root_port_is_last(dev)) + root_port_commit_config(); +} + +static void pcie_set_L1_ss_max_latency(struct device *dev, unsigned int off) +{ + /* Set max snoop and non-snoop latency for Broadwell */ + pci_write_config32(dev, off, + PCIE_LTR_MAX_NO_SNOOP_LATENCY_3146US << 16 | + PCIE_LTR_MAX_SNOOP_LATENCY_3146US); +} + +static struct pci_operations pcie_ops = { + .set_subsystem = pci_dev_set_subsystem, + .set_L1_ss_latency = pcie_set_L1_ss_max_latency, +}; + +static struct device_operations device_ops = { + .read_resources = pci_bus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_bus_enable_resources, + .init = pch_pcie_init, + .enable = pch_pcie_enable, + .scan_bus = pciexp_scan_bridge, + .ops_pci = &pcie_ops, +}; + +static const unsigned short pcie_device_ids[] = { + /* Lynxpoint-LP */ + 0x9c10, 0x9c12, 0x9c14, 0x9c16, 0x9c18, 0x9c1a, + /* WildcatPoint */ + 0x9c90, 0x9c92, 0x9c94, 0x9c96, 0x9c98, 0x9c9a, 0x2448, + 0 +}; + +static const struct pci_driver pch_pcie __pci_driver = { + .ops = &device_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pcie_device_ids, +}; diff --git a/src/soc/intel/broadwell/pch/pmutil.c b/src/soc/intel/broadwell/pch/pmutil.c new file mode 100644 index 0000000000..e63a981456 --- /dev/null +++ b/src/soc/intel/broadwell/pch/pmutil.c @@ -0,0 +1,455 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Helper functions for dealing with power management registers + * and the differences between PCH variants. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline uint16_t get_gpiobase(void) +{ + return GPIO_BASE_ADDRESS; +} + +/* Print status bits with descriptive names */ +static void print_status_bits(u32 status, const char *bit_names[]) +{ + int i; + + if (!status) + return; + + for (i = 31; i >= 0; i--) { + if (status & (1 << i)) { + if (bit_names[i]) + printk(BIOS_DEBUG, "%s ", bit_names[i]); + else + printk(BIOS_DEBUG, "BIT%d ", i); + } + } +} + +/* Print status bits as GPIO numbers */ +static void print_gpio_status(u32 status, int start) +{ + int i; + + if (!status) + return; + + for (i = 31; i >= 0; i--) { + if (status & (1 << i)) + printk(BIOS_DEBUG, "GPIO%d ", start + i); + } +} + +/* + * PM1_CNT + */ + +/* Enable events in PM1 control register */ +void enable_pm1_control(u32 mask) +{ + u32 pm1_cnt = inl(get_pmbase() + PM1_CNT); + pm1_cnt |= mask; + outl(pm1_cnt, get_pmbase() + PM1_CNT); +} + +/* Disable events in PM1 control register */ +void disable_pm1_control(u32 mask) +{ + u32 pm1_cnt = inl(get_pmbase() + PM1_CNT); + pm1_cnt &= ~mask; + outl(pm1_cnt, get_pmbase() + PM1_CNT); +} + +/* + * PM1 + */ + +/* Clear and return PM1 status register */ +static u16 reset_pm1_status(void) +{ + u16 pm1_sts = inw(get_pmbase() + PM1_STS); + outw(pm1_sts, get_pmbase() + PM1_STS); + return pm1_sts; +} + +/* Print PM1 status bits */ +static u16 print_pm1_status(u16 pm1_sts) +{ + const char *pm1_sts_bits[] = { + [0] = "TMROF", + [4] = "BM", + [5] = "GBL", + [8] = "PWRBTN", + [10] = "RTC", + [11] = "PRBTNOR", + [14] = "PCIEXPWAK", + [15] = "WAK", + }; + + if (!pm1_sts) + return 0; + + printk(BIOS_SPEW, "PM1_STS: "); + print_status_bits(pm1_sts, pm1_sts_bits); + printk(BIOS_SPEW, "\n"); + + return pm1_sts; +} + +/* Print, clear, and return PM1 status */ +u16 clear_pm1_status(void) +{ + return print_pm1_status(reset_pm1_status()); +} + +/* Set the PM1 register to events */ +void enable_pm1(u16 events) +{ + outw(events, get_pmbase() + PM1_EN); +} + +/* + * SMI + */ + +/* Clear and return SMI status register */ +static u32 reset_smi_status(void) +{ + u32 smi_sts = inl(get_pmbase() + SMI_STS); + outl(smi_sts, get_pmbase() + SMI_STS); + return smi_sts; +} + +/* Print SMI status bits */ +static u32 print_smi_status(u32 smi_sts) +{ + const char *smi_sts_bits[] = { + [2] = "BIOS", + [3] = "LEGACY_USB", + [4] = "SLP_SMI", + [5] = "APM", + [6] = "SWSMI_TMR", + [8] = "PM1", + [9] = "GPE0", + [10] = "GPI", + [11] = "MCSMI", + [12] = "DEVMON", + [13] = "TCO", + [14] = "PERIODIC", + [15] = "SERIRQ_SMI", + [16] = "SMBUS_SMI", + [17] = "LEGACY_USB2", + [18] = "INTEL_USB2", + [20] = "PCI_EXP_SMI", + [21] = "MONITOR", + [26] = "SPI", + [27] = "GPIO_UNLOCK" + }; + + if (!smi_sts) + return 0; + + printk(BIOS_DEBUG, "SMI_STS: "); + print_status_bits(smi_sts, smi_sts_bits); + printk(BIOS_DEBUG, "\n"); + + return smi_sts; +} + +/* Print, clear, and return SMI status */ +u32 clear_smi_status(void) +{ + return print_smi_status(reset_smi_status()); +} + +/* Enable SMI event */ +void enable_smi(u32 mask) +{ + u32 smi_en = inl(get_pmbase() + SMI_EN); + smi_en |= mask; + outl(smi_en, get_pmbase() + SMI_EN); +} + +/* Disable SMI event */ +void disable_smi(u32 mask) +{ + u32 smi_en = inl(get_pmbase() + SMI_EN); + smi_en &= ~mask; + outl(smi_en, get_pmbase() + SMI_EN); +} + +/* + * ALT_GP_SMI + */ + +/* Clear GPIO SMI status and return events that are enabled and active */ +static u32 reset_alt_smi_status(void) +{ + u32 alt_sts, alt_en; + + /* Low Power variant moves this to GPIO region as dword */ + alt_sts = inl(get_gpiobase() + GPIO_ALT_GPI_SMI_STS); + outl(alt_sts, get_gpiobase() + GPIO_ALT_GPI_SMI_STS); + alt_en = inl(get_gpiobase() + GPIO_ALT_GPI_SMI_EN); + + /* Only report enabled events */ + return alt_sts & alt_en; +} + +/* Print GPIO SMI status bits */ +static u32 print_alt_smi_status(u32 alt_sts) +{ + if (!alt_sts) + return 0; + + printk(BIOS_DEBUG, "ALT_STS: "); + + /* First 16 events are GPIO 32-47 */ + print_gpio_status(alt_sts & 0xffff, 32); + + printk(BIOS_DEBUG, "\n"); + + return alt_sts; +} + +/* Print, clear, and return GPIO SMI status */ +u32 clear_alt_smi_status(void) +{ + return print_alt_smi_status(reset_alt_smi_status()); +} + +/* Enable GPIO SMI events */ +void enable_alt_smi(u32 mask) +{ + u32 alt_en; + + alt_en = inl(get_gpiobase() + GPIO_ALT_GPI_SMI_EN); + alt_en |= mask; + outl(alt_en, get_gpiobase() + GPIO_ALT_GPI_SMI_EN); +} + +/* + * TCO + */ + +/* Clear TCO status and return events that are enabled and active */ +static u32 reset_tco_status(void) +{ + u32 tcobase = get_pmbase() + 0x60; + u32 tco_sts = inl(tcobase + 0x04); + u32 tco_en = inl(get_pmbase() + 0x68); + + /* Don't clear BOOT_STS before SECOND_TO_STS */ + outl(tco_sts & ~(1 << 18), tcobase + 0x04); + + /* Clear BOOT_STS */ + if (tco_sts & (1 << 18)) + outl(tco_sts & (1 << 18), tcobase + 0x04); + + return tco_sts & tco_en; +} + +/* Print TCO status bits */ +static u32 print_tco_status(u32 tco_sts) +{ + const char *tco_sts_bits[] = { + [0] = "NMI2SMI", + [1] = "SW_TCO", + [2] = "TCO_INT", + [3] = "TIMEOUT", + [7] = "NEWCENTURY", + [8] = "BIOSWR", + [9] = "DMISCI", + [10] = "DMISMI", + [12] = "DMISERR", + [13] = "SLVSEL", + [16] = "INTRD_DET", + [17] = "SECOND_TO", + [18] = "BOOT", + [20] = "SMLINK_SLV" + }; + + if (!tco_sts) + return 0; + + printk(BIOS_DEBUG, "TCO_STS: "); + print_status_bits(tco_sts, tco_sts_bits); + printk(BIOS_DEBUG, "\n"); + + return tco_sts; +} + +/* Print, clear, and return TCO status */ +u32 clear_tco_status(void) +{ + return print_tco_status(reset_tco_status()); +} + +/* Enable TCO SCI */ +void enable_tco_sci(void) +{ + /* Clear pending events */ + outl(get_pmbase() + GPE0_STS(3), TCOSCI_STS); + + /* Enable TCO SCI events */ + enable_gpe(TCOSCI_EN); +} + +/* + * GPE0 + */ + +/* Clear a GPE0 status and return events that are enabled and active */ +static u32 reset_gpe_status(u16 sts_reg, u16 en_reg) +{ + u32 gpe0_sts = inl(get_pmbase() + sts_reg); + u32 gpe0_en = inl(get_pmbase() + en_reg); + + outl(gpe0_sts, get_pmbase() + sts_reg); + + /* Only report enabled events */ + return gpe0_sts & gpe0_en; +} + +/* Print GPE0 status bits */ +static u32 print_gpe_status(u32 gpe0_sts, const char *bit_names[]) +{ + if (!gpe0_sts) + return 0; + + printk(BIOS_DEBUG, "GPE0_STS: "); + print_status_bits(gpe0_sts, bit_names); + printk(BIOS_DEBUG, "\n"); + + return gpe0_sts; +} + +/* Print GPE0 GPIO status bits */ +static u32 print_gpe_gpio(u32 gpe0_sts, int start) +{ + if (!gpe0_sts) + return 0; + + printk(BIOS_DEBUG, "GPE0_STS: "); + print_gpio_status(gpe0_sts, start); + printk(BIOS_DEBUG, "\n"); + + return gpe0_sts; +} + +/* Clear all GPE status and return "standard" GPE event status */ +u32 clear_gpe_status(void) +{ + const char *gpe0_sts_3_bits[] = { + [1] = "HOTPLUG", + [2] = "SWGPE", + [6] = "TCO_SCI", + [7] = "SMB_WAK", + [9] = "PCI_EXP", + [10] = "BATLOW", + [11] = "PME", + [12] = "ME", + [13] = "PME_B0", + [16] = "GPIO27", + [18] = "WADT" + }; + + print_gpe_gpio(reset_gpe_status(GPE0_STS(GPE_31_0), GPE0_EN(GPE_31_0)), 0); + print_gpe_gpio(reset_gpe_status(GPE0_STS(GPE_63_32), GPE0_EN(GPE_63_32)), 32); + print_gpe_gpio(reset_gpe_status(GPE0_STS(GPE_94_64), GPE0_EN(GPE_94_64)), 64); + return print_gpe_status(reset_gpe_status(GPE0_STS(GPE_STD), GPE0_EN(GPE_STD)), + gpe0_sts_3_bits); +} + +/* Enable all requested GPE */ +void enable_all_gpe(u32 set1, u32 set2, u32 set3, u32 set4) +{ + u16 pmbase = get_pmbase(); + + outl(set1, pmbase + GPE0_EN(GPE_31_0)); + outl(set2, pmbase + GPE0_EN(GPE_63_32)); + outl(set3, pmbase + GPE0_EN(GPE_94_64)); + outl(set4, pmbase + GPE0_EN(GPE_STD)); +} + +/* Disable all GPE */ +void disable_all_gpe(void) +{ + enable_all_gpe(0, 0, 0, 0); +} + +/* Enable a standard GPE */ +void enable_gpe(u32 mask) +{ + u32 gpe0_en = inl(get_pmbase() + GPE0_EN(GPE_STD)); + gpe0_en |= mask; + outl(gpe0_en, get_pmbase() + GPE0_EN(GPE_STD)); +} + +/* Disable a standard GPE */ +void disable_gpe(u32 mask) +{ + u32 gpe0_en = inl(get_pmbase() + GPE0_EN(GPE_STD)); + gpe0_en &= ~mask; + outl(gpe0_en, get_pmbase() + GPE0_EN(GPE_STD)); +} + +int acpi_sci_irq(void) +{ + int scis = pci_read_config32(PCH_DEV_LPC, ACPI_CNTL) & SCI_IRQ_SEL; + int sci_irq = 9; + + /* Determine how SCI is routed. */ + switch (scis) { + case SCIS_IRQ9: + case SCIS_IRQ10: + case SCIS_IRQ11: + sci_irq = scis - SCIS_IRQ9 + 9; + break; + case SCIS_IRQ20: + case SCIS_IRQ21: + case SCIS_IRQ22: + case SCIS_IRQ23: + sci_irq = scis - SCIS_IRQ20 + 20; + break; + default: + printk(BIOS_DEBUG, "Invalid SCI route! Defaulting to IRQ9.\n"); + sci_irq = 9; + break; + } + + printk(BIOS_DEBUG, "SCI is IRQ%d\n", sci_irq); + return sci_irq; +} + +int platform_is_resuming(void) +{ + if (!(inw(get_pmbase() + PM1_STS) & WAK_STS)) + return 0; + + return acpi_sleep_from_pm1(inl(get_pmbase() + PM1_CNT)) == ACPI_S3; +} + +/* STM Support */ +uint16_t get_pmbase(void) +{ + return (uint16_t) ACPI_BASE_ADDRESS; +} diff --git a/src/soc/intel/broadwell/pch/power_state.c b/src/soc/intel/broadwell/pch/power_state.c new file mode 100644 index 0000000000..cb1d3e5b9c --- /dev/null +++ b/src/soc/intel/broadwell/pch/power_state.c @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct chipset_power_state power_state; + +static void migrate_power_state(int is_recovery) +{ + struct chipset_power_state *ps_cbmem; + struct chipset_power_state *ps_car; + + ps_car = &power_state; + ps_cbmem = cbmem_add(CBMEM_ID_POWER_STATE, sizeof(*ps_cbmem)); + + if (ps_cbmem == NULL) { + printk(BIOS_DEBUG, "Not adding power state to cbmem!\n"); + return; + } + memcpy(ps_cbmem, ps_car, sizeof(*ps_cbmem)); +} +ROMSTAGE_CBMEM_INIT_HOOK(migrate_power_state) + +/* Return 0, 3, or 5 to indicate the previous sleep state. */ +static int prev_sleep_state(struct chipset_power_state *ps) +{ + /* Default to S0. */ + int prev_sleep_state = ACPI_S0; + + if (ps->pm1_sts & WAK_STS) { + switch (acpi_sleep_from_pm1(ps->pm1_cnt)) { + case ACPI_S3: + if (CONFIG(HAVE_ACPI_RESUME)) + prev_sleep_state = ACPI_S3; + break; + case ACPI_S5: + prev_sleep_state = ACPI_S5; + break; + } + /* Clear SLP_TYP. */ + outl(ps->pm1_cnt & ~(SLP_TYP), ACPI_BASE_ADDRESS + PM1_CNT); + } + + if (ps->gen_pmcon3 & (PWR_FLR | SUS_PWR_FLR)) + prev_sleep_state = ACPI_S5; + + return prev_sleep_state; +} + +static void dump_power_state(struct chipset_power_state *ps) +{ + printk(BIOS_DEBUG, "PM1_STS: %04x\n", ps->pm1_sts); + printk(BIOS_DEBUG, "PM1_EN: %04x\n", ps->pm1_en); + printk(BIOS_DEBUG, "PM1_CNT: %08x\n", ps->pm1_cnt); + printk(BIOS_DEBUG, "TCO_STS: %04x %04x\n", + ps->tco1_sts, ps->tco2_sts); + + printk(BIOS_DEBUG, "GPE0_STS: %08x %08x %08x %08x\n", + ps->gpe0_sts[0], ps->gpe0_sts[1], + ps->gpe0_sts[2], ps->gpe0_sts[3]); + printk(BIOS_DEBUG, "GPE0_EN: %08x %08x %08x %08x\n", + ps->gpe0_en[0], ps->gpe0_en[1], + ps->gpe0_en[2], ps->gpe0_en[3]); + + printk(BIOS_DEBUG, "GEN_PMCON: %04x %04x %04x\n", + ps->gen_pmcon1, ps->gen_pmcon2, ps->gen_pmcon3); + + printk(BIOS_DEBUG, "Previous Sleep State: S%d\n", + ps->prev_sleep_state); +} + +/* Fill power state structure from ACPI PM registers */ +struct chipset_power_state *fill_power_state(void) +{ + struct chipset_power_state *ps = &power_state; + + ps->pm1_sts = inw(ACPI_BASE_ADDRESS + PM1_STS); + ps->pm1_en = inw(ACPI_BASE_ADDRESS + PM1_EN); + ps->pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT); + ps->tco1_sts = inw(ACPI_BASE_ADDRESS + TCO1_STS); + ps->tco2_sts = inw(ACPI_BASE_ADDRESS + TCO2_STS); + ps->gpe0_sts[0] = inl(ACPI_BASE_ADDRESS + GPE0_STS(0)); + ps->gpe0_sts[1] = inl(ACPI_BASE_ADDRESS + GPE0_STS(1)); + ps->gpe0_sts[2] = inl(ACPI_BASE_ADDRESS + GPE0_STS(2)); + ps->gpe0_sts[3] = inl(ACPI_BASE_ADDRESS + GPE0_STS(3)); + ps->gpe0_en[0] = inl(ACPI_BASE_ADDRESS + GPE0_EN(0)); + ps->gpe0_en[1] = inl(ACPI_BASE_ADDRESS + GPE0_EN(1)); + ps->gpe0_en[2] = inl(ACPI_BASE_ADDRESS + GPE0_EN(2)); + ps->gpe0_en[3] = inl(ACPI_BASE_ADDRESS + GPE0_EN(3)); + + ps->gen_pmcon1 = pci_read_config16(PCH_DEV_LPC, GEN_PMCON_1); + ps->gen_pmcon2 = pci_read_config16(PCH_DEV_LPC, GEN_PMCON_2); + ps->gen_pmcon3 = pci_read_config16(PCH_DEV_LPC, GEN_PMCON_3); + + ps->prev_sleep_state = prev_sleep_state(ps); + + dump_power_state(ps); + + return ps; +} diff --git a/src/soc/intel/broadwell/pch/sata.c b/src/soc/intel/broadwell/pch/sata.c new file mode 100644 index 0000000000..b496e53e3d --- /dev/null +++ b/src/soc/intel/broadwell/pch/sata.c @@ -0,0 +1,286 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline u32 sir_read(struct device *dev, int idx) +{ + pci_write_config32(dev, SATA_SIRI, idx); + return pci_read_config32(dev, SATA_SIRD); +} + +static inline void sir_write(struct device *dev, int idx, u32 value) +{ + pci_write_config32(dev, SATA_SIRI, idx); + pci_write_config32(dev, SATA_SIRD, value); +} + +static void sata_init(struct device *dev) +{ + const struct soc_intel_broadwell_pch_config *config = config_of(dev); + u32 reg32; + u8 *abar; + u16 reg16; + int port; + + printk(BIOS_DEBUG, "SATA: Initializing controller in AHCI mode.\n"); + + /* Enable BARs */ + pci_write_config16(dev, PCI_COMMAND, + PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO); + + /* Set Interrupt Line */ + /* Interrupt Pin is set by D31IP.PIP */ + pci_write_config8(dev, PCI_INTERRUPT_LINE, 0x0a); + + /* Set timings */ + pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE); + pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE); + + /* for AHCI, Port Enable is managed in memory mapped space */ + reg16 = pci_read_config16(dev, 0x92); + reg16 &= ~0xf; + reg16 |= 0x8000 | config->sata_port_map; + pci_write_config16(dev, 0x92, reg16); + udelay(2); + + /* Setup register 98h */ + reg32 = pci_read_config32(dev, 0x98); + reg32 &= ~((1 << 31) | (1 << 30)); + reg32 |= 1 << 23; + reg32 |= 1 << 24; /* Enable MPHY Dynamic Power Gating */ + pci_write_config32(dev, 0x98, reg32); + + /* Setup register 9Ch */ + reg16 = (1 << 5); /* BWG step 12 */ + pci_write_config16(dev, 0x9c, reg16); + + /* SATA Initialization register */ + reg32 = 0x183; + reg32 |= (config->sata_port_map ^ 0xf) << 24; + reg32 |= (config->sata_devslp_mux & 1) << 15; + pci_write_config32(dev, 0x94, reg32); + + /* Initialize AHCI memory-mapped space */ + abar = (u8 *)(pci_read_config32(dev, PCI_BASE_ADDRESS_5)); + printk(BIOS_DEBUG, "ABAR: %p\n", abar); + + /* CAP (HBA Capabilities) : enable power management */ + reg32 = read32(abar + 0x00); + reg32 |= 0x0c006000; /* set PSC+SSC+SALP+SSS */ + reg32 &= ~0x00020060; /* clear SXS+EMS+PMS */ + reg32 |= (1 << 18); /* SAM: SATA AHCI MODE ONLY */ + write32(abar + 0x00, reg32); + + /* PI (Ports implemented) */ + write32(abar + 0x0c, config->sata_port_map); + (void) read32(abar + 0x0c); /* Read back 1 */ + (void) read32(abar + 0x0c); /* Read back 2 */ + + /* CAP2 (HBA Capabilities Extended)*/ + if (config->sata_devslp_disable) { + reg32 = read32(abar + 0x24); + reg32 &= ~(1 << 3); + write32(abar + 0x24, reg32); + } else { + /* Enable DEVSLP */ + reg32 = read32(abar + 0x24); + reg32 |= (1 << 5)|(1 << 4)|(1 << 3)|(1 << 2); + write32(abar + 0x24, reg32); + + for (port = 0; port < 4; port++) { + if (!(config->sata_port_map & (1 << port))) + continue; + reg32 = read32(abar + 0x144 + (0x80 * port)); + reg32 |= (1 << 1); /* DEVSLP DSP */ + write32(abar + 0x144 + (0x80 * port), reg32); + } + } + + /* + * Static Power Gating for unused ports + */ + reg32 = RCBA32(0x3a84); + /* Port 3 and 2 disabled */ + if ((config->sata_port_map & ((1 << 3)|(1 << 2))) == 0) + reg32 |= (1 << 24) | (1 << 26); + /* Port 1 and 0 disabled */ + if ((config->sata_port_map & ((1 << 1)|(1 << 0))) == 0) + reg32 |= (1 << 20) | (1 << 18); + RCBA32(0x3a84) = reg32; + + /* Set Gen3 Transmitter settings if needed */ + if (config->sata_port0_gen3_tx) + pch_iobp_update(SATA_IOBP_SP0_SECRT88, + ~(SATA_SECRT88_VADJ_MASK << + SATA_SECRT88_VADJ_SHIFT), + (config->sata_port0_gen3_tx & + SATA_SECRT88_VADJ_MASK) + << SATA_SECRT88_VADJ_SHIFT); + + if (config->sata_port1_gen3_tx) + pch_iobp_update(SATA_IOBP_SP1_SECRT88, + ~(SATA_SECRT88_VADJ_MASK << + SATA_SECRT88_VADJ_SHIFT), + (config->sata_port1_gen3_tx & + SATA_SECRT88_VADJ_MASK) + << SATA_SECRT88_VADJ_SHIFT); + + if (config->sata_port2_gen3_tx) + pch_iobp_update(SATA_IOBP_SP2_SECRT88, + ~(SATA_SECRT88_VADJ_MASK << + SATA_SECRT88_VADJ_SHIFT), + (config->sata_port2_gen3_tx & + SATA_SECRT88_VADJ_MASK) + << SATA_SECRT88_VADJ_SHIFT); + + if (config->sata_port3_gen3_tx) + pch_iobp_update(SATA_IOBP_SP3_SECRT88, + ~(SATA_SECRT88_VADJ_MASK << + SATA_SECRT88_VADJ_SHIFT), + (config->sata_port3_gen3_tx & + SATA_SECRT88_VADJ_MASK) + << SATA_SECRT88_VADJ_SHIFT); + + /* Set Gen3 DTLE DATA / EDGE registers if needed */ + if (config->sata_port0_gen3_dtle) { + pch_iobp_update(SATA_IOBP_SP0DTLE_DATA, + ~(SATA_DTLE_MASK << SATA_DTLE_DATA_SHIFT), + (config->sata_port0_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_DATA_SHIFT); + + pch_iobp_update(SATA_IOBP_SP0DTLE_EDGE, + ~(SATA_DTLE_MASK << SATA_DTLE_EDGE_SHIFT), + (config->sata_port0_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_EDGE_SHIFT); + } + + if (config->sata_port1_gen3_dtle) { + pch_iobp_update(SATA_IOBP_SP1DTLE_DATA, + ~(SATA_DTLE_MASK << SATA_DTLE_DATA_SHIFT), + (config->sata_port1_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_DATA_SHIFT); + + pch_iobp_update(SATA_IOBP_SP1DTLE_EDGE, + ~(SATA_DTLE_MASK << SATA_DTLE_EDGE_SHIFT), + (config->sata_port1_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_EDGE_SHIFT); + } + + if (config->sata_port2_gen3_dtle) { + pch_iobp_update(SATA_IOBP_SP2DTLE_DATA, + ~(SATA_DTLE_MASK << SATA_DTLE_DATA_SHIFT), + (config->sata_port2_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_DATA_SHIFT); + + pch_iobp_update(SATA_IOBP_SP2DTLE_EDGE, + ~(SATA_DTLE_MASK << SATA_DTLE_EDGE_SHIFT), + (config->sata_port2_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_EDGE_SHIFT); + } + if (config->sata_port3_gen3_dtle) { + pch_iobp_update(SATA_IOBP_SP3DTLE_DATA, + ~(SATA_DTLE_MASK << SATA_DTLE_DATA_SHIFT), + (config->sata_port3_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_DATA_SHIFT); + + pch_iobp_update(SATA_IOBP_SP3DTLE_EDGE, + ~(SATA_DTLE_MASK << SATA_DTLE_EDGE_SHIFT), + (config->sata_port3_gen3_dtle & SATA_DTLE_MASK) + << SATA_DTLE_EDGE_SHIFT); + } + + /* + * Additional Programming Requirements for Power Optimizer + */ + + /* Step 1 */ + sir_write(dev, 0x64, 0x883c9003); + + /* Step 2: SIR 68h[15:0] = 880Ah */ + reg32 = sir_read(dev, 0x68); + reg32 &= 0xffff0000; + reg32 |= 0x880a; + sir_write(dev, 0x68, reg32); + + /* Step 3: SIR 60h[3] = 1 */ + reg32 = sir_read(dev, 0x60); + reg32 |= (1 << 3); + sir_write(dev, 0x60, reg32); + + /* Step 4: SIR 60h[0] = 1 */ + reg32 = sir_read(dev, 0x60); + reg32 |= (1 << 0); + sir_write(dev, 0x60, reg32); + + /* Step 5: SIR 60h[1] = 1 */ + reg32 = sir_read(dev, 0x60); + reg32 |= (1 << 1); + sir_write(dev, 0x60, reg32); + + /* Clock Gating */ + sir_write(dev, 0x70, 0x3f00bf1f); + sir_write(dev, 0x54, 0xcf000f0f); + sir_write(dev, 0x58, 0x00190000); + RCBA32_AND_OR(0x333c, 0xffcfffff, 0x00c00000); + + reg32 = pci_read_config32(dev, 0x300); + reg32 |= (1 << 17) | (1 << 16) | (1 << 19); + reg32 |= (1 << 31) | (1 << 30) | (1 << 29); + pci_write_config32(dev, 0x300, reg32); + + reg32 = pci_read_config32(dev, 0x98); + reg32 |= 1 << 29; + pci_write_config32(dev, 0x98, reg32); + + /* Register Lock */ + reg32 = pci_read_config32(dev, 0x9c); + reg32 |= (1 << 31); + pci_write_config32(dev, 0x9c, reg32); +} + +/* + * Set SATA controller mode early so the resource allocator can + * properly assign IO/Memory resources for the controller. + */ +static void sata_enable(struct device *dev) +{ + /* Get the chip configuration */ + const struct soc_intel_broadwell_pch_config *config = config_of(dev); + u16 map = 0x0060; + + map |= (config->sata_port_map ^ 0xf) << 8; + + pci_write_config16(dev, 0x90, map); +} + +static struct device_operations sata_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = sata_init, + .enable = sata_enable, + .ops_pci = &pci_dev_ops_pci, +}; + +static const unsigned short pci_device_ids[] = { + 0x9c03, 0x9c05, 0x9c07, 0x9c0f, /* LynxPoint-LP */ + 0x9c83, 0x9c85, 0x282a, 0x9c87, 0x282a, 0x9c8f, /* WildcatPoint */ + 0 +}; + +static const struct pci_driver pch_sata __pci_driver = { + .ops = &sata_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/soc/intel/broadwell/pch/serialio.c b/src/soc/intel/broadwell/pch/serialio.c new file mode 100644 index 0000000000..d32a27ddca --- /dev/null +++ b/src/soc/intel/broadwell/pch/serialio.c @@ -0,0 +1,295 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Set D3Hot Power State in ACPI mode */ +static void serialio_enable_d3hot(struct resource *res) +{ + u32 reg32 = read32(res2mmio(res, PCH_PCS, 0)); + reg32 |= PCH_PCS_PS_D3HOT; + write32(res2mmio(res, PCH_PCS, 0), reg32); +} + +static int serialio_uart_is_debug(struct device *dev) +{ +#if CONFIG(INTEL_PCH_UART_CONSOLE) + switch (dev->path.pci.devfn) { + case PCH_DEVFN_UART0: /* UART0 */ + return !!(CONFIG_INTEL_PCH_UART_CONSOLE_NUMBER == 0); + case PCH_DEVFN_UART1: /* UART1 */ + return !!(CONFIG_INTEL_PCH_UART_CONSOLE_NUMBER == 1); + } +#endif + return 0; +} + +/* Enable clock in PCI mode */ +static void serialio_enable_clock(struct resource *bar0) +{ + u32 reg32 = read32(res2mmio(bar0, SIO_REG_PPR_CLOCK, 0)); + reg32 |= SIO_REG_PPR_CLOCK_EN; + write32(res2mmio(bar0, SIO_REG_PPR_CLOCK, 0), reg32); +} + +/* Put Serial IO D21:F0-F6 device into desired mode. */ +static void serialio_d21_mode(int sio_index, int int_pin, int acpi_mode) +{ + u32 portctrl = SIO_IOBP_PORTCTRL_PM_CAP_PRSNT; + + /* Snoop select 1. */ + portctrl |= SIO_IOBP_PORTCTRL_SNOOP_SELECT(1); + + /* Set interrupt pin. */ + portctrl |= SIO_IOBP_PORTCTRL_INT_PIN(int_pin); + + if (acpi_mode) { + /* Enable ACPI interrupt mode. */ + portctrl |= SIO_IOBP_PORTCTRL_ACPI_IRQ_EN; + + /* Disable PCI config space. */ + portctrl |= SIO_IOBP_PORTCTRL_PCI_CONF_DIS; + } + + pch_iobp_update(SIO_IOBP_PORTCTRLX(sio_index), 0, portctrl); +} + +/* Put Serial IO D23:F0 device into desired mode. */ +static void serialio_d23_mode(int acpi_mode) +{ + u32 portctrl = 0; + + /* Snoop select 1. */ + pch_iobp_update(SIO_IOBP_PORTCTRL1, 0, + SIO_IOBP_PORTCTRL1_SNOOP_SELECT(1)); + + if (acpi_mode) { + /* Enable ACPI interrupt mode. */ + portctrl |= SIO_IOBP_PORTCTRL0_ACPI_IRQ_EN; + + /* Disable PCI config space. */ + portctrl |= SIO_IOBP_PORTCTRL0_PCI_CONF_DIS; + } + + pch_iobp_update(SIO_IOBP_PORTCTRL0, 0, portctrl); +} + +/* Enable LTR Auto Mode for D21:F1-F6. */ +static void serialio_d21_ltr(struct resource *bar0) +{ + u32 reg; + + /* 1. Program BAR0 + 808h[2] = 0b */ + reg = read32(res2mmio(bar0, SIO_REG_PPR_GEN, 0)); + reg &= ~SIO_REG_PPR_GEN_LTR_MODE_MASK; + write32(res2mmio(bar0, SIO_REG_PPR_GEN, 0), reg); + + /* 2. Program BAR0 + 804h[1:0] = 00b */ + reg = read32(res2mmio(bar0, SIO_REG_PPR_RST, 0)); + reg &= ~SIO_REG_PPR_RST_ASSERT; + write32(res2mmio(bar0, SIO_REG_PPR_RST, 0), reg); + + /* 3. Program BAR0 + 804h[1:0] = 11b */ + reg = read32(res2mmio(bar0, SIO_REG_PPR_RST, 0)); + reg |= SIO_REG_PPR_RST_ASSERT; + write32(res2mmio(bar0, SIO_REG_PPR_RST, 0), reg); + + /* 4. Program BAR0 + 814h[31:0] = 00000000h */ + write32(res2mmio(bar0, SIO_REG_AUTO_LTR, 0), 0); +} + +/* Enable LTR Auto Mode for D23:F0. */ +static void serialio_d23_ltr(struct resource *bar0) +{ + u32 reg; + + /* Program BAR0 + 1008h[2] = 1b */ + reg = read32(res2mmio(bar0, SIO_REG_SDIO_PPR_GEN, 0)); + reg |= SIO_REG_PPR_GEN_LTR_MODE_MASK; + write32(res2mmio(bar0, SIO_REG_SDIO_PPR_GEN, 0), reg); + + /* Program BAR0 + 1010h = 0x00000000 */ + write32(res2mmio(bar0, SIO_REG_SDIO_PPR_SW_LTR, 0), 0); + + /* Program BAR0 + 3Ch[30] = 1b */ + reg = read32(res2mmio(bar0, SIO_REG_SDIO_PPR_CMD12, 0)); + reg |= SIO_REG_SDIO_PPR_CMD12_B30; + write32(res2mmio(bar0, SIO_REG_SDIO_PPR_CMD12, 0), reg); +} + +/* Select I2C voltage of 1.8V or 3.3V. */ +static void serialio_i2c_voltage_sel(struct resource *bar0, u8 voltage) +{ + u32 reg32 = read32(res2mmio(bar0, SIO_REG_PPR_GEN, 0)); + reg32 &= ~SIO_REG_PPR_GEN_VOLTAGE_MASK; + reg32 |= SIO_REG_PPR_GEN_VOLTAGE(voltage); + write32(res2mmio(bar0, SIO_REG_PPR_GEN, 0), reg32); +} + +/* Init sequence to be run once, done as part of D21:F0 (SDMA) init. */ +static void serialio_init_once(int acpi_mode) +{ + if (acpi_mode) { + /* Enable ACPI IRQ for IRQ13, IRQ7, IRQ6, IRQ5 in RCBA. */ + RCBA32_OR(ACPIIRQEN, (1 << 13)|(1 << 7)|(1 << 6)|(1 << 5)); + } + + /* Program IOBP CB000154h[12,9:8,4:0] = 1001100011111b. */ + pch_iobp_update(SIO_IOBP_GPIODF, ~0x0000131f, 0x0000131f); + + /* Program IOBP CB000180h[5:0] = 111111b (undefined register) */ + pch_iobp_update(0xcb000180, ~0x0000003f, 0x0000003f); +} + +static void serialio_init(struct device *dev) +{ + const struct soc_intel_broadwell_pch_config *config = config_of(dev); + struct resource *bar0, *bar1; + int sio_index = -1; + + printk(BIOS_DEBUG, "Initializing Serial IO device\n"); + + /* Ensure memory and bus master are enabled */ + pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + + /* Find BAR0 and BAR1 */ + bar0 = find_resource(dev, PCI_BASE_ADDRESS_0); + if (!bar0) + return; + bar1 = find_resource(dev, PCI_BASE_ADDRESS_1); + if (!bar1) + return; + + if (!config->sio_acpi_mode) + serialio_enable_clock(bar0); + + switch (dev->path.pci.devfn) { + case PCH_DEVFN_SDMA: /* SDMA */ + sio_index = SIO_ID_SDMA; + serialio_init_once(config->sio_acpi_mode); + serialio_d21_mode(sio_index, SIO_PIN_INTB, + config->sio_acpi_mode); + break; + case PCH_DEVFN_I2C0: /* I2C0 */ + sio_index = SIO_ID_I2C0; + serialio_d21_ltr(bar0); + serialio_i2c_voltage_sel(bar0, config->sio_i2c0_voltage); + serialio_d21_mode(sio_index, SIO_PIN_INTC, + config->sio_acpi_mode); + break; + case PCH_DEVFN_I2C1: /* I2C1 */ + sio_index = SIO_ID_I2C1; + serialio_d21_ltr(bar0); + serialio_i2c_voltage_sel(bar0, config->sio_i2c1_voltage); + serialio_d21_mode(sio_index, SIO_PIN_INTC, + config->sio_acpi_mode); + break; + case PCH_DEVFN_SPI0: /* SPI0 */ + sio_index = SIO_ID_SPI0; + serialio_d21_ltr(bar0); + serialio_d21_mode(sio_index, SIO_PIN_INTC, + config->sio_acpi_mode); + break; + case PCH_DEVFN_SPI1: /* SPI1 */ + sio_index = SIO_ID_SPI1; + serialio_d21_ltr(bar0); + serialio_d21_mode(sio_index, SIO_PIN_INTC, + config->sio_acpi_mode); + break; + case PCH_DEVFN_UART0: /* UART0 */ + sio_index = SIO_ID_UART0; + if (!serialio_uart_is_debug(dev)) + serialio_d21_ltr(bar0); + serialio_d21_mode(sio_index, SIO_PIN_INTD, + config->sio_acpi_mode); + break; + case PCH_DEVFN_UART1: /* UART1 */ + sio_index = SIO_ID_UART1; + if (!serialio_uart_is_debug(dev)) + serialio_d21_ltr(bar0); + serialio_d21_mode(sio_index, SIO_PIN_INTD, + config->sio_acpi_mode); + break; + case PCH_DEVFN_SDIO: /* SDIO */ + sio_index = SIO_ID_SDIO; + serialio_d23_ltr(bar0); + serialio_d23_mode(config->sio_acpi_mode); + break; + default: + return; + } + + if (config->sio_acpi_mode) { + struct global_nvs *gnvs; + + /* Find ACPI NVS to update BARs */ + gnvs = acpi_get_gnvs(); + if (!gnvs) + return; + + /* Save BAR0 and BAR1 to ACPI NVS */ + gnvs->dev.bar0[sio_index] = (u32)bar0->base; + gnvs->dev.bar1[sio_index] = (u32)bar1->base; + + /* Do not enable UART if it is used as debug port */ + if (!serialio_uart_is_debug(dev)) + gnvs->dev.enable[sio_index] = 1; + + /* Put device in D3hot state via BAR1 */ + if (dev->path.pci.devfn != PCH_DEVFN_SDMA) + serialio_enable_d3hot(bar1); /* all but SDMA */ + } +} + +static void serialio_set_resources(struct device *dev) +{ + pci_dev_set_resources(dev); + +#if CONFIG(INTEL_PCH_UART_CONSOLE) + /* Update UART base address if used for debug */ + if (serialio_uart_is_debug(dev)) { + struct resource *res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (res) + uartmem_setbaseaddr(res->base); + } +#endif +} + +static struct device_operations device_ops = { + .read_resources = &pci_dev_read_resources, + .set_resources = &serialio_set_resources, + .enable_resources = &pci_dev_enable_resources, + .init = &serialio_init, + .ops_pci = &pci_dev_ops_pci, +}; + +static const unsigned short pci_device_ids[] = { + 0x9c60, 0x9ce0, /* 0:15.0 - SDMA */ + 0x9c61, 0x9ce1, /* 0:15.1 - I2C0 */ + 0x9c62, 0x9ce2, /* 0:15.2 - I2C1 */ + 0x9c65, 0x9ce5, /* 0:15.3 - SPI0 */ + 0x9c66, 0x9ce6, /* 0:15.4 - SPI1 */ + 0x9c63, 0x9ce3, /* 0:15.5 - UART0 */ + 0x9c64, 0x9ce4, /* 0:15.6 - UART1 */ + 0x9c35, 0x9cb5, /* 0:17.0 - SDIO */ + 0 +}; + +static const struct pci_driver pch_pcie __pci_driver = { + .ops = &device_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/soc/intel/broadwell/pch/smbus.c b/src/soc/intel/broadwell/pch/smbus.c new file mode 100644 index 0000000000..70655fc891 --- /dev/null +++ b/src/soc/intel/broadwell/pch/smbus.c @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void pch_smbus_init(struct device *dev) +{ + struct resource *res; + u16 reg16; + + /* Enable clock gating */ + /* FIXME: Using 32-bit ops with a 16-bit variable is a bug! These should be 16-bit! */ + reg16 = pci_read_config32(dev, 0x80); + reg16 &= ~((1 << 8)|(1 << 10)|(1 << 12)|(1 << 14)); + pci_write_config32(dev, 0x80, reg16); + + /* Set Receive Slave Address */ + res = find_resource(dev, PCI_BASE_ADDRESS_4); + if (res) + smbus_set_slave_addr(res->base, SMBUS_SLAVE_ADDR); +} + +static int lsmbus_read_byte(struct device *dev, u8 address) +{ + u16 device; + struct resource *res; + struct bus *pbus; + + device = dev->path.i2c.device; + pbus = get_pbus_smbus(dev); + res = find_resource(pbus->dev, PCI_BASE_ADDRESS_4); + + return do_smbus_read_byte(res->base, device, address); +} + +static int lsmbus_write_byte(struct device *dev, u8 address, u8 data) +{ + u16 device; + struct resource *res; + struct bus *pbus; + + device = dev->path.i2c.device; + pbus = get_pbus_smbus(dev); + res = find_resource(pbus->dev, PCI_BASE_ADDRESS_4); + return do_smbus_write_byte(res->base, device, address, data); +} + +static struct smbus_bus_operations lops_smbus_bus = { + .read_byte = lsmbus_read_byte, + .write_byte = lsmbus_write_byte, +}; + +static void smbus_read_resources(struct device *dev) +{ + struct resource *res = new_resource(dev, PCI_BASE_ADDRESS_4); + res->base = SMBUS_BASE_ADDRESS; + res->size = 32; + res->limit = res->base + res->size - 1; + res->flags = IORESOURCE_IO | IORESOURCE_FIXED | IORESOURCE_RESERVE | + IORESOURCE_STORED | IORESOURCE_ASSIGNED; + + /* Also add MMIO resource */ + res = pci_get_resource(dev, PCI_BASE_ADDRESS_0); +} + +static struct device_operations smbus_ops = { + .read_resources = smbus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .scan_bus = scan_smbus, + .init = pch_smbus_init, + .ops_smbus_bus = &lops_smbus_bus, + .ops_pci = &pci_dev_ops_pci, +}; + +static const unsigned short pci_device_ids[] = { + 0x9c22, /* LynxPoint */ + 0x9ca2, /* WildcatPoint */ + 0 +}; + +static const struct pci_driver pch_smbus __pci_driver = { + .ops = &smbus_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/soc/intel/broadwell/pch/smi.c b/src/soc/intel/broadwell/pch/smi.c new file mode 100644 index 0000000000..d7704fd8fa --- /dev/null +++ b/src/soc/intel/broadwell/pch/smi.c @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void smm_southbridge_clear_state(void) +{ + u32 smi_en; + + printk(BIOS_DEBUG, "Initializing Southbridge SMI..."); + printk(BIOS_SPEW, " ... pmbase = 0x%04x\n", ACPI_BASE_ADDRESS); + + smi_en = inl(ACPI_BASE_ADDRESS + SMI_EN); + if (smi_en & APMC_EN) { + printk(BIOS_INFO, "SMI# handler already enabled?\n"); + return; + } + + printk(BIOS_DEBUG, "\n"); + + /* Dump and clear status registers */ + clear_smi_status(); + clear_pm1_status(); + clear_tco_status(); + clear_gpe_status(); +} + +static void smm_southbridge_enable(uint16_t pm1_events) +{ + printk(BIOS_DEBUG, "Enabling SMIs.\n"); + /* Configure events */ + enable_pm1(pm1_events); + disable_gpe(PME_B0_EN); + + /* Enable SMI generation: + * - on APMC writes (io 0xb2) + * - on writes to SLP_EN (sleep states) + * - on writes to GBL_RLS (bios commands) + * No SMIs: + * - on microcontroller writes (io 0x62/0x66) + * - on TCO events + */ + enable_smi(APMC_EN | SLP_SMI_EN | GBL_SMI_EN | EOS); +} + +void global_smi_enable(void) +{ + smm_southbridge_enable(PWRBTN_EN | GBL_EN); +} diff --git a/src/soc/intel/broadwell/pch/smihandler.c b/src/soc/intel/broadwell/pch/smihandler.c new file mode 100644 index 0000000000..fd5d4522fa --- /dev/null +++ b/src/soc/intel/broadwell/pch/smihandler.c @@ -0,0 +1,569 @@ +/* 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 +#include +#include +#include +#include + +static u8 smm_initialized = 0; + +int southbridge_io_trap_handler(int smif) +{ + switch (smif) { + case 0x32: + printk(BIOS_DEBUG, "OS Init\n"); + /* gnvs->smif: + * On success, the IO Trap Handler returns 0 + * On failure, the IO Trap Handler returns a value != 0 + */ + gnvs->smif = 0; + return 1; /* IO trap handled */ + } + + /* Not handled */ + return 0; +} + +/** + * @brief Set the EOS bit + */ +void southbridge_smi_set_eos(void) +{ + enable_smi(EOS); +} + +static void busmaster_disable_on_bus(int bus) +{ + int slot, func; + unsigned int val; + unsigned char hdr; + + for (slot = 0; slot < 0x20; slot++) { + for (func = 0; func < 8; func++) { + pci_devfn_t dev = PCI_DEV(bus, slot, func); + + val = pci_read_config32(dev, PCI_VENDOR_ID); + + if (val == 0xffffffff || val == 0x00000000 || + val == 0x0000ffff || val == 0xffff0000) + continue; + + /* Disable Bus Mastering for this one device */ + pci_and_config16(dev, PCI_COMMAND, ~PCI_COMMAND_MASTER); + + /* If this is a bridge, then follow it. */ + hdr = pci_read_config8(dev, PCI_HEADER_TYPE); + hdr &= 0x7f; + if (hdr == PCI_HEADER_TYPE_BRIDGE || + hdr == PCI_HEADER_TYPE_CARDBUS) { + unsigned int buses; + buses = pci_read_config32(dev, PCI_PRIMARY_BUS); + busmaster_disable_on_bus((buses >> 8) & 0xff); + } + } + } +} + +/* + * Turn off the backlight if it is on, and wait for the specified + * backlight off delay. This will allow panel power timings to meet + * spec and prevent brief garbage on the screen when turned off + * during firmware with power button triggered SMI. + */ +static void backlight_off(void) +{ + void *reg_base; + uint32_t pp_ctrl; + uint32_t bl_off_delay; + + reg_base = (void *)((uintptr_t)pci_read_config32(SA_DEV_IGD, + PCI_BASE_ADDRESS_0) & ~0xf); + + /* Validate pointer before using it */ + if (smm_points_to_smram(reg_base, PCH_PP_OFF_DELAYS + sizeof(uint32_t))) + return; + + /* Check if backlight is enabled */ + pp_ctrl = read32(reg_base + PCH_PP_CONTROL); + if (!(pp_ctrl & EDP_BLC_ENABLE)) + return; + + /* Enable writes to this register */ + pp_ctrl &= ~PANEL_UNLOCK_MASK; + pp_ctrl |= PANEL_UNLOCK_REGS; + + /* Turn off backlight */ + pp_ctrl &= ~EDP_BLC_ENABLE; + + write32(reg_base + PCH_PP_CONTROL, pp_ctrl); + read32(reg_base + PCH_PP_CONTROL); + + /* Read backlight off delay in 100us units */ + bl_off_delay = read32(reg_base + PCH_PP_OFF_DELAYS); + bl_off_delay &= PANEL_LIGHT_OFF_DELAY_MASK; + bl_off_delay *= 100; + + /* Wait for backlight to turn off */ + udelay(bl_off_delay); + + printk(BIOS_INFO, "Backlight turned off\n"); +} + +static void southbridge_smi_sleep(void) +{ + u8 reg8; + u32 reg32; + u8 slp_typ; + u8 s5pwr = CONFIG_MAINBOARD_POWER_FAILURE_STATE; + u16 pmbase = get_pmbase(); + + /* save and recover RTC port values */ + u8 tmp70, tmp72; + tmp70 = inb(0x70); + tmp72 = inb(0x72); + get_option(&s5pwr, "power_on_after_fail"); + outb(tmp70, 0x70); + outb(tmp72, 0x72); + + /* First, disable further SMIs */ + disable_smi(SLP_SMI_EN); + + /* Figure out SLP_TYP */ + reg32 = inl(pmbase + PM1_CNT); + printk(BIOS_SPEW, "SMI#: SLP = 0x%08x\n", reg32); + slp_typ = acpi_sleep_from_pm1(reg32); + + /* Do any mainboard sleep handling */ + mainboard_smi_sleep(slp_typ); + + /* USB sleep preparations */ + usb_xhci_sleep_prepare(PCH_DEV_XHCI, slp_typ); + + /* Log S3, S4, and S5 entry */ + if (slp_typ >= ACPI_S3) + elog_gsmi_add_event_byte(ELOG_TYPE_ACPI_ENTER, slp_typ); + + /* Clear pending GPE events */ + clear_gpe_status(); + + /* Next, do the deed. + */ + + switch (slp_typ) { + case ACPI_S0: + printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n"); + break; + case ACPI_S1: + printk(BIOS_DEBUG, "SMI#: Entering S1 (Assert STPCLK#)\n"); + break; + case ACPI_S3: + printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n"); + + /* Invalidate the cache before going to S3 */ + wbinvd(); + break; + case ACPI_S4: + printk(BIOS_DEBUG, "SMI#: Entering S4 (Suspend-To-Disk)\n"); + break; + case ACPI_S5: + printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n"); + + /* Turn off backlight if needed */ + backlight_off(); + + /* Disable all GPE */ + disable_all_gpe(); + + /* Always set the flag in case CMOS was changed on runtime. For + * "KEEP", switch to "OFF" - KEEP is software emulated + */ + reg8 = pci_read_config8(PCH_DEV_LPC, GEN_PMCON_3); + if (s5pwr == MAINBOARD_POWER_ON) + reg8 &= ~1; + else + reg8 |= 1; + pci_write_config8(PCH_DEV_LPC, GEN_PMCON_3, reg8); + + /* also iterates over all bridges on bus 0 */ + busmaster_disable_on_bus(0); + break; + default: + printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n"); + break; + } + + /* + * Write back to the SLP register to cause the originally intended + * event again. We need to set BIT13 (SLP_EN) though to make the + * sleep happen. + */ + enable_pm1_control(SLP_EN); + + /* Make sure to stop executing code here for S3/S4/S5 */ + if (slp_typ >= ACPI_S3) + halt(); + + /* + * In most sleep states, the code flow of this function ends at + * the line above. However, if we entered sleep state S1 and wake + * up again, we will continue to execute code in this function. + */ + reg32 = inl(pmbase + PM1_CNT); + if (reg32 & SCI_EN) { + /* The OS is not an ACPI OS, so we set the state to S0 */ + disable_pm1_control(SLP_EN | SLP_TYP); + } +} + +/* + * Look for Synchronous IO SMI and use save state from that + * core in case we are not running on the same core that + * initiated the IO transaction. + */ +static em64t101_smm_state_save_area_t *smi_apmc_find_state_save(u8 cmd) +{ + em64t101_smm_state_save_area_t *state; + int node; + + /* Check all nodes looking for the one that issued the IO */ + for (node = 0; node < CONFIG_MAX_CPUS; node++) { + state = smm_get_save_state(node); + + /* Check for Synchronous IO (bit0 == 1) */ + if (!(state->io_misc_info & (1 << 0))) + continue; + + /* Make sure it was a write (bit4 == 0) */ + if (state->io_misc_info & (1 << 4)) + continue; + + /* Check for APMC IO port */ + if (((state->io_misc_info >> 16) & 0xff) != APM_CNT) + continue; + + /* Check AX against the requested command */ + if ((state->rax & 0xff) != cmd) + continue; + + return state; + } + + return NULL; +} + +static void southbridge_smi_gsmi(void) +{ + u32 *ret, *param; + u8 sub_command; + em64t101_smm_state_save_area_t *io_smi = + smi_apmc_find_state_save(APM_CNT_ELOG_GSMI); + + if (!io_smi) + return; + + /* Command and return value in EAX */ + ret = (u32 *)&io_smi->rax; + sub_command = (u8)(*ret >> 8); + + /* Parameter buffer in EBX */ + param = (u32 *)&io_smi->rbx; + + /* drivers/elog/gsmi.c */ + *ret = gsmi_exec(sub_command, param); +} + +static void southbridge_smi_store(void) +{ + u8 sub_command, ret; + em64t101_smm_state_save_area_t *io_smi = + smi_apmc_find_state_save(APM_CNT_SMMSTORE); + uint32_t reg_ebx; + + if (!io_smi) + return; + /* Command and return value in EAX */ + sub_command = (io_smi->rax >> 8) & 0xff; + + /* Parameter buffer in EBX */ + reg_ebx = io_smi->rbx; + + /* drivers/smmstore/smi.c */ + ret = smmstore_exec(sub_command, (void *)reg_ebx); + io_smi->rax = ret; +} + +static void southbridge_smi_apmc(void) +{ + u8 reg8; + em64t101_smm_state_save_area_t *state; + + /* Emulate B2 register as the FADT / Linux expects it */ + + reg8 = inb(APM_CNT); + switch (reg8) { + case APM_CNT_CST_CONTROL: + printk(BIOS_DEBUG, "C-state control\n"); + break; + case APM_CNT_PST_CONTROL: + printk(BIOS_DEBUG, "P-state control\n"); + break; + case APM_CNT_ACPI_DISABLE: + disable_pm1_control(SCI_EN); + printk(BIOS_DEBUG, "SMI#: ACPI disabled.\n"); + break; + case APM_CNT_ACPI_ENABLE: + enable_pm1_control(SCI_EN); + printk(BIOS_DEBUG, "SMI#: ACPI enabled.\n"); + break; + case APM_CNT_GNVS_UPDATE: + if (smm_initialized) { + printk(BIOS_DEBUG, + "SMI#: SMM structures already initialized!\n"); + return; + } + state = smi_apmc_find_state_save(reg8); + if (state) { + /* EBX in the state save contains the GNVS pointer */ + gnvs = (struct global_nvs *)((u32)state->rbx); + if (smm_points_to_smram(gnvs, sizeof(*gnvs))) { + printk(BIOS_ERR, "SMI#: ERROR: GNVS overlaps SMM\n"); + return; + } + smm_initialized = 1; + printk(BIOS_DEBUG, "SMI#: Setting GNVS to %p\n", gnvs); + } + break; + case APM_CNT_ELOG_GSMI: + if (CONFIG(ELOG_GSMI)) + southbridge_smi_gsmi(); + break; + case APM_CNT_SMMSTORE: + if (CONFIG(SMMSTORE)) + southbridge_smi_store(); + break; + } + + mainboard_smi_apmc(reg8); +} + +static void southbridge_smi_pm1(void) +{ + u16 pm1_sts = clear_pm1_status(); + + /* While OSPM is not active, poweroff immediately + * on a power button event. + */ + if (pm1_sts & PWRBTN_STS) { + /* power button pressed */ + elog_gsmi_add_event(ELOG_TYPE_POWER_BUTTON); + disable_pm1_control(-1UL); + enable_pm1_control(SLP_EN | (SLP_TYP_S5 << 10)); + } +} + +static void southbridge_smi_gpe0(void) +{ + clear_gpe_status(); +} + +static void southbridge_smi_gpi(void) +{ + mainboard_smi_gpi(clear_alt_smi_status()); + + /* Clear again after mainboard handler */ + clear_alt_smi_status(); +} + +static void southbridge_smi_mc(void) +{ + u32 reg32 = inl(get_pmbase() + SMI_EN); + + /* Are microcontroller SMIs enabled? */ + if ((reg32 & MCSMI_EN) == 0) + return; + + printk(BIOS_DEBUG, "Microcontroller SMI.\n"); +} + +static void southbridge_smi_tco(void) +{ + u32 tco_sts = clear_tco_status(); + + /* Any TCO event? */ + if (!tco_sts) + return; + + // BIOSWR + if (tco_sts & (1 << 8)) { + u8 bios_cntl = pci_read_config16(PCH_DEV_LPC, BIOS_CNTL); + + if (bios_cntl & 1) { + /* + * BWE is RW, so the SMI was caused by a + * write to BWE, not by a write to the BIOS + * + * This is the place where we notice someone + * is trying to tinker with the BIOS. We are + * trying to be nice and just ignore it. A more + * resolute answer would be to power down the + * box. + */ + printk(BIOS_DEBUG, "Switching back to RO\n"); + pci_write_config32(PCH_DEV_LPC, BIOS_CNTL, (bios_cntl & ~1)); + } /* No else for now? */ + } else if (tco_sts & (1 << 3)) { /* TIMEOUT */ + /* Handle TCO timeout */ + printk(BIOS_DEBUG, "TCO Timeout.\n"); + } +} + +static void southbridge_smi_periodic(void) +{ + u32 reg32 = inl(get_pmbase() + SMI_EN); + + /* Are periodic SMIs enabled? */ + if ((reg32 & PERIODIC_EN) == 0) + return; + + printk(BIOS_DEBUG, "Periodic SMI.\n"); +} + +static void southbridge_smi_monitor(void) +{ +#define IOTRAP(x) (trap_sts & (1 << x)) + u32 trap_sts, trap_cycle; + u32 mask = 0; + int i; + + trap_sts = RCBA32(0x1e00); // TRSR - Trap Status Register + RCBA32(0x1e00) = trap_sts; // Clear trap(s) in TRSR + + trap_cycle = RCBA32(0x1e10); + for (i = 16; i < 20; i++) { + if (trap_cycle & (1 << i)) + mask |= (0xff << ((i - 16) << 2)); + } + + /* IOTRAP(3) SMI function call */ + if (IOTRAP(3)) { + if (gnvs && gnvs->smif) + io_trap_handler(gnvs->smif); // call function smif + return; + } + + /* IOTRAP(2) currently unused + * IOTRAP(1) currently unused */ + + /* IOTRAP(0) SMIC */ + if (IOTRAP(0)) { + // It's a write + if (!(trap_cycle & (1 << 24))) { + printk(BIOS_DEBUG, "SMI1 command\n"); + (void)RCBA32(0x1e18); + // data = RCBA32(0x1e18); + // data &= mask; + // if (smi1) + // southbridge_smi_command(data); + // return; + } + // Fall through to debug + } + + printk(BIOS_DEBUG, " trapped io address = 0x%x\n", + trap_cycle & 0xfffc); + for (i = 0; i < 4; i++) + if (IOTRAP(i)) + printk(BIOS_DEBUG, " TRAP = %d\n", i); + printk(BIOS_DEBUG, " AHBE = %x\n", (trap_cycle >> 16) & 0xf); + printk(BIOS_DEBUG, " MASK = 0x%08x\n", mask); + printk(BIOS_DEBUG, " read/write: %s\n", + (trap_cycle & (1 << 24)) ? "read" : "write"); + + if (!(trap_cycle & (1 << 24))) { + /* Write Cycle */ + printk(BIOS_DEBUG, " iotrap written data = 0x%08x\n", RCBA32(0x1e18)); + } +#undef IOTRAP +} + +typedef void (*smi_handler_t)(void); + +static smi_handler_t southbridge_smi[32] = { + NULL, // [0] reserved + NULL, // [1] reserved + NULL, // [2] BIOS_STS + NULL, // [3] LEGACY_USB_STS + southbridge_smi_sleep, // [4] SLP_SMI_STS + southbridge_smi_apmc, // [5] APM_STS + NULL, // [6] SWSMI_TMR_STS + NULL, // [7] reserved + southbridge_smi_pm1, // [8] PM1_STS + southbridge_smi_gpe0, // [9] GPE0_STS + southbridge_smi_gpi, // [10] GPI_STS + southbridge_smi_mc, // [11] MCSMI_STS + NULL, // [12] DEVMON_STS + southbridge_smi_tco, // [13] TCO_STS + southbridge_smi_periodic, // [14] PERIODIC_STS + NULL, // [15] SERIRQ_SMI_STS + NULL, // [16] SMBUS_SMI_STS + NULL, // [17] LEGACY_USB2_STS + NULL, // [18] INTEL_USB2_STS + NULL, // [19] reserved + NULL, // [20] PCI_EXP_SMI_STS + southbridge_smi_monitor, // [21] MONITOR_STS + NULL, // [22] reserved + NULL, // [23] reserved + NULL, // [24] reserved + NULL, // [25] EL_SMI_STS + NULL, // [26] SPI_STS + NULL, // [27] reserved + NULL, // [28] reserved + NULL, // [29] reserved + NULL, // [30] reserved + NULL // [31] reserved +}; + +/** + * @brief Interrupt handler for SMI# + */ +void southbridge_smi_handler(void) +{ + int i; + u32 smi_sts; + + /* We need to clear the SMI status registers, or we won't see what's + * happening in the following calls. + */ + smi_sts = clear_smi_status(); + + /* Call SMI sub handler for each of the status bits */ + for (i = 0; i < 31; i++) { + if (smi_sts & (1 << i)) { + if (southbridge_smi[i]) { + southbridge_smi[i](); + } else { + printk(BIOS_DEBUG, + "SMI_STS[%d] occurred, but no " + "handler available.\n", i); + } + } + } +} diff --git a/src/soc/intel/broadwell/pch/uart.c b/src/soc/intel/broadwell/pch/uart.c new file mode 100644 index 0000000000..6b0da2d65c --- /dev/null +++ b/src/soc/intel/broadwell/pch/uart.c @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include + +const struct reg_script uart_init[] = { + /* Set MMIO BAR */ + REG_PCI_WRITE32(PCI_BASE_ADDRESS_0, CONFIG_TTYS0_BASE), + /* Enable Memory access and Bus Master */ + REG_PCI_OR32(PCI_COMMAND, PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER), + /* Initialize LTR */ + REG_MMIO_RMW32(CONFIG_TTYS0_BASE + SIO_REG_PPR_GEN, + ~SIO_REG_PPR_GEN_LTR_MODE_MASK, 0), + REG_MMIO_RMW32(CONFIG_TTYS0_BASE + SIO_REG_PPR_RST, + ~(SIO_REG_PPR_RST_ASSERT), 0), + /* Take UART out of reset */ + REG_MMIO_OR32(CONFIG_TTYS0_BASE + SIO_REG_PPR_RST, + SIO_REG_PPR_RST_ASSERT), + /* Set M and N divisor inputs and enable clock */ + REG_MMIO_WRITE32(CONFIG_TTYS0_BASE + SIO_REG_PPR_CLOCK, + SIO_REG_PPR_CLOCK_EN | SIO_REG_PPR_CLOCK_UPDATE | + (SIO_REG_PPR_CLOCK_N_DIV << 16) | + (SIO_REG_PPR_CLOCK_M_DIV << 1)), + REG_SCRIPT_END +}; + +void pch_uart_init(void) +{ + /* Program IOBP CB000154h[12,9:8,4:0] = 1001100011111b */ + u32 gpiodf = 0x131f; +#if defined(__SIMPLE_DEVICE__) + pci_devfn_t dev; +#else + struct device *dev; +#endif + + /* Put UART in byte access mode for 16550 compatibility */ + switch (CONFIG_INTEL_PCH_UART_CONSOLE_NUMBER) { + case 0: + dev = PCH_DEV_UART0; + gpiodf |= SIO_IOBP_GPIODF_UART0_BYTE_ACCESS; + break; + case 1: + dev = PCH_DEV_UART1; + gpiodf |= SIO_IOBP_GPIODF_UART1_BYTE_ACCESS; + break; + default: + return; + } + + /* Program IOBP GPIODF */ + pch_iobp_update(SIO_IOBP_GPIODF, ~gpiodf, gpiodf); + + /* Program IOBP CB000180h[5:0] = 111111b (undefined register) */ + pch_iobp_update(0xcb000180, ~0x0000003f, 0x0000003f); + + /* Initialize chipset uart interface */ + reg_script_run_on_dev(dev, uart_init); + + /* + * Perform standard UART initialization + * Divisor 1 is 115200 BAUD + */ + uart8250_mem_init(CONFIG_TTYS0_BASE, 1); +} diff --git a/src/soc/intel/broadwell/pch/usb_debug.c b/src/soc/intel/broadwell/pch/usb_debug.c new file mode 100644 index 0000000000..0fda336d14 --- /dev/null +++ b/src/soc/intel/broadwell/pch/usb_debug.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +// Use simple device model for this file even in ramstage +#define __SIMPLE_DEVICE__ + +#include +#include + +pci_devfn_t pci_ehci_dbg_dev(unsigned int hcd_idx) +{ + return PCI_DEV(0, 0x1d, 0); +} + +void pci_ehci_dbg_set_port(pci_devfn_t dev, unsigned int port) +{ + /* Hardcoded to physical port 1 */ +} diff --git a/src/soc/intel/broadwell/pch/xhci.c b/src/soc/intel/broadwell/pch/xhci.c new file mode 100644 index 0000000000..baaf5ba6e6 --- /dev/null +++ b/src/soc/intel/broadwell/pch/xhci.c @@ -0,0 +1,219 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __SIMPLE_DEVICE__ +static u8 *usb_xhci_mem_base(pci_devfn_t dev) +{ + u32 mem_base = pci_read_config32(dev, PCI_BASE_ADDRESS_0); + + /* Check if the controller is disabled or not present */ + if (mem_base == 0 || mem_base == 0xffffffff) + return 0; + + return (u8 *)(mem_base & ~0xf); +} + +static int usb_xhci_port_count_usb3(pci_devfn_t dev) +{ + /* PCH-LP has 4 SS ports */ + return 4; +} + +static void usb_xhci_reset_status_usb3(u8 *mem_base, int port) +{ + u8 *portsc = mem_base + XHCI_USB3_PORTSC(port); + u32 status = read32(portsc); + /* Do not set Port Enabled/Disabled field */ + status &= ~XHCI_USB3_PORTSC_PED; + /* Clear all change status bits */ + status |= XHCI_USB3_PORTSC_CHST; + write32(portsc, status); +} + +static void usb_xhci_reset_port_usb3(u8 *mem_base, int port) +{ + u8 *portsc = mem_base + XHCI_USB3_PORTSC(port); + write32(portsc, read32(portsc) | XHCI_USB3_PORTSC_WPR); +} + +#define XHCI_RESET_DELAY_US 1000 /* 1ms */ +#define XHCI_RESET_TIMEOUT 100 /* 100ms */ + +/* + * 1) Wait until port is done polling + * 2) If port is disconnected + * a) Issue warm port reset + * b) Poll for warm reset complete + * c) Write 1 to port change status bits + */ +static void usb_xhci_reset_usb3(pci_devfn_t dev, int all) +{ + u32 status, port_disabled; + int timeout, port; + int port_count = usb_xhci_port_count_usb3(dev); + u8 *mem_base = usb_xhci_mem_base(dev); + + if (!mem_base || !port_count) + return; + + /* Get mask of disabled ports */ + port_disabled = pci_read_config32(dev, XHCI_USB3PDO); + + /* Wait until all enabled ports are done polling */ + for (timeout = XHCI_RESET_TIMEOUT; timeout; timeout--) { + int complete = 1; + for (port = 0; port < port_count; port++) { + /* Skip disabled ports */ + if (port_disabled & (1 << port)) + continue; + /* Read port link status field */ + status = read32(mem_base + XHCI_USB3_PORTSC(port)); + status &= XHCI_USB3_PORTSC_PLS; + if (status == XHCI_PLSR_POLLING) + complete = 0; + } + /* Exit if all ports not polling */ + if (complete) + break; + udelay(XHCI_RESET_DELAY_US); + } + + /* Reset all requested ports */ + for (port = 0; port < port_count; port++) { + u8 *portsc = mem_base + XHCI_USB3_PORTSC(port); + /* Skip disabled ports */ + if (port_disabled & (1 << port)) + continue; + status = read32(portsc) & XHCI_USB3_PORTSC_PLS; + /* Reset all or only disconnected ports */ + if (all || (status == XHCI_PLSR_RXDETECT || + status == XHCI_PLSR_POLLING)) + usb_xhci_reset_port_usb3(mem_base, port); + else + port_disabled |= 1 << port; + } + + /* Wait for warm reset complete on all reset ports */ + for (timeout = XHCI_RESET_TIMEOUT; timeout; timeout--) { + int complete = 1; + for (port = 0; port < port_count; port++) { + /* Only check ports that were reset */ + if (port_disabled & (1 << port)) + continue; + /* Check if warm reset is complete */ + status = read32(mem_base + XHCI_USB3_PORTSC(port)); + if (!(status & XHCI_USB3_PORTSC_WRC)) + complete = 0; + } + /* Check for warm reset complete in any port */ + if (complete) + break; + udelay(XHCI_RESET_DELAY_US); + } + + /* Clear port change status bits */ + for (port = 0; port < port_count; port++) + usb_xhci_reset_status_usb3(mem_base, port); +} + +/* Handler for XHCI controller on entry to S3/S4/S5 */ +void usb_xhci_sleep_prepare(pci_devfn_t dev, u8 slp_typ) +{ + u32 reg32; + u8 *mem_base = usb_xhci_mem_base(dev); + u8 is_broadwell = !!(cpu_family_model() == BROADWELL_FAMILY_ULT); + + if (!mem_base || slp_typ < ACPI_S3) + return; + + /* Set D0 state */ + pci_update_config16(dev, XHCI_PWR_CTL_STS, ~XHCI_PWR_CTL_SET_MASK, XHCI_PWR_CTL_SET_D0); + + if (!is_broadwell) { + /* This WA is only for lpt */ + + /* Clear PCI 0xB0[14:13] */ + pci_and_config32(dev, 0xb0, ~((1 << 14) | (1 << 13))); + + /* Clear MMIO 0x816c[14,2] */ + reg32 = read32(mem_base + 0x816c); + reg32 &= ~((1 << 14) | (1 << 2)); + write32(mem_base + 0x816c, reg32); + + /* Reset disconnected USB3 ports */ + usb_xhci_reset_usb3(dev, 0); + + /* Set MMIO 0x80e0[15] */ + reg32 = read32(mem_base + 0x80e0); + reg32 |= (1 << 15); + write32(mem_base + 0x80e0, reg32); + } else { + /* + * Clear port change status bits. Clearing CSC alone seemed to + * fix wakeup from S3 if entering USB compliance state even if + * bit wasn't set on the port. + */ + int port; + for (port = 0; port < usb_xhci_port_count_usb3(dev); port++) + usb_xhci_reset_status_usb3(mem_base, port); + } + + reg32 = read32(mem_base + 0x8154); + reg32 &= ~(1 << 31); + write32(mem_base + 0x8154, reg32); + + /* Set D3Hot state and enable PME */ + pci_or_config16(dev, XHCI_PWR_CTL_STS, XHCI_PWR_CTL_SET_D3); + pci_or_config16(dev, XHCI_PWR_CTL_STS, XHCI_PWR_CTL_STATUS_PME); + pci_or_config16(dev, XHCI_PWR_CTL_STS, XHCI_PWR_CTL_ENABLE_PME); +} +#else /* !__SIMPLE_DEVICE__ */ + +static void xhci_init(struct device *dev) +{ + struct resource *res = find_resource(dev, PCI_BASE_ADDRESS_0); + u16 reg16; + u32 reg32; + + /* Ensure controller is in D0 state */ + reg16 = pci_read_config16(dev, XHCI_PWR_CTL_STS); + reg16 &= ~XHCI_PWR_CTL_SET_MASK; + reg16 |= XHCI_PWR_CTL_SET_D0; + pci_write_config16(dev, XHCI_PWR_CTL_STS, reg16); + + /* Disable Compliance Mode Entry */ + reg32 = read32(res2mmio(res, 0x80ec, 0)); + reg32 |= (1 << 0); + write32(res2mmio(res, 0x80ec, 0), reg32); +} + +static struct device_operations usb_xhci_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .ops_pci = &pci_dev_ops_pci, + .init = xhci_init, +}; + +static const unsigned short pci_device_ids[] = { + 0x9c31, /* LynxPoint-LP */ + 0x9cb1, /* WildcatPoint */ + 0 +}; + +static const struct pci_driver pch_usb_xhci __pci_driver = { + .ops = &usb_xhci_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; +#endif /* !__SIMPLE_DEVICE__ */ diff --git a/src/soc/intel/broadwell/pcie.c b/src/soc/intel/broadwell/pcie.c deleted file mode 100644 index c98201e5ab..0000000000 --- a/src/soc/intel/broadwell/pcie.c +++ /dev/null @@ -1,650 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Low Power variant has 6 root ports. */ -#define MAX_NUM_ROOT_PORTS 6 - -struct root_port_config { - /* RPFN is a write-once register so keep a copy until it is written */ - u32 orig_rpfn; - u32 new_rpfn; - u32 pin_ownership; - u32 strpfusecfg1; - u32 strpfusecfg2; - u32 strpfusecfg3; - u32 b0d28f0_32c; - u32 b0d28f4_32c; - u32 b0d28f5_32c; - int coalesce; - int gbe_port; - int num_ports; - struct device *ports[MAX_NUM_ROOT_PORTS]; -}; - -static struct root_port_config rpc; - -static inline int root_port_is_first(struct device *dev) -{ - return PCI_FUNC(dev->path.pci.devfn) == 0; -} - -static inline int root_port_is_last(struct device *dev) -{ - return PCI_FUNC(dev->path.pci.devfn) == (rpc.num_ports - 1); -} - -/* Root ports are numbered 1..N in the documentation. */ -static inline int root_port_number(struct device *dev) -{ - return PCI_FUNC(dev->path.pci.devfn) + 1; -} - -static void root_port_config_update_gbe_port(void) -{ - /* Is the Gbe Port enabled? */ - if (!((rpc.strpfusecfg1 >> 19) & 1)) - return; - - switch ((rpc.strpfusecfg1 >> 16) & 0x7) { - case 0: - rpc.gbe_port = 3; - break; - case 1: - rpc.gbe_port = 4; - break; - case 2: - case 3: - case 4: - case 5: - /* Lanes 0-4 of Root Port 5. */ - rpc.gbe_port = 5; - break; - default: - printk(BIOS_DEBUG, "Invalid GbE Port Selection.\n"); - } -} - -static void pcie_iosf_port_grant_count(struct device *dev) -{ - u8 update_val; - u32 rpcd = (pci_read_config32(dev, 0xfc) >> 14) & 0x3; - - switch (rpcd) { - case 1: - case 3: - update_val = 0x02; - break; - case 2: - update_val = 0x22; - break; - default: - update_val = 0x00; - break; - } - - RCBA32(0x103C) = (RCBA32(0x103C) & (~0xff)) | update_val; -} - -static void root_port_init_config(struct device *dev) -{ - int rp; - u32 data = 0; - u8 resp, id; - - if (root_port_is_first(dev)) { - rpc.orig_rpfn = RCBA32(RPFN); - rpc.new_rpfn = rpc.orig_rpfn; - rpc.num_ports = MAX_NUM_ROOT_PORTS; - rpc.gbe_port = -1; - /* RP0 f5[3:0] = 0101b*/ - pci_update_config8(dev, 0xf5, ~0xa, 0x5); - - pcie_iosf_port_grant_count(dev); - - rpc.pin_ownership = pci_read_config32(dev, 0x410); - root_port_config_update_gbe_port(); - - pci_update_config8(dev, 0xe2, ~(3 << 4), (3 << 4)); - const struct soc_intel_broadwell_pch_config *config = config_of(dev); - rpc.coalesce = config->pcie_port_coalesce; - } - - rp = root_port_number(dev); - if (rp > rpc.num_ports) { - printk(BIOS_ERR, "Found Root Port %d, expecting %d\n", - rp, rpc.num_ports); - return; - } - - /* Read the fuse configuration and pin ownership. */ - switch (rp) { - case 1: - rpc.strpfusecfg1 = pci_read_config32(dev, 0xfc); - rpc.b0d28f0_32c = pci_read_config32(dev, 0x32c); - break; - case 5: - rpc.strpfusecfg2 = pci_read_config32(dev, 0xfc); - rpc.b0d28f4_32c = pci_read_config32(dev, 0x32c); - break; - case 6: - rpc.b0d28f5_32c = pci_read_config32(dev, 0x32c); - rpc.strpfusecfg3 = pci_read_config32(dev, 0xfc); - break; - default: - break; - } - - pci_update_config32(dev, 0x418, 0, 0x02000430); - - if (root_port_is_first(dev)) { - /* - * set RP0 PCICFG E2h[5:4] = 11b and E1h[6] = 1 - * before configuring ASPM - */ - id = 0xe0 + (u8)(RCBA32(RPFN) & 0x07); - pch_iobp_exec(0xE00000E0, IOBP_PCICFG_READ, id, &data, &resp); - data |= ((0x30 << 16) | (0x40 << 8)); - pch_iobp_exec(0xE00000E0, IOBP_PCICFG_WRITE, id, &data, &resp); - } - - /* Cache pci device. */ - rpc.ports[rp - 1] = dev; -} - -/* Update devicetree with new Root Port function number assignment */ -static void pch_pcie_device_set_func(int index, int pci_func) -{ - struct device *dev; - unsigned int new_devfn; - - dev = rpc.ports[index]; - - /* Set the new PCI function field for this Root Port. */ - rpc.new_rpfn &= ~RPFN_FNMASK(index); - rpc.new_rpfn |= RPFN_FNSET(index, pci_func); - - /* Determine the new devfn for this port */ - new_devfn = PCI_DEVFN(PCH_DEV_SLOT_PCIE, pci_func); - - if (dev && dev->path.pci.devfn != new_devfn) { - printk(BIOS_DEBUG, - "PCH: PCIe map %02x.%1x -> %02x.%1x\n", - PCI_SLOT(dev->path.pci.devfn), - PCI_FUNC(dev->path.pci.devfn), - PCI_SLOT(new_devfn), PCI_FUNC(new_devfn)); - - dev->path.pci.devfn = new_devfn; - } -} - -static void pcie_enable_clock_gating(void) -{ - int i; - int enabled_ports = 0; - int is_broadwell = !!(cpu_family_model() == BROADWELL_FAMILY_ULT); - - for (i = 0; i < rpc.num_ports; i++) { - struct device *dev; - int rp; - - dev = rpc.ports[i]; - if (!dev) - continue; - - rp = root_port_number(dev); - - if (!dev->enabled) { - /* Configure shared resource clock gating. */ - if (rp == 1 || rp == 5 || rp == 6) - pci_update_config8(dev, 0xe1, 0xc3, 0x3c); - - pci_update_config8(dev, 0xe2, ~(3 << 4), (3 << 4)); - pci_update_config32(dev, 0x420, ~(1 << 31), (1 << 31)); - - /* Per-Port CLKREQ# handling. */ - if (gpio_is_native(18 + rp - 1)) - pci_update_config32(dev, 0x420, ~0, (3 << 29)); - - /* Enable static clock gating. */ - if (rp == 1 && !rpc.ports[1]->enabled && - !rpc.ports[2]->enabled && !rpc.ports[3]->enabled) { - pci_update_config8(dev, 0xe2, ~1, 1); - pci_update_config8(dev, 0xe1, 0x7f, 0x80); - } else if (rp == 5 || rp == 6) { - pci_update_config8(dev, 0xe2, ~1, 1); - pci_update_config8(dev, 0xe1, 0x7f, 0x80); - } - continue; - } - - enabled_ports++; - - /* Enable dynamic clock gating. */ - pci_update_config8(dev, 0xe1, 0xfc, 0x03); - pci_update_config8(dev, 0xe2, ~(1 << 6), (1 << 6)); - pci_update_config8(dev, 0xe8, ~(3 << 2), (2 << 2)); - - /* Update PECR1 register. */ - pci_update_config8(dev, 0xe8, ~0, 3); - if (is_broadwell) { - pci_update_config32(dev, 0x324, ~((1 << 5) | (1 << 14)), - ((1 << 5) | (1 << 14))); - } else { - pci_update_config32(dev, 0x324, ~(1 << 5), (1 << 5)); - } - /* Per-Port CLKREQ# handling. */ - if (gpio_is_native(18 + rp - 1)) - /* - * In addition to D28Fx PCICFG 420h[30:29] = 11b, - * set 420h[17] = 0b and 420[0] = 1b for L1 SubState. - */ - pci_update_config32(dev, 0x420, ~0x20000, - (3 << 29) | 1); - - /* Configure shared resource clock gating. */ - if (rp == 1 || rp == 5 || rp == 6) - pci_update_config8(dev, 0xe1, 0xc3, 0x3c); - - /* CLKREQ# VR Idle Enable */ - RCBA32_OR(0x2b1c, (1 << (16 + i))); - } - - if (!enabled_ports) - pci_update_config8(rpc.ports[0], 0xe1, ~(1 << 6), (1 << 6)); -} - -static void root_port_commit_config(void) -{ - int i; - - /* If the first root port is disabled the coalesce ports. */ - if (!rpc.ports[0]->enabled) - rpc.coalesce = 1; - - /* Perform clock gating configuration. */ - pcie_enable_clock_gating(); - - for (i = 0; i < rpc.num_ports; i++) { - struct device *dev; - u32 reg32; - int n = 0; - - dev = rpc.ports[i]; - - if (dev == NULL) { - printk(BIOS_ERR, "Root Port %d device is NULL?\n", i+1); - continue; - } - - if (dev->enabled) - continue; - - printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev)); - - /* 8.2 Configuration of PCI Express Root Ports */ - pci_update_config32(dev, 0x338, ~(1 << 26), 1 << 26); - - do { - reg32 = pci_read_config32(dev, 0x328); - n++; - if (((reg32 & 0xff000000) == 0x01000000) || (n > 50)) - break; - udelay(100); - } while (1); - - if (n > 50) - printk(BIOS_DEBUG, "%s: Timeout waiting for 328h\n", - dev_path(dev)); - - pci_update_config32(dev, 0x408, ~(1 << 27), 1 << 27); - - /* Disable this device if possible */ - pch_disable_devfn(dev); - } - - if (rpc.coalesce) { - int current_func; - - /* For all Root Ports N enabled ports get assigned the lower - * PCI function number. The disabled ones get upper PCI - * function numbers. */ - current_func = 0; - for (i = 0; i < rpc.num_ports; i++) { - if (!rpc.ports[i]->enabled) - continue; - pch_pcie_device_set_func(i, current_func); - current_func++; - } - - /* Allocate the disabled devices' PCI function number. */ - for (i = 0; i < rpc.num_ports; i++) { - if (rpc.ports[i]->enabled) - continue; - pch_pcie_device_set_func(i, current_func); - current_func++; - } - } - - printk(BIOS_SPEW, "PCH: RPFN 0x%08x -> 0x%08x\n", - rpc.orig_rpfn, rpc.new_rpfn); - RCBA32(RPFN) = rpc.new_rpfn; -} - -static void root_port_mark_disable(struct device *dev) -{ - /* Mark device as disabled. */ - dev->enabled = 0; - /* Mark device to be hidden. */ - rpc.new_rpfn |= RPFN_HIDE(PCI_FUNC(dev->path.pci.devfn)); -} - -static void root_port_check_disable(struct device *dev) -{ - int rp; - - /* Device already disabled. */ - if (!dev->enabled) { - root_port_mark_disable(dev); - return; - } - - rp = root_port_number(dev); - - /* Is the GbE port mapped to this Root Port? */ - if (rp == rpc.gbe_port) { - root_port_mark_disable(dev); - return; - } - - /* Check Root Port Configuration. */ - switch (rp) { - case 2: - /* Root Port 2 is disabled for all lane configurations - * but config 00b (4x1 links). */ - if ((rpc.strpfusecfg1 >> 14) & 0x3) { - root_port_mark_disable(dev); - return; - } - break; - case 3: - /* Root Port 3 is disabled in config 11b (1x4 links). */ - if (((rpc.strpfusecfg1 >> 14) & 0x3) == 0x3) { - root_port_mark_disable(dev); - return; - } - break; - case 4: - /* Root Port 4 is disabled in configs 11b (1x4 links) - * and 10b (2x2 links). */ - if ((rpc.strpfusecfg1 >> 14) & 0x2) { - root_port_mark_disable(dev); - return; - } - break; - } - - /* Check Pin Ownership. */ - switch (rp) { - case 1: - /* Bit 0 is Root Port 1 ownership. */ - if ((rpc.pin_ownership & 0x1) == 0) { - root_port_mark_disable(dev); - return; - } - break; - case 2: - /* Bit 2 is Root Port 2 ownership. */ - if ((rpc.pin_ownership & 0x4) == 0) { - root_port_mark_disable(dev); - return; - } - break; - case 6: - /* Bits 7:4 are Root Port 6 pin-lane ownership. */ - if ((rpc.pin_ownership & 0xf0) == 0) { - root_port_mark_disable(dev); - return; - } - break; - } -} - -static void pcie_add_0x0202000_iobp(u32 reg) -{ - u32 reg32; - - reg32 = pch_iobp_read(reg); - reg32 += (0x2 << 16) | (0x2 << 8); - pch_iobp_write(reg, reg32); -} - -static void pch_pcie_early(struct device *dev) -{ - const struct soc_intel_broadwell_pch_config *config = config_of(dev); - int do_aspm = 0; - int rp = root_port_number(dev); - - switch (rp) { - case 1: - case 2: - case 3: - case 4: - /* - * Bits 31:28 of b0d28f0 0x32c register correspond to - * Root Ports 4:1. - */ - do_aspm = !!(rpc.b0d28f0_32c & (1 << (28 + rp - 1))); - break; - case 5: - /* - * Bit 28 of b0d28f4 0x32c register correspond to - * Root Ports 4:1. - */ - do_aspm = !!(rpc.b0d28f4_32c & (1 << 28)); - break; - case 6: - /* - * Bit 28 of b0d28f5 0x32c register correspond to - * Root Ports 4:1. - */ - do_aspm = !!(rpc.b0d28f5_32c & (1 << 28)); - break; - } - - /* Allow ASPM to be forced on in devicetree */ - if ((config->pcie_port_force_aspm & (1 << (rp - 1)))) - do_aspm = 1; - - printk(BIOS_DEBUG, "PCIe Root Port %d ASPM is %sabled\n", - rp, do_aspm ? "en" : "dis"); - - if (do_aspm) { - /* Set ASPM bits in MPC2 register. */ - pci_update_config32(dev, 0xd4, ~(0x3 << 2), (1 << 4) | (0x2 << 2)); - - /* Set unique clock exit latency in MPC register. */ - pci_update_config32(dev, 0xd8, ~(0x7 << 18), (0x7 << 18)); - - switch (rp) { - case 1: - pcie_add_0x0202000_iobp(0xe9002440); - break; - case 2: - pcie_add_0x0202000_iobp(0xe9002640); - break; - case 3: - pcie_add_0x0202000_iobp(0xe9000840); - break; - case 4: - pcie_add_0x0202000_iobp(0xe9000a40); - break; - case 5: - pcie_add_0x0202000_iobp(0xe9000c40); - pcie_add_0x0202000_iobp(0xe9000e40); - pcie_add_0x0202000_iobp(0xe9001040); - pcie_add_0x0202000_iobp(0xe9001240); - break; - case 6: - /* Update IOBP based on lane ownership. */ - if (rpc.pin_ownership & (1 << 4)) - pcie_add_0x0202000_iobp(0xea002040); - if (rpc.pin_ownership & (1 << 5)) - pcie_add_0x0202000_iobp(0xea002240); - if (rpc.pin_ownership & (1 << 6)) - pcie_add_0x0202000_iobp(0xea002440); - if (rpc.pin_ownership & (1 << 7)) - pcie_add_0x0202000_iobp(0xea002640); - break; - } - - pci_update_config32(dev, 0x338, ~(1 << 26), 0); - } - - /* Enable LTR in Root Port. Disable OBFF. */ - pci_update_config32(dev, 0x64, ~(1 << 11) & ~(3 << 18), (1 << 11)); - pci_update_config32(dev, 0x68, ~(1 << 10), (1 << 10)); - - pci_update_config32(dev, 0x318, ~(0xffff << 16), (0x1414 << 16)); - - /* Set L1 exit latency in LCAP register. */ - if (!do_aspm && (pci_read_config8(dev, 0xf5) & 0x1)) - pci_update_config32(dev, 0x4c, ~(0x7 << 15), (0x4 << 15)); - else - pci_update_config32(dev, 0x4c, ~(0x7 << 15), (0x2 << 15)); - - pci_update_config32(dev, 0x314, 0, 0x743a361b); - - /* Set Common Clock Exit Latency in MPC register. */ - pci_update_config32(dev, 0xd8, ~(0x7 << 15), (0x3 << 15)); - - pci_update_config32(dev, 0x33c, ~0x00ffffff, 0x854d74); - - /* Set Invalid Receive Range Check Enable in MPC register. */ - pci_update_config32(dev, 0xd8, ~0, (1 << 25)); - - pci_update_config8(dev, 0xf5, 0x0f, 0); - - /* Set AER Extended Cap ID to 01h and Next Cap Pointer to 200h. */ - if (CONFIG(PCIEXP_AER)) - pci_update_config32(dev, 0x100, ~(1 << 29) & ~0xfffff, - (1 << 29) | 0x10001); - else - pci_update_config32(dev, 0x100, ~(1 << 29) & ~0xfffff, - (1 << 29)); - - /* Set L1 Sub-State Cap ID to 1Eh and Next Cap Pointer to None. */ - if (CONFIG(PCIEXP_L1_SUB_STATE)) - pci_update_config32(dev, 0x200, ~0xfffff, 0x001e); - else - pci_update_config32(dev, 0x200, ~0xfffff, 0); - - pci_update_config32(dev, 0x320, ~(3 << 20) & ~(7 << 6), - (1 << 20) | (3 << 6)); - /* Enable Relaxed Order from Root Port. */ - pci_update_config32(dev, 0x320, ~(3 << 23), (3 << 23)); - - if (rp == 1 || rp == 5 || rp == 6) - pci_update_config8(dev, 0xf7, ~0xc, 0); - - /* Set EOI forwarding disable. */ - pci_update_config32(dev, 0xd4, ~0, (1 << 1)); - - /* Read and write back write-once capability registers. */ - pci_update_config32(dev, 0x34, ~0, 0); - pci_update_config32(dev, 0x40, ~0, 0); - pci_update_config32(dev, 0x80, ~0, 0); - pci_update_config32(dev, 0x90, ~0, 0); -} - -static void pch_pcie_init(struct device *dev) -{ - printk(BIOS_DEBUG, "Initializing PCH PCIe bridge.\n"); - - /* Enable SERR */ - pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_SERR); - - /* Enable Bus Master */ - pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER); - - /* Set Cache Line Size to 0x10 */ - pci_write_config8(dev, 0x0c, 0x10); - - pci_and_config16(dev, PCI_BRIDGE_CONTROL, ~PCI_BRIDGE_CTL_PARITY); - - /* Clear errors in status registers */ - pci_update_config16(dev, 0x06, ~0, 0); - pci_update_config16(dev, 0x1e, ~0, 0); -} - -static void pch_pcie_enable(struct device *dev) -{ - /* Add this device to the root port config structure. */ - root_port_init_config(dev); - - /* Check to see if this Root Port should be disabled. */ - root_port_check_disable(dev); - - /* Power Management init before enumeration */ - if (dev->enabled) - pch_pcie_early(dev); - - /* - * When processing the last PCIe root port we can now - * update the Root Port Function Number and Hide register. - */ - if (root_port_is_last(dev)) - root_port_commit_config(); -} - -static void pcie_set_L1_ss_max_latency(struct device *dev, unsigned int off) -{ - /* Set max snoop and non-snoop latency for Broadwell */ - pci_write_config32(dev, off, - PCIE_LTR_MAX_NO_SNOOP_LATENCY_3146US << 16 | - PCIE_LTR_MAX_SNOOP_LATENCY_3146US); -} - -static struct pci_operations pcie_ops = { - .set_subsystem = pci_dev_set_subsystem, - .set_L1_ss_latency = pcie_set_L1_ss_max_latency, -}; - -static struct device_operations device_ops = { - .read_resources = pci_bus_read_resources, - .set_resources = pci_dev_set_resources, - .enable_resources = pci_bus_enable_resources, - .init = pch_pcie_init, - .enable = pch_pcie_enable, - .scan_bus = pciexp_scan_bridge, - .ops_pci = &pcie_ops, -}; - -static const unsigned short pcie_device_ids[] = { - /* Lynxpoint-LP */ - 0x9c10, 0x9c12, 0x9c14, 0x9c16, 0x9c18, 0x9c1a, - /* WildcatPoint */ - 0x9c90, 0x9c92, 0x9c94, 0x9c96, 0x9c98, 0x9c9a, 0x2448, - 0 -}; - -static const struct pci_driver pch_pcie __pci_driver = { - .ops = &device_ops, - .vendor = PCI_VENDOR_ID_INTEL, - .devices = pcie_device_ids, -}; diff --git a/src/soc/intel/broadwell/pmutil.c b/src/soc/intel/broadwell/pmutil.c deleted file mode 100644 index e63a981456..0000000000 --- a/src/soc/intel/broadwell/pmutil.c +++ /dev/null @@ -1,455 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -/* - * Helper functions for dealing with power management registers - * and the differences between PCH variants. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static inline uint16_t get_gpiobase(void) -{ - return GPIO_BASE_ADDRESS; -} - -/* Print status bits with descriptive names */ -static void print_status_bits(u32 status, const char *bit_names[]) -{ - int i; - - if (!status) - return; - - for (i = 31; i >= 0; i--) { - if (status & (1 << i)) { - if (bit_names[i]) - printk(BIOS_DEBUG, "%s ", bit_names[i]); - else - printk(BIOS_DEBUG, "BIT%d ", i); - } - } -} - -/* Print status bits as GPIO numbers */ -static void print_gpio_status(u32 status, int start) -{ - int i; - - if (!status) - return; - - for (i = 31; i >= 0; i--) { - if (status & (1 << i)) - printk(BIOS_DEBUG, "GPIO%d ", start + i); - } -} - -/* - * PM1_CNT - */ - -/* Enable events in PM1 control register */ -void enable_pm1_control(u32 mask) -{ - u32 pm1_cnt = inl(get_pmbase() + PM1_CNT); - pm1_cnt |= mask; - outl(pm1_cnt, get_pmbase() + PM1_CNT); -} - -/* Disable events in PM1 control register */ -void disable_pm1_control(u32 mask) -{ - u32 pm1_cnt = inl(get_pmbase() + PM1_CNT); - pm1_cnt &= ~mask; - outl(pm1_cnt, get_pmbase() + PM1_CNT); -} - -/* - * PM1 - */ - -/* Clear and return PM1 status register */ -static u16 reset_pm1_status(void) -{ - u16 pm1_sts = inw(get_pmbase() + PM1_STS); - outw(pm1_sts, get_pmbase() + PM1_STS); - return pm1_sts; -} - -/* Print PM1 status bits */ -static u16 print_pm1_status(u16 pm1_sts) -{ - const char *pm1_sts_bits[] = { - [0] = "TMROF", - [4] = "BM", - [5] = "GBL", - [8] = "PWRBTN", - [10] = "RTC", - [11] = "PRBTNOR", - [14] = "PCIEXPWAK", - [15] = "WAK", - }; - - if (!pm1_sts) - return 0; - - printk(BIOS_SPEW, "PM1_STS: "); - print_status_bits(pm1_sts, pm1_sts_bits); - printk(BIOS_SPEW, "\n"); - - return pm1_sts; -} - -/* Print, clear, and return PM1 status */ -u16 clear_pm1_status(void) -{ - return print_pm1_status(reset_pm1_status()); -} - -/* Set the PM1 register to events */ -void enable_pm1(u16 events) -{ - outw(events, get_pmbase() + PM1_EN); -} - -/* - * SMI - */ - -/* Clear and return SMI status register */ -static u32 reset_smi_status(void) -{ - u32 smi_sts = inl(get_pmbase() + SMI_STS); - outl(smi_sts, get_pmbase() + SMI_STS); - return smi_sts; -} - -/* Print SMI status bits */ -static u32 print_smi_status(u32 smi_sts) -{ - const char *smi_sts_bits[] = { - [2] = "BIOS", - [3] = "LEGACY_USB", - [4] = "SLP_SMI", - [5] = "APM", - [6] = "SWSMI_TMR", - [8] = "PM1", - [9] = "GPE0", - [10] = "GPI", - [11] = "MCSMI", - [12] = "DEVMON", - [13] = "TCO", - [14] = "PERIODIC", - [15] = "SERIRQ_SMI", - [16] = "SMBUS_SMI", - [17] = "LEGACY_USB2", - [18] = "INTEL_USB2", - [20] = "PCI_EXP_SMI", - [21] = "MONITOR", - [26] = "SPI", - [27] = "GPIO_UNLOCK" - }; - - if (!smi_sts) - return 0; - - printk(BIOS_DEBUG, "SMI_STS: "); - print_status_bits(smi_sts, smi_sts_bits); - printk(BIOS_DEBUG, "\n"); - - return smi_sts; -} - -/* Print, clear, and return SMI status */ -u32 clear_smi_status(void) -{ - return print_smi_status(reset_smi_status()); -} - -/* Enable SMI event */ -void enable_smi(u32 mask) -{ - u32 smi_en = inl(get_pmbase() + SMI_EN); - smi_en |= mask; - outl(smi_en, get_pmbase() + SMI_EN); -} - -/* Disable SMI event */ -void disable_smi(u32 mask) -{ - u32 smi_en = inl(get_pmbase() + SMI_EN); - smi_en &= ~mask; - outl(smi_en, get_pmbase() + SMI_EN); -} - -/* - * ALT_GP_SMI - */ - -/* Clear GPIO SMI status and return events that are enabled and active */ -static u32 reset_alt_smi_status(void) -{ - u32 alt_sts, alt_en; - - /* Low Power variant moves this to GPIO region as dword */ - alt_sts = inl(get_gpiobase() + GPIO_ALT_GPI_SMI_STS); - outl(alt_sts, get_gpiobase() + GPIO_ALT_GPI_SMI_STS); - alt_en = inl(get_gpiobase() + GPIO_ALT_GPI_SMI_EN); - - /* Only report enabled events */ - return alt_sts & alt_en; -} - -/* Print GPIO SMI status bits */ -static u32 print_alt_smi_status(u32 alt_sts) -{ - if (!alt_sts) - return 0; - - printk(BIOS_DEBUG, "ALT_STS: "); - - /* First 16 events are GPIO 32-47 */ - print_gpio_status(alt_sts & 0xffff, 32); - - printk(BIOS_DEBUG, "\n"); - - return alt_sts; -} - -/* Print, clear, and return GPIO SMI status */ -u32 clear_alt_smi_status(void) -{ - return print_alt_smi_status(reset_alt_smi_status()); -} - -/* Enable GPIO SMI events */ -void enable_alt_smi(u32 mask) -{ - u32 alt_en; - - alt_en = inl(get_gpiobase() + GPIO_ALT_GPI_SMI_EN); - alt_en |= mask; - outl(alt_en, get_gpiobase() + GPIO_ALT_GPI_SMI_EN); -} - -/* - * TCO - */ - -/* Clear TCO status and return events that are enabled and active */ -static u32 reset_tco_status(void) -{ - u32 tcobase = get_pmbase() + 0x60; - u32 tco_sts = inl(tcobase + 0x04); - u32 tco_en = inl(get_pmbase() + 0x68); - - /* Don't clear BOOT_STS before SECOND_TO_STS */ - outl(tco_sts & ~(1 << 18), tcobase + 0x04); - - /* Clear BOOT_STS */ - if (tco_sts & (1 << 18)) - outl(tco_sts & (1 << 18), tcobase + 0x04); - - return tco_sts & tco_en; -} - -/* Print TCO status bits */ -static u32 print_tco_status(u32 tco_sts) -{ - const char *tco_sts_bits[] = { - [0] = "NMI2SMI", - [1] = "SW_TCO", - [2] = "TCO_INT", - [3] = "TIMEOUT", - [7] = "NEWCENTURY", - [8] = "BIOSWR", - [9] = "DMISCI", - [10] = "DMISMI", - [12] = "DMISERR", - [13] = "SLVSEL", - [16] = "INTRD_DET", - [17] = "SECOND_TO", - [18] = "BOOT", - [20] = "SMLINK_SLV" - }; - - if (!tco_sts) - return 0; - - printk(BIOS_DEBUG, "TCO_STS: "); - print_status_bits(tco_sts, tco_sts_bits); - printk(BIOS_DEBUG, "\n"); - - return tco_sts; -} - -/* Print, clear, and return TCO status */ -u32 clear_tco_status(void) -{ - return print_tco_status(reset_tco_status()); -} - -/* Enable TCO SCI */ -void enable_tco_sci(void) -{ - /* Clear pending events */ - outl(get_pmbase() + GPE0_STS(3), TCOSCI_STS); - - /* Enable TCO SCI events */ - enable_gpe(TCOSCI_EN); -} - -/* - * GPE0 - */ - -/* Clear a GPE0 status and return events that are enabled and active */ -static u32 reset_gpe_status(u16 sts_reg, u16 en_reg) -{ - u32 gpe0_sts = inl(get_pmbase() + sts_reg); - u32 gpe0_en = inl(get_pmbase() + en_reg); - - outl(gpe0_sts, get_pmbase() + sts_reg); - - /* Only report enabled events */ - return gpe0_sts & gpe0_en; -} - -/* Print GPE0 status bits */ -static u32 print_gpe_status(u32 gpe0_sts, const char *bit_names[]) -{ - if (!gpe0_sts) - return 0; - - printk(BIOS_DEBUG, "GPE0_STS: "); - print_status_bits(gpe0_sts, bit_names); - printk(BIOS_DEBUG, "\n"); - - return gpe0_sts; -} - -/* Print GPE0 GPIO status bits */ -static u32 print_gpe_gpio(u32 gpe0_sts, int start) -{ - if (!gpe0_sts) - return 0; - - printk(BIOS_DEBUG, "GPE0_STS: "); - print_gpio_status(gpe0_sts, start); - printk(BIOS_DEBUG, "\n"); - - return gpe0_sts; -} - -/* Clear all GPE status and return "standard" GPE event status */ -u32 clear_gpe_status(void) -{ - const char *gpe0_sts_3_bits[] = { - [1] = "HOTPLUG", - [2] = "SWGPE", - [6] = "TCO_SCI", - [7] = "SMB_WAK", - [9] = "PCI_EXP", - [10] = "BATLOW", - [11] = "PME", - [12] = "ME", - [13] = "PME_B0", - [16] = "GPIO27", - [18] = "WADT" - }; - - print_gpe_gpio(reset_gpe_status(GPE0_STS(GPE_31_0), GPE0_EN(GPE_31_0)), 0); - print_gpe_gpio(reset_gpe_status(GPE0_STS(GPE_63_32), GPE0_EN(GPE_63_32)), 32); - print_gpe_gpio(reset_gpe_status(GPE0_STS(GPE_94_64), GPE0_EN(GPE_94_64)), 64); - return print_gpe_status(reset_gpe_status(GPE0_STS(GPE_STD), GPE0_EN(GPE_STD)), - gpe0_sts_3_bits); -} - -/* Enable all requested GPE */ -void enable_all_gpe(u32 set1, u32 set2, u32 set3, u32 set4) -{ - u16 pmbase = get_pmbase(); - - outl(set1, pmbase + GPE0_EN(GPE_31_0)); - outl(set2, pmbase + GPE0_EN(GPE_63_32)); - outl(set3, pmbase + GPE0_EN(GPE_94_64)); - outl(set4, pmbase + GPE0_EN(GPE_STD)); -} - -/* Disable all GPE */ -void disable_all_gpe(void) -{ - enable_all_gpe(0, 0, 0, 0); -} - -/* Enable a standard GPE */ -void enable_gpe(u32 mask) -{ - u32 gpe0_en = inl(get_pmbase() + GPE0_EN(GPE_STD)); - gpe0_en |= mask; - outl(gpe0_en, get_pmbase() + GPE0_EN(GPE_STD)); -} - -/* Disable a standard GPE */ -void disable_gpe(u32 mask) -{ - u32 gpe0_en = inl(get_pmbase() + GPE0_EN(GPE_STD)); - gpe0_en &= ~mask; - outl(gpe0_en, get_pmbase() + GPE0_EN(GPE_STD)); -} - -int acpi_sci_irq(void) -{ - int scis = pci_read_config32(PCH_DEV_LPC, ACPI_CNTL) & SCI_IRQ_SEL; - int sci_irq = 9; - - /* Determine how SCI is routed. */ - switch (scis) { - case SCIS_IRQ9: - case SCIS_IRQ10: - case SCIS_IRQ11: - sci_irq = scis - SCIS_IRQ9 + 9; - break; - case SCIS_IRQ20: - case SCIS_IRQ21: - case SCIS_IRQ22: - case SCIS_IRQ23: - sci_irq = scis - SCIS_IRQ20 + 20; - break; - default: - printk(BIOS_DEBUG, "Invalid SCI route! Defaulting to IRQ9.\n"); - sci_irq = 9; - break; - } - - printk(BIOS_DEBUG, "SCI is IRQ%d\n", sci_irq); - return sci_irq; -} - -int platform_is_resuming(void) -{ - if (!(inw(get_pmbase() + PM1_STS) & WAK_STS)) - return 0; - - return acpi_sleep_from_pm1(inl(get_pmbase() + PM1_CNT)) == ACPI_S3; -} - -/* STM Support */ -uint16_t get_pmbase(void) -{ - return (uint16_t) ACPI_BASE_ADDRESS; -} diff --git a/src/soc/intel/broadwell/romstage/Makefile.inc b/src/soc/intel/broadwell/romstage/Makefile.inc index edfec30fdc..b77e7a579d 100644 --- a/src/soc/intel/broadwell/romstage/Makefile.inc +++ b/src/soc/intel/broadwell/romstage/Makefile.inc @@ -1,9 +1,6 @@ romstage-y += ../../../../cpu/intel/car/romstage.c romstage-y += cpu.c -romstage-y += pch.c -romstage-y += power_state.c romstage-y += raminit.c romstage-y += report_platform.c romstage-y += romstage.c romstage-y += systemagent.c -romstage-$(CONFIG_DRIVERS_UART_8250MEM) += uart.c diff --git a/src/soc/intel/broadwell/romstage/pch.c b/src/soc/intel/broadwell/romstage/pch.c deleted file mode 100644 index 149dda1ca0..0000000000 --- a/src/soc/intel/broadwell/romstage/pch.c +++ /dev/null @@ -1,77 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void pch_route_interrupts(void) -{ - /* - * GFX INTA -> PIRQA (MSI) - * D28IP_P1IP PCIE INTA -> PIRQA - * D29IP_E1P EHCI INTA -> PIRQD - * D20IP_XHCI XHCI INTA -> PIRQC (MSI) - * D31IP_SIP SATA INTA -> PIRQF (MSI) - * D31IP_SMIP SMBUS INTB -> PIRQG - * D31IP_TTIP THRT INTC -> PIRQA - * D27IP_ZIP HDA INTA -> PIRQG (MSI) - */ - - /* Device interrupt pin register (board specific) */ - RCBA32(D31IP) = (INTC << D31IP_TTIP) | (NOINT << D31IP_SIP2) | - (INTB << D31IP_SMIP) | (INTA << D31IP_SIP); - RCBA32(D29IP) = (INTA << D29IP_E1P); - RCBA32(D28IP) = (INTA << D28IP_P1IP) | (INTC << D28IP_P3IP) | - (INTB << D28IP_P4IP); - RCBA32(D27IP) = (INTA << D27IP_ZIP); - RCBA32(D26IP) = (INTA << D26IP_E2P); - RCBA32(D22IP) = (NOINT << D22IP_MEI1IP); - RCBA32(D20IP) = (INTA << D20IP_XHCI); - - /* Device interrupt route registers */ - RCBA32(D31IR) = DIR_ROUTE(PIRQG, PIRQC, PIRQB, PIRQA); /* LPC */ - RCBA32(D29IR) = DIR_ROUTE(PIRQD, PIRQD, PIRQD, PIRQD); /* EHCI */ - RCBA32(D28IR) = DIR_ROUTE(PIRQA, PIRQB, PIRQC, PIRQD); /* PCIE */ - RCBA32(D27IR) = DIR_ROUTE(PIRQG, PIRQG, PIRQG, PIRQG); /* HDA */ - RCBA32(D23IR) = DIR_ROUTE(PIRQH, PIRQH, PIRQH, PIRQH); /* SDIO */ - RCBA32(D22IR) = DIR_ROUTE(PIRQA, PIRQA, PIRQA, PIRQA); /* ME */ - RCBA32(D21IR) = DIR_ROUTE(PIRQE, PIRQF, PIRQF, PIRQF); /* SIO */ - RCBA32(D20IR) = DIR_ROUTE(PIRQC, PIRQC, PIRQC, PIRQC); /* XHCI */ -} - -static void pch_enable_lpc(void) -{ - /* Lookup device tree in romstage */ - const struct device *const dev = pcidev_on_root(0x1f, 0); - - const struct soc_intel_broadwell_pch_config *config = config_of(dev); - - pci_write_config32(PCH_DEV_LPC, LPC_GEN1_DEC, config->gen1_dec); - pci_write_config32(PCH_DEV_LPC, LPC_GEN2_DEC, config->gen2_dec); - pci_write_config32(PCH_DEV_LPC, LPC_GEN3_DEC, config->gen3_dec); - pci_write_config32(PCH_DEV_LPC, LPC_GEN4_DEC, config->gen4_dec); -} - -void pch_early_init(void) -{ - pch_route_interrupts(); - - pch_enable_lpc(); - - enable_smbus(); - - /* 8.14 Additional PCI Express Programming Steps, step #1 */ - pci_update_config32(_PCH_DEV(PCIE, 0), 0xf4, ~0x60, 0); - pci_update_config32(_PCH_DEV(PCIE, 0), 0xf4, ~0x80, 0x80); - pci_update_config32(_PCH_DEV(PCIE, 0), 0xe2, ~0x30, 0x30); -} diff --git a/src/soc/intel/broadwell/romstage/power_state.c b/src/soc/intel/broadwell/romstage/power_state.c deleted file mode 100644 index cb1d3e5b9c..0000000000 --- a/src/soc/intel/broadwell/romstage/power_state.c +++ /dev/null @@ -1,111 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct chipset_power_state power_state; - -static void migrate_power_state(int is_recovery) -{ - struct chipset_power_state *ps_cbmem; - struct chipset_power_state *ps_car; - - ps_car = &power_state; - ps_cbmem = cbmem_add(CBMEM_ID_POWER_STATE, sizeof(*ps_cbmem)); - - if (ps_cbmem == NULL) { - printk(BIOS_DEBUG, "Not adding power state to cbmem!\n"); - return; - } - memcpy(ps_cbmem, ps_car, sizeof(*ps_cbmem)); -} -ROMSTAGE_CBMEM_INIT_HOOK(migrate_power_state) - -/* Return 0, 3, or 5 to indicate the previous sleep state. */ -static int prev_sleep_state(struct chipset_power_state *ps) -{ - /* Default to S0. */ - int prev_sleep_state = ACPI_S0; - - if (ps->pm1_sts & WAK_STS) { - switch (acpi_sleep_from_pm1(ps->pm1_cnt)) { - case ACPI_S3: - if (CONFIG(HAVE_ACPI_RESUME)) - prev_sleep_state = ACPI_S3; - break; - case ACPI_S5: - prev_sleep_state = ACPI_S5; - break; - } - /* Clear SLP_TYP. */ - outl(ps->pm1_cnt & ~(SLP_TYP), ACPI_BASE_ADDRESS + PM1_CNT); - } - - if (ps->gen_pmcon3 & (PWR_FLR | SUS_PWR_FLR)) - prev_sleep_state = ACPI_S5; - - return prev_sleep_state; -} - -static void dump_power_state(struct chipset_power_state *ps) -{ - printk(BIOS_DEBUG, "PM1_STS: %04x\n", ps->pm1_sts); - printk(BIOS_DEBUG, "PM1_EN: %04x\n", ps->pm1_en); - printk(BIOS_DEBUG, "PM1_CNT: %08x\n", ps->pm1_cnt); - printk(BIOS_DEBUG, "TCO_STS: %04x %04x\n", - ps->tco1_sts, ps->tco2_sts); - - printk(BIOS_DEBUG, "GPE0_STS: %08x %08x %08x %08x\n", - ps->gpe0_sts[0], ps->gpe0_sts[1], - ps->gpe0_sts[2], ps->gpe0_sts[3]); - printk(BIOS_DEBUG, "GPE0_EN: %08x %08x %08x %08x\n", - ps->gpe0_en[0], ps->gpe0_en[1], - ps->gpe0_en[2], ps->gpe0_en[3]); - - printk(BIOS_DEBUG, "GEN_PMCON: %04x %04x %04x\n", - ps->gen_pmcon1, ps->gen_pmcon2, ps->gen_pmcon3); - - printk(BIOS_DEBUG, "Previous Sleep State: S%d\n", - ps->prev_sleep_state); -} - -/* Fill power state structure from ACPI PM registers */ -struct chipset_power_state *fill_power_state(void) -{ - struct chipset_power_state *ps = &power_state; - - ps->pm1_sts = inw(ACPI_BASE_ADDRESS + PM1_STS); - ps->pm1_en = inw(ACPI_BASE_ADDRESS + PM1_EN); - ps->pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT); - ps->tco1_sts = inw(ACPI_BASE_ADDRESS + TCO1_STS); - ps->tco2_sts = inw(ACPI_BASE_ADDRESS + TCO2_STS); - ps->gpe0_sts[0] = inl(ACPI_BASE_ADDRESS + GPE0_STS(0)); - ps->gpe0_sts[1] = inl(ACPI_BASE_ADDRESS + GPE0_STS(1)); - ps->gpe0_sts[2] = inl(ACPI_BASE_ADDRESS + GPE0_STS(2)); - ps->gpe0_sts[3] = inl(ACPI_BASE_ADDRESS + GPE0_STS(3)); - ps->gpe0_en[0] = inl(ACPI_BASE_ADDRESS + GPE0_EN(0)); - ps->gpe0_en[1] = inl(ACPI_BASE_ADDRESS + GPE0_EN(1)); - ps->gpe0_en[2] = inl(ACPI_BASE_ADDRESS + GPE0_EN(2)); - ps->gpe0_en[3] = inl(ACPI_BASE_ADDRESS + GPE0_EN(3)); - - ps->gen_pmcon1 = pci_read_config16(PCH_DEV_LPC, GEN_PMCON_1); - ps->gen_pmcon2 = pci_read_config16(PCH_DEV_LPC, GEN_PMCON_2); - ps->gen_pmcon3 = pci_read_config16(PCH_DEV_LPC, GEN_PMCON_3); - - ps->prev_sleep_state = prev_sleep_state(ps); - - dump_power_state(ps); - - return ps; -} diff --git a/src/soc/intel/broadwell/romstage/uart.c b/src/soc/intel/broadwell/romstage/uart.c deleted file mode 100644 index 6b0da2d65c..0000000000 --- a/src/soc/intel/broadwell/romstage/uart.c +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include - -const struct reg_script uart_init[] = { - /* Set MMIO BAR */ - REG_PCI_WRITE32(PCI_BASE_ADDRESS_0, CONFIG_TTYS0_BASE), - /* Enable Memory access and Bus Master */ - REG_PCI_OR32(PCI_COMMAND, PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER), - /* Initialize LTR */ - REG_MMIO_RMW32(CONFIG_TTYS0_BASE + SIO_REG_PPR_GEN, - ~SIO_REG_PPR_GEN_LTR_MODE_MASK, 0), - REG_MMIO_RMW32(CONFIG_TTYS0_BASE + SIO_REG_PPR_RST, - ~(SIO_REG_PPR_RST_ASSERT), 0), - /* Take UART out of reset */ - REG_MMIO_OR32(CONFIG_TTYS0_BASE + SIO_REG_PPR_RST, - SIO_REG_PPR_RST_ASSERT), - /* Set M and N divisor inputs and enable clock */ - REG_MMIO_WRITE32(CONFIG_TTYS0_BASE + SIO_REG_PPR_CLOCK, - SIO_REG_PPR_CLOCK_EN | SIO_REG_PPR_CLOCK_UPDATE | - (SIO_REG_PPR_CLOCK_N_DIV << 16) | - (SIO_REG_PPR_CLOCK_M_DIV << 1)), - REG_SCRIPT_END -}; - -void pch_uart_init(void) -{ - /* Program IOBP CB000154h[12,9:8,4:0] = 1001100011111b */ - u32 gpiodf = 0x131f; -#if defined(__SIMPLE_DEVICE__) - pci_devfn_t dev; -#else - struct device *dev; -#endif - - /* Put UART in byte access mode for 16550 compatibility */ - switch (CONFIG_INTEL_PCH_UART_CONSOLE_NUMBER) { - case 0: - dev = PCH_DEV_UART0; - gpiodf |= SIO_IOBP_GPIODF_UART0_BYTE_ACCESS; - break; - case 1: - dev = PCH_DEV_UART1; - gpiodf |= SIO_IOBP_GPIODF_UART1_BYTE_ACCESS; - break; - default: - return; - } - - /* Program IOBP GPIODF */ - pch_iobp_update(SIO_IOBP_GPIODF, ~gpiodf, gpiodf); - - /* Program IOBP CB000180h[5:0] = 111111b (undefined register) */ - pch_iobp_update(0xcb000180, ~0x0000003f, 0x0000003f); - - /* Initialize chipset uart interface */ - reg_script_run_on_dev(dev, uart_init); - - /* - * Perform standard UART initialization - * Divisor 1 is 115200 BAUD - */ - uart8250_mem_init(CONFIG_TTYS0_BASE, 1); -} diff --git a/src/soc/intel/broadwell/sata.c b/src/soc/intel/broadwell/sata.c deleted file mode 100644 index b496e53e3d..0000000000 --- a/src/soc/intel/broadwell/sata.c +++ /dev/null @@ -1,286 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static inline u32 sir_read(struct device *dev, int idx) -{ - pci_write_config32(dev, SATA_SIRI, idx); - return pci_read_config32(dev, SATA_SIRD); -} - -static inline void sir_write(struct device *dev, int idx, u32 value) -{ - pci_write_config32(dev, SATA_SIRI, idx); - pci_write_config32(dev, SATA_SIRD, value); -} - -static void sata_init(struct device *dev) -{ - const struct soc_intel_broadwell_pch_config *config = config_of(dev); - u32 reg32; - u8 *abar; - u16 reg16; - int port; - - printk(BIOS_DEBUG, "SATA: Initializing controller in AHCI mode.\n"); - - /* Enable BARs */ - pci_write_config16(dev, PCI_COMMAND, - PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO); - - /* Set Interrupt Line */ - /* Interrupt Pin is set by D31IP.PIP */ - pci_write_config8(dev, PCI_INTERRUPT_LINE, 0x0a); - - /* Set timings */ - pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE); - pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE); - - /* for AHCI, Port Enable is managed in memory mapped space */ - reg16 = pci_read_config16(dev, 0x92); - reg16 &= ~0xf; - reg16 |= 0x8000 | config->sata_port_map; - pci_write_config16(dev, 0x92, reg16); - udelay(2); - - /* Setup register 98h */ - reg32 = pci_read_config32(dev, 0x98); - reg32 &= ~((1 << 31) | (1 << 30)); - reg32 |= 1 << 23; - reg32 |= 1 << 24; /* Enable MPHY Dynamic Power Gating */ - pci_write_config32(dev, 0x98, reg32); - - /* Setup register 9Ch */ - reg16 = (1 << 5); /* BWG step 12 */ - pci_write_config16(dev, 0x9c, reg16); - - /* SATA Initialization register */ - reg32 = 0x183; - reg32 |= (config->sata_port_map ^ 0xf) << 24; - reg32 |= (config->sata_devslp_mux & 1) << 15; - pci_write_config32(dev, 0x94, reg32); - - /* Initialize AHCI memory-mapped space */ - abar = (u8 *)(pci_read_config32(dev, PCI_BASE_ADDRESS_5)); - printk(BIOS_DEBUG, "ABAR: %p\n", abar); - - /* CAP (HBA Capabilities) : enable power management */ - reg32 = read32(abar + 0x00); - reg32 |= 0x0c006000; /* set PSC+SSC+SALP+SSS */ - reg32 &= ~0x00020060; /* clear SXS+EMS+PMS */ - reg32 |= (1 << 18); /* SAM: SATA AHCI MODE ONLY */ - write32(abar + 0x00, reg32); - - /* PI (Ports implemented) */ - write32(abar + 0x0c, config->sata_port_map); - (void) read32(abar + 0x0c); /* Read back 1 */ - (void) read32(abar + 0x0c); /* Read back 2 */ - - /* CAP2 (HBA Capabilities Extended)*/ - if (config->sata_devslp_disable) { - reg32 = read32(abar + 0x24); - reg32 &= ~(1 << 3); - write32(abar + 0x24, reg32); - } else { - /* Enable DEVSLP */ - reg32 = read32(abar + 0x24); - reg32 |= (1 << 5)|(1 << 4)|(1 << 3)|(1 << 2); - write32(abar + 0x24, reg32); - - for (port = 0; port < 4; port++) { - if (!(config->sata_port_map & (1 << port))) - continue; - reg32 = read32(abar + 0x144 + (0x80 * port)); - reg32 |= (1 << 1); /* DEVSLP DSP */ - write32(abar + 0x144 + (0x80 * port), reg32); - } - } - - /* - * Static Power Gating for unused ports - */ - reg32 = RCBA32(0x3a84); - /* Port 3 and 2 disabled */ - if ((config->sata_port_map & ((1 << 3)|(1 << 2))) == 0) - reg32 |= (1 << 24) | (1 << 26); - /* Port 1 and 0 disabled */ - if ((config->sata_port_map & ((1 << 1)|(1 << 0))) == 0) - reg32 |= (1 << 20) | (1 << 18); - RCBA32(0x3a84) = reg32; - - /* Set Gen3 Transmitter settings if needed */ - if (config->sata_port0_gen3_tx) - pch_iobp_update(SATA_IOBP_SP0_SECRT88, - ~(SATA_SECRT88_VADJ_MASK << - SATA_SECRT88_VADJ_SHIFT), - (config->sata_port0_gen3_tx & - SATA_SECRT88_VADJ_MASK) - << SATA_SECRT88_VADJ_SHIFT); - - if (config->sata_port1_gen3_tx) - pch_iobp_update(SATA_IOBP_SP1_SECRT88, - ~(SATA_SECRT88_VADJ_MASK << - SATA_SECRT88_VADJ_SHIFT), - (config->sata_port1_gen3_tx & - SATA_SECRT88_VADJ_MASK) - << SATA_SECRT88_VADJ_SHIFT); - - if (config->sata_port2_gen3_tx) - pch_iobp_update(SATA_IOBP_SP2_SECRT88, - ~(SATA_SECRT88_VADJ_MASK << - SATA_SECRT88_VADJ_SHIFT), - (config->sata_port2_gen3_tx & - SATA_SECRT88_VADJ_MASK) - << SATA_SECRT88_VADJ_SHIFT); - - if (config->sata_port3_gen3_tx) - pch_iobp_update(SATA_IOBP_SP3_SECRT88, - ~(SATA_SECRT88_VADJ_MASK << - SATA_SECRT88_VADJ_SHIFT), - (config->sata_port3_gen3_tx & - SATA_SECRT88_VADJ_MASK) - << SATA_SECRT88_VADJ_SHIFT); - - /* Set Gen3 DTLE DATA / EDGE registers if needed */ - if (config->sata_port0_gen3_dtle) { - pch_iobp_update(SATA_IOBP_SP0DTLE_DATA, - ~(SATA_DTLE_MASK << SATA_DTLE_DATA_SHIFT), - (config->sata_port0_gen3_dtle & SATA_DTLE_MASK) - << SATA_DTLE_DATA_SHIFT); - - pch_iobp_update(SATA_IOBP_SP0DTLE_EDGE, - ~(SATA_DTLE_MASK << SATA_DTLE_EDGE_SHIFT), - (config->sata_port0_gen3_dtle & SATA_DTLE_MASK) - << SATA_DTLE_EDGE_SHIFT); - } - - if (config->sata_port1_gen3_dtle) { - pch_iobp_update(SATA_IOBP_SP1DTLE_DATA, - ~(SATA_DTLE_MASK << SATA_DTLE_DATA_SHIFT), - (config->sata_port1_gen3_dtle & SATA_DTLE_MASK) - << SATA_DTLE_DATA_SHIFT); - - pch_iobp_update(SATA_IOBP_SP1DTLE_EDGE, - ~(SATA_DTLE_MASK << SATA_DTLE_EDGE_SHIFT), - (config->sata_port1_gen3_dtle & SATA_DTLE_MASK) - << SATA_DTLE_EDGE_SHIFT); - } - - if (config->sata_port2_gen3_dtle) { - pch_iobp_update(SATA_IOBP_SP2DTLE_DATA, - ~(SATA_DTLE_MASK << SATA_DTLE_DATA_SHIFT), - (config->sata_port2_gen3_dtle & SATA_DTLE_MASK) - << SATA_DTLE_DATA_SHIFT); - - pch_iobp_update(SATA_IOBP_SP2DTLE_EDGE, - ~(SATA_DTLE_MASK << SATA_DTLE_EDGE_SHIFT), - (config->sata_port2_gen3_dtle & SATA_DTLE_MASK) - << SATA_DTLE_EDGE_SHIFT); - } - if (config->sata_port3_gen3_dtle) { - pch_iobp_update(SATA_IOBP_SP3DTLE_DATA, - ~(SATA_DTLE_MASK << SATA_DTLE_DATA_SHIFT), - (config->sata_port3_gen3_dtle & SATA_DTLE_MASK) - << SATA_DTLE_DATA_SHIFT); - - pch_iobp_update(SATA_IOBP_SP3DTLE_EDGE, - ~(SATA_DTLE_MASK << SATA_DTLE_EDGE_SHIFT), - (config->sata_port3_gen3_dtle & SATA_DTLE_MASK) - << SATA_DTLE_EDGE_SHIFT); - } - - /* - * Additional Programming Requirements for Power Optimizer - */ - - /* Step 1 */ - sir_write(dev, 0x64, 0x883c9003); - - /* Step 2: SIR 68h[15:0] = 880Ah */ - reg32 = sir_read(dev, 0x68); - reg32 &= 0xffff0000; - reg32 |= 0x880a; - sir_write(dev, 0x68, reg32); - - /* Step 3: SIR 60h[3] = 1 */ - reg32 = sir_read(dev, 0x60); - reg32 |= (1 << 3); - sir_write(dev, 0x60, reg32); - - /* Step 4: SIR 60h[0] = 1 */ - reg32 = sir_read(dev, 0x60); - reg32 |= (1 << 0); - sir_write(dev, 0x60, reg32); - - /* Step 5: SIR 60h[1] = 1 */ - reg32 = sir_read(dev, 0x60); - reg32 |= (1 << 1); - sir_write(dev, 0x60, reg32); - - /* Clock Gating */ - sir_write(dev, 0x70, 0x3f00bf1f); - sir_write(dev, 0x54, 0xcf000f0f); - sir_write(dev, 0x58, 0x00190000); - RCBA32_AND_OR(0x333c, 0xffcfffff, 0x00c00000); - - reg32 = pci_read_config32(dev, 0x300); - reg32 |= (1 << 17) | (1 << 16) | (1 << 19); - reg32 |= (1 << 31) | (1 << 30) | (1 << 29); - pci_write_config32(dev, 0x300, reg32); - - reg32 = pci_read_config32(dev, 0x98); - reg32 |= 1 << 29; - pci_write_config32(dev, 0x98, reg32); - - /* Register Lock */ - reg32 = pci_read_config32(dev, 0x9c); - reg32 |= (1 << 31); - pci_write_config32(dev, 0x9c, reg32); -} - -/* - * Set SATA controller mode early so the resource allocator can - * properly assign IO/Memory resources for the controller. - */ -static void sata_enable(struct device *dev) -{ - /* Get the chip configuration */ - const struct soc_intel_broadwell_pch_config *config = config_of(dev); - u16 map = 0x0060; - - map |= (config->sata_port_map ^ 0xf) << 8; - - pci_write_config16(dev, 0x90, map); -} - -static struct device_operations sata_ops = { - .read_resources = pci_dev_read_resources, - .set_resources = pci_dev_set_resources, - .enable_resources = pci_dev_enable_resources, - .init = sata_init, - .enable = sata_enable, - .ops_pci = &pci_dev_ops_pci, -}; - -static const unsigned short pci_device_ids[] = { - 0x9c03, 0x9c05, 0x9c07, 0x9c0f, /* LynxPoint-LP */ - 0x9c83, 0x9c85, 0x282a, 0x9c87, 0x282a, 0x9c8f, /* WildcatPoint */ - 0 -}; - -static const struct pci_driver pch_sata __pci_driver = { - .ops = &sata_ops, - .vendor = PCI_VENDOR_ID_INTEL, - .devices = pci_device_ids, -}; diff --git a/src/soc/intel/broadwell/serialio.c b/src/soc/intel/broadwell/serialio.c deleted file mode 100644 index d32a27ddca..0000000000 --- a/src/soc/intel/broadwell/serialio.c +++ /dev/null @@ -1,295 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Set D3Hot Power State in ACPI mode */ -static void serialio_enable_d3hot(struct resource *res) -{ - u32 reg32 = read32(res2mmio(res, PCH_PCS, 0)); - reg32 |= PCH_PCS_PS_D3HOT; - write32(res2mmio(res, PCH_PCS, 0), reg32); -} - -static int serialio_uart_is_debug(struct device *dev) -{ -#if CONFIG(INTEL_PCH_UART_CONSOLE) - switch (dev->path.pci.devfn) { - case PCH_DEVFN_UART0: /* UART0 */ - return !!(CONFIG_INTEL_PCH_UART_CONSOLE_NUMBER == 0); - case PCH_DEVFN_UART1: /* UART1 */ - return !!(CONFIG_INTEL_PCH_UART_CONSOLE_NUMBER == 1); - } -#endif - return 0; -} - -/* Enable clock in PCI mode */ -static void serialio_enable_clock(struct resource *bar0) -{ - u32 reg32 = read32(res2mmio(bar0, SIO_REG_PPR_CLOCK, 0)); - reg32 |= SIO_REG_PPR_CLOCK_EN; - write32(res2mmio(bar0, SIO_REG_PPR_CLOCK, 0), reg32); -} - -/* Put Serial IO D21:F0-F6 device into desired mode. */ -static void serialio_d21_mode(int sio_index, int int_pin, int acpi_mode) -{ - u32 portctrl = SIO_IOBP_PORTCTRL_PM_CAP_PRSNT; - - /* Snoop select 1. */ - portctrl |= SIO_IOBP_PORTCTRL_SNOOP_SELECT(1); - - /* Set interrupt pin. */ - portctrl |= SIO_IOBP_PORTCTRL_INT_PIN(int_pin); - - if (acpi_mode) { - /* Enable ACPI interrupt mode. */ - portctrl |= SIO_IOBP_PORTCTRL_ACPI_IRQ_EN; - - /* Disable PCI config space. */ - portctrl |= SIO_IOBP_PORTCTRL_PCI_CONF_DIS; - } - - pch_iobp_update(SIO_IOBP_PORTCTRLX(sio_index), 0, portctrl); -} - -/* Put Serial IO D23:F0 device into desired mode. */ -static void serialio_d23_mode(int acpi_mode) -{ - u32 portctrl = 0; - - /* Snoop select 1. */ - pch_iobp_update(SIO_IOBP_PORTCTRL1, 0, - SIO_IOBP_PORTCTRL1_SNOOP_SELECT(1)); - - if (acpi_mode) { - /* Enable ACPI interrupt mode. */ - portctrl |= SIO_IOBP_PORTCTRL0_ACPI_IRQ_EN; - - /* Disable PCI config space. */ - portctrl |= SIO_IOBP_PORTCTRL0_PCI_CONF_DIS; - } - - pch_iobp_update(SIO_IOBP_PORTCTRL0, 0, portctrl); -} - -/* Enable LTR Auto Mode for D21:F1-F6. */ -static void serialio_d21_ltr(struct resource *bar0) -{ - u32 reg; - - /* 1. Program BAR0 + 808h[2] = 0b */ - reg = read32(res2mmio(bar0, SIO_REG_PPR_GEN, 0)); - reg &= ~SIO_REG_PPR_GEN_LTR_MODE_MASK; - write32(res2mmio(bar0, SIO_REG_PPR_GEN, 0), reg); - - /* 2. Program BAR0 + 804h[1:0] = 00b */ - reg = read32(res2mmio(bar0, SIO_REG_PPR_RST, 0)); - reg &= ~SIO_REG_PPR_RST_ASSERT; - write32(res2mmio(bar0, SIO_REG_PPR_RST, 0), reg); - - /* 3. Program BAR0 + 804h[1:0] = 11b */ - reg = read32(res2mmio(bar0, SIO_REG_PPR_RST, 0)); - reg |= SIO_REG_PPR_RST_ASSERT; - write32(res2mmio(bar0, SIO_REG_PPR_RST, 0), reg); - - /* 4. Program BAR0 + 814h[31:0] = 00000000h */ - write32(res2mmio(bar0, SIO_REG_AUTO_LTR, 0), 0); -} - -/* Enable LTR Auto Mode for D23:F0. */ -static void serialio_d23_ltr(struct resource *bar0) -{ - u32 reg; - - /* Program BAR0 + 1008h[2] = 1b */ - reg = read32(res2mmio(bar0, SIO_REG_SDIO_PPR_GEN, 0)); - reg |= SIO_REG_PPR_GEN_LTR_MODE_MASK; - write32(res2mmio(bar0, SIO_REG_SDIO_PPR_GEN, 0), reg); - - /* Program BAR0 + 1010h = 0x00000000 */ - write32(res2mmio(bar0, SIO_REG_SDIO_PPR_SW_LTR, 0), 0); - - /* Program BAR0 + 3Ch[30] = 1b */ - reg = read32(res2mmio(bar0, SIO_REG_SDIO_PPR_CMD12, 0)); - reg |= SIO_REG_SDIO_PPR_CMD12_B30; - write32(res2mmio(bar0, SIO_REG_SDIO_PPR_CMD12, 0), reg); -} - -/* Select I2C voltage of 1.8V or 3.3V. */ -static void serialio_i2c_voltage_sel(struct resource *bar0, u8 voltage) -{ - u32 reg32 = read32(res2mmio(bar0, SIO_REG_PPR_GEN, 0)); - reg32 &= ~SIO_REG_PPR_GEN_VOLTAGE_MASK; - reg32 |= SIO_REG_PPR_GEN_VOLTAGE(voltage); - write32(res2mmio(bar0, SIO_REG_PPR_GEN, 0), reg32); -} - -/* Init sequence to be run once, done as part of D21:F0 (SDMA) init. */ -static void serialio_init_once(int acpi_mode) -{ - if (acpi_mode) { - /* Enable ACPI IRQ for IRQ13, IRQ7, IRQ6, IRQ5 in RCBA. */ - RCBA32_OR(ACPIIRQEN, (1 << 13)|(1 << 7)|(1 << 6)|(1 << 5)); - } - - /* Program IOBP CB000154h[12,9:8,4:0] = 1001100011111b. */ - pch_iobp_update(SIO_IOBP_GPIODF, ~0x0000131f, 0x0000131f); - - /* Program IOBP CB000180h[5:0] = 111111b (undefined register) */ - pch_iobp_update(0xcb000180, ~0x0000003f, 0x0000003f); -} - -static void serialio_init(struct device *dev) -{ - const struct soc_intel_broadwell_pch_config *config = config_of(dev); - struct resource *bar0, *bar1; - int sio_index = -1; - - printk(BIOS_DEBUG, "Initializing Serial IO device\n"); - - /* Ensure memory and bus master are enabled */ - pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); - - /* Find BAR0 and BAR1 */ - bar0 = find_resource(dev, PCI_BASE_ADDRESS_0); - if (!bar0) - return; - bar1 = find_resource(dev, PCI_BASE_ADDRESS_1); - if (!bar1) - return; - - if (!config->sio_acpi_mode) - serialio_enable_clock(bar0); - - switch (dev->path.pci.devfn) { - case PCH_DEVFN_SDMA: /* SDMA */ - sio_index = SIO_ID_SDMA; - serialio_init_once(config->sio_acpi_mode); - serialio_d21_mode(sio_index, SIO_PIN_INTB, - config->sio_acpi_mode); - break; - case PCH_DEVFN_I2C0: /* I2C0 */ - sio_index = SIO_ID_I2C0; - serialio_d21_ltr(bar0); - serialio_i2c_voltage_sel(bar0, config->sio_i2c0_voltage); - serialio_d21_mode(sio_index, SIO_PIN_INTC, - config->sio_acpi_mode); - break; - case PCH_DEVFN_I2C1: /* I2C1 */ - sio_index = SIO_ID_I2C1; - serialio_d21_ltr(bar0); - serialio_i2c_voltage_sel(bar0, config->sio_i2c1_voltage); - serialio_d21_mode(sio_index, SIO_PIN_INTC, - config->sio_acpi_mode); - break; - case PCH_DEVFN_SPI0: /* SPI0 */ - sio_index = SIO_ID_SPI0; - serialio_d21_ltr(bar0); - serialio_d21_mode(sio_index, SIO_PIN_INTC, - config->sio_acpi_mode); - break; - case PCH_DEVFN_SPI1: /* SPI1 */ - sio_index = SIO_ID_SPI1; - serialio_d21_ltr(bar0); - serialio_d21_mode(sio_index, SIO_PIN_INTC, - config->sio_acpi_mode); - break; - case PCH_DEVFN_UART0: /* UART0 */ - sio_index = SIO_ID_UART0; - if (!serialio_uart_is_debug(dev)) - serialio_d21_ltr(bar0); - serialio_d21_mode(sio_index, SIO_PIN_INTD, - config->sio_acpi_mode); - break; - case PCH_DEVFN_UART1: /* UART1 */ - sio_index = SIO_ID_UART1; - if (!serialio_uart_is_debug(dev)) - serialio_d21_ltr(bar0); - serialio_d21_mode(sio_index, SIO_PIN_INTD, - config->sio_acpi_mode); - break; - case PCH_DEVFN_SDIO: /* SDIO */ - sio_index = SIO_ID_SDIO; - serialio_d23_ltr(bar0); - serialio_d23_mode(config->sio_acpi_mode); - break; - default: - return; - } - - if (config->sio_acpi_mode) { - struct global_nvs *gnvs; - - /* Find ACPI NVS to update BARs */ - gnvs = acpi_get_gnvs(); - if (!gnvs) - return; - - /* Save BAR0 and BAR1 to ACPI NVS */ - gnvs->dev.bar0[sio_index] = (u32)bar0->base; - gnvs->dev.bar1[sio_index] = (u32)bar1->base; - - /* Do not enable UART if it is used as debug port */ - if (!serialio_uart_is_debug(dev)) - gnvs->dev.enable[sio_index] = 1; - - /* Put device in D3hot state via BAR1 */ - if (dev->path.pci.devfn != PCH_DEVFN_SDMA) - serialio_enable_d3hot(bar1); /* all but SDMA */ - } -} - -static void serialio_set_resources(struct device *dev) -{ - pci_dev_set_resources(dev); - -#if CONFIG(INTEL_PCH_UART_CONSOLE) - /* Update UART base address if used for debug */ - if (serialio_uart_is_debug(dev)) { - struct resource *res = find_resource(dev, PCI_BASE_ADDRESS_0); - if (res) - uartmem_setbaseaddr(res->base); - } -#endif -} - -static struct device_operations device_ops = { - .read_resources = &pci_dev_read_resources, - .set_resources = &serialio_set_resources, - .enable_resources = &pci_dev_enable_resources, - .init = &serialio_init, - .ops_pci = &pci_dev_ops_pci, -}; - -static const unsigned short pci_device_ids[] = { - 0x9c60, 0x9ce0, /* 0:15.0 - SDMA */ - 0x9c61, 0x9ce1, /* 0:15.1 - I2C0 */ - 0x9c62, 0x9ce2, /* 0:15.2 - I2C1 */ - 0x9c65, 0x9ce5, /* 0:15.3 - SPI0 */ - 0x9c66, 0x9ce6, /* 0:15.4 - SPI1 */ - 0x9c63, 0x9ce3, /* 0:15.5 - UART0 */ - 0x9c64, 0x9ce4, /* 0:15.6 - UART1 */ - 0x9c35, 0x9cb5, /* 0:17.0 - SDIO */ - 0 -}; - -static const struct pci_driver pch_pcie __pci_driver = { - .ops = &device_ops, - .vendor = PCI_VENDOR_ID_INTEL, - .devices = pci_device_ids, -}; diff --git a/src/soc/intel/broadwell/smbus.c b/src/soc/intel/broadwell/smbus.c deleted file mode 100644 index 70655fc891..0000000000 --- a/src/soc/intel/broadwell/smbus.c +++ /dev/null @@ -1,94 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void pch_smbus_init(struct device *dev) -{ - struct resource *res; - u16 reg16; - - /* Enable clock gating */ - /* FIXME: Using 32-bit ops with a 16-bit variable is a bug! These should be 16-bit! */ - reg16 = pci_read_config32(dev, 0x80); - reg16 &= ~((1 << 8)|(1 << 10)|(1 << 12)|(1 << 14)); - pci_write_config32(dev, 0x80, reg16); - - /* Set Receive Slave Address */ - res = find_resource(dev, PCI_BASE_ADDRESS_4); - if (res) - smbus_set_slave_addr(res->base, SMBUS_SLAVE_ADDR); -} - -static int lsmbus_read_byte(struct device *dev, u8 address) -{ - u16 device; - struct resource *res; - struct bus *pbus; - - device = dev->path.i2c.device; - pbus = get_pbus_smbus(dev); - res = find_resource(pbus->dev, PCI_BASE_ADDRESS_4); - - return do_smbus_read_byte(res->base, device, address); -} - -static int lsmbus_write_byte(struct device *dev, u8 address, u8 data) -{ - u16 device; - struct resource *res; - struct bus *pbus; - - device = dev->path.i2c.device; - pbus = get_pbus_smbus(dev); - res = find_resource(pbus->dev, PCI_BASE_ADDRESS_4); - return do_smbus_write_byte(res->base, device, address, data); -} - -static struct smbus_bus_operations lops_smbus_bus = { - .read_byte = lsmbus_read_byte, - .write_byte = lsmbus_write_byte, -}; - -static void smbus_read_resources(struct device *dev) -{ - struct resource *res = new_resource(dev, PCI_BASE_ADDRESS_4); - res->base = SMBUS_BASE_ADDRESS; - res->size = 32; - res->limit = res->base + res->size - 1; - res->flags = IORESOURCE_IO | IORESOURCE_FIXED | IORESOURCE_RESERVE | - IORESOURCE_STORED | IORESOURCE_ASSIGNED; - - /* Also add MMIO resource */ - res = pci_get_resource(dev, PCI_BASE_ADDRESS_0); -} - -static struct device_operations smbus_ops = { - .read_resources = smbus_read_resources, - .set_resources = pci_dev_set_resources, - .enable_resources = pci_dev_enable_resources, - .scan_bus = scan_smbus, - .init = pch_smbus_init, - .ops_smbus_bus = &lops_smbus_bus, - .ops_pci = &pci_dev_ops_pci, -}; - -static const unsigned short pci_device_ids[] = { - 0x9c22, /* LynxPoint */ - 0x9ca2, /* WildcatPoint */ - 0 -}; - -static const struct pci_driver pch_smbus __pci_driver = { - .ops = &smbus_ops, - .vendor = PCI_VENDOR_ID_INTEL, - .devices = pci_device_ids, -}; diff --git a/src/soc/intel/broadwell/smi.c b/src/soc/intel/broadwell/smi.c deleted file mode 100644 index d7704fd8fa..0000000000 --- a/src/soc/intel/broadwell/smi.c +++ /dev/null @@ -1,56 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void smm_southbridge_clear_state(void) -{ - u32 smi_en; - - printk(BIOS_DEBUG, "Initializing Southbridge SMI..."); - printk(BIOS_SPEW, " ... pmbase = 0x%04x\n", ACPI_BASE_ADDRESS); - - smi_en = inl(ACPI_BASE_ADDRESS + SMI_EN); - if (smi_en & APMC_EN) { - printk(BIOS_INFO, "SMI# handler already enabled?\n"); - return; - } - - printk(BIOS_DEBUG, "\n"); - - /* Dump and clear status registers */ - clear_smi_status(); - clear_pm1_status(); - clear_tco_status(); - clear_gpe_status(); -} - -static void smm_southbridge_enable(uint16_t pm1_events) -{ - printk(BIOS_DEBUG, "Enabling SMIs.\n"); - /* Configure events */ - enable_pm1(pm1_events); - disable_gpe(PME_B0_EN); - - /* Enable SMI generation: - * - on APMC writes (io 0xb2) - * - on writes to SLP_EN (sleep states) - * - on writes to GBL_RLS (bios commands) - * No SMIs: - * - on microcontroller writes (io 0x62/0x66) - * - on TCO events - */ - enable_smi(APMC_EN | SLP_SMI_EN | GBL_SMI_EN | EOS); -} - -void global_smi_enable(void) -{ - smm_southbridge_enable(PWRBTN_EN | GBL_EN); -} diff --git a/src/soc/intel/broadwell/smihandler.c b/src/soc/intel/broadwell/smihandler.c deleted file mode 100644 index fd5d4522fa..0000000000 --- a/src/soc/intel/broadwell/smihandler.c +++ /dev/null @@ -1,569 +0,0 @@ -/* 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 -#include -#include -#include -#include - -static u8 smm_initialized = 0; - -int southbridge_io_trap_handler(int smif) -{ - switch (smif) { - case 0x32: - printk(BIOS_DEBUG, "OS Init\n"); - /* gnvs->smif: - * On success, the IO Trap Handler returns 0 - * On failure, the IO Trap Handler returns a value != 0 - */ - gnvs->smif = 0; - return 1; /* IO trap handled */ - } - - /* Not handled */ - return 0; -} - -/** - * @brief Set the EOS bit - */ -void southbridge_smi_set_eos(void) -{ - enable_smi(EOS); -} - -static void busmaster_disable_on_bus(int bus) -{ - int slot, func; - unsigned int val; - unsigned char hdr; - - for (slot = 0; slot < 0x20; slot++) { - for (func = 0; func < 8; func++) { - pci_devfn_t dev = PCI_DEV(bus, slot, func); - - val = pci_read_config32(dev, PCI_VENDOR_ID); - - if (val == 0xffffffff || val == 0x00000000 || - val == 0x0000ffff || val == 0xffff0000) - continue; - - /* Disable Bus Mastering for this one device */ - pci_and_config16(dev, PCI_COMMAND, ~PCI_COMMAND_MASTER); - - /* If this is a bridge, then follow it. */ - hdr = pci_read_config8(dev, PCI_HEADER_TYPE); - hdr &= 0x7f; - if (hdr == PCI_HEADER_TYPE_BRIDGE || - hdr == PCI_HEADER_TYPE_CARDBUS) { - unsigned int buses; - buses = pci_read_config32(dev, PCI_PRIMARY_BUS); - busmaster_disable_on_bus((buses >> 8) & 0xff); - } - } - } -} - -/* - * Turn off the backlight if it is on, and wait for the specified - * backlight off delay. This will allow panel power timings to meet - * spec and prevent brief garbage on the screen when turned off - * during firmware with power button triggered SMI. - */ -static void backlight_off(void) -{ - void *reg_base; - uint32_t pp_ctrl; - uint32_t bl_off_delay; - - reg_base = (void *)((uintptr_t)pci_read_config32(SA_DEV_IGD, - PCI_BASE_ADDRESS_0) & ~0xf); - - /* Validate pointer before using it */ - if (smm_points_to_smram(reg_base, PCH_PP_OFF_DELAYS + sizeof(uint32_t))) - return; - - /* Check if backlight is enabled */ - pp_ctrl = read32(reg_base + PCH_PP_CONTROL); - if (!(pp_ctrl & EDP_BLC_ENABLE)) - return; - - /* Enable writes to this register */ - pp_ctrl &= ~PANEL_UNLOCK_MASK; - pp_ctrl |= PANEL_UNLOCK_REGS; - - /* Turn off backlight */ - pp_ctrl &= ~EDP_BLC_ENABLE; - - write32(reg_base + PCH_PP_CONTROL, pp_ctrl); - read32(reg_base + PCH_PP_CONTROL); - - /* Read backlight off delay in 100us units */ - bl_off_delay = read32(reg_base + PCH_PP_OFF_DELAYS); - bl_off_delay &= PANEL_LIGHT_OFF_DELAY_MASK; - bl_off_delay *= 100; - - /* Wait for backlight to turn off */ - udelay(bl_off_delay); - - printk(BIOS_INFO, "Backlight turned off\n"); -} - -static void southbridge_smi_sleep(void) -{ - u8 reg8; - u32 reg32; - u8 slp_typ; - u8 s5pwr = CONFIG_MAINBOARD_POWER_FAILURE_STATE; - u16 pmbase = get_pmbase(); - - /* save and recover RTC port values */ - u8 tmp70, tmp72; - tmp70 = inb(0x70); - tmp72 = inb(0x72); - get_option(&s5pwr, "power_on_after_fail"); - outb(tmp70, 0x70); - outb(tmp72, 0x72); - - /* First, disable further SMIs */ - disable_smi(SLP_SMI_EN); - - /* Figure out SLP_TYP */ - reg32 = inl(pmbase + PM1_CNT); - printk(BIOS_SPEW, "SMI#: SLP = 0x%08x\n", reg32); - slp_typ = acpi_sleep_from_pm1(reg32); - - /* Do any mainboard sleep handling */ - mainboard_smi_sleep(slp_typ); - - /* USB sleep preparations */ - usb_xhci_sleep_prepare(PCH_DEV_XHCI, slp_typ); - - /* Log S3, S4, and S5 entry */ - if (slp_typ >= ACPI_S3) - elog_gsmi_add_event_byte(ELOG_TYPE_ACPI_ENTER, slp_typ); - - /* Clear pending GPE events */ - clear_gpe_status(); - - /* Next, do the deed. - */ - - switch (slp_typ) { - case ACPI_S0: - printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n"); - break; - case ACPI_S1: - printk(BIOS_DEBUG, "SMI#: Entering S1 (Assert STPCLK#)\n"); - break; - case ACPI_S3: - printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n"); - - /* Invalidate the cache before going to S3 */ - wbinvd(); - break; - case ACPI_S4: - printk(BIOS_DEBUG, "SMI#: Entering S4 (Suspend-To-Disk)\n"); - break; - case ACPI_S5: - printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n"); - - /* Turn off backlight if needed */ - backlight_off(); - - /* Disable all GPE */ - disable_all_gpe(); - - /* Always set the flag in case CMOS was changed on runtime. For - * "KEEP", switch to "OFF" - KEEP is software emulated - */ - reg8 = pci_read_config8(PCH_DEV_LPC, GEN_PMCON_3); - if (s5pwr == MAINBOARD_POWER_ON) - reg8 &= ~1; - else - reg8 |= 1; - pci_write_config8(PCH_DEV_LPC, GEN_PMCON_3, reg8); - - /* also iterates over all bridges on bus 0 */ - busmaster_disable_on_bus(0); - break; - default: - printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n"); - break; - } - - /* - * Write back to the SLP register to cause the originally intended - * event again. We need to set BIT13 (SLP_EN) though to make the - * sleep happen. - */ - enable_pm1_control(SLP_EN); - - /* Make sure to stop executing code here for S3/S4/S5 */ - if (slp_typ >= ACPI_S3) - halt(); - - /* - * In most sleep states, the code flow of this function ends at - * the line above. However, if we entered sleep state S1 and wake - * up again, we will continue to execute code in this function. - */ - reg32 = inl(pmbase + PM1_CNT); - if (reg32 & SCI_EN) { - /* The OS is not an ACPI OS, so we set the state to S0 */ - disable_pm1_control(SLP_EN | SLP_TYP); - } -} - -/* - * Look for Synchronous IO SMI and use save state from that - * core in case we are not running on the same core that - * initiated the IO transaction. - */ -static em64t101_smm_state_save_area_t *smi_apmc_find_state_save(u8 cmd) -{ - em64t101_smm_state_save_area_t *state; - int node; - - /* Check all nodes looking for the one that issued the IO */ - for (node = 0; node < CONFIG_MAX_CPUS; node++) { - state = smm_get_save_state(node); - - /* Check for Synchronous IO (bit0 == 1) */ - if (!(state->io_misc_info & (1 << 0))) - continue; - - /* Make sure it was a write (bit4 == 0) */ - if (state->io_misc_info & (1 << 4)) - continue; - - /* Check for APMC IO port */ - if (((state->io_misc_info >> 16) & 0xff) != APM_CNT) - continue; - - /* Check AX against the requested command */ - if ((state->rax & 0xff) != cmd) - continue; - - return state; - } - - return NULL; -} - -static void southbridge_smi_gsmi(void) -{ - u32 *ret, *param; - u8 sub_command; - em64t101_smm_state_save_area_t *io_smi = - smi_apmc_find_state_save(APM_CNT_ELOG_GSMI); - - if (!io_smi) - return; - - /* Command and return value in EAX */ - ret = (u32 *)&io_smi->rax; - sub_command = (u8)(*ret >> 8); - - /* Parameter buffer in EBX */ - param = (u32 *)&io_smi->rbx; - - /* drivers/elog/gsmi.c */ - *ret = gsmi_exec(sub_command, param); -} - -static void southbridge_smi_store(void) -{ - u8 sub_command, ret; - em64t101_smm_state_save_area_t *io_smi = - smi_apmc_find_state_save(APM_CNT_SMMSTORE); - uint32_t reg_ebx; - - if (!io_smi) - return; - /* Command and return value in EAX */ - sub_command = (io_smi->rax >> 8) & 0xff; - - /* Parameter buffer in EBX */ - reg_ebx = io_smi->rbx; - - /* drivers/smmstore/smi.c */ - ret = smmstore_exec(sub_command, (void *)reg_ebx); - io_smi->rax = ret; -} - -static void southbridge_smi_apmc(void) -{ - u8 reg8; - em64t101_smm_state_save_area_t *state; - - /* Emulate B2 register as the FADT / Linux expects it */ - - reg8 = inb(APM_CNT); - switch (reg8) { - case APM_CNT_CST_CONTROL: - printk(BIOS_DEBUG, "C-state control\n"); - break; - case APM_CNT_PST_CONTROL: - printk(BIOS_DEBUG, "P-state control\n"); - break; - case APM_CNT_ACPI_DISABLE: - disable_pm1_control(SCI_EN); - printk(BIOS_DEBUG, "SMI#: ACPI disabled.\n"); - break; - case APM_CNT_ACPI_ENABLE: - enable_pm1_control(SCI_EN); - printk(BIOS_DEBUG, "SMI#: ACPI enabled.\n"); - break; - case APM_CNT_GNVS_UPDATE: - if (smm_initialized) { - printk(BIOS_DEBUG, - "SMI#: SMM structures already initialized!\n"); - return; - } - state = smi_apmc_find_state_save(reg8); - if (state) { - /* EBX in the state save contains the GNVS pointer */ - gnvs = (struct global_nvs *)((u32)state->rbx); - if (smm_points_to_smram(gnvs, sizeof(*gnvs))) { - printk(BIOS_ERR, "SMI#: ERROR: GNVS overlaps SMM\n"); - return; - } - smm_initialized = 1; - printk(BIOS_DEBUG, "SMI#: Setting GNVS to %p\n", gnvs); - } - break; - case APM_CNT_ELOG_GSMI: - if (CONFIG(ELOG_GSMI)) - southbridge_smi_gsmi(); - break; - case APM_CNT_SMMSTORE: - if (CONFIG(SMMSTORE)) - southbridge_smi_store(); - break; - } - - mainboard_smi_apmc(reg8); -} - -static void southbridge_smi_pm1(void) -{ - u16 pm1_sts = clear_pm1_status(); - - /* While OSPM is not active, poweroff immediately - * on a power button event. - */ - if (pm1_sts & PWRBTN_STS) { - /* power button pressed */ - elog_gsmi_add_event(ELOG_TYPE_POWER_BUTTON); - disable_pm1_control(-1UL); - enable_pm1_control(SLP_EN | (SLP_TYP_S5 << 10)); - } -} - -static void southbridge_smi_gpe0(void) -{ - clear_gpe_status(); -} - -static void southbridge_smi_gpi(void) -{ - mainboard_smi_gpi(clear_alt_smi_status()); - - /* Clear again after mainboard handler */ - clear_alt_smi_status(); -} - -static void southbridge_smi_mc(void) -{ - u32 reg32 = inl(get_pmbase() + SMI_EN); - - /* Are microcontroller SMIs enabled? */ - if ((reg32 & MCSMI_EN) == 0) - return; - - printk(BIOS_DEBUG, "Microcontroller SMI.\n"); -} - -static void southbridge_smi_tco(void) -{ - u32 tco_sts = clear_tco_status(); - - /* Any TCO event? */ - if (!tco_sts) - return; - - // BIOSWR - if (tco_sts & (1 << 8)) { - u8 bios_cntl = pci_read_config16(PCH_DEV_LPC, BIOS_CNTL); - - if (bios_cntl & 1) { - /* - * BWE is RW, so the SMI was caused by a - * write to BWE, not by a write to the BIOS - * - * This is the place where we notice someone - * is trying to tinker with the BIOS. We are - * trying to be nice and just ignore it. A more - * resolute answer would be to power down the - * box. - */ - printk(BIOS_DEBUG, "Switching back to RO\n"); - pci_write_config32(PCH_DEV_LPC, BIOS_CNTL, (bios_cntl & ~1)); - } /* No else for now? */ - } else if (tco_sts & (1 << 3)) { /* TIMEOUT */ - /* Handle TCO timeout */ - printk(BIOS_DEBUG, "TCO Timeout.\n"); - } -} - -static void southbridge_smi_periodic(void) -{ - u32 reg32 = inl(get_pmbase() + SMI_EN); - - /* Are periodic SMIs enabled? */ - if ((reg32 & PERIODIC_EN) == 0) - return; - - printk(BIOS_DEBUG, "Periodic SMI.\n"); -} - -static void southbridge_smi_monitor(void) -{ -#define IOTRAP(x) (trap_sts & (1 << x)) - u32 trap_sts, trap_cycle; - u32 mask = 0; - int i; - - trap_sts = RCBA32(0x1e00); // TRSR - Trap Status Register - RCBA32(0x1e00) = trap_sts; // Clear trap(s) in TRSR - - trap_cycle = RCBA32(0x1e10); - for (i = 16; i < 20; i++) { - if (trap_cycle & (1 << i)) - mask |= (0xff << ((i - 16) << 2)); - } - - /* IOTRAP(3) SMI function call */ - if (IOTRAP(3)) { - if (gnvs && gnvs->smif) - io_trap_handler(gnvs->smif); // call function smif - return; - } - - /* IOTRAP(2) currently unused - * IOTRAP(1) currently unused */ - - /* IOTRAP(0) SMIC */ - if (IOTRAP(0)) { - // It's a write - if (!(trap_cycle & (1 << 24))) { - printk(BIOS_DEBUG, "SMI1 command\n"); - (void)RCBA32(0x1e18); - // data = RCBA32(0x1e18); - // data &= mask; - // if (smi1) - // southbridge_smi_command(data); - // return; - } - // Fall through to debug - } - - printk(BIOS_DEBUG, " trapped io address = 0x%x\n", - trap_cycle & 0xfffc); - for (i = 0; i < 4; i++) - if (IOTRAP(i)) - printk(BIOS_DEBUG, " TRAP = %d\n", i); - printk(BIOS_DEBUG, " AHBE = %x\n", (trap_cycle >> 16) & 0xf); - printk(BIOS_DEBUG, " MASK = 0x%08x\n", mask); - printk(BIOS_DEBUG, " read/write: %s\n", - (trap_cycle & (1 << 24)) ? "read" : "write"); - - if (!(trap_cycle & (1 << 24))) { - /* Write Cycle */ - printk(BIOS_DEBUG, " iotrap written data = 0x%08x\n", RCBA32(0x1e18)); - } -#undef IOTRAP -} - -typedef void (*smi_handler_t)(void); - -static smi_handler_t southbridge_smi[32] = { - NULL, // [0] reserved - NULL, // [1] reserved - NULL, // [2] BIOS_STS - NULL, // [3] LEGACY_USB_STS - southbridge_smi_sleep, // [4] SLP_SMI_STS - southbridge_smi_apmc, // [5] APM_STS - NULL, // [6] SWSMI_TMR_STS - NULL, // [7] reserved - southbridge_smi_pm1, // [8] PM1_STS - southbridge_smi_gpe0, // [9] GPE0_STS - southbridge_smi_gpi, // [10] GPI_STS - southbridge_smi_mc, // [11] MCSMI_STS - NULL, // [12] DEVMON_STS - southbridge_smi_tco, // [13] TCO_STS - southbridge_smi_periodic, // [14] PERIODIC_STS - NULL, // [15] SERIRQ_SMI_STS - NULL, // [16] SMBUS_SMI_STS - NULL, // [17] LEGACY_USB2_STS - NULL, // [18] INTEL_USB2_STS - NULL, // [19] reserved - NULL, // [20] PCI_EXP_SMI_STS - southbridge_smi_monitor, // [21] MONITOR_STS - NULL, // [22] reserved - NULL, // [23] reserved - NULL, // [24] reserved - NULL, // [25] EL_SMI_STS - NULL, // [26] SPI_STS - NULL, // [27] reserved - NULL, // [28] reserved - NULL, // [29] reserved - NULL, // [30] reserved - NULL // [31] reserved -}; - -/** - * @brief Interrupt handler for SMI# - */ -void southbridge_smi_handler(void) -{ - int i; - u32 smi_sts; - - /* We need to clear the SMI status registers, or we won't see what's - * happening in the following calls. - */ - smi_sts = clear_smi_status(); - - /* Call SMI sub handler for each of the status bits */ - for (i = 0; i < 31; i++) { - if (smi_sts & (1 << i)) { - if (southbridge_smi[i]) { - southbridge_smi[i](); - } else { - printk(BIOS_DEBUG, - "SMI_STS[%d] occurred, but no " - "handler available.\n", i); - } - } - } -} diff --git a/src/soc/intel/broadwell/usb_debug.c b/src/soc/intel/broadwell/usb_debug.c deleted file mode 100644 index 0fda336d14..0000000000 --- a/src/soc/intel/broadwell/usb_debug.c +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -// Use simple device model for this file even in ramstage -#define __SIMPLE_DEVICE__ - -#include -#include - -pci_devfn_t pci_ehci_dbg_dev(unsigned int hcd_idx) -{ - return PCI_DEV(0, 0x1d, 0); -} - -void pci_ehci_dbg_set_port(pci_devfn_t dev, unsigned int port) -{ - /* Hardcoded to physical port 1 */ -} diff --git a/src/soc/intel/broadwell/xhci.c b/src/soc/intel/broadwell/xhci.c deleted file mode 100644 index baaf5ba6e6..0000000000 --- a/src/soc/intel/broadwell/xhci.c +++ /dev/null @@ -1,219 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __SIMPLE_DEVICE__ -static u8 *usb_xhci_mem_base(pci_devfn_t dev) -{ - u32 mem_base = pci_read_config32(dev, PCI_BASE_ADDRESS_0); - - /* Check if the controller is disabled or not present */ - if (mem_base == 0 || mem_base == 0xffffffff) - return 0; - - return (u8 *)(mem_base & ~0xf); -} - -static int usb_xhci_port_count_usb3(pci_devfn_t dev) -{ - /* PCH-LP has 4 SS ports */ - return 4; -} - -static void usb_xhci_reset_status_usb3(u8 *mem_base, int port) -{ - u8 *portsc = mem_base + XHCI_USB3_PORTSC(port); - u32 status = read32(portsc); - /* Do not set Port Enabled/Disabled field */ - status &= ~XHCI_USB3_PORTSC_PED; - /* Clear all change status bits */ - status |= XHCI_USB3_PORTSC_CHST; - write32(portsc, status); -} - -static void usb_xhci_reset_port_usb3(u8 *mem_base, int port) -{ - u8 *portsc = mem_base + XHCI_USB3_PORTSC(port); - write32(portsc, read32(portsc) | XHCI_USB3_PORTSC_WPR); -} - -#define XHCI_RESET_DELAY_US 1000 /* 1ms */ -#define XHCI_RESET_TIMEOUT 100 /* 100ms */ - -/* - * 1) Wait until port is done polling - * 2) If port is disconnected - * a) Issue warm port reset - * b) Poll for warm reset complete - * c) Write 1 to port change status bits - */ -static void usb_xhci_reset_usb3(pci_devfn_t dev, int all) -{ - u32 status, port_disabled; - int timeout, port; - int port_count = usb_xhci_port_count_usb3(dev); - u8 *mem_base = usb_xhci_mem_base(dev); - - if (!mem_base || !port_count) - return; - - /* Get mask of disabled ports */ - port_disabled = pci_read_config32(dev, XHCI_USB3PDO); - - /* Wait until all enabled ports are done polling */ - for (timeout = XHCI_RESET_TIMEOUT; timeout; timeout--) { - int complete = 1; - for (port = 0; port < port_count; port++) { - /* Skip disabled ports */ - if (port_disabled & (1 << port)) - continue; - /* Read port link status field */ - status = read32(mem_base + XHCI_USB3_PORTSC(port)); - status &= XHCI_USB3_PORTSC_PLS; - if (status == XHCI_PLSR_POLLING) - complete = 0; - } - /* Exit if all ports not polling */ - if (complete) - break; - udelay(XHCI_RESET_DELAY_US); - } - - /* Reset all requested ports */ - for (port = 0; port < port_count; port++) { - u8 *portsc = mem_base + XHCI_USB3_PORTSC(port); - /* Skip disabled ports */ - if (port_disabled & (1 << port)) - continue; - status = read32(portsc) & XHCI_USB3_PORTSC_PLS; - /* Reset all or only disconnected ports */ - if (all || (status == XHCI_PLSR_RXDETECT || - status == XHCI_PLSR_POLLING)) - usb_xhci_reset_port_usb3(mem_base, port); - else - port_disabled |= 1 << port; - } - - /* Wait for warm reset complete on all reset ports */ - for (timeout = XHCI_RESET_TIMEOUT; timeout; timeout--) { - int complete = 1; - for (port = 0; port < port_count; port++) { - /* Only check ports that were reset */ - if (port_disabled & (1 << port)) - continue; - /* Check if warm reset is complete */ - status = read32(mem_base + XHCI_USB3_PORTSC(port)); - if (!(status & XHCI_USB3_PORTSC_WRC)) - complete = 0; - } - /* Check for warm reset complete in any port */ - if (complete) - break; - udelay(XHCI_RESET_DELAY_US); - } - - /* Clear port change status bits */ - for (port = 0; port < port_count; port++) - usb_xhci_reset_status_usb3(mem_base, port); -} - -/* Handler for XHCI controller on entry to S3/S4/S5 */ -void usb_xhci_sleep_prepare(pci_devfn_t dev, u8 slp_typ) -{ - u32 reg32; - u8 *mem_base = usb_xhci_mem_base(dev); - u8 is_broadwell = !!(cpu_family_model() == BROADWELL_FAMILY_ULT); - - if (!mem_base || slp_typ < ACPI_S3) - return; - - /* Set D0 state */ - pci_update_config16(dev, XHCI_PWR_CTL_STS, ~XHCI_PWR_CTL_SET_MASK, XHCI_PWR_CTL_SET_D0); - - if (!is_broadwell) { - /* This WA is only for lpt */ - - /* Clear PCI 0xB0[14:13] */ - pci_and_config32(dev, 0xb0, ~((1 << 14) | (1 << 13))); - - /* Clear MMIO 0x816c[14,2] */ - reg32 = read32(mem_base + 0x816c); - reg32 &= ~((1 << 14) | (1 << 2)); - write32(mem_base + 0x816c, reg32); - - /* Reset disconnected USB3 ports */ - usb_xhci_reset_usb3(dev, 0); - - /* Set MMIO 0x80e0[15] */ - reg32 = read32(mem_base + 0x80e0); - reg32 |= (1 << 15); - write32(mem_base + 0x80e0, reg32); - } else { - /* - * Clear port change status bits. Clearing CSC alone seemed to - * fix wakeup from S3 if entering USB compliance state even if - * bit wasn't set on the port. - */ - int port; - for (port = 0; port < usb_xhci_port_count_usb3(dev); port++) - usb_xhci_reset_status_usb3(mem_base, port); - } - - reg32 = read32(mem_base + 0x8154); - reg32 &= ~(1 << 31); - write32(mem_base + 0x8154, reg32); - - /* Set D3Hot state and enable PME */ - pci_or_config16(dev, XHCI_PWR_CTL_STS, XHCI_PWR_CTL_SET_D3); - pci_or_config16(dev, XHCI_PWR_CTL_STS, XHCI_PWR_CTL_STATUS_PME); - pci_or_config16(dev, XHCI_PWR_CTL_STS, XHCI_PWR_CTL_ENABLE_PME); -} -#else /* !__SIMPLE_DEVICE__ */ - -static void xhci_init(struct device *dev) -{ - struct resource *res = find_resource(dev, PCI_BASE_ADDRESS_0); - u16 reg16; - u32 reg32; - - /* Ensure controller is in D0 state */ - reg16 = pci_read_config16(dev, XHCI_PWR_CTL_STS); - reg16 &= ~XHCI_PWR_CTL_SET_MASK; - reg16 |= XHCI_PWR_CTL_SET_D0; - pci_write_config16(dev, XHCI_PWR_CTL_STS, reg16); - - /* Disable Compliance Mode Entry */ - reg32 = read32(res2mmio(res, 0x80ec, 0)); - reg32 |= (1 << 0); - write32(res2mmio(res, 0x80ec, 0), reg32); -} - -static struct device_operations usb_xhci_ops = { - .read_resources = pci_dev_read_resources, - .set_resources = pci_dev_set_resources, - .enable_resources = pci_dev_enable_resources, - .ops_pci = &pci_dev_ops_pci, - .init = xhci_init, -}; - -static const unsigned short pci_device_ids[] = { - 0x9c31, /* LynxPoint-LP */ - 0x9cb1, /* WildcatPoint */ - 0 -}; - -static const struct pci_driver pch_usb_xhci __pci_driver = { - .ops = &usb_xhci_ops, - .vendor = PCI_VENDOR_ID_INTEL, - .devices = pci_device_ids, -}; -#endif /* !__SIMPLE_DEVICE__ */ -- cgit v1.2.3