diff options
Diffstat (limited to 'src/southbridge/amd/rs690/rs690_pcie.c')
-rw-r--r-- | src/southbridge/amd/rs690/rs690_pcie.c | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/src/southbridge/amd/rs690/rs690_pcie.c b/src/southbridge/amd/rs690/rs690_pcie.c new file mode 100644 index 0000000000..7b09a935d9 --- /dev/null +++ b/src/southbridge/amd/rs690/rs690_pcie.c @@ -0,0 +1,422 @@ +/* + * 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 <console/console.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +#include <delay.h> +#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))) { + } + /* 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_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 struct pci_operations lops_pci = { + .set_subsystem = 0, +}; + +static struct device_operations pcie_ops = { + .read_resources = pci_bus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_bus_enable_resources, + .init = pcie_init, + .scan_bus = pci_scan_bridge, + /*.enable = rs690_enable, */ + .ops_pci = &lops_pci, +}; + +static struct pci_driver pcie_driver __pci_driver = { + .ops = &pcie_ops, + .vendor = PCI_VENDOR_ID_ATI, + .device = PCI_DEVICE_ID_ATI_RS690_PCIE, +}; + +static struct pci_driver pcie_driver_dev7 __pci_driver = { + .ops = &pcie_ops, + .vendor = PCI_VENDOR_ID_ATI, + .device = PCI_DEVICE_ID_ATI_RS690_PCIE_DEV7, +}; +static struct pci_driver pcie_driver_dev8 __pci_driver = { + .ops = &pcie_ops, + .vendor = PCI_VENDOR_ID_ATI, + .device = PCI_DEVICE_ID_ATI_RS690_PCIE_DEV8, +}; + +/********************************************************************** +**********************************************************************/ +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 &= 0xff0f; /* clean */ + reg |= cfg->gpp_configuration; + 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 &= 0x1f; /* remain LSB 5 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_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_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_debug("gpp_sb_init nb_dev=0x%x, dev=0x%x, 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 0*/ + set_pcie_enable_bits(dev, 0x70, 7 << 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(dev, 0x20, 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_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) { + /* 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); +#if 0 + 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); +} |