blob: cb3188052bf0465a9a70f18aff90f9218402b42b [file] [log] [blame]
Angel Pons182dbde2020-04-02 23:49:05 +02001/* SPDX-License-Identifier: GPL-2.0-only */
2/* This file is part of the coreboot project. */
Stefan Reinauer8e073822012-04-04 00:07:22 +02003
4#include <console/console.h>
5#include <delay.h>
6#include <device/device.h>
7#include <device/pci.h>
Kyösti Mälkki21d6a272019-11-05 18:50:38 +02008#include <device/pci_def.h>
Kyösti Mälkkif1b58b72019-03-01 13:43:02 +02009#include <device/pci_ops.h>
Bill XIE8c57d092017-08-25 22:07:12 +080010#include <string.h>
Stefan Reinauer8e073822012-04-04 00:07:22 +020011
Kyösti Mälkki12b121c2019-08-18 16:33:39 +030012#include "chip.h"
13#include "pch.h"
14
Stefan Reinauer8e073822012-04-04 00:07:22 +020015int pch_silicon_revision(void)
16{
Felix Held82bd0c32016-08-13 23:27:15 +020017 static int pch_revision_id = -1;
Marc Jones783f2262013-02-11 14:36:35 -070018
Antonello Dettoridac82402016-09-02 09:14:39 +020019#ifdef __SIMPLE_DEVICE__
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030020 pci_devfn_t dev = PCI_DEV(0, 0x1f, 0);
Marc Jones783f2262013-02-11 14:36:35 -070021#else
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030022 struct device *dev = pcidev_on_root(0x1f, 0);
Marc Jones783f2262013-02-11 14:36:35 -070023#endif
24
Stefan Reinauer8e073822012-04-04 00:07:22 +020025 if (pch_revision_id < 0)
Marc Jones783f2262013-02-11 14:36:35 -070026 pch_revision_id = pci_read_config8(dev, PCI_REVISION_ID);
Stefan Reinauer8e073822012-04-04 00:07:22 +020027 return pch_revision_id;
28}
29
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070030int pch_silicon_type(void)
31{
Felix Held82bd0c32016-08-13 23:27:15 +020032 static int pch_type = -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
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070040 if (pch_type < 0)
Marc Jones783f2262013-02-11 14:36:35 -070041 pch_type = pci_read_config8(dev, PCI_DEVICE_ID + 1);
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070042 return pch_type;
43}
44
45int pch_silicon_supported(int type, int rev)
46{
47 int cur_type = pch_silicon_type();
48 int cur_rev = pch_silicon_revision();
49
50 switch (type) {
51 case PCH_TYPE_CPT:
52 /* CougarPoint minimum revision */
53 if (cur_type == PCH_TYPE_CPT && cur_rev >= rev)
54 return 1;
55 /* PantherPoint any revision */
56 if (cur_type == PCH_TYPE_PPT)
57 return 1;
58 break;
59
60 case PCH_TYPE_PPT:
61 /* PantherPoint minimum revision */
62 if (cur_type == PCH_TYPE_PPT && cur_rev >= rev)
63 return 1;
64 break;
65 }
66
67 return 0;
68}
69
Stefan Reinauer8e073822012-04-04 00:07:22 +020070#define IOBP_RETRY 1000
71static inline int iobp_poll(void)
72{
Martin Rothff744bf2019-10-23 21:46:03 -060073 unsigned int try = IOBP_RETRY;
Stefan Reinauer8e073822012-04-04 00:07:22 +020074 u32 data;
75
76 while (try--) {
77 data = RCBA32(IOBPS);
78 if ((data & 1) == 0)
79 return 1;
80 udelay(10);
81 }
82
83 printk(BIOS_ERR, "IOBP timeout\n");
84 return 0;
85}
86
87void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue)
88{
89 u32 data;
90
91 /* Set the address */
92 RCBA32(IOBPIRI) = address;
93
94 /* READ OPCODE */
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070095 if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B0))
Stefan Reinauer8e073822012-04-04 00:07:22 +020096 RCBA32(IOBPS) = IOBPS_RW_BX;
97 else
98 RCBA32(IOBPS) = IOBPS_READ_AX;
99 if (!iobp_poll())
100 return;
101
102 /* Read IOBP data */
103 data = RCBA32(IOBPD);
104 if (!iobp_poll())
105 return;
106
107 /* Check for successful transaction */
108 if ((RCBA32(IOBPS) & 0x6) != 0) {
109 printk(BIOS_ERR, "IOBP read 0x%08x failed\n", address);
110 return;
111 }
112
113 /* Update the data */
114 data &= andvalue;
115 data |= orvalue;
116
117 /* WRITE OPCODE */
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700118 if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B0))
Stefan Reinauer8e073822012-04-04 00:07:22 +0200119 RCBA32(IOBPS) = IOBPS_RW_BX;
120 else
121 RCBA32(IOBPS) = IOBPS_WRITE_AX;
122 if (!iobp_poll())
123 return;
124
125 /* Write IOBP data */
126 RCBA32(IOBPD) = data;
127 if (!iobp_poll())
128 return;
129}
130
Kyösti Mälkki21d6a272019-11-05 18:50:38 +0200131#ifndef __SIMPLE_DEVICE__
Frans Hendrikse6bf51f2019-05-01 10:48:31 +0200132/* Set bit in function disable register to hide this device */
Martin Rothff744bf2019-10-23 21:46:03 -0600133static void pch_hide_devfn(unsigned int devfn)
Marc Jones783f2262013-02-11 14:36:35 -0700134{
135 switch (devfn) {
Patrick Rudolph403f4332019-07-14 17:43:52 +0200136 case PCI_DEVFN(20, 0): /* xHCI */
137 if (pch_silicon_type() == PCH_TYPE_PPT) {
138 /* on CPT this bit is reserved */
139 RCBA32_OR(FD, PCH_DISABLE_XHCI);
140 }
141 break;
Marc Jones783f2262013-02-11 14:36:35 -0700142 case PCI_DEVFN(22, 0): /* MEI #1 */
143 RCBA32_OR(FD2, PCH_DISABLE_MEI1);
144 break;
145 case PCI_DEVFN(22, 1): /* MEI #2 */
146 RCBA32_OR(FD2, PCH_DISABLE_MEI2);
147 break;
148 case PCI_DEVFN(22, 2): /* IDE-R */
149 RCBA32_OR(FD2, PCH_DISABLE_IDER);
150 break;
151 case PCI_DEVFN(22, 3): /* KT */
152 RCBA32_OR(FD2, PCH_DISABLE_KT);
153 break;
154 case PCI_DEVFN(25, 0): /* Gigabit Ethernet */
Nico Huber6760e0b2019-11-17 02:34:53 +0100155 /* BUC is already handled in `early_pch.c`. */
Marc Jones783f2262013-02-11 14:36:35 -0700156 break;
157 case PCI_DEVFN(26, 0): /* EHCI #2 */
158 RCBA32_OR(FD, PCH_DISABLE_EHCI2);
159 break;
160 case PCI_DEVFN(27, 0): /* HD Audio Controller */
161 RCBA32_OR(FD, PCH_DISABLE_HD_AUDIO);
162 break;
163 case PCI_DEVFN(28, 0): /* PCI Express Root Port 1 */
164 case PCI_DEVFN(28, 1): /* PCI Express Root Port 2 */
165 case PCI_DEVFN(28, 2): /* PCI Express Root Port 3 */
166 case PCI_DEVFN(28, 3): /* PCI Express Root Port 4 */
167 case PCI_DEVFN(28, 4): /* PCI Express Root Port 5 */
168 case PCI_DEVFN(28, 5): /* PCI Express Root Port 6 */
169 case PCI_DEVFN(28, 6): /* PCI Express Root Port 7 */
170 case PCI_DEVFN(28, 7): /* PCI Express Root Port 8 */
171 RCBA32_OR(FD, PCH_DISABLE_PCIE(PCI_FUNC(devfn)));
172 break;
173 case PCI_DEVFN(29, 0): /* EHCI #1 */
174 RCBA32_OR(FD, PCH_DISABLE_EHCI1);
175 break;
176 case PCI_DEVFN(30, 0): /* PCI-to-PCI Bridge */
177 RCBA32_OR(FD, PCH_DISABLE_P2P);
178 break;
179 case PCI_DEVFN(31, 0): /* LPC */
180 RCBA32_OR(FD, PCH_DISABLE_LPC);
181 break;
182 case PCI_DEVFN(31, 2): /* SATA #1 */
183 RCBA32_OR(FD, PCH_DISABLE_SATA1);
184 break;
185 case PCI_DEVFN(31, 3): /* SMBUS */
186 RCBA32_OR(FD, PCH_DISABLE_SMBUS);
187 break;
188 case PCI_DEVFN(31, 5): /* SATA #22 */
189 RCBA32_OR(FD, PCH_DISABLE_SATA2);
190 break;
191 case PCI_DEVFN(31, 6): /* Thermal Subsystem */
192 RCBA32_OR(FD, PCH_DISABLE_THERMAL);
193 break;
194 }
195}
196
Stefan Reinauer8e073822012-04-04 00:07:22 +0200197/* Check if any port in set X to X+3 is enabled */
Elyes HAOUASdc035282018-09-18 13:28:49 +0200198static int pch_pcie_check_set_enabled(struct device *dev)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200199{
Elyes HAOUASdc035282018-09-18 13:28:49 +0200200 struct device *port;
Stefan Reinauer8e073822012-04-04 00:07:22 +0200201 int port_func;
202 int dev_func = PCI_FUNC(dev->path.pci.devfn);
203
204 printk(BIOS_DEBUG, "%s: check set enabled\n", dev_path(dev));
205
206 /* Go through static device tree list of devices
207 * because enumeration is still in progress */
208 for (port = all_devices; port; port = port->next) {
209 /* Only care about PCIe root ports */
210 if (PCI_SLOT(port->path.pci.devfn) !=
211 PCI_SLOT(dev->path.pci.devfn))
212 continue;
213
214 /* Check if port is in range and enabled */
215 port_func = PCI_FUNC(port->path.pci.devfn);
216 if (port_func >= dev_func &&
217 port_func < (dev_func + 4) &&
218 port->enabled)
219 return 1;
220 }
221
222 /* None of the ports in this set are enabled */
223 return 0;
224}
225
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700226/* RPFN is a write-once register so keep a copy until it is written */
227static u32 new_rpfn;
228
229/* Swap function numbers assigned to two PCIe Root Ports */
230static void pch_pcie_function_swap(u8 old_fn, u8 new_fn)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200231{
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700232 u32 old_rpfn = new_rpfn;
233
234 printk(BIOS_DEBUG, "PCH: Remap PCIe function %d to %d\n",
235 old_fn, new_fn);
236
237 new_rpfn &= ~(RPFN_FNMASK(old_fn) | RPFN_FNMASK(new_fn));
238
239 /* Old function set to new function and disabled */
240 new_rpfn |= RPFN_FNSET(old_fn, RPFN_FNGET(old_rpfn, new_fn));
241 new_rpfn |= RPFN_FNSET(new_fn, RPFN_FNGET(old_rpfn, old_fn));
242}
243
244/* Update devicetree with new Root Port function number assignment */
Bill XIE8c57d092017-08-25 22:07:12 +0800245static void pch_pcie_devicetree_update(
246 struct southbridge_intel_bd82x6x_config *config)
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700247{
Elyes HAOUASdc035282018-09-18 13:28:49 +0200248 struct device *dev;
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700249
Bill XIE8c57d092017-08-25 22:07:12 +0800250 /*
251 * hotplug map should also be updated along with their
252 * corresponding port
253 */
254 u8 new_hotplug_map[sizeof(config->pcie_hotplug_map)];
255
256 /*
257 * Slots that didn't move need the hotplug setting copied too,
258 * so "new_hotplug_map" is initialized with the values of the old map.
259 */
260 memcpy(new_hotplug_map, config->pcie_hotplug_map,
261 sizeof(new_hotplug_map));
262
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700263 /* Update the function numbers in the static devicetree */
264 for (dev = all_devices; dev; dev = dev->next) {
265 u8 new_devfn;
266
267 /* Only care about PCH PCIe root ports */
268 if (PCI_SLOT(dev->path.pci.devfn) !=
269 PCH_PCIE_DEV_SLOT)
270 continue;
271
272 /* Determine the new devfn for this port */
273 new_devfn = PCI_DEVFN(PCH_PCIE_DEV_SLOT,
274 RPFN_FNGET(new_rpfn,
275 PCI_FUNC(dev->path.pci.devfn)));
276
277 if (dev->path.pci.devfn != new_devfn) {
278 printk(BIOS_DEBUG,
279 "PCH: PCIe map %02x.%1x -> %02x.%1x\n",
280 PCI_SLOT(dev->path.pci.devfn),
281 PCI_FUNC(dev->path.pci.devfn),
282 PCI_SLOT(new_devfn), PCI_FUNC(new_devfn));
283
Bill XIE8c57d092017-08-25 22:07:12 +0800284 /*
285 * Copy the flag to its new position along with
286 * the corresponding port
287 */
288 new_hotplug_map[PCI_FUNC(new_devfn)] =
289 config->pcie_hotplug_map
290 [PCI_FUNC(dev->path.pci.devfn)];
291
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700292 dev->path.pci.devfn = new_devfn;
293 }
294 }
Bill XIE8c57d092017-08-25 22:07:12 +0800295
296 /* Copy the updated map back to its place */
297 memcpy(config->pcie_hotplug_map, new_hotplug_map,
298 sizeof(new_hotplug_map));
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700299}
300
301/* Special handling for PCIe Root Port devices */
Elyes HAOUASdc035282018-09-18 13:28:49 +0200302static void pch_pcie_enable(struct device *dev)
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700303{
304 struct southbridge_intel_bd82x6x_config *config = dev->chip_info;
Stefan Reinauer8e073822012-04-04 00:07:22 +0200305 u32 reg32;
306
Bill XIE8c57d092017-08-25 22:07:12 +0800307 if (!config)
308 return;
309
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700310 /*
311 * Save a copy of the Root Port Function Number map when
312 * starting to walk the list of PCIe Root Ports so it can
313 * be updated locally and written out when the last port
314 * has been processed.
315 */
316 if (PCI_FUNC(dev->path.pci.devfn) == 0) {
317 new_rpfn = RCBA32(RPFN);
Stefan Reinauer8e073822012-04-04 00:07:22 +0200318
319 /*
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700320 * Enable Root Port coalescing if the first port is disabled
321 * or the other devices will not be enumerated by the OS.
322 */
323 if (!dev->enabled)
324 config->pcie_port_coalesce = 1;
325
326 if (config->pcie_port_coalesce)
327 printk(BIOS_INFO,
328 "PCH: PCIe Root Port coalescing is enabled\n");
329 }
330
331 if (!dev->enabled) {
Marc Jonesef6b08c2012-06-15 23:03:15 -0600332 printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev));
333
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700334 /*
335 * PCIE Power Savings for PantherPoint and CougarPoint/B1+
Stefan Reinauer8e073822012-04-04 00:07:22 +0200336 *
337 * If PCIe 0-3 disabled set Function 0 0xE2[0] = 1
338 * If PCIe 4-7 disabled set Function 4 0xE2[0] = 1
339 *
Elyes HAOUAS79ccc692020-02-24 13:43:39 +0100340 * This check is done here instead of PCIe driver
341 * because the PCIe driver enable() handler is not
Stefan Reinauer8e073822012-04-04 00:07:22 +0200342 * called unless the device is enabled.
343 */
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700344 if ((PCI_FUNC(dev->path.pci.devfn) == 0 ||
Stefan Reinauer8e073822012-04-04 00:07:22 +0200345 PCI_FUNC(dev->path.pci.devfn) == 4)) {
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700346 /* Handle workaround for PPT and CPT/B1+ */
347 if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B1) &&
348 !pch_pcie_check_set_enabled(dev)) {
Stefan Reinauer8e073822012-04-04 00:07:22 +0200349 u8 reg8 = pci_read_config8(dev, 0xe2);
350 reg8 |= 1;
351 pci_write_config8(dev, 0xe2, reg8);
352 }
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700353
354 /*
355 * Enable Clock Gating for shared PCIe resources
356 * before disabling this particular port.
357 */
358 pci_write_config8(dev, 0xe1, 0x3c);
Stefan Reinauer8e073822012-04-04 00:07:22 +0200359 }
360
361 /* Ensure memory, io, and bus master are all disabled */
362 reg32 = pci_read_config32(dev, PCI_COMMAND);
363 reg32 &= ~(PCI_COMMAND_MASTER |
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700364 PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
365 pci_write_config32(dev, PCI_COMMAND, reg32);
366
367 /* Do not claim downstream transactions for PCIe ports */
368 new_rpfn |= RPFN_HIDE(PCI_FUNC(dev->path.pci.devfn));
369
370 /* Hide this device if possible */
371 pch_hide_devfn(dev->path.pci.devfn);
372 } else {
373 int fn;
374
375 /*
376 * Check if there is a lower disabled port to swap with this
377 * port in order to maintain linear order starting at zero.
378 */
379 if (config->pcie_port_coalesce) {
380 for (fn=0; fn < PCI_FUNC(dev->path.pci.devfn); fn++) {
381 if (!(new_rpfn & RPFN_HIDE(fn)))
382 continue;
383
384 /* Swap places with this function */
385 pch_pcie_function_swap(
386 PCI_FUNC(dev->path.pci.devfn), fn);
387 break;
388 }
389 }
390
391 /* Enable SERR */
392 reg32 = pci_read_config32(dev, PCI_COMMAND);
393 reg32 |= PCI_COMMAND_SERR;
394 pci_write_config32(dev, PCI_COMMAND, reg32);
395 }
396
397 /*
398 * When processing the last PCIe root port we can now
399 * update the Root Port Function Number and Hide register.
400 */
401 if (PCI_FUNC(dev->path.pci.devfn) == 7) {
402 printk(BIOS_SPEW, "PCH: RPFN 0x%08x -> 0x%08x\n",
403 RCBA32(RPFN), new_rpfn);
404 RCBA32(RPFN) = new_rpfn;
405
406 /* Update static devictree with new function numbers */
407 if (config->pcie_port_coalesce)
Bill XIE8c57d092017-08-25 22:07:12 +0800408 pch_pcie_devicetree_update(config);
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700409 }
410}
411
Elyes HAOUASdc035282018-09-18 13:28:49 +0200412void pch_enable(struct device *dev)
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700413{
414 u32 reg32;
415
416 /* PCH PCIe Root Ports get special handling */
417 if (PCI_SLOT(dev->path.pci.devfn) == PCH_PCIE_DEV_SLOT)
418 return pch_pcie_enable(dev);
419
420 if (!dev->enabled) {
421 printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev));
422
423 /* Ensure memory, io, and bus master are all disabled */
424 reg32 = pci_read_config32(dev, PCI_COMMAND);
425 reg32 &= ~(PCI_COMMAND_MASTER |
426 PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
Stefan Reinauer8e073822012-04-04 00:07:22 +0200427 pci_write_config32(dev, PCI_COMMAND, reg32);
428
429 /* Hide this device if possible */
430 pch_hide_devfn(dev->path.pci.devfn);
431 } else {
432 /* Enable SERR */
433 reg32 = pci_read_config32(dev, PCI_COMMAND);
434 reg32 |= PCI_COMMAND_SERR;
435 pci_write_config32(dev, PCI_COMMAND, reg32);
436 }
437}
438
439struct chip_operations southbridge_intel_bd82x6x_ops = {
Stefan Reinauer9ca1c0a2012-07-25 16:10:36 -0700440 CHIP_NAME("Intel Series 6/7 (Cougar Point/Panther Point) Southbridge")
Stefan Reinauer8e073822012-04-04 00:07:22 +0200441 .enable_dev = pch_enable,
442};
Marc Jones783f2262013-02-11 14:36:35 -0700443#endif