/* * This file is part of the coreboot project. * * Copyright (C) 2008 Advanced Micro Devices, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "rs690.h" /*------------------------------------------------ * Global variable ------------------------------------------------*/ PCIE_CFG AtiPcieCfg = { PCIE_ENABLE_STATIC_DEV_REMAP, /* Config */ 0, /* ResetReleaseDelay */ 0, /* Gfx0Width */ 0, /* Gfx1Width */ 0, /* GfxPayload */ 0, /* GppPayload */ 0, /* PortDetect, filled by GppSbInit */ 0, /* PortHp */ 0, /* DbgConfig */ 0, /* DbgConfig2 */ 0, /* GfxLx */ 0, /* GppLx */ 0, /* NBSBLx */ 0, /* PortSlotInit */ 0, /* Gfx0Pwr */ 0, /* Gfx1Pwr */ 0 /* GppPwr */ }; static void PciePowerOffGppPorts(device_t nb_dev, device_t dev, u32 port); static void ValidatePortEn(device_t nb_dev); static void ValidatePortEn(device_t nb_dev) { } /***************************************************************** * Compliant with CIM_33's PCIEPowerOffGppPorts * Power off unused GPP lines *****************************************************************/ static void PciePowerOffGppPorts(device_t nb_dev, device_t dev, u32 port) { u32 reg; u16 state_save; struct southbridge_amd_rs690_config *cfg = (struct southbridge_amd_rs690_config *)nb_dev->chip_info; u8 state = cfg->port_enable; if (!(AtiPcieCfg.Config & PCIE_DISABLE_HIDE_UNUSED_PORTS)) state &= AtiPcieCfg.PortDetect; state = ~state; state &= (1 << 4) + (1 << 5) + (1 << 6) + (1 << 7); state_save = state << 17; state &= !(AtiPcieCfg.PortHp); reg = nbmisc_read_index(nb_dev, 0x0c); reg |= state; nbmisc_write_index(nb_dev, 0x0c, reg); reg = nbmisc_read_index(nb_dev, 0x08); reg |= state_save; nbmisc_write_index(nb_dev, 0x08, reg); if ((AtiPcieCfg.Config & PCIE_OFF_UNUSED_GPP_LANES) && !(AtiPcieCfg. Config & (PCIE_DISABLE_HIDE_UNUSED_PORTS + PCIE_GFX_COMPLIANCE))) { } if (!cfg->gfx_tmds){ /* step 3 Power Down Control for Southbridge */ reg = nbpcie_p_read_index(dev, 0xa2); switch ((reg >> 4) & 0x7) { /* get bit 4-6, LC_LINK_WIDTH_RD */ case 1: nbpcie_ind_write_index(nb_dev, 0x65, 0x0e0e); break; case 2: nbpcie_ind_write_index(nb_dev, 0x65, 0x0c0c); break; default: break; } } } static void pcie_init(struct device *dev) { /* Enable pci error detecting */ u32 dword; printk(BIOS_DEBUG, "pcie_init in rs690_pcie.c\n"); /* System error enable */ dword = pci_read_config32(dev, 0x04); dword |= (1 << 8); /* System error enable */ dword |= (1 << 30); /* Clear possible errors */ pci_write_config32(dev, 0x04, dword); } /********************************************************************** **********************************************************************/ static void switching_gpp_configurations(device_t nb_dev, device_t sb_dev) { u32 reg; struct southbridge_amd_rs690_config *cfg = (struct southbridge_amd_rs690_config *)nb_dev->chip_info; /* enables GPP reconfiguration */ reg = nbmisc_read_index(nb_dev, PCIE_NBCFG_REG7); reg |= (RECONFIG_GPPSB_EN + RECONFIG_GPPSB_LINK_CONFIG + RECONFIG_GPPSB_ATOMIC_RESET); nbmisc_write_index(nb_dev, PCIE_NBCFG_REG7, reg); /* sets desired GPPSB configurations, bit4-7 */ reg = nbmisc_read_index(nb_dev, 0x67); reg &= 0xffffff0f; /* clean */ reg |= cfg->gpp_configuration << 4; nbmisc_write_index(nb_dev, 0x67, reg); /* read bit14 and write back its inverst value */ reg = nbmisc_read_index(nb_dev, PCIE_NBCFG_REG7); reg ^= RECONFIG_GPPSB_GPPSB; nbmisc_write_index(nb_dev, PCIE_NBCFG_REG7, reg); /* delay 1ms */ mdelay(1); /* waits until SB has trained to L0, poll for bit0-5 = 0x10 */ do { reg = nbpcie_p_read_index(sb_dev, PCIE_LC_STATE0); reg &= 0x3f; /* remain LSB [5:0] bits */ } while (LC_STATE_RECONFIG_GPPSB != reg); /* ensures that virtual channel negotiation is completed. poll for bit1 = 0 */ do { reg = pci_ext_read_config32(nb_dev, sb_dev, PCIE_VC0_RESOURCE_STATUS); } while (reg & VC_NEGOTIATION_PENDING); } /***************************************************************** * The rs690 uses NBCONFIG:0x1c (BAR3) to map the PCIE Extended Configuration * Space to a 256MB range within the first 4GB of addressable memory. *****************************************************************/ void enable_pcie_bar3(device_t nb_dev) { printk(BIOS_DEBUG, "enable_pcie_bar3()\n"); set_nbcfg_enable_bits(nb_dev, 0x7C, 1 << 30, 1 << 30); /* Enables writes to the BAR3 register. */ set_nbcfg_enable_bits(nb_dev, 0x84, 7 << 16, 0 << 16); pci_write_config32(nb_dev, 0x1C, EXT_CONF_BASE_ADDRESS); /* PCIEMiscInit */ pci_write_config32(nb_dev, 0x20, 0x00000000); set_htiu_enable_bits(nb_dev, 0x32, 1 << 28, 1 << 28); /* PCIEMiscInit */ ProgK8TempMmioBase(1, EXT_CONF_BASE_ADDRESS, TEMP_MMIO_BASE_ADDRESS); } /***************************************************************** * We should disable bar3 when we want to exit rs690_enable, because bar3 will be * remapped in set_resource later. *****************************************************************/ void disable_pcie_bar3(device_t nb_dev) { printk(BIOS_DEBUG, "disable_pcie_bar3()\n"); set_nbcfg_enable_bits(nb_dev, 0x7C, 1 << 30, 0 << 30); /* Disable writes to the BAR3. */ pci_write_config32(nb_dev, 0x1C, 0); /* clear BAR3 address */ ProgK8TempMmioBase(0, EXT_CONF_BASE_ADDRESS, TEMP_MMIO_BASE_ADDRESS); } /***************************************** * Compliant with CIM_33's PCIEGPPInit * nb_dev: * root bridge struct * dev: * p2p bridge struct * port: * p2p bridge number, 4-8 *****************************************/ void rs690_gpp_sb_init(device_t nb_dev, device_t dev, u32 port) { u8 reg8; u16 reg16; device_t sb_dev; struct southbridge_amd_rs690_config *cfg = (struct southbridge_amd_rs690_config *)nb_dev->chip_info; printk(BIOS_DEBUG, "gpp_sb_init nb_dev=0x%p, dev=0x%p, port=0x%x\n", nb_dev, dev, port); /* init GPP core */ set_pcie_enable_bits(nb_dev, 0x20 | PCIE_CORE_INDEX_GPPSB, 1 << 8, 1 << 8); /* PCIE initialization 5.10.2: rpr 2.12*/ set_pcie_enable_bits(nb_dev, 0x02 | PCIE_CORE_INDEX_GPPSB, 1 << 0, 1 << 0); /* no description in datasheet. */ /* init GPPSB port */ /* Sets RCB timeout to be 100ms by setting bits[18:16] to 3 b101 and shortens the enumeration timer by setting bit[19] to 1*/ set_pcie_enable_bits(dev, 0x70, 0xF << 16, 0xd << 16); /* PCIE initialization 5.10.2: rpr 2.4 */ set_pcie_enable_bits(dev, 0x02, ~0xffffffff, 1 << 14); /* Do not gate the electrical idle from the PHY and enables the escape from L1L23 */ set_pcie_enable_bits(dev, 0xA0, ~0xffffffbf, (3 << 30) | (3 << 12) | (3 << 4)); /* PCIE initialization 5.10.2: rpr 2.13 */ set_pcie_enable_bits(dev, 0x02, ~0xffffffff, 1 << 6); /* SLOT_IMPLEMENTED in pcieConfig space */ reg8 = pci_read_config8(dev, 0x5b); reg8 |= 1 << 0; pci_write_config8(dev, 0x5b, reg8); reg16 = pci_read_config16(dev, 0x5a); reg16 |= 0x100; pci_write_config16(dev, 0x5a, reg16); nbmisc_write_index(nb_dev, 0x34, 0); /* check compliance rpr step 2.1*/ if (AtiPcieCfg.Config & PCIE_GPP_COMPLIANCE) { u32 tmp; tmp = nbmisc_read_index(nb_dev, 0x67); tmp |= 1 << 3; nbmisc_write_index(nb_dev, 0x67, tmp); } /* step 5: dynamic slave CPL buffer allocation */ set_pcie_enable_bits(nb_dev, 0x20 | PCIE_CORE_INDEX_GPPSB, 1 << 11, 1 << 11); /* step 5a: Training for GPP devices */ /* init GPP */ switch (port) { case 4: /* GPP */ case 5: case 6: case 7: /* Blocks DMA traffic during C3 state */ set_pcie_enable_bits(dev, 0x10, 1 << 0, 0 << 0); /* Enabels TLP flushing */ set_pcie_enable_bits(dev, 0x20, 1 << 19, 0 << 19); /* check port enable */ if (cfg->port_enable & (1 << port)) { PcieReleasePortTraining(nb_dev, dev, port); if (!(AtiPcieCfg.Config & PCIE_GPP_COMPLIANCE)) { u8 res = PcieTrainPort(nb_dev, dev, port); printk(BIOS_DEBUG, "PcieTrainPort port=0x%x result=%d\n", port, res); if (res) { AtiPcieCfg.PortDetect |= 1 << port; } } } break; case 8: /* SB */ break; } PciePowerOffGppPorts(nb_dev, dev, port); /* step 5b: GFX devices in a GPP slot */ /* step 6a: VCI */ sb_dev = dev_find_slot(0, PCI_DEVFN(8, 0)); if (port == 8) { /* The code below between #if and #endif causes a hang on HDA init. * So we skip it. */ #if 0 /* Clear bits 7:1 */ pci_ext_write_config32(nb_dev, sb_dev, 0x114, 0x3f << 1, 0 << 1); /* Maps Traffic Class 1-7 to VC1 */ pci_ext_write_config32(nb_dev, sb_dev, 0x120, 0x7f << 1, 0x7f << 1); /* Assigns VC ID to 1 */ pci_ext_write_config32(nb_dev, sb_dev, 0x120, 7 << 24, 1 << 24); /* Enables VC1 */ pci_ext_write_config32(nb_dev, sb_dev, 0x120, 1 << 31, 1 << 31); do { reg16 = pci_ext_read_config32(nb_dev, sb_dev, 0x124); reg16 &= 0x2; } while (reg16); /*bit[1] = 0 means VC1 flow control initialization is successful */ #endif } /* step 6b: L0s for the southbridge link */ /* To enalbe L0s in the southbridage*/ /* step 6c: L0s for the GPP link(s) */ /* To eable L0s in the RS690 for the GPP port(s) */ set_pcie_enable_bits(nb_dev, 0xf9, 3 << 13, 2 << 13); set_pcie_enable_bits(dev, 0xa0, 0xf << 8, 0x9 << 8); reg16 = pci_read_config16(dev, 0x68); reg16 |= 1 << 0; pci_write_config16(dev, 0x68, reg16); /* step 6d: ASPM L1 for the southbridge link */ /* To enalbe L1s in the southbridage*/ /* step 6e: ASPM L1 for GPP link(s) */; set_pcie_enable_bits(nb_dev, 0xf9, 3 << 13, 2 << 13); set_pcie_enable_bits(dev, 0xa0, 3 << 12, 3 << 12); set_pcie_enable_bits(dev, 0xa0, 0xf << 4, 3 << 4); reg16 = pci_read_config16(dev, 0x68); reg16 &= ~0xff; reg16 |= 1 << 1; pci_write_config16(dev, 0x68, reg16); /* step 6f: Turning off PLL during L1/L23 */ set_pcie_enable_bits(nb_dev, 0x40, 1 << 3, 1 << 3); set_pcie_enable_bits(nb_dev, 0x40, 1 << 9, 1 << 9); /* step 6g: TXCLK clock gating */ set_nbmisc_enable_bits(nb_dev, 0x7, 3 << 4, 3 << 4); set_nbmisc_enable_bits(nb_dev, 0x7, 1 << 22, 1 << 22); set_pcie_enable_bits(nb_dev, 0x11, 0xf << 4, 0xc << 4); /* step 6h: LCLK clock gating, done in rs690_config_misc_clk() */ } /***************************************** * Compliant with CIM_33's PCIEConfigureGPPCore *****************************************/ void config_gpp_core(device_t nb_dev, device_t sb_dev) { u32 reg; struct southbridge_amd_rs690_config *cfg = (struct southbridge_amd_rs690_config *)nb_dev->chip_info; reg = nbmisc_read_index(nb_dev, 0x20); if (AtiPcieCfg.Config & PCIE_ENABLE_STATIC_DEV_REMAP) reg &= 0xfffffffd; /* set bit1 = 0 */ else reg |= 0x2; /* set bit1 = 1 */ nbmisc_write_index(nb_dev, 0x20, reg); reg = nbmisc_read_index(nb_dev, 0x67); /* get STRAP_BIF_LINK_CONFIG_GPPSB at bit 4-7 */ if (cfg->gpp_configuration != ((reg >> 4) & 0xf)) switching_gpp_configurations(nb_dev, sb_dev); ValidatePortEn(nb_dev); } /***************************************** * Compliant with CIM_33's PCIEMiscClkProg *****************************************/ void pcie_config_misc_clk(device_t nb_dev) { u32 reg; struct bus pbus; /* fake bus for dev0 fun1 */ reg = pci_read_config32(nb_dev, 0x4c); reg |= 1 << 0; pci_write_config32(nb_dev, 0x4c, reg); if (AtiPcieCfg.Config & PCIE_GFX_CLK_GATING) { /* TXCLK Clock Gating */ set_nbmisc_enable_bits(nb_dev, 0x07, 3 << 0, 3 << 0); set_nbmisc_enable_bits(nb_dev, 0x07, 1 << 22, 1 << 22); set_pcie_enable_bits(nb_dev, 0x11 | PCIE_CORE_INDEX_GFX, (3 << 6) | (~0xf), 3 << 6); /* LCLK Clock Gating */ reg = pci_cf8_conf1.read32(&pbus, 0, 1, 0x94); reg &= ~(1 << 16); pci_cf8_conf1.write32(&pbus, 0, 1, 0x94, reg); } if (AtiPcieCfg.Config & PCIE_GPP_CLK_GATING) { /* TXCLK Clock Gating */ set_nbmisc_enable_bits(nb_dev, 0x07, 3 << 4, 3 << 4); set_nbmisc_enable_bits(nb_dev, 0x07, 1 << 22, 1 << 22); set_pcie_enable_bits(nb_dev, 0x11 | PCIE_CORE_INDEX_GPPSB, (3 << 6) | (~0xf), 3 << 6); /* LCLK Clock Gating */ reg = pci_cf8_conf1.read32(&pbus, 0, 1, 0x94); reg &= ~(1 << 24); pci_cf8_conf1.write32(&pbus, 0, 1, 0x94, reg); } reg = pci_read_config32(nb_dev, 0x4c); reg &= ~(1 << 0); pci_write_config32(nb_dev, 0x4c, reg); }