blob: f2c7dc16486f2151fcaf5b9e4252fce47a014405 [file] [log] [blame]
Stefan Reinauer8e073822012-04-04 00:07:22 +02001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2008-2009 coresystems GmbH
Duncan Laurieb9fe01c2012-04-27 10:30:51 -07005 * Copyright (C) 2012 The Chromium OS Authors. All rights reserved.
Stefan Reinauer8e073822012-04-04 00:07:22 +02006 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; version 2 of
10 * the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
Paul Menzela46a7122013-02-23 18:37:27 +010019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Stefan Reinauer8e073822012-04-04 00:07:22 +020020 */
21
22#include <console/console.h>
23#include <delay.h>
Marc Jones783f2262013-02-11 14:36:35 -070024#ifdef __SMM__
25#include <arch/io.h>
26#include <arch/romcc_io.h>
27#include <device/pci_def.h>
28#else /* !__SMM__ */
Stefan Reinauer8e073822012-04-04 00:07:22 +020029#include <device/device.h>
30#include <device/pci.h>
Marc Jones783f2262013-02-11 14:36:35 -070031#endif
Stefan Reinauer8e073822012-04-04 00:07:22 +020032#include "pch.h"
33
34static int pch_revision_id = -1;
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070035static int pch_type = -1;
Stefan Reinauer8e073822012-04-04 00:07:22 +020036
37int pch_silicon_revision(void)
38{
Marc Jones783f2262013-02-11 14:36:35 -070039 device_t dev;
40
41#ifdef __SMM__
42 dev = PCI_DEV(0, 0x1f, 0);
43#else
44 dev = dev_find_slot(0, PCI_DEVFN(0x1f, 0));
45#endif
46
Stefan Reinauer8e073822012-04-04 00:07:22 +020047 if (pch_revision_id < 0)
Marc Jones783f2262013-02-11 14:36:35 -070048 pch_revision_id = pci_read_config8(dev, PCI_REVISION_ID);
Stefan Reinauer8e073822012-04-04 00:07:22 +020049 return pch_revision_id;
50}
51
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070052int pch_silicon_type(void)
53{
Marc Jones783f2262013-02-11 14:36:35 -070054 device_t dev;
55
56#ifdef __SMM__
57 dev = PCI_DEV(0, 0x1f, 0);
58#else
59 dev = dev_find_slot(0, PCI_DEVFN(0x1f, 0));
60#endif
61
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070062 if (pch_type < 0)
Marc Jones783f2262013-02-11 14:36:35 -070063 pch_type = pci_read_config8(dev, PCI_DEVICE_ID + 1);
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070064 return pch_type;
65}
66
67int pch_silicon_supported(int type, int rev)
68{
69 int cur_type = pch_silicon_type();
70 int cur_rev = pch_silicon_revision();
71
72 switch (type) {
73 case PCH_TYPE_CPT:
74 /* CougarPoint minimum revision */
75 if (cur_type == PCH_TYPE_CPT && cur_rev >= rev)
76 return 1;
77 /* PantherPoint any revision */
78 if (cur_type == PCH_TYPE_PPT)
79 return 1;
80 break;
81
82 case PCH_TYPE_PPT:
83 /* PantherPoint minimum revision */
84 if (cur_type == PCH_TYPE_PPT && cur_rev >= rev)
85 return 1;
86 break;
87 }
88
89 return 0;
90}
91
Stefan Reinauer8e073822012-04-04 00:07:22 +020092#define IOBP_RETRY 1000
93static inline int iobp_poll(void)
94{
95 unsigned try = IOBP_RETRY;
96 u32 data;
97
98 while (try--) {
99 data = RCBA32(IOBPS);
100 if ((data & 1) == 0)
101 return 1;
102 udelay(10);
103 }
104
105 printk(BIOS_ERR, "IOBP timeout\n");
106 return 0;
107}
108
109void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue)
110{
111 u32 data;
112
113 /* Set the address */
114 RCBA32(IOBPIRI) = address;
115
116 /* READ OPCODE */
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700117 if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B0))
Stefan Reinauer8e073822012-04-04 00:07:22 +0200118 RCBA32(IOBPS) = IOBPS_RW_BX;
119 else
120 RCBA32(IOBPS) = IOBPS_READ_AX;
121 if (!iobp_poll())
122 return;
123
124 /* Read IOBP data */
125 data = RCBA32(IOBPD);
126 if (!iobp_poll())
127 return;
128
129 /* Check for successful transaction */
130 if ((RCBA32(IOBPS) & 0x6) != 0) {
131 printk(BIOS_ERR, "IOBP read 0x%08x failed\n", address);
132 return;
133 }
134
135 /* Update the data */
136 data &= andvalue;
137 data |= orvalue;
138
139 /* WRITE OPCODE */
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700140 if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B0))
Stefan Reinauer8e073822012-04-04 00:07:22 +0200141 RCBA32(IOBPS) = IOBPS_RW_BX;
142 else
143 RCBA32(IOBPS) = IOBPS_WRITE_AX;
144 if (!iobp_poll())
145 return;
146
147 /* Write IOBP data */
148 RCBA32(IOBPD) = data;
149 if (!iobp_poll())
150 return;
151}
152
Marc Jones783f2262013-02-11 14:36:35 -0700153#ifndef __SMM__
154/* Set bit in Function Disble register to hide this device */
155static void pch_hide_devfn(unsigned devfn)
156{
157 switch (devfn) {
158 case PCI_DEVFN(22, 0): /* MEI #1 */
159 RCBA32_OR(FD2, PCH_DISABLE_MEI1);
160 break;
161 case PCI_DEVFN(22, 1): /* MEI #2 */
162 RCBA32_OR(FD2, PCH_DISABLE_MEI2);
163 break;
164 case PCI_DEVFN(22, 2): /* IDE-R */
165 RCBA32_OR(FD2, PCH_DISABLE_IDER);
166 break;
167 case PCI_DEVFN(22, 3): /* KT */
168 RCBA32_OR(FD2, PCH_DISABLE_KT);
169 break;
170 case PCI_DEVFN(25, 0): /* Gigabit Ethernet */
171 RCBA32_OR(BUC, PCH_DISABLE_GBE);
172 break;
173 case PCI_DEVFN(26, 0): /* EHCI #2 */
174 RCBA32_OR(FD, PCH_DISABLE_EHCI2);
175 break;
176 case PCI_DEVFN(27, 0): /* HD Audio Controller */
177 RCBA32_OR(FD, PCH_DISABLE_HD_AUDIO);
178 break;
179 case PCI_DEVFN(28, 0): /* PCI Express Root Port 1 */
180 case PCI_DEVFN(28, 1): /* PCI Express Root Port 2 */
181 case PCI_DEVFN(28, 2): /* PCI Express Root Port 3 */
182 case PCI_DEVFN(28, 3): /* PCI Express Root Port 4 */
183 case PCI_DEVFN(28, 4): /* PCI Express Root Port 5 */
184 case PCI_DEVFN(28, 5): /* PCI Express Root Port 6 */
185 case PCI_DEVFN(28, 6): /* PCI Express Root Port 7 */
186 case PCI_DEVFN(28, 7): /* PCI Express Root Port 8 */
187 RCBA32_OR(FD, PCH_DISABLE_PCIE(PCI_FUNC(devfn)));
188 break;
189 case PCI_DEVFN(29, 0): /* EHCI #1 */
190 RCBA32_OR(FD, PCH_DISABLE_EHCI1);
191 break;
192 case PCI_DEVFN(30, 0): /* PCI-to-PCI Bridge */
193 RCBA32_OR(FD, PCH_DISABLE_P2P);
194 break;
195 case PCI_DEVFN(31, 0): /* LPC */
196 RCBA32_OR(FD, PCH_DISABLE_LPC);
197 break;
198 case PCI_DEVFN(31, 2): /* SATA #1 */
199 RCBA32_OR(FD, PCH_DISABLE_SATA1);
200 break;
201 case PCI_DEVFN(31, 3): /* SMBUS */
202 RCBA32_OR(FD, PCH_DISABLE_SMBUS);
203 break;
204 case PCI_DEVFN(31, 5): /* SATA #22 */
205 RCBA32_OR(FD, PCH_DISABLE_SATA2);
206 break;
207 case PCI_DEVFN(31, 6): /* Thermal Subsystem */
208 RCBA32_OR(FD, PCH_DISABLE_THERMAL);
209 break;
210 }
211}
212
Stefan Reinauer8e073822012-04-04 00:07:22 +0200213/* Check if any port in set X to X+3 is enabled */
214static int pch_pcie_check_set_enabled(device_t dev)
215{
216 device_t port;
217 int port_func;
218 int dev_func = PCI_FUNC(dev->path.pci.devfn);
219
220 printk(BIOS_DEBUG, "%s: check set enabled\n", dev_path(dev));
221
222 /* Go through static device tree list of devices
223 * because enumeration is still in progress */
224 for (port = all_devices; port; port = port->next) {
225 /* Only care about PCIe root ports */
226 if (PCI_SLOT(port->path.pci.devfn) !=
227 PCI_SLOT(dev->path.pci.devfn))
228 continue;
229
230 /* Check if port is in range and enabled */
231 port_func = PCI_FUNC(port->path.pci.devfn);
232 if (port_func >= dev_func &&
233 port_func < (dev_func + 4) &&
234 port->enabled)
235 return 1;
236 }
237
238 /* None of the ports in this set are enabled */
239 return 0;
240}
241
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700242/* RPFN is a write-once register so keep a copy until it is written */
243static u32 new_rpfn;
244
245/* Swap function numbers assigned to two PCIe Root Ports */
246static void pch_pcie_function_swap(u8 old_fn, u8 new_fn)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200247{
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700248 u32 old_rpfn = new_rpfn;
249
250 printk(BIOS_DEBUG, "PCH: Remap PCIe function %d to %d\n",
251 old_fn, new_fn);
252
253 new_rpfn &= ~(RPFN_FNMASK(old_fn) | RPFN_FNMASK(new_fn));
254
255 /* Old function set to new function and disabled */
256 new_rpfn |= RPFN_FNSET(old_fn, RPFN_FNGET(old_rpfn, new_fn));
257 new_rpfn |= RPFN_FNSET(new_fn, RPFN_FNGET(old_rpfn, old_fn));
258}
259
260/* Update devicetree with new Root Port function number assignment */
261static void pch_pcie_devicetree_update(void)
262{
263 device_t dev;
264
265 /* Update the function numbers in the static devicetree */
266 for (dev = all_devices; dev; dev = dev->next) {
267 u8 new_devfn;
268
269 /* Only care about PCH PCIe root ports */
270 if (PCI_SLOT(dev->path.pci.devfn) !=
271 PCH_PCIE_DEV_SLOT)
272 continue;
273
274 /* Determine the new devfn for this port */
275 new_devfn = PCI_DEVFN(PCH_PCIE_DEV_SLOT,
276 RPFN_FNGET(new_rpfn,
277 PCI_FUNC(dev->path.pci.devfn)));
278
279 if (dev->path.pci.devfn != new_devfn) {
280 printk(BIOS_DEBUG,
281 "PCH: PCIe map %02x.%1x -> %02x.%1x\n",
282 PCI_SLOT(dev->path.pci.devfn),
283 PCI_FUNC(dev->path.pci.devfn),
284 PCI_SLOT(new_devfn), PCI_FUNC(new_devfn));
285
286 dev->path.pci.devfn = new_devfn;
287 }
288 }
289}
290
291/* Special handling for PCIe Root Port devices */
292static void pch_pcie_enable(device_t dev)
293{
294 struct southbridge_intel_bd82x6x_config *config = dev->chip_info;
Stefan Reinauer8e073822012-04-04 00:07:22 +0200295 u32 reg32;
296
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700297 /*
298 * Save a copy of the Root Port Function Number map when
299 * starting to walk the list of PCIe Root Ports so it can
300 * be updated locally and written out when the last port
301 * has been processed.
302 */
303 if (PCI_FUNC(dev->path.pci.devfn) == 0) {
304 new_rpfn = RCBA32(RPFN);
Stefan Reinauer8e073822012-04-04 00:07:22 +0200305
306 /*
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700307 * Enable Root Port coalescing if the first port is disabled
308 * or the other devices will not be enumerated by the OS.
309 */
310 if (!dev->enabled)
311 config->pcie_port_coalesce = 1;
312
313 if (config->pcie_port_coalesce)
314 printk(BIOS_INFO,
315 "PCH: PCIe Root Port coalescing is enabled\n");
316 }
317
318 if (!dev->enabled) {
Marc Jonesef6b08c2012-06-15 23:03:15 -0600319 printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev));
320
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700321 /*
322 * PCIE Power Savings for PantherPoint and CougarPoint/B1+
Stefan Reinauer8e073822012-04-04 00:07:22 +0200323 *
324 * If PCIe 0-3 disabled set Function 0 0xE2[0] = 1
325 * If PCIe 4-7 disabled set Function 4 0xE2[0] = 1
326 *
327 * This check is done here instead of pcie driver
328 * because the pcie driver enable() handler is not
329 * called unless the device is enabled.
330 */
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700331 if ((PCI_FUNC(dev->path.pci.devfn) == 0 ||
Stefan Reinauer8e073822012-04-04 00:07:22 +0200332 PCI_FUNC(dev->path.pci.devfn) == 4)) {
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700333 /* Handle workaround for PPT and CPT/B1+ */
334 if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B1) &&
335 !pch_pcie_check_set_enabled(dev)) {
Stefan Reinauer8e073822012-04-04 00:07:22 +0200336 u8 reg8 = pci_read_config8(dev, 0xe2);
337 reg8 |= 1;
338 pci_write_config8(dev, 0xe2, reg8);
339 }
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700340
341 /*
342 * Enable Clock Gating for shared PCIe resources
343 * before disabling this particular port.
344 */
345 pci_write_config8(dev, 0xe1, 0x3c);
Stefan Reinauer8e073822012-04-04 00:07:22 +0200346 }
347
348 /* Ensure memory, io, and bus master are all disabled */
349 reg32 = pci_read_config32(dev, PCI_COMMAND);
350 reg32 &= ~(PCI_COMMAND_MASTER |
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700351 PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
352 pci_write_config32(dev, PCI_COMMAND, reg32);
353
354 /* Do not claim downstream transactions for PCIe ports */
355 new_rpfn |= RPFN_HIDE(PCI_FUNC(dev->path.pci.devfn));
356
357 /* Hide this device if possible */
358 pch_hide_devfn(dev->path.pci.devfn);
359 } else {
360 int fn;
361
362 /*
363 * Check if there is a lower disabled port to swap with this
364 * port in order to maintain linear order starting at zero.
365 */
366 if (config->pcie_port_coalesce) {
367 for (fn=0; fn < PCI_FUNC(dev->path.pci.devfn); fn++) {
368 if (!(new_rpfn & RPFN_HIDE(fn)))
369 continue;
370
371 /* Swap places with this function */
372 pch_pcie_function_swap(
373 PCI_FUNC(dev->path.pci.devfn), fn);
374 break;
375 }
376 }
377
378 /* Enable SERR */
379 reg32 = pci_read_config32(dev, PCI_COMMAND);
380 reg32 |= PCI_COMMAND_SERR;
381 pci_write_config32(dev, PCI_COMMAND, reg32);
382 }
383
384 /*
385 * When processing the last PCIe root port we can now
386 * update the Root Port Function Number and Hide register.
387 */
388 if (PCI_FUNC(dev->path.pci.devfn) == 7) {
389 printk(BIOS_SPEW, "PCH: RPFN 0x%08x -> 0x%08x\n",
390 RCBA32(RPFN), new_rpfn);
391 RCBA32(RPFN) = new_rpfn;
392
393 /* Update static devictree with new function numbers */
394 if (config->pcie_port_coalesce)
395 pch_pcie_devicetree_update();
396 }
397}
398
399void pch_enable(device_t dev)
400{
401 u32 reg32;
402
403 /* PCH PCIe Root Ports get special handling */
404 if (PCI_SLOT(dev->path.pci.devfn) == PCH_PCIE_DEV_SLOT)
405 return pch_pcie_enable(dev);
406
407 if (!dev->enabled) {
408 printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev));
409
410 /* Ensure memory, io, and bus master are all disabled */
411 reg32 = pci_read_config32(dev, PCI_COMMAND);
412 reg32 &= ~(PCI_COMMAND_MASTER |
413 PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
Stefan Reinauer8e073822012-04-04 00:07:22 +0200414 pci_write_config32(dev, PCI_COMMAND, reg32);
415
416 /* Hide this device if possible */
417 pch_hide_devfn(dev->path.pci.devfn);
418 } else {
419 /* Enable SERR */
420 reg32 = pci_read_config32(dev, PCI_COMMAND);
421 reg32 |= PCI_COMMAND_SERR;
422 pci_write_config32(dev, PCI_COMMAND, reg32);
423 }
424}
425
426struct chip_operations southbridge_intel_bd82x6x_ops = {
Stefan Reinauer9ca1c0a2012-07-25 16:10:36 -0700427 CHIP_NAME("Intel Series 6/7 (Cougar Point/Panther Point) Southbridge")
Stefan Reinauer8e073822012-04-04 00:07:22 +0200428 .enable_dev = pch_enable,
429};
Marc Jones783f2262013-02-11 14:36:35 -0700430#endif