blob: 1a646b17b98f0dd128a02f77770f0102792e6621 [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.
Stefan Reinauer8e073822012-04-04 00:07:22 +020016 */
17
18#include <console/console.h>
19#include <delay.h>
Marc Jones783f2262013-02-11 14:36:35 -070020#ifdef __SMM__
21#include <arch/io.h>
Marc Jones783f2262013-02-11 14:36:35 -070022#include <device/pci_def.h>
23#else /* !__SMM__ */
Stefan Reinauer8e073822012-04-04 00:07:22 +020024#include <device/device.h>
25#include <device/pci.h>
Marc Jones783f2262013-02-11 14:36:35 -070026#endif
Stefan Reinauer8e073822012-04-04 00:07:22 +020027#include "pch.h"
Bill XIE8c57d092017-08-25 22:07:12 +080028#include <string.h>
Stefan Reinauer8e073822012-04-04 00:07:22 +020029
30int pch_silicon_revision(void)
31{
Felix Held82bd0c32016-08-13 23:27:15 +020032 static int pch_revision_id = -1;
Marc Jones783f2262013-02-11 14:36:35 -070033
Antonello Dettoridac82402016-09-02 09:14:39 +020034#ifdef __SIMPLE_DEVICE__
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030035 pci_devfn_t dev = PCI_DEV(0, 0x1f, 0);
Marc Jones783f2262013-02-11 14:36:35 -070036#else
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030037 struct device *dev = pcidev_on_root(0x1f, 0);
Marc Jones783f2262013-02-11 14:36:35 -070038#endif
39
Stefan Reinauer8e073822012-04-04 00:07:22 +020040 if (pch_revision_id < 0)
Marc Jones783f2262013-02-11 14:36:35 -070041 pch_revision_id = pci_read_config8(dev, PCI_REVISION_ID);
Stefan Reinauer8e073822012-04-04 00:07:22 +020042 return pch_revision_id;
43}
44
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070045int pch_silicon_type(void)
46{
Felix Held82bd0c32016-08-13 23:27:15 +020047 static int pch_type = -1;
Marc Jones783f2262013-02-11 14:36:35 -070048
Antonello Dettoridac82402016-09-02 09:14:39 +020049#ifdef __SIMPLE_DEVICE__
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030050 pci_devfn_t dev = PCI_DEV(0, 0x1f, 0);
Marc Jones783f2262013-02-11 14:36:35 -070051#else
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030052 struct device *dev = pcidev_on_root(0x1f, 0);
Marc Jones783f2262013-02-11 14:36:35 -070053#endif
54
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070055 if (pch_type < 0)
Marc Jones783f2262013-02-11 14:36:35 -070056 pch_type = pci_read_config8(dev, PCI_DEVICE_ID + 1);
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070057 return pch_type;
58}
59
60int pch_silicon_supported(int type, int rev)
61{
62 int cur_type = pch_silicon_type();
63 int cur_rev = pch_silicon_revision();
64
65 switch (type) {
66 case PCH_TYPE_CPT:
67 /* CougarPoint minimum revision */
68 if (cur_type == PCH_TYPE_CPT && cur_rev >= rev)
69 return 1;
70 /* PantherPoint any revision */
71 if (cur_type == PCH_TYPE_PPT)
72 return 1;
73 break;
74
75 case PCH_TYPE_PPT:
76 /* PantherPoint minimum revision */
77 if (cur_type == PCH_TYPE_PPT && cur_rev >= rev)
78 return 1;
79 break;
80 }
81
82 return 0;
83}
84
Stefan Reinauer8e073822012-04-04 00:07:22 +020085#define IOBP_RETRY 1000
86static inline int iobp_poll(void)
87{
88 unsigned try = IOBP_RETRY;
89 u32 data;
90
91 while (try--) {
92 data = RCBA32(IOBPS);
93 if ((data & 1) == 0)
94 return 1;
95 udelay(10);
96 }
97
98 printk(BIOS_ERR, "IOBP timeout\n");
99 return 0;
100}
101
102void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue)
103{
104 u32 data;
105
106 /* Set the address */
107 RCBA32(IOBPIRI) = address;
108
109 /* READ OPCODE */
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700110 if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B0))
Stefan Reinauer8e073822012-04-04 00:07:22 +0200111 RCBA32(IOBPS) = IOBPS_RW_BX;
112 else
113 RCBA32(IOBPS) = IOBPS_READ_AX;
114 if (!iobp_poll())
115 return;
116
117 /* Read IOBP data */
118 data = RCBA32(IOBPD);
119 if (!iobp_poll())
120 return;
121
122 /* Check for successful transaction */
123 if ((RCBA32(IOBPS) & 0x6) != 0) {
124 printk(BIOS_ERR, "IOBP read 0x%08x failed\n", address);
125 return;
126 }
127
128 /* Update the data */
129 data &= andvalue;
130 data |= orvalue;
131
132 /* WRITE OPCODE */
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700133 if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B0))
Stefan Reinauer8e073822012-04-04 00:07:22 +0200134 RCBA32(IOBPS) = IOBPS_RW_BX;
135 else
136 RCBA32(IOBPS) = IOBPS_WRITE_AX;
137 if (!iobp_poll())
138 return;
139
140 /* Write IOBP data */
141 RCBA32(IOBPD) = data;
142 if (!iobp_poll())
143 return;
144}
145
Marc Jones783f2262013-02-11 14:36:35 -0700146#ifndef __SMM__
147/* Set bit in Function Disble register to hide this device */
148static void pch_hide_devfn(unsigned devfn)
149{
150 switch (devfn) {
151 case PCI_DEVFN(22, 0): /* MEI #1 */
152 RCBA32_OR(FD2, PCH_DISABLE_MEI1);
153 break;
154 case PCI_DEVFN(22, 1): /* MEI #2 */
155 RCBA32_OR(FD2, PCH_DISABLE_MEI2);
156 break;
157 case PCI_DEVFN(22, 2): /* IDE-R */
158 RCBA32_OR(FD2, PCH_DISABLE_IDER);
159 break;
160 case PCI_DEVFN(22, 3): /* KT */
161 RCBA32_OR(FD2, PCH_DISABLE_KT);
162 break;
163 case PCI_DEVFN(25, 0): /* Gigabit Ethernet */
164 RCBA32_OR(BUC, PCH_DISABLE_GBE);
165 break;
166 case PCI_DEVFN(26, 0): /* EHCI #2 */
167 RCBA32_OR(FD, PCH_DISABLE_EHCI2);
168 break;
169 case PCI_DEVFN(27, 0): /* HD Audio Controller */
170 RCBA32_OR(FD, PCH_DISABLE_HD_AUDIO);
171 break;
172 case PCI_DEVFN(28, 0): /* PCI Express Root Port 1 */
173 case PCI_DEVFN(28, 1): /* PCI Express Root Port 2 */
174 case PCI_DEVFN(28, 2): /* PCI Express Root Port 3 */
175 case PCI_DEVFN(28, 3): /* PCI Express Root Port 4 */
176 case PCI_DEVFN(28, 4): /* PCI Express Root Port 5 */
177 case PCI_DEVFN(28, 5): /* PCI Express Root Port 6 */
178 case PCI_DEVFN(28, 6): /* PCI Express Root Port 7 */
179 case PCI_DEVFN(28, 7): /* PCI Express Root Port 8 */
180 RCBA32_OR(FD, PCH_DISABLE_PCIE(PCI_FUNC(devfn)));
181 break;
182 case PCI_DEVFN(29, 0): /* EHCI #1 */
183 RCBA32_OR(FD, PCH_DISABLE_EHCI1);
184 break;
185 case PCI_DEVFN(30, 0): /* PCI-to-PCI Bridge */
186 RCBA32_OR(FD, PCH_DISABLE_P2P);
187 break;
188 case PCI_DEVFN(31, 0): /* LPC */
189 RCBA32_OR(FD, PCH_DISABLE_LPC);
190 break;
191 case PCI_DEVFN(31, 2): /* SATA #1 */
192 RCBA32_OR(FD, PCH_DISABLE_SATA1);
193 break;
194 case PCI_DEVFN(31, 3): /* SMBUS */
195 RCBA32_OR(FD, PCH_DISABLE_SMBUS);
196 break;
197 case PCI_DEVFN(31, 5): /* SATA #22 */
198 RCBA32_OR(FD, PCH_DISABLE_SATA2);
199 break;
200 case PCI_DEVFN(31, 6): /* Thermal Subsystem */
201 RCBA32_OR(FD, PCH_DISABLE_THERMAL);
202 break;
203 }
204}
205
Stefan Reinauer8e073822012-04-04 00:07:22 +0200206/* Check if any port in set X to X+3 is enabled */
Elyes HAOUASdc035282018-09-18 13:28:49 +0200207static int pch_pcie_check_set_enabled(struct device *dev)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200208{
Elyes HAOUASdc035282018-09-18 13:28:49 +0200209 struct device *port;
Stefan Reinauer8e073822012-04-04 00:07:22 +0200210 int port_func;
211 int dev_func = PCI_FUNC(dev->path.pci.devfn);
212
213 printk(BIOS_DEBUG, "%s: check set enabled\n", dev_path(dev));
214
215 /* Go through static device tree list of devices
216 * because enumeration is still in progress */
217 for (port = all_devices; port; port = port->next) {
218 /* Only care about PCIe root ports */
219 if (PCI_SLOT(port->path.pci.devfn) !=
220 PCI_SLOT(dev->path.pci.devfn))
221 continue;
222
223 /* Check if port is in range and enabled */
224 port_func = PCI_FUNC(port->path.pci.devfn);
225 if (port_func >= dev_func &&
226 port_func < (dev_func + 4) &&
227 port->enabled)
228 return 1;
229 }
230
231 /* None of the ports in this set are enabled */
232 return 0;
233}
234
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700235/* RPFN is a write-once register so keep a copy until it is written */
236static u32 new_rpfn;
237
238/* Swap function numbers assigned to two PCIe Root Ports */
239static void pch_pcie_function_swap(u8 old_fn, u8 new_fn)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200240{
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700241 u32 old_rpfn = new_rpfn;
242
243 printk(BIOS_DEBUG, "PCH: Remap PCIe function %d to %d\n",
244 old_fn, new_fn);
245
246 new_rpfn &= ~(RPFN_FNMASK(old_fn) | RPFN_FNMASK(new_fn));
247
248 /* Old function set to new function and disabled */
249 new_rpfn |= RPFN_FNSET(old_fn, RPFN_FNGET(old_rpfn, new_fn));
250 new_rpfn |= RPFN_FNSET(new_fn, RPFN_FNGET(old_rpfn, old_fn));
251}
252
253/* Update devicetree with new Root Port function number assignment */
Bill XIE8c57d092017-08-25 22:07:12 +0800254static void pch_pcie_devicetree_update(
255 struct southbridge_intel_bd82x6x_config *config)
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700256{
Elyes HAOUASdc035282018-09-18 13:28:49 +0200257 struct device *dev;
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700258
Bill XIE8c57d092017-08-25 22:07:12 +0800259 /*
260 * hotplug map should also be updated along with their
261 * corresponding port
262 */
263 u8 new_hotplug_map[sizeof(config->pcie_hotplug_map)];
264
265 /*
266 * Slots that didn't move need the hotplug setting copied too,
267 * so "new_hotplug_map" is initialized with the values of the old map.
268 */
269 memcpy(new_hotplug_map, config->pcie_hotplug_map,
270 sizeof(new_hotplug_map));
271
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700272 /* Update the function numbers in the static devicetree */
273 for (dev = all_devices; dev; dev = dev->next) {
274 u8 new_devfn;
275
276 /* Only care about PCH PCIe root ports */
277 if (PCI_SLOT(dev->path.pci.devfn) !=
278 PCH_PCIE_DEV_SLOT)
279 continue;
280
281 /* Determine the new devfn for this port */
282 new_devfn = PCI_DEVFN(PCH_PCIE_DEV_SLOT,
283 RPFN_FNGET(new_rpfn,
284 PCI_FUNC(dev->path.pci.devfn)));
285
286 if (dev->path.pci.devfn != new_devfn) {
287 printk(BIOS_DEBUG,
288 "PCH: PCIe map %02x.%1x -> %02x.%1x\n",
289 PCI_SLOT(dev->path.pci.devfn),
290 PCI_FUNC(dev->path.pci.devfn),
291 PCI_SLOT(new_devfn), PCI_FUNC(new_devfn));
292
Bill XIE8c57d092017-08-25 22:07:12 +0800293 /*
294 * Copy the flag to its new position along with
295 * the corresponding port
296 */
297 new_hotplug_map[PCI_FUNC(new_devfn)] =
298 config->pcie_hotplug_map
299 [PCI_FUNC(dev->path.pci.devfn)];
300
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700301 dev->path.pci.devfn = new_devfn;
302 }
303 }
Bill XIE8c57d092017-08-25 22:07:12 +0800304
305 /* Copy the updated map back to its place */
306 memcpy(config->pcie_hotplug_map, new_hotplug_map,
307 sizeof(new_hotplug_map));
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700308}
309
310/* Special handling for PCIe Root Port devices */
Elyes HAOUASdc035282018-09-18 13:28:49 +0200311static void pch_pcie_enable(struct device *dev)
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700312{
313 struct southbridge_intel_bd82x6x_config *config = dev->chip_info;
Stefan Reinauer8e073822012-04-04 00:07:22 +0200314 u32 reg32;
315
Bill XIE8c57d092017-08-25 22:07:12 +0800316 if (!config)
317 return;
318
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700319 /*
320 * Save a copy of the Root Port Function Number map when
321 * starting to walk the list of PCIe Root Ports so it can
322 * be updated locally and written out when the last port
323 * has been processed.
324 */
325 if (PCI_FUNC(dev->path.pci.devfn) == 0) {
326 new_rpfn = RCBA32(RPFN);
Stefan Reinauer8e073822012-04-04 00:07:22 +0200327
328 /*
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700329 * Enable Root Port coalescing if the first port is disabled
330 * or the other devices will not be enumerated by the OS.
331 */
332 if (!dev->enabled)
333 config->pcie_port_coalesce = 1;
334
335 if (config->pcie_port_coalesce)
336 printk(BIOS_INFO,
337 "PCH: PCIe Root Port coalescing is enabled\n");
338 }
339
340 if (!dev->enabled) {
Marc Jonesef6b08c2012-06-15 23:03:15 -0600341 printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev));
342
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700343 /*
344 * PCIE Power Savings for PantherPoint and CougarPoint/B1+
Stefan Reinauer8e073822012-04-04 00:07:22 +0200345 *
346 * If PCIe 0-3 disabled set Function 0 0xE2[0] = 1
347 * If PCIe 4-7 disabled set Function 4 0xE2[0] = 1
348 *
349 * This check is done here instead of pcie driver
350 * because the pcie driver enable() handler is not
351 * called unless the device is enabled.
352 */
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700353 if ((PCI_FUNC(dev->path.pci.devfn) == 0 ||
Stefan Reinauer8e073822012-04-04 00:07:22 +0200354 PCI_FUNC(dev->path.pci.devfn) == 4)) {
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700355 /* Handle workaround for PPT and CPT/B1+ */
356 if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B1) &&
357 !pch_pcie_check_set_enabled(dev)) {
Stefan Reinauer8e073822012-04-04 00:07:22 +0200358 u8 reg8 = pci_read_config8(dev, 0xe2);
359 reg8 |= 1;
360 pci_write_config8(dev, 0xe2, reg8);
361 }
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700362
363 /*
364 * Enable Clock Gating for shared PCIe resources
365 * before disabling this particular port.
366 */
367 pci_write_config8(dev, 0xe1, 0x3c);
Stefan Reinauer8e073822012-04-04 00:07:22 +0200368 }
369
370 /* Ensure memory, io, and bus master are all disabled */
371 reg32 = pci_read_config32(dev, PCI_COMMAND);
372 reg32 &= ~(PCI_COMMAND_MASTER |
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700373 PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
374 pci_write_config32(dev, PCI_COMMAND, reg32);
375
376 /* Do not claim downstream transactions for PCIe ports */
377 new_rpfn |= RPFN_HIDE(PCI_FUNC(dev->path.pci.devfn));
378
379 /* Hide this device if possible */
380 pch_hide_devfn(dev->path.pci.devfn);
381 } else {
382 int fn;
383
384 /*
385 * Check if there is a lower disabled port to swap with this
386 * port in order to maintain linear order starting at zero.
387 */
388 if (config->pcie_port_coalesce) {
389 for (fn=0; fn < PCI_FUNC(dev->path.pci.devfn); fn++) {
390 if (!(new_rpfn & RPFN_HIDE(fn)))
391 continue;
392
393 /* Swap places with this function */
394 pch_pcie_function_swap(
395 PCI_FUNC(dev->path.pci.devfn), fn);
396 break;
397 }
398 }
399
400 /* Enable SERR */
401 reg32 = pci_read_config32(dev, PCI_COMMAND);
402 reg32 |= PCI_COMMAND_SERR;
403 pci_write_config32(dev, PCI_COMMAND, reg32);
404 }
405
406 /*
407 * When processing the last PCIe root port we can now
408 * update the Root Port Function Number and Hide register.
409 */
410 if (PCI_FUNC(dev->path.pci.devfn) == 7) {
411 printk(BIOS_SPEW, "PCH: RPFN 0x%08x -> 0x%08x\n",
412 RCBA32(RPFN), new_rpfn);
413 RCBA32(RPFN) = new_rpfn;
414
415 /* Update static devictree with new function numbers */
416 if (config->pcie_port_coalesce)
Bill XIE8c57d092017-08-25 22:07:12 +0800417 pch_pcie_devicetree_update(config);
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700418 }
419}
420
Elyes HAOUASdc035282018-09-18 13:28:49 +0200421void pch_enable(struct device *dev)
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700422{
423 u32 reg32;
424
425 /* PCH PCIe Root Ports get special handling */
426 if (PCI_SLOT(dev->path.pci.devfn) == PCH_PCIE_DEV_SLOT)
427 return pch_pcie_enable(dev);
428
429 if (!dev->enabled) {
430 printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev));
431
432 /* Ensure memory, io, and bus master are all disabled */
433 reg32 = pci_read_config32(dev, PCI_COMMAND);
434 reg32 &= ~(PCI_COMMAND_MASTER |
435 PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
Stefan Reinauer8e073822012-04-04 00:07:22 +0200436 pci_write_config32(dev, PCI_COMMAND, reg32);
437
438 /* Hide this device if possible */
439 pch_hide_devfn(dev->path.pci.devfn);
440 } else {
441 /* Enable SERR */
442 reg32 = pci_read_config32(dev, PCI_COMMAND);
443 reg32 |= PCI_COMMAND_SERR;
444 pci_write_config32(dev, PCI_COMMAND, reg32);
445 }
446}
447
448struct chip_operations southbridge_intel_bd82x6x_ops = {
Stefan Reinauer9ca1c0a2012-07-25 16:10:36 -0700449 CHIP_NAME("Intel Series 6/7 (Cougar Point/Panther Point) Southbridge")
Stefan Reinauer8e073822012-04-04 00:07:22 +0200450 .enable_dev = pch_enable,
451};
Marc Jones783f2262013-02-11 14:36:35 -0700452#endif