Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
| 4 | * Copyright (C) 2008 Advanced Micro Devices, Inc. |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; version 2 of the License. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 14 | */ |
| 15 | |
| 16 | /* |
| 17 | * for rs690 internal graphics device |
Martin Roth | a9e3a75 | 2014-12-16 20:52:23 -0700 | [diff] [blame] | 18 | * device id of internal graphics: |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 19 | * RS690M/T: 0x791f |
| 20 | * RS690: 0x791e |
| 21 | */ |
| 22 | #include <console/console.h> |
| 23 | #include <device/device.h> |
| 24 | #include <device/pci.h> |
| 25 | #include <device/pci_ids.h> |
| 26 | #include <device/pci_ops.h> |
| 27 | #include <delay.h> |
| 28 | #include "rs690.h" |
| 29 | |
| 30 | #define CLK_CNTL_INDEX 0x8 |
| 31 | #define CLK_CNTL_DATA 0xC |
| 32 | |
Stefan Reinauer | e46c1c8 | 2010-04-15 23:01:59 +0000 | [diff] [blame] | 33 | #ifdef UNUSED_CODE |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 34 | static u32 clkind_read(device_t dev, u32 index) |
| 35 | { |
| 36 | u32 gfx_bar2 = pci_read_config32(dev, 0x18) & ~0xF; |
| 37 | |
| 38 | *(u32*)(gfx_bar2+CLK_CNTL_INDEX) = index & 0x7F; |
| 39 | return *(u32*)(gfx_bar2+CLK_CNTL_DATA); |
| 40 | } |
Stefan Reinauer | 6a445e8 | 2010-04-09 11:34:59 +0000 | [diff] [blame] | 41 | #endif |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 42 | |
| 43 | static void clkind_write(device_t dev, u32 index, u32 data) |
| 44 | { |
| 45 | u32 gfx_bar2 = pci_read_config32(dev, 0x18) & ~0xF; |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 46 | /* printk(BIOS_INFO, "gfx bar 2 %02x\n", gfx_bar2); */ |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 47 | |
| 48 | *(u32*)(gfx_bar2+CLK_CNTL_INDEX) = index | 1<<7; |
| 49 | *(u32*)(gfx_bar2+CLK_CNTL_DATA) = data; |
| 50 | } |
| 51 | |
| 52 | /* |
| 53 | * pci_dev_read_resources thinks it is a IO type. |
| 54 | * We have to force it to mem type. |
| 55 | */ |
| 56 | static void rs690_gfx_read_resources(device_t dev) |
| 57 | { |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 58 | printk(BIOS_INFO, "rs690_gfx_read_resources.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 59 | |
| 60 | /* The initial value of 0x24 is 0xFFFFFFFF, which is confusing. |
| 61 | Even if we write 0xFFFFFFFF into it, it will be 0xFFF00000, |
| 62 | which tells us it is a memory address base. |
| 63 | */ |
| 64 | pci_write_config32(dev, 0x24, 0x00000000); |
| 65 | |
| 66 | /* Get the normal pci resources of this device */ |
| 67 | pci_dev_read_resources(dev); |
| 68 | compact_resources(dev); |
| 69 | } |
| 70 | |
| 71 | static void internal_gfx_pci_dev_init(struct device *dev) |
| 72 | { |
Joe Bao | 40d46ba | 2008-12-01 19:49:57 +0000 | [diff] [blame] | 73 | u16 deviceid, vendorid; |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 74 | deviceid = pci_read_config16(dev, PCI_DEVICE_ID); |
| 75 | vendorid = pci_read_config16(dev, PCI_VENDOR_ID); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 76 | printk(BIOS_INFO, "internal_gfx_pci_dev_init device=%x, vendor=%x.\n", |
Zheng Bao | 289a2f5 | 2009-10-16 07:44:04 +0000 | [diff] [blame] | 77 | deviceid, vendorid); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 78 | |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 79 | pci_dev_init(dev); |
| 80 | |
| 81 | /* clk ind */ |
| 82 | clkind_write(dev, 0x08, 0x01); |
| 83 | clkind_write(dev, 0x0C, 0x22); |
| 84 | clkind_write(dev, 0x0F, 0x0); |
| 85 | clkind_write(dev, 0x11, 0x0); |
| 86 | clkind_write(dev, 0x12, 0x0); |
| 87 | clkind_write(dev, 0x14, 0x0); |
| 88 | clkind_write(dev, 0x15, 0x0); |
| 89 | clkind_write(dev, 0x16, 0x0); |
| 90 | clkind_write(dev, 0x17, 0x0); |
| 91 | clkind_write(dev, 0x18, 0x0); |
| 92 | clkind_write(dev, 0x19, 0x0); |
| 93 | clkind_write(dev, 0x1A, 0x0); |
| 94 | clkind_write(dev, 0x1B, 0x0); |
| 95 | clkind_write(dev, 0x1C, 0x0); |
| 96 | clkind_write(dev, 0x1D, 0x0); |
| 97 | clkind_write(dev, 0x1E, 0x0); |
| 98 | clkind_write(dev, 0x26, 0x0); |
| 99 | clkind_write(dev, 0x27, 0x0); |
| 100 | clkind_write(dev, 0x28, 0x0); |
| 101 | clkind_write(dev, 0x5C, 0x0); |
| 102 | } |
| 103 | |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 104 | |
| 105 | /* |
| 106 | * Set registers in RS690 and CPU to enable the internal GFX. |
| 107 | * Please refer to CIM source code and BKDG. |
| 108 | */ |
| 109 | static void rs690_internal_gfx_enable(device_t dev) |
| 110 | { |
| 111 | u32 l_dword; |
| 112 | int i; |
Josef Kellermann | cf37a59 | 2011-02-14 19:19:58 +0000 | [diff] [blame] | 113 | device_t k8_f2 = 0; |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 114 | device_t nb_dev = dev_find_slot(0, 0); |
| 115 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 116 | printk(BIOS_INFO, "rs690_internal_gfx_enable dev=0x%p, nb_dev=0x%p.\n", dev, |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 117 | nb_dev); |
| 118 | |
| 119 | /* set APERTURE_SIZE, 128M. */ |
| 120 | l_dword = pci_read_config32(nb_dev, 0x8c); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 121 | printk(BIOS_INFO, "nb_dev, 0x8c=0x%x\n", l_dword); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 122 | l_dword &= 0xffffff8f; |
| 123 | pci_write_config32(nb_dev, 0x8c, l_dword); |
| 124 | |
| 125 | /* set TOM */ |
| 126 | rs690_set_tom(nb_dev); |
Joe Bao | 40d46ba | 2008-12-01 19:49:57 +0000 | [diff] [blame] | 127 | |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 128 | /* Enable 64bit mode. */ |
| 129 | set_nbmc_enable_bits(nb_dev, 0x5f, 0, 1 << 9); |
| 130 | set_nbmc_enable_bits(nb_dev, 0xb0, 0, 1 << 8); |
| 131 | |
| 132 | /* 64bit Latency. */ |
| 133 | set_nbmc_enable_bits(nb_dev, 0x5f, 0x7c00, 0x800); |
| 134 | |
| 135 | /* UMA dual channel control register. */ |
| 136 | nbmc_write_index(nb_dev, 0x86, 0x3d); |
| 137 | |
| 138 | /* check the setting later!! */ |
| 139 | set_htiu_enable_bits(nb_dev, 0x07, 1 << 7, 0); |
| 140 | |
| 141 | /* UMA mode, powerdown memory PLL. */ |
| 142 | set_nbmc_enable_bits(nb_dev, 0x74, 0, 1 << 31); |
| 143 | |
| 144 | /* Copy CPU DDR Controller to NB MC. */ |
| 145 | /* Why K8_MC_REG80 is special? */ |
| 146 | k8_f2 = dev_find_slot(0, PCI_DEVFN(0x18, 2)); |
| 147 | for (i = 0; i <= (0x80 - 0x40) / 4; i++) { |
| 148 | l_dword = pci_read_config32(k8_f2, 0x40 + i * 4); |
| 149 | nbmc_write_index(nb_dev, 0x63 + i, l_dword); |
| 150 | } |
| 151 | |
| 152 | /* Set K8 MC for UMA, Family F. */ |
| 153 | l_dword = pci_read_config32(k8_f2, 0xa0); |
| 154 | l_dword |= 0x2c; |
| 155 | pci_write_config32(k8_f2, 0xa0, l_dword); |
| 156 | l_dword = pci_read_config32(k8_f2, 0x94); |
| 157 | l_dword &= 0xf0ffffff; |
| 158 | l_dword |= 0x07000000; |
| 159 | pci_write_config32(k8_f2, 0x94, l_dword); |
| 160 | |
| 161 | /* set FB size and location. */ |
| 162 | nbmc_write_index(nb_dev, 0x1b, 0x00); |
| 163 | l_dword = nbmc_read_index(nb_dev, 0x1c); |
| 164 | l_dword &= 0xffff0; |
| 165 | l_dword |= 0x400 << 20; |
| 166 | l_dword |= 0x4; |
| 167 | nbmc_write_index(nb_dev, 0x1c, l_dword); |
| 168 | l_dword = nbmc_read_index(nb_dev, 0x1d); |
| 169 | l_dword &= 0xfffff000; |
| 170 | l_dword |= 0x0400; |
| 171 | nbmc_write_index(nb_dev, 0x1d, l_dword); |
| 172 | nbmc_write_index(nb_dev, 0x100, 0x3fff3800); |
| 173 | |
| 174 | /* Program MC table. */ |
| 175 | set_nbmc_enable_bits(nb_dev, 0x00, 0, 1 << 31); |
| 176 | l_dword = nbmc_read_index(nb_dev, 0x91); |
| 177 | l_dword |= 0x5; |
| 178 | nbmc_write_index(nb_dev, 0x91, l_dword); |
| 179 | set_nbmc_enable_bits(nb_dev, 0xb1, 0, 1 << 6); |
| 180 | set_nbmc_enable_bits(nb_dev, 0xc3, 0, 1); |
| 181 | |
| 182 | /* TODO: the optimization of voltage and frequency */ |
| 183 | } |
| 184 | |
Josef Kellermann | a96b218 | 2011-02-03 09:29:57 +0000 | [diff] [blame] | 185 | static void gfx_dev_set_subsystem(struct device *dev, unsigned vendor, unsigned device) |
| 186 | { |
| 187 | pci_write_config32(dev, 0x4c, ((device & 0xffff) << 16) | (vendor & 0xffff)); |
| 188 | } |
| 189 | |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 190 | static struct pci_operations lops_pci = { |
Josef Kellermann | a96b218 | 2011-02-03 09:29:57 +0000 | [diff] [blame] | 191 | .set_subsystem = gfx_dev_set_subsystem, |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 192 | }; |
| 193 | |
Joe Bao | 40d46ba | 2008-12-01 19:49:57 +0000 | [diff] [blame] | 194 | static struct device_operations pcie_ops = { |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 195 | .read_resources = rs690_gfx_read_resources, |
Joe Bao | 40d46ba | 2008-12-01 19:49:57 +0000 | [diff] [blame] | 196 | .set_resources = pci_dev_set_resources, |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 197 | .enable_resources = pci_dev_enable_resources, |
| 198 | .init = internal_gfx_pci_dev_init, /* The option ROM initializes the device. rs690_gfx_init, */ |
| 199 | .scan_bus = 0, |
| 200 | .enable = rs690_internal_gfx_enable, |
| 201 | .ops_pci = &lops_pci, |
| 202 | }; |
| 203 | |
Zheng Bao | bb069e1 | 2008-12-17 02:14:24 +0000 | [diff] [blame] | 204 | /* |
| 205 | * The dev id of 690G is 791E, while the id of 690M, 690T is 791F. |
| 206 | * We should list both of them here. |
| 207 | * */ |
Stefan Reinauer | 8e96ba2 | 2010-03-16 23:33:29 +0000 | [diff] [blame] | 208 | static const struct pci_driver pcie_driver_690t __pci_driver = { |
Joe Bao | 40d46ba | 2008-12-01 19:49:57 +0000 | [diff] [blame] | 209 | .ops = &pcie_ops, |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 210 | .vendor = PCI_VENDOR_ID_ATI, |
| 211 | .device = PCI_DEVICE_ID_ATI_RS690MT_INT_GFX, |
| 212 | }; |
| 213 | |
Stefan Reinauer | 8e96ba2 | 2010-03-16 23:33:29 +0000 | [diff] [blame] | 214 | static const struct pci_driver pcie_driver_690 __pci_driver = { |
Zheng Bao | bb069e1 | 2008-12-17 02:14:24 +0000 | [diff] [blame] | 215 | .ops = &pcie_ops, |
| 216 | .vendor = PCI_VENDOR_ID_ATI, |
| 217 | .device = PCI_DEVICE_ID_ATI_RS690_INT_GFX, |
| 218 | }; |
| 219 | |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 220 | /* step 12 ~ step 14 from rpr */ |
| 221 | static void single_port_configuration(device_t nb_dev, device_t dev) |
| 222 | { |
| 223 | u8 result, width; |
| 224 | u32 reg32; |
| 225 | struct southbridge_amd_rs690_config *cfg = |
| 226 | (struct southbridge_amd_rs690_config *)nb_dev->chip_info; |
| 227 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 228 | printk(BIOS_INFO, "rs690_gfx_init single_port_configuration.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 229 | |
| 230 | /* step 12 training, releases hold training for GFX port 0 (device 2) */ |
| 231 | set_nbmisc_enable_bits(nb_dev, 0x8, 1 << 4, 0<<4); |
| 232 | PcieReleasePortTraining(nb_dev, dev, 2); |
| 233 | result = PcieTrainPort(nb_dev, dev, 2); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 234 | printk(BIOS_INFO, "rs690_gfx_init single_port_configuration step12.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 235 | |
| 236 | /* step 13 Power Down Control */ |
| 237 | /* step 13.1 Enables powering down transmitter and receiver pads along with PLL macros. */ |
| 238 | set_pcie_enable_bits(nb_dev, 0x40, 1 << 0, 1 << 0); |
| 239 | |
| 240 | /* step 13.a Link Training was NOT successful */ |
| 241 | if (!result) { |
| 242 | set_nbmisc_enable_bits(nb_dev, 0x8, 0, 0x3 << 4); /* prevent from training. */ |
| 243 | set_nbmisc_enable_bits(nb_dev, 0xc, 0, 0x3 << 2); /* hide the GFX bridge. */ |
| 244 | if (cfg->gfx_tmds) |
| 245 | nbpcie_ind_write_index(nb_dev, 0x65, 0xccf0f0); |
| 246 | else { |
| 247 | nbpcie_ind_write_index(nb_dev, 0x65, 0xffffffff); |
| 248 | set_nbmisc_enable_bits(nb_dev, 0x7, 1 << 3, 1 << 3); |
| 249 | } |
| 250 | } else { /* step 13.b Link Training was successful */ |
| 251 | |
| 252 | reg32 = nbpcie_p_read_index(dev, 0xa2); |
| 253 | width = (reg32 >> 4) & 0x7; |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 254 | printk(BIOS_DEBUG, "GFX LC_LINK_WIDTH = 0x%x.\n", width); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 255 | switch (width) { |
| 256 | case 1: |
| 257 | case 2: |
| 258 | nbpcie_ind_write_index(nb_dev, 0x65, |
| 259 | cfg->gfx_lane_reversal ? 0x7f7f : 0xccfefe); |
| 260 | break; |
| 261 | case 4: |
| 262 | nbpcie_ind_write_index(nb_dev, 0x65, |
| 263 | cfg->gfx_lane_reversal ? 0x3f3f : 0xccfcfc); |
| 264 | break; |
| 265 | case 8: |
| 266 | nbpcie_ind_write_index(nb_dev, 0x65, |
| 267 | cfg->gfx_lane_reversal ? 0x0f0f : 0xccf0f0); |
| 268 | break; |
| 269 | } |
| 270 | } |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 271 | printk(BIOS_INFO, "rs690_gfx_init single_port_configuration step13.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 272 | |
| 273 | /* step 14 Reset Enumeration Timer, disables the shortening of the enumeration timer */ |
| 274 | set_pcie_enable_bits(dev, 0x70, 1 << 19, 0 << 19); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 275 | printk(BIOS_INFO, "rs690_gfx_init single_port_configuration step14.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 276 | } |
| 277 | |
| 278 | /* step 15 ~ step 18 from rpr */ |
| 279 | static void dual_port_configuration(device_t nb_dev, device_t dev) |
| 280 | { |
| 281 | u8 result, width; |
| 282 | u32 reg32; |
| 283 | struct southbridge_amd_rs690_config *cfg = |
| 284 | (struct southbridge_amd_rs690_config *)nb_dev->chip_info; |
| 285 | |
| 286 | /* step 15: Training for Device 2 */ |
| 287 | set_nbmisc_enable_bits(nb_dev, 0x8, 1 << 4, 0 << 4); |
| 288 | /* Releases hold training for GFX port 0 (device 2) */ |
| 289 | PcieReleasePortTraining(nb_dev, dev, 2); |
| 290 | /* PCIE Link Training Sequence */ |
| 291 | result = PcieTrainPort(nb_dev, dev, 2); |
| 292 | |
| 293 | /* step 16: Power Down Control for Device 2 */ |
| 294 | /* step 16.a Link Training was NOT successful */ |
| 295 | if (!result) { |
| 296 | /* Powers down all lanes for port A */ |
| 297 | nbpcie_ind_write_index(nb_dev, 0x65, 0x0f0f); |
| 298 | } else { /* step 16.b Link Training was successful */ |
| 299 | |
| 300 | reg32 = nbpcie_p_read_index(dev, 0xa2); |
| 301 | width = (reg32 >> 4) & 0x7; |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 302 | printk(BIOS_DEBUG, "GFX LC_LINK_WIDTH = 0x%x.\n", width); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 303 | switch (width) { |
| 304 | case 1: |
| 305 | case 2: |
| 306 | nbpcie_ind_write_index(nb_dev, 0x65, |
| 307 | cfg->gfx_lane_reversal ? 0x0707 : 0x0e0e); |
| 308 | break; |
| 309 | case 4: |
| 310 | nbpcie_ind_write_index(nb_dev, 0x65, |
| 311 | cfg->gfx_lane_reversal ? 0x0303 : 0x0c0c); |
| 312 | break; |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | /* step 17: Training for Device 3 */ |
| 317 | set_nbmisc_enable_bits(nb_dev, 0x8, 1 << 5, 0 << 5); |
| 318 | /* Releases hold training for GFX port 0 (device 3) */ |
| 319 | PcieReleasePortTraining(nb_dev, dev, 3); |
| 320 | /* PCIE Link Training Sequence */ |
| 321 | result = PcieTrainPort(nb_dev, dev, 3); |
| 322 | |
| 323 | /*step 18: Power Down Control for Device 3 */ |
| 324 | /* step 18.a Link Training was NOT successful */ |
| 325 | if (!result) { |
| 326 | /* Powers down all lanes for port B and PLL1 */ |
| 327 | nbpcie_ind_write_index(nb_dev, 0x65, 0xccf0f0); |
| 328 | } else { /* step 18.b Link Training was successful */ |
| 329 | |
| 330 | reg32 = nbpcie_p_read_index(dev, 0xa2); |
| 331 | width = (reg32 >> 4) & 0x7; |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 332 | printk(BIOS_DEBUG, "GFX LC_LINK_WIDTH = 0x%x.\n", width); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 333 | switch (width) { |
| 334 | case 1: |
| 335 | case 2: |
| 336 | nbpcie_ind_write_index(nb_dev, 0x65, |
| 337 | cfg->gfx_lane_reversal ? 0x7070 : 0xe0e0); |
| 338 | break; |
| 339 | case 4: |
| 340 | nbpcie_ind_write_index(nb_dev, 0x65, |
| 341 | cfg->gfx_lane_reversal ? 0x3030 : 0xc0c0); |
| 342 | break; |
| 343 | } |
| 344 | } |
| 345 | } |
| 346 | |
| 347 | |
Joe Bao | 40d46ba | 2008-12-01 19:49:57 +0000 | [diff] [blame] | 348 | /* For single port GFX configuration Only |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 349 | * width: |
| 350 | * 000 = x16 |
| 351 | * 001 = x1 |
| 352 | * 010 = x2 |
| 353 | * 011 = x4 |
| 354 | * 100 = x8 |
| 355 | * 101 = x12 (not supported) |
| 356 | * 110 = x16 |
| 357 | */ |
| 358 | static void dynamic_link_width_control(device_t nb_dev, device_t dev, u8 width) |
| 359 | { |
| 360 | u32 reg32; |
| 361 | device_t sb_dev; |
| 362 | struct southbridge_amd_rs690_config *cfg = |
| 363 | (struct southbridge_amd_rs690_config *)nb_dev->chip_info; |
| 364 | |
| 365 | /* step 5.9.1.1 */ |
| 366 | reg32 = nbpcie_p_read_index(dev, 0xa2); |
| 367 | |
| 368 | /* step 5.9.1.2 */ |
| 369 | set_pcie_enable_bits(nb_dev, 0x40, 1 << 0, 1 << 0); |
| 370 | /* step 5.9.1.3 */ |
| 371 | set_pcie_enable_bits(dev, 0xa2, 3 << 0, width << 0); |
| 372 | /* step 5.9.1.4 */ |
| 373 | set_pcie_enable_bits(dev, 0xa2, 1 << 8, 1 << 8); |
| 374 | /* step 5.9.2.4 */ |
| 375 | if (0 == cfg->gfx_reconfiguration) |
| 376 | set_pcie_enable_bits(dev, 0xa2, 1 << 11, 1 << 11); |
| 377 | |
| 378 | /* step 5.9.1.5 */ |
| 379 | do { |
| 380 | reg32 = nbpcie_p_read_index(dev, 0xa2); |
| 381 | } |
| 382 | while (reg32 & 0x100); |
| 383 | |
| 384 | /* step 5.9.1.6 */ |
| 385 | sb_dev = dev_find_slot(0, PCI_DEVFN(8, 0)); |
| 386 | do { |
| 387 | reg32 = pci_ext_read_config32(nb_dev, sb_dev, |
| 388 | PCIE_VC0_RESOURCE_STATUS); |
| 389 | } while (reg32 & VC_NEGOTIATION_PENDING); |
| 390 | |
| 391 | /* step 5.9.1.7 */ |
| 392 | reg32 = nbpcie_p_read_index(dev, 0xa2); |
| 393 | if (((reg32 & 0x70) >> 4) != 0x6) { |
| 394 | /* the unused lanes should be powered off. */ |
| 395 | } |
| 396 | |
| 397 | /* step 5.9.1.8 */ |
| 398 | set_pcie_enable_bits(nb_dev, 0x40, 1 << 0, 0 << 0); |
| 399 | } |
| 400 | |
| 401 | /* |
| 402 | * GFX Core initialization, dev2, dev3 |
| 403 | */ |
| 404 | void rs690_gfx_init(device_t nb_dev, device_t dev, u32 port) |
| 405 | { |
| 406 | u16 reg16; |
| 407 | struct southbridge_amd_rs690_config *cfg = |
| 408 | (struct southbridge_amd_rs690_config *)nb_dev->chip_info; |
| 409 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 410 | printk(BIOS_INFO, "rs690_gfx_init, nb_dev=0x%p, dev=0x%p, port=0x%x.\n", |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 411 | nb_dev, dev, port); |
| 412 | |
| 413 | /* step 0, REFCLK_SEL, skip A11 revision */ |
| 414 | set_nbmisc_enable_bits(nb_dev, 0x6a, 1 << 9, |
| 415 | cfg->gfx_dev2_dev3 ? 1 << 9 : 0 << 9); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 416 | printk(BIOS_INFO, "rs690_gfx_init step0.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 417 | |
| 418 | /* step 1, lane reversal (only need if CMOS option is enabled) */ |
| 419 | if (cfg->gfx_lane_reversal) { |
| 420 | set_nbmisc_enable_bits(nb_dev, 0x33, 1 << 2, 1 << 2); |
| 421 | if (cfg->gfx_dual_slot) |
| 422 | set_nbmisc_enable_bits(nb_dev, 0x33, 1 << 3, 1 << 3); |
| 423 | } |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 424 | printk(BIOS_INFO, "rs690_gfx_init step1.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 425 | |
| 426 | /* step 1.1, dual-slot gfx configuration (only need if CMOS option is enabled) */ |
| 427 | /* AMD calls the configuration CrossFire */ |
| 428 | if (cfg->gfx_dual_slot) |
| 429 | set_nbmisc_enable_bits(nb_dev, 0x0, 0xf << 8, 5 << 8); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 430 | printk(BIOS_INFO, "rs690_gfx_init step2.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 431 | |
| 432 | /* step 2, TMDS, (only need if CMOS option is enabled) */ |
| 433 | if (cfg->gfx_tmds) { |
| 434 | } |
| 435 | |
| 436 | /* step 3, GFX overclocking, (only need if CMOS option is enabled) */ |
| 437 | /* skip */ |
| 438 | |
| 439 | /* step 4, reset the GFX link */ |
| 440 | /* step 4.1 asserts both calibration reset and global reset */ |
| 441 | set_nbmisc_enable_bits(nb_dev, 0x8, 0x3 << 14, 0x3 << 14); |
| 442 | |
| 443 | /* step 4.2 de-asserts calibration reset */ |
| 444 | set_nbmisc_enable_bits(nb_dev, 0x8, 1 << 14, 0 << 14); |
| 445 | |
| 446 | /* step 4.3 wait for at least 200us */ |
| 447 | udelay(200); |
| 448 | |
| 449 | /* step 4.4 de-asserts global reset */ |
| 450 | set_nbmisc_enable_bits(nb_dev, 0x8, 1 << 15, 0 << 15); |
| 451 | |
| 452 | /* step 4.5 asserts both calibration reset and global reset */ |
| 453 | /* a weird step in RPR, don't do that */ |
| 454 | /* set_nbmisc_enable_bits(nb_dev, 0x8, 0x3 << 14, 0x3 << 14); */ |
| 455 | |
| 456 | /* step 4.6 bring external GFX device out of reset, wait for 1ms */ |
| 457 | mdelay(1); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 458 | printk(BIOS_INFO, "rs690_gfx_init step4.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 459 | |
| 460 | /* step 5 program PCIE memory mapped configuration space */ |
| 461 | /* done by enable_pci_bar3() before */ |
| 462 | |
| 463 | /* step 6 SBIOS compile flags */ |
Libra Li | f608205 | 2009-08-26 16:04:47 +0000 | [diff] [blame] | 464 | if (cfg->gfx_tmds) { |
| 465 | /* step 6.2.2 Clock-Muxing Control */ |
| 466 | /* step 6.2.2.1 */ |
| 467 | set_nbmisc_enable_bits(nb_dev, 0x7, 1 << 16, 1 << 16); |
| 468 | |
| 469 | /* step 6.2.2.2 */ |
| 470 | set_nbmisc_enable_bits(nb_dev, 0x7, 1 << 8, 1 << 8); |
| 471 | set_nbmisc_enable_bits(nb_dev, 0x7, 1 << 10, 1 << 10); |
| 472 | |
| 473 | /* step 6.2.2.3 */ |
| 474 | set_nbmisc_enable_bits(nb_dev, 0x7, 1 << 26, 1 << 26); |
| 475 | |
| 476 | /* step 6.2.3 Lane-Muxing Control */ |
| 477 | /* step 6.2.3.1 */ |
| 478 | set_nbmisc_enable_bits(nb_dev, 0x37, 0x3 << 8, 0x2 << 8); |
| 479 | |
| 480 | /* step 6.2.4 Received Data Control */ |
| 481 | /* step 6.2.4.1 */ |
| 482 | set_pcie_enable_bits(nb_dev, 0x40, 0x3 << 16, 0x2 << 16); |
| 483 | |
| 484 | /* step 6.2.4.2 */ |
| 485 | set_pcie_enable_bits(nb_dev, 0x40, 0x3 << 18, 0x3 << 18); |
| 486 | |
| 487 | /* step 6.2.4.3 */ |
| 488 | set_pcie_enable_bits(nb_dev, 0x40, 0x3 << 20, 0x0 << 20); |
| 489 | |
| 490 | /* step 6.2.4.4 */ |
| 491 | set_pcie_enable_bits(nb_dev, 0x40, 0x3 << 22, 0x1 << 22); |
| 492 | |
| 493 | /* step 6.2.5 PLL Power Down Control */ |
| 494 | /* step 6.2.5.1 */ |
| 495 | set_nbmisc_enable_bits(nb_dev, 0x35, 0x3 << 6, 0x0 << 6); |
| 496 | |
| 497 | /* step 6.2.6 Driving Strength Control */ |
| 498 | /* step 6.2.6.1 */ |
| 499 | set_nbmisc_enable_bits(nb_dev, 0x34, 0x1 << 24, 0x0 << 24); |
| 500 | |
| 501 | /* step 6.2.6.2 */ |
| 502 | set_nbmisc_enable_bits(nb_dev, 0x35, 0x3 << 2, 0x3 << 2); |
| 503 | } |
| 504 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 505 | printk(BIOS_INFO, "rs690_gfx_init step6.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 506 | |
| 507 | /* step 7 compliance state, (only need if CMOS option is enabled) */ |
Martin Roth | a9e3a75 | 2014-12-16 20:52:23 -0700 | [diff] [blame] | 508 | /* the compliance state is just for test. refer to 4.2.5.2 of PCIe specification */ |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 509 | if (cfg->gfx_compliance) { |
| 510 | /* force compliance */ |
| 511 | set_nbmisc_enable_bits(nb_dev, 0x32, 1 << 6, 1 << 6); |
| 512 | /* release hold training for device 2. GFX initialization is done. */ |
| 513 | set_nbmisc_enable_bits(nb_dev, 0x8, 1 << 4, 0 << 4); |
| 514 | dynamic_link_width_control(nb_dev, dev, cfg->gfx_link_width); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 515 | printk(BIOS_INFO, "rs690_gfx_init step7.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 516 | return; |
| 517 | } |
| 518 | |
| 519 | /* step 8 common initialization */ |
| 520 | /* step 8.1 sets RCB timeout to be 25ms */ |
| 521 | set_pcie_enable_bits(dev, 0x70, 7 << 16, 3 << 16); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 522 | printk(BIOS_INFO, "rs690_gfx_init step8.1.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 523 | |
| 524 | /* step 8.2 disables slave ordering logic */ |
| 525 | set_pcie_enable_bits(nb_dev, 0x20, 1 << 8, 1 << 8); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 526 | printk(BIOS_INFO, "rs690_gfx_init step8.2.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 527 | |
| 528 | /* step 8.3 sets DMA payload size to 64 bytes */ |
| 529 | set_pcie_enable_bits(nb_dev, 0x10, 7 << 10, 4 << 10); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 530 | printk(BIOS_INFO, "rs690_gfx_init step8.3.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 531 | |
Joe Bao | 40d46ba | 2008-12-01 19:49:57 +0000 | [diff] [blame] | 532 | /* step 8.4 if the LTSSM could not see all 8 TS1 during Polling Active, it can still |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 533 | * time out and go back to Detect Idle.*/ |
| 534 | set_pcie_enable_bits(dev, 0x02, 1 << 14, 1 << 14); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 535 | printk(BIOS_INFO, "rs690_gfx_init step8.4.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 536 | |
| 537 | /* step 8.5 shortens the enumeration timer */ |
| 538 | set_pcie_enable_bits(dev, 0x70, 1 << 19, 1 << 19); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 539 | printk(BIOS_INFO, "rs690_gfx_init step8.5.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 540 | |
| 541 | /* step 8.6 blocks DMA traffic during C3 state */ |
| 542 | set_pcie_enable_bits(dev, 0x10, 1 << 0, 0 << 0); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 543 | printk(BIOS_INFO, "rs690_gfx_init step8.6.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 544 | |
| 545 | /* step 8.7 Do not gate the electrical idle form the PHY |
| 546 | * step 8.8 Enables the escape from L1L23 */ |
| 547 | set_pcie_enable_bits(dev, 0xa0, 3 << 30, 3 << 30); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 548 | printk(BIOS_INFO, "rs690_gfx_init step8.8.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 549 | |
Joe Bao | 40d46ba | 2008-12-01 19:49:57 +0000 | [diff] [blame] | 550 | /* step 8.9 Setting this register to 0x1 will workaround a PCI Compliance failure reported by Vista DTM. |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 551 | * SLOT_IMPLEMENTED@PCIE_CAP */ |
| 552 | reg16 = pci_read_config16(dev, 0x5a); |
| 553 | reg16 |= 0x100; |
| 554 | pci_write_config16(dev, 0x5a, reg16); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 555 | printk(BIOS_INFO, "rs690_gfx_init step8.9.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 556 | |
Martin Roth | a9e3a75 | 2014-12-16 20:52:23 -0700 | [diff] [blame] | 557 | /* step 8.10 Setting this register to 0x1 will hide the Advanced Error Reporting Capabilities in the PCIE Bridge. |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 558 | * This will workaround several failures reported by the PCI Compliance test under Vista DTM. */ |
| 559 | set_nbmisc_enable_bits(nb_dev, 0x33, 1 << 31, 0 << 31); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 560 | printk(BIOS_INFO, "rs690_gfx_init step8.10.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 561 | |
| 562 | /* step 8.11 Sets REGS_DLP_IGNORE_IN_L1_EN to ignore DLLPs during L1 so that txclk can be turned off. */ |
| 563 | set_pcie_enable_bits(nb_dev, 0x02, 1 << 0, 1 << 0); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 564 | printk(BIOS_INFO, "rs690_gfx_init step8.11.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 565 | |
| 566 | /* step 8.12 Sets REGS_LC_DONT_GO_TO_L0S_IF_L1_ARMED to prevent lc to go to from L0 to Rcv_L0s if L1 is armed. */ |
| 567 | set_pcie_enable_bits(nb_dev, 0x02, 1 << 6, 1 << 6); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 568 | printk(BIOS_INFO, "rs690_gfx_init step8.12.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 569 | |
| 570 | /* step 8.13 Sets CMGOOD_OVERRIDE. */ |
| 571 | set_nbmisc_enable_bits(nb_dev, 0x6a, 1 << 17, 1 << 17); |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 572 | printk(BIOS_INFO, "rs690_gfx_init step8.13.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 573 | |
| 574 | /* step 9 Enable TLP Flushing, for non-AMD GFX devices and Hot-Plug devices only. */ |
| 575 | /* skip */ |
| 576 | |
| 577 | /* step 10 Optional Features, only needed if CMOS option is enabled. */ |
| 578 | /* step 10.a: L0s */ |
| 579 | /* enabling L0s in the RS690 GFX port(s) */ |
| 580 | set_pcie_enable_bits(nb_dev, 0xF9, 3 << 13, 2 << 13); |
| 581 | set_pcie_enable_bits(dev, 0xA0, 0xf << 8, 8 << 8); |
| 582 | reg16 = pci_read_config16(dev, 0x68); |
| 583 | reg16 |= 1 << 0; |
| 584 | /* L0s is intended as a power saving state */ |
| 585 | /* pci_write_config16(dev, 0x68, reg16); */ |
| 586 | |
| 587 | /* enabling L0s in the External GFX Device(s) */ |
| 588 | |
| 589 | /* step 10.b: active state power management (ASPM L1) */ |
| 590 | /* TO DO */ |
| 591 | |
| 592 | /* step 10.c: turning off PLL During L1/L23 */ |
| 593 | set_pcie_enable_bits(nb_dev, 0x40, 1 << 3, 1 << 3); |
| 594 | set_pcie_enable_bits(nb_dev, 0x40, 1 << 9, 1 << 9); |
| 595 | |
| 596 | /* step 10.d: TXCLK clock gating */ |
| 597 | set_nbmisc_enable_bits(nb_dev, 0x7, 3, 3); |
| 598 | set_nbmisc_enable_bits(nb_dev, 0x7, 1 << 22, 1 << 22); |
| 599 | set_pcie_enable_bits(nb_dev, 0x11, 0xf << 4, 0xc << 4); |
| 600 | |
| 601 | /* step 10.e: LCLK clock gating, done in rs690_config_misc_clk() */ |
| 602 | |
Joe Bao | 40d46ba | 2008-12-01 19:49:57 +0000 | [diff] [blame] | 603 | /* step 11 Poll GPIO to determine whether it is single-port or dual-port configuration. |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 604 | * While details will be added later in the document, for now assue the single-port configuration. */ |
| 605 | /* skip */ |
| 606 | |
| 607 | /* Single-port/Dual-port configureation. */ |
| 608 | switch (cfg->gfx_dual_slot) { |
| 609 | case 0: |
| 610 | single_port_configuration(nb_dev, dev); |
| 611 | break; |
| 612 | case 1: |
| 613 | dual_port_configuration(nb_dev, dev); |
| 614 | break; |
| 615 | default: |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 616 | printk(BIOS_INFO, "Incorrect configuration of external gfx slot.\n"); |
Michael Xie | 06755e4 | 2008-09-22 13:07:20 +0000 | [diff] [blame] | 617 | break; |
| 618 | } |
| 619 | } |