blob: 0839ecfb7746a2b1f232404d66315e47ac3ac953 [file] [log] [blame]
Stefan Reinauer8e073822012-04-04 00:07:22 +02001/*
2 * This file is part of the coreboot project.
3 *
Stefan Reinauer8e073822012-04-04 00:07:22 +02004 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; version 2 of
8 * 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.
Stefan Reinauer8e073822012-04-04 00:07:22 +020014 */
15
16#include <console/console.h>
17#include <delay.h>
18#include <device/device.h>
19#include <device/pci.h>
Kyösti Mälkki21d6a272019-11-05 18:50:38 +020020#include <device/pci_def.h>
Kyösti Mälkkif1b58b72019-03-01 13:43:02 +020021#include <device/pci_ops.h>
Bill XIE8c57d092017-08-25 22:07:12 +080022#include <string.h>
Stefan Reinauer8e073822012-04-04 00:07:22 +020023
Kyösti Mälkki12b121c2019-08-18 16:33:39 +030024#include "chip.h"
25#include "pch.h"
26
Stefan Reinauer8e073822012-04-04 00:07:22 +020027int pch_silicon_revision(void)
28{
Felix Held82bd0c32016-08-13 23:27:15 +020029 static int pch_revision_id = -1;
Marc Jones783f2262013-02-11 14:36:35 -070030
Antonello Dettoridac82402016-09-02 09:14:39 +020031#ifdef __SIMPLE_DEVICE__
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030032 pci_devfn_t dev = PCI_DEV(0, 0x1f, 0);
Marc Jones783f2262013-02-11 14:36:35 -070033#else
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030034 struct device *dev = pcidev_on_root(0x1f, 0);
Marc Jones783f2262013-02-11 14:36:35 -070035#endif
36
Stefan Reinauer8e073822012-04-04 00:07:22 +020037 if (pch_revision_id < 0)
Marc Jones783f2262013-02-11 14:36:35 -070038 pch_revision_id = pci_read_config8(dev, PCI_REVISION_ID);
Stefan Reinauer8e073822012-04-04 00:07:22 +020039 return pch_revision_id;
40}
41
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070042int pch_silicon_type(void)
43{
Felix Held82bd0c32016-08-13 23:27:15 +020044 static int pch_type = -1;
Marc Jones783f2262013-02-11 14:36:35 -070045
Antonello Dettoridac82402016-09-02 09:14:39 +020046#ifdef __SIMPLE_DEVICE__
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030047 pci_devfn_t dev = PCI_DEV(0, 0x1f, 0);
Marc Jones783f2262013-02-11 14:36:35 -070048#else
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030049 struct device *dev = pcidev_on_root(0x1f, 0);
Marc Jones783f2262013-02-11 14:36:35 -070050#endif
51
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070052 if (pch_type < 0)
Marc Jones783f2262013-02-11 14:36:35 -070053 pch_type = pci_read_config8(dev, PCI_DEVICE_ID + 1);
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070054 return pch_type;
55}
56
57int pch_silicon_supported(int type, int rev)
58{
59 int cur_type = pch_silicon_type();
60 int cur_rev = pch_silicon_revision();
61
62 switch (type) {
63 case PCH_TYPE_CPT:
64 /* CougarPoint minimum revision */
65 if (cur_type == PCH_TYPE_CPT && cur_rev >= rev)
66 return 1;
67 /* PantherPoint any revision */
68 if (cur_type == PCH_TYPE_PPT)
69 return 1;
70 break;
71
72 case PCH_TYPE_PPT:
73 /* PantherPoint minimum revision */
74 if (cur_type == PCH_TYPE_PPT && cur_rev >= rev)
75 return 1;
76 break;
77 }
78
79 return 0;
80}
81
Stefan Reinauer8e073822012-04-04 00:07:22 +020082#define IOBP_RETRY 1000
83static inline int iobp_poll(void)
84{
Martin Rothff744bf2019-10-23 21:46:03 -060085 unsigned int try = IOBP_RETRY;
Stefan Reinauer8e073822012-04-04 00:07:22 +020086 u32 data;
87
88 while (try--) {
89 data = RCBA32(IOBPS);
90 if ((data & 1) == 0)
91 return 1;
92 udelay(10);
93 }
94
95 printk(BIOS_ERR, "IOBP timeout\n");
96 return 0;
97}
98
99void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue)
100{
101 u32 data;
102
103 /* Set the address */
104 RCBA32(IOBPIRI) = address;
105
106 /* READ OPCODE */
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700107 if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B0))
Stefan Reinauer8e073822012-04-04 00:07:22 +0200108 RCBA32(IOBPS) = IOBPS_RW_BX;
109 else
110 RCBA32(IOBPS) = IOBPS_READ_AX;
111 if (!iobp_poll())
112 return;
113
114 /* Read IOBP data */
115 data = RCBA32(IOBPD);
116 if (!iobp_poll())
117 return;
118
119 /* Check for successful transaction */
120 if ((RCBA32(IOBPS) & 0x6) != 0) {
121 printk(BIOS_ERR, "IOBP read 0x%08x failed\n", address);
122 return;
123 }
124
125 /* Update the data */
126 data &= andvalue;
127 data |= orvalue;
128
129 /* WRITE OPCODE */
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700130 if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B0))
Stefan Reinauer8e073822012-04-04 00:07:22 +0200131 RCBA32(IOBPS) = IOBPS_RW_BX;
132 else
133 RCBA32(IOBPS) = IOBPS_WRITE_AX;
134 if (!iobp_poll())
135 return;
136
137 /* Write IOBP data */
138 RCBA32(IOBPD) = data;
139 if (!iobp_poll())
140 return;
141}
142
Kyösti Mälkki21d6a272019-11-05 18:50:38 +0200143#ifndef __SIMPLE_DEVICE__
Frans Hendrikse6bf51f2019-05-01 10:48:31 +0200144/* Set bit in function disable register to hide this device */
Martin Rothff744bf2019-10-23 21:46:03 -0600145static void pch_hide_devfn(unsigned int devfn)
Marc Jones783f2262013-02-11 14:36:35 -0700146{
147 switch (devfn) {
Patrick Rudolph403f4332019-07-14 17:43:52 +0200148 case PCI_DEVFN(20, 0): /* xHCI */
149 if (pch_silicon_type() == PCH_TYPE_PPT) {
150 /* on CPT this bit is reserved */
151 RCBA32_OR(FD, PCH_DISABLE_XHCI);
152 }
153 break;
Marc Jones783f2262013-02-11 14:36:35 -0700154 case PCI_DEVFN(22, 0): /* MEI #1 */
155 RCBA32_OR(FD2, PCH_DISABLE_MEI1);
156 break;
157 case PCI_DEVFN(22, 1): /* MEI #2 */
158 RCBA32_OR(FD2, PCH_DISABLE_MEI2);
159 break;
160 case PCI_DEVFN(22, 2): /* IDE-R */
161 RCBA32_OR(FD2, PCH_DISABLE_IDER);
162 break;
163 case PCI_DEVFN(22, 3): /* KT */
164 RCBA32_OR(FD2, PCH_DISABLE_KT);
165 break;
166 case PCI_DEVFN(25, 0): /* Gigabit Ethernet */
Nico Huber6760e0b2019-11-17 02:34:53 +0100167 /* BUC is already handled in `early_pch.c`. */
Marc Jones783f2262013-02-11 14:36:35 -0700168 break;
169 case PCI_DEVFN(26, 0): /* EHCI #2 */
170 RCBA32_OR(FD, PCH_DISABLE_EHCI2);
171 break;
172 case PCI_DEVFN(27, 0): /* HD Audio Controller */
173 RCBA32_OR(FD, PCH_DISABLE_HD_AUDIO);
174 break;
175 case PCI_DEVFN(28, 0): /* PCI Express Root Port 1 */
176 case PCI_DEVFN(28, 1): /* PCI Express Root Port 2 */
177 case PCI_DEVFN(28, 2): /* PCI Express Root Port 3 */
178 case PCI_DEVFN(28, 3): /* PCI Express Root Port 4 */
179 case PCI_DEVFN(28, 4): /* PCI Express Root Port 5 */
180 case PCI_DEVFN(28, 5): /* PCI Express Root Port 6 */
181 case PCI_DEVFN(28, 6): /* PCI Express Root Port 7 */
182 case PCI_DEVFN(28, 7): /* PCI Express Root Port 8 */
183 RCBA32_OR(FD, PCH_DISABLE_PCIE(PCI_FUNC(devfn)));
184 break;
185 case PCI_DEVFN(29, 0): /* EHCI #1 */
186 RCBA32_OR(FD, PCH_DISABLE_EHCI1);
187 break;
188 case PCI_DEVFN(30, 0): /* PCI-to-PCI Bridge */
189 RCBA32_OR(FD, PCH_DISABLE_P2P);
190 break;
191 case PCI_DEVFN(31, 0): /* LPC */
192 RCBA32_OR(FD, PCH_DISABLE_LPC);
193 break;
194 case PCI_DEVFN(31, 2): /* SATA #1 */
195 RCBA32_OR(FD, PCH_DISABLE_SATA1);
196 break;
197 case PCI_DEVFN(31, 3): /* SMBUS */
198 RCBA32_OR(FD, PCH_DISABLE_SMBUS);
199 break;
200 case PCI_DEVFN(31, 5): /* SATA #22 */
201 RCBA32_OR(FD, PCH_DISABLE_SATA2);
202 break;
203 case PCI_DEVFN(31, 6): /* Thermal Subsystem */
204 RCBA32_OR(FD, PCH_DISABLE_THERMAL);
205 break;
206 }
207}
208
Stefan Reinauer8e073822012-04-04 00:07:22 +0200209/* Check if any port in set X to X+3 is enabled */
Elyes HAOUASdc035282018-09-18 13:28:49 +0200210static int pch_pcie_check_set_enabled(struct device *dev)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200211{
Elyes HAOUASdc035282018-09-18 13:28:49 +0200212 struct device *port;
Stefan Reinauer8e073822012-04-04 00:07:22 +0200213 int port_func;
214 int dev_func = PCI_FUNC(dev->path.pci.devfn);
215
216 printk(BIOS_DEBUG, "%s: check set enabled\n", dev_path(dev));
217
218 /* Go through static device tree list of devices
219 * because enumeration is still in progress */
220 for (port = all_devices; port; port = port->next) {
221 /* Only care about PCIe root ports */
222 if (PCI_SLOT(port->path.pci.devfn) !=
223 PCI_SLOT(dev->path.pci.devfn))
224 continue;
225
226 /* Check if port is in range and enabled */
227 port_func = PCI_FUNC(port->path.pci.devfn);
228 if (port_func >= dev_func &&
229 port_func < (dev_func + 4) &&
230 port->enabled)
231 return 1;
232 }
233
234 /* None of the ports in this set are enabled */
235 return 0;
236}
237
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700238/* RPFN is a write-once register so keep a copy until it is written */
239static u32 new_rpfn;
240
241/* Swap function numbers assigned to two PCIe Root Ports */
242static void pch_pcie_function_swap(u8 old_fn, u8 new_fn)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200243{
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700244 u32 old_rpfn = new_rpfn;
245
246 printk(BIOS_DEBUG, "PCH: Remap PCIe function %d to %d\n",
247 old_fn, new_fn);
248
249 new_rpfn &= ~(RPFN_FNMASK(old_fn) | RPFN_FNMASK(new_fn));
250
251 /* Old function set to new function and disabled */
252 new_rpfn |= RPFN_FNSET(old_fn, RPFN_FNGET(old_rpfn, new_fn));
253 new_rpfn |= RPFN_FNSET(new_fn, RPFN_FNGET(old_rpfn, old_fn));
254}
255
256/* Update devicetree with new Root Port function number assignment */
Bill XIE8c57d092017-08-25 22:07:12 +0800257static void pch_pcie_devicetree_update(
258 struct southbridge_intel_bd82x6x_config *config)
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700259{
Elyes HAOUASdc035282018-09-18 13:28:49 +0200260 struct device *dev;
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700261
Bill XIE8c57d092017-08-25 22:07:12 +0800262 /*
263 * hotplug map should also be updated along with their
264 * corresponding port
265 */
266 u8 new_hotplug_map[sizeof(config->pcie_hotplug_map)];
267
268 /*
269 * Slots that didn't move need the hotplug setting copied too,
270 * so "new_hotplug_map" is initialized with the values of the old map.
271 */
272 memcpy(new_hotplug_map, config->pcie_hotplug_map,
273 sizeof(new_hotplug_map));
274
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700275 /* Update the function numbers in the static devicetree */
276 for (dev = all_devices; dev; dev = dev->next) {
277 u8 new_devfn;
278
279 /* Only care about PCH PCIe root ports */
280 if (PCI_SLOT(dev->path.pci.devfn) !=
281 PCH_PCIE_DEV_SLOT)
282 continue;
283
284 /* Determine the new devfn for this port */
285 new_devfn = PCI_DEVFN(PCH_PCIE_DEV_SLOT,
286 RPFN_FNGET(new_rpfn,
287 PCI_FUNC(dev->path.pci.devfn)));
288
289 if (dev->path.pci.devfn != new_devfn) {
290 printk(BIOS_DEBUG,
291 "PCH: PCIe map %02x.%1x -> %02x.%1x\n",
292 PCI_SLOT(dev->path.pci.devfn),
293 PCI_FUNC(dev->path.pci.devfn),
294 PCI_SLOT(new_devfn), PCI_FUNC(new_devfn));
295
Bill XIE8c57d092017-08-25 22:07:12 +0800296 /*
297 * Copy the flag to its new position along with
298 * the corresponding port
299 */
300 new_hotplug_map[PCI_FUNC(new_devfn)] =
301 config->pcie_hotplug_map
302 [PCI_FUNC(dev->path.pci.devfn)];
303
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700304 dev->path.pci.devfn = new_devfn;
305 }
306 }
Bill XIE8c57d092017-08-25 22:07:12 +0800307
308 /* Copy the updated map back to its place */
309 memcpy(config->pcie_hotplug_map, new_hotplug_map,
310 sizeof(new_hotplug_map));
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700311}
312
313/* Special handling for PCIe Root Port devices */
Elyes HAOUASdc035282018-09-18 13:28:49 +0200314static void pch_pcie_enable(struct device *dev)
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700315{
316 struct southbridge_intel_bd82x6x_config *config = dev->chip_info;
Stefan Reinauer8e073822012-04-04 00:07:22 +0200317 u32 reg32;
318
Bill XIE8c57d092017-08-25 22:07:12 +0800319 if (!config)
320 return;
321
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700322 /*
323 * Save a copy of the Root Port Function Number map when
324 * starting to walk the list of PCIe Root Ports so it can
325 * be updated locally and written out when the last port
326 * has been processed.
327 */
328 if (PCI_FUNC(dev->path.pci.devfn) == 0) {
329 new_rpfn = RCBA32(RPFN);
Stefan Reinauer8e073822012-04-04 00:07:22 +0200330
331 /*
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700332 * Enable Root Port coalescing if the first port is disabled
333 * or the other devices will not be enumerated by the OS.
334 */
335 if (!dev->enabled)
336 config->pcie_port_coalesce = 1;
337
338 if (config->pcie_port_coalesce)
339 printk(BIOS_INFO,
340 "PCH: PCIe Root Port coalescing is enabled\n");
341 }
342
343 if (!dev->enabled) {
Marc Jonesef6b08c2012-06-15 23:03:15 -0600344 printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev));
345
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700346 /*
347 * PCIE Power Savings for PantherPoint and CougarPoint/B1+
Stefan Reinauer8e073822012-04-04 00:07:22 +0200348 *
349 * If PCIe 0-3 disabled set Function 0 0xE2[0] = 1
350 * If PCIe 4-7 disabled set Function 4 0xE2[0] = 1
351 *
Elyes HAOUAS79ccc692020-02-24 13:43:39 +0100352 * This check is done here instead of PCIe driver
353 * because the PCIe driver enable() handler is not
Stefan Reinauer8e073822012-04-04 00:07:22 +0200354 * called unless the device is enabled.
355 */
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700356 if ((PCI_FUNC(dev->path.pci.devfn) == 0 ||
Stefan Reinauer8e073822012-04-04 00:07:22 +0200357 PCI_FUNC(dev->path.pci.devfn) == 4)) {
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700358 /* Handle workaround for PPT and CPT/B1+ */
359 if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B1) &&
360 !pch_pcie_check_set_enabled(dev)) {
Stefan Reinauer8e073822012-04-04 00:07:22 +0200361 u8 reg8 = pci_read_config8(dev, 0xe2);
362 reg8 |= 1;
363 pci_write_config8(dev, 0xe2, reg8);
364 }
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700365
366 /*
367 * Enable Clock Gating for shared PCIe resources
368 * before disabling this particular port.
369 */
370 pci_write_config8(dev, 0xe1, 0x3c);
Stefan Reinauer8e073822012-04-04 00:07:22 +0200371 }
372
373 /* Ensure memory, io, and bus master are all disabled */
374 reg32 = pci_read_config32(dev, PCI_COMMAND);
375 reg32 &= ~(PCI_COMMAND_MASTER |
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700376 PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
377 pci_write_config32(dev, PCI_COMMAND, reg32);
378
379 /* Do not claim downstream transactions for PCIe ports */
380 new_rpfn |= RPFN_HIDE(PCI_FUNC(dev->path.pci.devfn));
381
382 /* Hide this device if possible */
383 pch_hide_devfn(dev->path.pci.devfn);
384 } else {
385 int fn;
386
387 /*
388 * Check if there is a lower disabled port to swap with this
389 * port in order to maintain linear order starting at zero.
390 */
391 if (config->pcie_port_coalesce) {
392 for (fn=0; fn < PCI_FUNC(dev->path.pci.devfn); fn++) {
393 if (!(new_rpfn & RPFN_HIDE(fn)))
394 continue;
395
396 /* Swap places with this function */
397 pch_pcie_function_swap(
398 PCI_FUNC(dev->path.pci.devfn), fn);
399 break;
400 }
401 }
402
403 /* Enable SERR */
404 reg32 = pci_read_config32(dev, PCI_COMMAND);
405 reg32 |= PCI_COMMAND_SERR;
406 pci_write_config32(dev, PCI_COMMAND, reg32);
407 }
408
409 /*
410 * When processing the last PCIe root port we can now
411 * update the Root Port Function Number and Hide register.
412 */
413 if (PCI_FUNC(dev->path.pci.devfn) == 7) {
414 printk(BIOS_SPEW, "PCH: RPFN 0x%08x -> 0x%08x\n",
415 RCBA32(RPFN), new_rpfn);
416 RCBA32(RPFN) = new_rpfn;
417
418 /* Update static devictree with new function numbers */
419 if (config->pcie_port_coalesce)
Bill XIE8c57d092017-08-25 22:07:12 +0800420 pch_pcie_devicetree_update(config);
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700421 }
422}
423
Elyes HAOUASdc035282018-09-18 13:28:49 +0200424void pch_enable(struct device *dev)
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700425{
426 u32 reg32;
427
428 /* PCH PCIe Root Ports get special handling */
429 if (PCI_SLOT(dev->path.pci.devfn) == PCH_PCIE_DEV_SLOT)
430 return pch_pcie_enable(dev);
431
432 if (!dev->enabled) {
433 printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev));
434
435 /* Ensure memory, io, and bus master are all disabled */
436 reg32 = pci_read_config32(dev, PCI_COMMAND);
437 reg32 &= ~(PCI_COMMAND_MASTER |
438 PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
Stefan Reinauer8e073822012-04-04 00:07:22 +0200439 pci_write_config32(dev, PCI_COMMAND, reg32);
440
441 /* Hide this device if possible */
442 pch_hide_devfn(dev->path.pci.devfn);
443 } else {
444 /* Enable SERR */
445 reg32 = pci_read_config32(dev, PCI_COMMAND);
446 reg32 |= PCI_COMMAND_SERR;
447 pci_write_config32(dev, PCI_COMMAND, reg32);
448 }
449}
450
451struct chip_operations southbridge_intel_bd82x6x_ops = {
Stefan Reinauer9ca1c0a2012-07-25 16:10:36 -0700452 CHIP_NAME("Intel Series 6/7 (Cougar Point/Panther Point) Southbridge")
Stefan Reinauer8e073822012-04-04 00:07:22 +0200453 .enable_dev = pch_enable,
454};
Marc Jones783f2262013-02-11 14:36:35 -0700455#endif