blob: 5f440e61cd77c07c436ea7a8f994c3f0c5886b5f [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
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <console/console.h>
23#include <delay.h>
24#include <device/device.h>
25#include <device/pci.h>
26#include "pch.h"
27
28static int pch_revision_id = -1;
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070029static int pch_type = -1;
Stefan Reinauer8e073822012-04-04 00:07:22 +020030
31int pch_silicon_revision(void)
32{
33 if (pch_revision_id < 0)
34 pch_revision_id = pci_read_config8(
35 dev_find_slot(0, PCI_DEVFN(0x1f, 0)),
36 PCI_REVISION_ID);
37 return pch_revision_id;
38}
39
Duncan Laurieb9fe01c2012-04-27 10:30:51 -070040int pch_silicon_type(void)
41{
42 if (pch_type < 0)
43 pch_type = pci_read_config8(
44 dev_find_slot(0, PCI_DEVFN(0x1f, 0)),
45 PCI_DEVICE_ID + 1);
46 return pch_type;
47}
48
49int pch_silicon_supported(int type, int rev)
50{
51 int cur_type = pch_silicon_type();
52 int cur_rev = pch_silicon_revision();
53
54 switch (type) {
55 case PCH_TYPE_CPT:
56 /* CougarPoint minimum revision */
57 if (cur_type == PCH_TYPE_CPT && cur_rev >= rev)
58 return 1;
59 /* PantherPoint any revision */
60 if (cur_type == PCH_TYPE_PPT)
61 return 1;
62 break;
63
64 case PCH_TYPE_PPT:
65 /* PantherPoint minimum revision */
66 if (cur_type == PCH_TYPE_PPT && cur_rev >= rev)
67 return 1;
68 break;
69 }
70
71 return 0;
72}
73
Stefan Reinauer8e073822012-04-04 00:07:22 +020074/* Set bit in Function Disble register to hide this device */
75static void pch_hide_devfn(unsigned devfn)
76{
77 switch (devfn) {
78 case PCI_DEVFN(22, 0): /* MEI #1 */
79 RCBA32_OR(FD2, PCH_DISABLE_MEI1);
80 break;
81 case PCI_DEVFN(22, 1): /* MEI #2 */
82 RCBA32_OR(FD2, PCH_DISABLE_MEI2);
83 break;
84 case PCI_DEVFN(22, 2): /* IDE-R */
85 RCBA32_OR(FD2, PCH_DISABLE_IDER);
86 break;
87 case PCI_DEVFN(22, 3): /* KT */
88 RCBA32_OR(FD2, PCH_DISABLE_KT);
89 break;
90 case PCI_DEVFN(25, 0): /* Gigabit Ethernet */
91 RCBA32_OR(BUC, PCH_DISABLE_GBE);
92 break;
93 case PCI_DEVFN(26, 0): /* EHCI #2 */
94 RCBA32_OR(FD, PCH_DISABLE_EHCI2);
95 break;
96 case PCI_DEVFN(27, 0): /* HD Audio Controller */
97 RCBA32_OR(FD, PCH_DISABLE_HD_AUDIO);
98 break;
99 case PCI_DEVFN(28, 0): /* PCI Express Root Port 1 */
100 case PCI_DEVFN(28, 1): /* PCI Express Root Port 2 */
101 case PCI_DEVFN(28, 2): /* PCI Express Root Port 3 */
102 case PCI_DEVFN(28, 3): /* PCI Express Root Port 4 */
103 case PCI_DEVFN(28, 4): /* PCI Express Root Port 5 */
104 case PCI_DEVFN(28, 5): /* PCI Express Root Port 6 */
105 case PCI_DEVFN(28, 6): /* PCI Express Root Port 7 */
106 case PCI_DEVFN(28, 7): /* PCI Express Root Port 8 */
107 RCBA32_OR(FD, PCH_DISABLE_PCIE(PCI_FUNC(devfn)));
108 break;
109 case PCI_DEVFN(29, 0): /* EHCI #1 */
110 RCBA32_OR(FD, PCH_DISABLE_EHCI1);
111 break;
112 case PCI_DEVFN(30, 0): /* PCI-to-PCI Bridge */
113 RCBA32_OR(FD, PCH_DISABLE_P2P);
114 break;
115 case PCI_DEVFN(31, 0): /* LPC */
116 RCBA32_OR(FD, PCH_DISABLE_LPC);
117 break;
118 case PCI_DEVFN(31, 2): /* SATA #1 */
119 RCBA32_OR(FD, PCH_DISABLE_SATA1);
120 break;
121 case PCI_DEVFN(31, 3): /* SMBUS */
122 RCBA32_OR(FD, PCH_DISABLE_SMBUS);
123 break;
124 case PCI_DEVFN(31, 5): /* SATA #22 */
125 RCBA32_OR(FD, PCH_DISABLE_SATA2);
126 break;
127 case PCI_DEVFN(31, 6): /* Thermal Subsystem */
128 RCBA32_OR(FD, PCH_DISABLE_THERMAL);
129 break;
130 }
131}
132
133#define IOBP_RETRY 1000
134static inline int iobp_poll(void)
135{
136 unsigned try = IOBP_RETRY;
137 u32 data;
138
139 while (try--) {
140 data = RCBA32(IOBPS);
141 if ((data & 1) == 0)
142 return 1;
143 udelay(10);
144 }
145
146 printk(BIOS_ERR, "IOBP timeout\n");
147 return 0;
148}
149
150void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue)
151{
152 u32 data;
153
154 /* Set the address */
155 RCBA32(IOBPIRI) = address;
156
157 /* READ OPCODE */
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700158 if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B0))
Stefan Reinauer8e073822012-04-04 00:07:22 +0200159 RCBA32(IOBPS) = IOBPS_RW_BX;
160 else
161 RCBA32(IOBPS) = IOBPS_READ_AX;
162 if (!iobp_poll())
163 return;
164
165 /* Read IOBP data */
166 data = RCBA32(IOBPD);
167 if (!iobp_poll())
168 return;
169
170 /* Check for successful transaction */
171 if ((RCBA32(IOBPS) & 0x6) != 0) {
172 printk(BIOS_ERR, "IOBP read 0x%08x failed\n", address);
173 return;
174 }
175
176 /* Update the data */
177 data &= andvalue;
178 data |= orvalue;
179
180 /* WRITE OPCODE */
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700181 if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B0))
Stefan Reinauer8e073822012-04-04 00:07:22 +0200182 RCBA32(IOBPS) = IOBPS_RW_BX;
183 else
184 RCBA32(IOBPS) = IOBPS_WRITE_AX;
185 if (!iobp_poll())
186 return;
187
188 /* Write IOBP data */
189 RCBA32(IOBPD) = data;
190 if (!iobp_poll())
191 return;
192}
193
194/* Check if any port in set X to X+3 is enabled */
195static int pch_pcie_check_set_enabled(device_t dev)
196{
197 device_t port;
198 int port_func;
199 int dev_func = PCI_FUNC(dev->path.pci.devfn);
200
201 printk(BIOS_DEBUG, "%s: check set enabled\n", dev_path(dev));
202
203 /* Go through static device tree list of devices
204 * because enumeration is still in progress */
205 for (port = all_devices; port; port = port->next) {
206 /* Only care about PCIe root ports */
207 if (PCI_SLOT(port->path.pci.devfn) !=
208 PCI_SLOT(dev->path.pci.devfn))
209 continue;
210
211 /* Check if port is in range and enabled */
212 port_func = PCI_FUNC(port->path.pci.devfn);
213 if (port_func >= dev_func &&
214 port_func < (dev_func + 4) &&
215 port->enabled)
216 return 1;
217 }
218
219 /* None of the ports in this set are enabled */
220 return 0;
221}
222
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700223/* RPFN is a write-once register so keep a copy until it is written */
224static u32 new_rpfn;
225
226/* Swap function numbers assigned to two PCIe Root Ports */
227static void pch_pcie_function_swap(u8 old_fn, u8 new_fn)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200228{
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700229 u32 old_rpfn = new_rpfn;
230
231 printk(BIOS_DEBUG, "PCH: Remap PCIe function %d to %d\n",
232 old_fn, new_fn);
233
234 new_rpfn &= ~(RPFN_FNMASK(old_fn) | RPFN_FNMASK(new_fn));
235
236 /* Old function set to new function and disabled */
237 new_rpfn |= RPFN_FNSET(old_fn, RPFN_FNGET(old_rpfn, new_fn));
238 new_rpfn |= RPFN_FNSET(new_fn, RPFN_FNGET(old_rpfn, old_fn));
239}
240
241/* Update devicetree with new Root Port function number assignment */
242static void pch_pcie_devicetree_update(void)
243{
244 device_t dev;
245
246 /* Update the function numbers in the static devicetree */
247 for (dev = all_devices; dev; dev = dev->next) {
248 u8 new_devfn;
249
250 /* Only care about PCH PCIe root ports */
251 if (PCI_SLOT(dev->path.pci.devfn) !=
252 PCH_PCIE_DEV_SLOT)
253 continue;
254
255 /* Determine the new devfn for this port */
256 new_devfn = PCI_DEVFN(PCH_PCIE_DEV_SLOT,
257 RPFN_FNGET(new_rpfn,
258 PCI_FUNC(dev->path.pci.devfn)));
259
260 if (dev->path.pci.devfn != new_devfn) {
261 printk(BIOS_DEBUG,
262 "PCH: PCIe map %02x.%1x -> %02x.%1x\n",
263 PCI_SLOT(dev->path.pci.devfn),
264 PCI_FUNC(dev->path.pci.devfn),
265 PCI_SLOT(new_devfn), PCI_FUNC(new_devfn));
266
267 dev->path.pci.devfn = new_devfn;
268 }
269 }
270}
271
272/* Special handling for PCIe Root Port devices */
273static void pch_pcie_enable(device_t dev)
274{
275 struct southbridge_intel_bd82x6x_config *config = dev->chip_info;
Stefan Reinauer8e073822012-04-04 00:07:22 +0200276 u32 reg32;
277
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700278 /*
279 * Save a copy of the Root Port Function Number map when
280 * starting to walk the list of PCIe Root Ports so it can
281 * be updated locally and written out when the last port
282 * has been processed.
283 */
284 if (PCI_FUNC(dev->path.pci.devfn) == 0) {
285 new_rpfn = RCBA32(RPFN);
Stefan Reinauer8e073822012-04-04 00:07:22 +0200286
287 /*
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700288 * Enable Root Port coalescing if the first port is disabled
289 * or the other devices will not be enumerated by the OS.
290 */
291 if (!dev->enabled)
292 config->pcie_port_coalesce = 1;
293
294 if (config->pcie_port_coalesce)
295 printk(BIOS_INFO,
296 "PCH: PCIe Root Port coalescing is enabled\n");
297 }
298
299 if (!dev->enabled) {
300 /*
301 * PCIE Power Savings for PantherPoint and CougarPoint/B1+
Stefan Reinauer8e073822012-04-04 00:07:22 +0200302 *
303 * If PCIe 0-3 disabled set Function 0 0xE2[0] = 1
304 * If PCIe 4-7 disabled set Function 4 0xE2[0] = 1
305 *
306 * This check is done here instead of pcie driver
307 * because the pcie driver enable() handler is not
308 * called unless the device is enabled.
309 */
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700310 if ((PCI_FUNC(dev->path.pci.devfn) == 0 ||
Stefan Reinauer8e073822012-04-04 00:07:22 +0200311 PCI_FUNC(dev->path.pci.devfn) == 4)) {
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700312 /* Handle workaround for PPT and CPT/B1+ */
313 if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B1) &&
314 !pch_pcie_check_set_enabled(dev)) {
Stefan Reinauer8e073822012-04-04 00:07:22 +0200315 u8 reg8 = pci_read_config8(dev, 0xe2);
316 reg8 |= 1;
317 pci_write_config8(dev, 0xe2, reg8);
318 }
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700319
320 /*
321 * Enable Clock Gating for shared PCIe resources
322 * before disabling this particular port.
323 */
324 pci_write_config8(dev, 0xe1, 0x3c);
Stefan Reinauer8e073822012-04-04 00:07:22 +0200325 }
326
327 /* Ensure memory, io, and bus master are all disabled */
328 reg32 = pci_read_config32(dev, PCI_COMMAND);
329 reg32 &= ~(PCI_COMMAND_MASTER |
Duncan Laurieb9fe01c2012-04-27 10:30:51 -0700330 PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
331 pci_write_config32(dev, PCI_COMMAND, reg32);
332
333 /* Do not claim downstream transactions for PCIe ports */
334 new_rpfn |= RPFN_HIDE(PCI_FUNC(dev->path.pci.devfn));
335
336 /* Hide this device if possible */
337 pch_hide_devfn(dev->path.pci.devfn);
338 } else {
339 int fn;
340
341 /*
342 * Check if there is a lower disabled port to swap with this
343 * port in order to maintain linear order starting at zero.
344 */
345 if (config->pcie_port_coalesce) {
346 for (fn=0; fn < PCI_FUNC(dev->path.pci.devfn); fn++) {
347 if (!(new_rpfn & RPFN_HIDE(fn)))
348 continue;
349
350 /* Swap places with this function */
351 pch_pcie_function_swap(
352 PCI_FUNC(dev->path.pci.devfn), fn);
353 break;
354 }
355 }
356
357 /* Enable SERR */
358 reg32 = pci_read_config32(dev, PCI_COMMAND);
359 reg32 |= PCI_COMMAND_SERR;
360 pci_write_config32(dev, PCI_COMMAND, reg32);
361 }
362
363 /*
364 * When processing the last PCIe root port we can now
365 * update the Root Port Function Number and Hide register.
366 */
367 if (PCI_FUNC(dev->path.pci.devfn) == 7) {
368 printk(BIOS_SPEW, "PCH: RPFN 0x%08x -> 0x%08x\n",
369 RCBA32(RPFN), new_rpfn);
370 RCBA32(RPFN) = new_rpfn;
371
372 /* Update static devictree with new function numbers */
373 if (config->pcie_port_coalesce)
374 pch_pcie_devicetree_update();
375 }
376}
377
378void pch_enable(device_t dev)
379{
380 u32 reg32;
381
382 /* PCH PCIe Root Ports get special handling */
383 if (PCI_SLOT(dev->path.pci.devfn) == PCH_PCIE_DEV_SLOT)
384 return pch_pcie_enable(dev);
385
386 if (!dev->enabled) {
387 printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev));
388
389 /* Ensure memory, io, and bus master are all disabled */
390 reg32 = pci_read_config32(dev, PCI_COMMAND);
391 reg32 &= ~(PCI_COMMAND_MASTER |
392 PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
Stefan Reinauer8e073822012-04-04 00:07:22 +0200393 pci_write_config32(dev, PCI_COMMAND, reg32);
394
395 /* Hide this device if possible */
396 pch_hide_devfn(dev->path.pci.devfn);
397 } else {
398 /* Enable SERR */
399 reg32 = pci_read_config32(dev, PCI_COMMAND);
400 reg32 |= PCI_COMMAND_SERR;
401 pci_write_config32(dev, PCI_COMMAND, reg32);
402 }
403}
404
405struct chip_operations southbridge_intel_bd82x6x_ops = {
406 CHIP_NAME("Intel Series 6 (" CONFIG_PCH_CHIP_NAME ") Southbridge")
407 .enable_dev = pch_enable,
408};