blob: aa3a45754118ad9cde57ce9c159490af03acf822 [file] [log] [blame]
Uwe Hermannb80dbf02007-04-22 19:08:13 +00001/*
Stefan Reinauer7e61e452008-01-18 10:35:56 +00002 * This file is part of the coreboot project.
Uwe Hermannb80dbf02007-04-22 19:08:13 +00003 *
4 * Copyright (C) 2005 Linux Networx
5 * (Written by Eric Biederman <ebiederman@lnxi.com> for Linux Networx)
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
Patrick Georgib890a122015-03-26 15:17:45 +010018 * Foundation, Inc.
Uwe Hermannb80dbf02007-04-22 19:08:13 +000019 */
Yinghai Lu13f1c2a2005-07-08 02:49:49 +000020
21#include <console/console.h>
Duncan Laurie90dcdd42011-10-25 14:15:11 -070022#include <delay.h>
Yinghai Lu13f1c2a2005-07-08 02:49:49 +000023#include <device/device.h>
24#include <device/pci.h>
25#include <device/pci_ids.h>
26#include <device/pciexp.h>
27
Kenji Chen31c6e632014-10-04 01:14:44 +080028#if IS_ENABLED(CONFIG_MMCONF_SUPPORT)
29unsigned int pciexp_find_extended_cap(device_t dev, unsigned int cap)
30{
31 unsigned int this_cap_offset, next_cap_offset;
32 unsigned int this_cap, cafe;
33
34 this_cap_offset = PCIE_EXT_CAP_OFFSET;
35 do {
36 this_cap = pci_mmio_read_config32(dev, this_cap_offset);
37 next_cap_offset = this_cap >> 20;
38 this_cap &= 0xffff;
39 cafe = pci_mmio_read_config32(dev, this_cap_offset + 4);
40 cafe &= 0xffff;
41 if (this_cap == cap)
42 return this_cap_offset;
43 else if (cafe == cap)
44 return this_cap_offset + 4;
45 else
46 this_cap_offset = next_cap_offset;
47 } while (next_cap_offset != 0);
48
49 return 0;
50}
51#endif
52
Duncan Laurie90dcdd42011-10-25 14:15:11 -070053#if CONFIG_PCIEXP_COMMON_CLOCK
54/*
55 * Re-train a PCIe link
56 */
57#define PCIE_TRAIN_RETRY 10000
58static int pciexp_retrain_link(device_t dev, unsigned cap)
59{
60 unsigned try = PCIE_TRAIN_RETRY;
61 u16 lnk;
62
63 /* Start link retraining */
64 lnk = pci_read_config16(dev, cap + PCI_EXP_LNKCTL);
65 lnk |= PCI_EXP_LNKCTL_RL;
66 pci_write_config16(dev, cap + PCI_EXP_LNKCTL, lnk);
67
68 /* Wait for training to complete */
69 while (try--) {
70 lnk = pci_read_config16(dev, cap + PCI_EXP_LNKSTA);
71 if (!(lnk & PCI_EXP_LNKSTA_LT))
72 return 0;
73 udelay(100);
74 }
75
76 printk(BIOS_ERR, "%s: Link Retrain timeout\n", dev_path(dev));
77 return -1;
78}
79
80/*
81 * Check the Slot Clock Configuration for root port and endpoint
82 * and enable Common Clock Configuration if possible. If CCC is
83 * enabled the link must be retrained.
84 */
85static void pciexp_enable_common_clock(device_t root, unsigned root_cap,
86 device_t endp, unsigned endp_cap)
87{
88 u16 root_scc, endp_scc, lnkctl;
89
90 /* Get Slot Clock Configuration for root port */
91 root_scc = pci_read_config16(root, root_cap + PCI_EXP_LNKSTA);
92 root_scc &= PCI_EXP_LNKSTA_SLC;
93
94 /* Get Slot Clock Configuration for endpoint */
95 endp_scc = pci_read_config16(endp, endp_cap + PCI_EXP_LNKSTA);
96 endp_scc &= PCI_EXP_LNKSTA_SLC;
97
98 /* Enable Common Clock Configuration and retrain */
99 if (root_scc && endp_scc) {
100 printk(BIOS_INFO, "Enabling Common Clock Configuration\n");
101
102 /* Set in endpoint */
103 lnkctl = pci_read_config16(endp, endp_cap + PCI_EXP_LNKCTL);
104 lnkctl |= PCI_EXP_LNKCTL_CCC;
105 pci_write_config16(endp, endp_cap + PCI_EXP_LNKCTL, lnkctl);
106
107 /* Set in root port */
108 lnkctl = pci_read_config16(root, root_cap + PCI_EXP_LNKCTL);
109 lnkctl |= PCI_EXP_LNKCTL_CCC;
110 pci_write_config16(root, root_cap + PCI_EXP_LNKCTL, lnkctl);
111
112 /* Retrain link if CCC was enabled */
113 pciexp_retrain_link(root, root_cap);
114 }
115}
116#endif /* CONFIG_PCIEXP_COMMON_CLOCK */
117
Kane Chen18cb1342014-10-01 11:13:54 +0800118#if CONFIG_PCIEXP_CLK_PM
119static void pciexp_enable_clock_power_pm(device_t endp, unsigned endp_cap)
120{
121 /* check if per port clk req is supported in device */
122 u32 endp_ca;
123 u16 lnkctl;
124 endp_ca = pci_read_config32(endp, endp_cap + PCI_EXP_LNKCAP);
125 if ((endp_ca & PCI_EXP_CLK_PM) == 0) {
126 printk(BIOS_INFO, "PCIE CLK PM is not supported by endpoint");
127 return;
128 }
129 lnkctl = pci_read_config16(endp, endp_cap + PCI_EXP_LNKCTL);
130 lnkctl = lnkctl | PCI_EXP_EN_CLK_PM;
131 pci_write_config16(endp, endp_cap + PCI_EXP_LNKCTL, lnkctl);
132}
133#endif /* CONFIG_PCIEXP_CLK_PM */
134
Kenji Chen31c6e632014-10-04 01:14:44 +0800135#if IS_ENABLED(CONFIG_PCIEXP_L1_SUB_STATE) && IS_ENABLED(CONFIG_MMCONF_SUPPORT)
136static void pcie_update_cfg(device_t dev, int reg, u32 mask, u32 or)
137{
138 u32 reg32;
139
140 reg32 = pci_mmio_read_config32(dev, reg);
141 reg32 &= mask;
142 reg32 |= or;
143 pci_mmio_write_config32(dev, reg, reg32);
144}
145
146static void pciexp_config_max_latency(device_t root, device_t dev)
147{
148 unsigned int cap;
149 cap = pciexp_find_extended_cap(dev, PCIE_EXT_CAP_LTR_ID);
Pratik Prajapati0cd0d282015-06-09 12:06:20 -0700150 if ((cap) && (root->ops->ops_pci != NULL) &&
151 (root->ops->ops_pci->set_L1_ss_latency != NULL))
152 root->ops->ops_pci->set_L1_ss_latency(dev, cap + 4);
Kenji Chen31c6e632014-10-04 01:14:44 +0800153}
154
155static void pciexp_enable_ltr(device_t dev)
156{
157 unsigned int cap;
158 cap = pci_find_capability(dev, PCI_CAP_ID_PCIE);
Pratik Prajapati0cd0d282015-06-09 12:06:20 -0700159 if(!cap) {
160 printk(BIOS_INFO, "Failed to enable LTR for dev = %s\n",
161 dev_path(dev));
162 return;
163 }
Kenji Chen31c6e632014-10-04 01:14:44 +0800164 pcie_update_cfg(dev, cap + 0x28, ~(1 << 10), 1 << 10);
165}
166
167static unsigned char pciexp_L1_substate_cal(device_t dev, unsigned int endp_cap,
168 unsigned int *data)
169{
170 unsigned char mult[4] = {2, 10, 100, 0};
171
172 unsigned int L1SubStateSupport = *data & 0xf;
173 unsigned int comm_mode_rst_time = (*data >> 8) & 0xff;
174 unsigned int power_on_scale = (*data >> 16) & 0x3;
175 unsigned int power_on_value = (*data >> 19) & 0x1f;
176
177 unsigned int endp_data = pci_mmio_read_config32(dev, endp_cap + 4);
178 unsigned int endp_L1SubStateSupport = endp_data & 0xf;
179 unsigned int endp_comm_mode_restore_time = (endp_data >> 8) & 0xff;
180 unsigned int endp_power_on_scale = (endp_data >> 16) & 0x3;
181 unsigned int endp_power_on_value = (endp_data >> 19) & 0x1f;
182
183 L1SubStateSupport &= endp_L1SubStateSupport;
184
185 if (L1SubStateSupport == 0)
186 return 0;
187
188 if (power_on_value * mult[power_on_scale] <
189 endp_power_on_value * mult[endp_power_on_scale]) {
190 power_on_value = endp_power_on_value;
191 power_on_scale = endp_power_on_scale;
192 }
193 if (comm_mode_rst_time < endp_comm_mode_restore_time)
194 comm_mode_rst_time = endp_comm_mode_restore_time;
195
196 *data = (comm_mode_rst_time << 8) | (power_on_scale << 16)
197 | (power_on_value << 19) | L1SubStateSupport;
198
199 return 1;
200}
201
202static void pciexp_L1_substate_commit(device_t root, device_t dev,
203 unsigned int root_cap, unsigned int end_cap)
204{
205 device_t dev_t;
206 unsigned char L1_ss_ok;
207 unsigned int rp_L1_support = pci_mmio_read_config32(root, root_cap + 4);
208 unsigned int L1SubStateSupport;
209 unsigned int comm_mode_rst_time;
210 unsigned int power_on_scale;
211 unsigned int endp_power_on_value;
212
213 for (dev_t = dev; dev_t; dev_t = dev_t->sibling) {
214 /*
215 * rp_L1_support is init'd above from root port.
216 * it needs coordination with endpoints to reach in common.
217 * if certain endpoint doesn't support L1 Sub-State, abort
218 * this feature enabling.
219 */
220 L1_ss_ok = pciexp_L1_substate_cal(dev_t, end_cap,
221 &rp_L1_support);
222 if (!L1_ss_ok)
223 return;
224 }
225
226 L1SubStateSupport = rp_L1_support & 0xf;
227 comm_mode_rst_time = (rp_L1_support >> 8) & 0xff;
228 power_on_scale = (rp_L1_support >> 16) & 0x3;
229 endp_power_on_value = (rp_L1_support >> 19) & 0x1f;
230
231 printk(BIOS_INFO, "L1 Sub-State supported from root port %d\n",
232 root->path.pci.devfn >> 3);
233 printk(BIOS_INFO, "L1 Sub-State Support = 0x%x\n", L1SubStateSupport);
234 printk(BIOS_INFO, "CommonModeRestoreTime = 0x%x\n", comm_mode_rst_time);
235 printk(BIOS_INFO, "Power On Value = 0x%x, Power On Scale = 0x%x\n",
236 endp_power_on_value, power_on_scale);
237
238 pciexp_enable_ltr(root);
239
240 pcie_update_cfg(root, root_cap + 0x08, ~0xff00,
241 (comm_mode_rst_time << 8));
242
243 pcie_update_cfg(root, root_cap + 0x0c , 0xffffff04,
244 (endp_power_on_value << 3) | (power_on_scale));
245
246 pcie_update_cfg(root, root_cap + 0x08, ~0xe3ff0000,
247 (1 << 21) | (1 << 23) | (1 << 30));
248
Kenji Chena874a7c2015-01-30 13:57:42 +0800249 pcie_update_cfg(root, root_cap + 0x08, ~0x1f,
Kenji Chen31c6e632014-10-04 01:14:44 +0800250 L1SubStateSupport);
251
252 for (dev_t = dev; dev_t; dev_t = dev_t->sibling) {
Kenji Chen31c6e632014-10-04 01:14:44 +0800253 pcie_update_cfg(dev_t, end_cap + 0x0c , 0xffffff04,
254 (endp_power_on_value << 3) | (power_on_scale));
255
256 pcie_update_cfg(dev_t, end_cap + 0x08, ~0xe3ff0000,
257 (1 << 21) | (1 << 23) | (1 << 30));
258
Kenji Chena874a7c2015-01-30 13:57:42 +0800259 pcie_update_cfg(dev_t, end_cap + 0x08, ~0x1f,
Kenji Chen31c6e632014-10-04 01:14:44 +0800260 L1SubStateSupport);
261
262 pciexp_enable_ltr(dev_t);
263
264 pciexp_config_max_latency(root, dev_t);
265 }
266}
267
268static void pciexp_config_L1_sub_state(device_t root, device_t dev)
269{
270 unsigned int root_cap, end_cap;
271
272 /* Do it for function 0 only */
273 if (dev->path.pci.devfn & 0x7)
274 return;
275
276 root_cap = pciexp_find_extended_cap(root, PCIE_EXT_CAP_L1SS_ID);
277 if (!root_cap)
278 return;
279
280 end_cap = pciexp_find_extended_cap(dev, PCIE_EXT_CAP_L1SS_ID);
281 if (!end_cap) {
282 end_cap = pciexp_find_extended_cap(dev, 0xcafe);
283 if (!end_cap)
284 return;
285 }
286
287 pciexp_L1_substate_commit(root, dev, root_cap, end_cap);
288}
289#endif /* CONFIG_PCIEXP_L1_SUB_STATE */
290
Duncan Laurie90dcdd42011-10-25 14:15:11 -0700291#if CONFIG_PCIEXP_ASPM
292/*
293 * Determine the ASPM L0s or L1 exit latency for a link
294 * by checking both root port and endpoint and returning
295 * the highest latency value.
296 */
297static int pciexp_aspm_latency(device_t root, unsigned root_cap,
298 device_t endp, unsigned endp_cap,
299 enum aspm_type type)
300{
301 int root_lat = 0, endp_lat = 0;
302 u32 root_lnkcap, endp_lnkcap;
303
304 root_lnkcap = pci_read_config32(root, root_cap + PCI_EXP_LNKCAP);
305 endp_lnkcap = pci_read_config32(endp, endp_cap + PCI_EXP_LNKCAP);
306
307 /* Make sure the link supports this ASPM type by checking
308 * capability bits 11:10 with aspm_type offset by 1 */
309 if (!(root_lnkcap & (1 << (type + 9))) ||
310 !(endp_lnkcap & (1 << (type + 9))))
311 return -1;
312
313 /* Find the one with higher latency */
314 switch (type) {
315 case PCIE_ASPM_L0S:
316 root_lat = (root_lnkcap & PCI_EXP_LNKCAP_L0SEL) >> 12;
317 endp_lat = (endp_lnkcap & PCI_EXP_LNKCAP_L0SEL) >> 12;
318 break;
319 case PCIE_ASPM_L1:
320 root_lat = (root_lnkcap & PCI_EXP_LNKCAP_L1EL) >> 15;
321 endp_lat = (endp_lnkcap & PCI_EXP_LNKCAP_L1EL) >> 15;
322 break;
323 default:
324 return -1;
325 }
326
327 return (endp_lat > root_lat) ? endp_lat : root_lat;
328}
329
330/*
331 * Enable ASPM on PCIe root port and endpoint.
332 *
333 * Returns APMC value:
334 * -1 = Error
335 * 0 = no ASPM
336 * 1 = L0s Enabled
337 * 2 = L1 Enabled
338 * 3 = L0s and L1 Enabled
339 */
340static enum aspm_type pciexp_enable_aspm(device_t root, unsigned root_cap,
341 device_t endp, unsigned endp_cap)
342{
343 const char *aspm_type_str[] = { "None", "L0s", "L1", "L0s and L1" };
344 enum aspm_type apmc = PCIE_ASPM_NONE;
345 int exit_latency, ok_latency;
346 u16 lnkctl;
347 u32 devcap;
348
349 /* Get endpoint device capabilities for acceptable limits */
350 devcap = pci_read_config32(endp, endp_cap + PCI_EXP_DEVCAP);
351
352 /* Enable L0s if it is within endpoint acceptable limit */
353 ok_latency = (devcap & PCI_EXP_DEVCAP_L0S) >> 6;
354 exit_latency = pciexp_aspm_latency(root, root_cap, endp, endp_cap,
355 PCIE_ASPM_L0S);
356 if (exit_latency >= 0 && exit_latency <= ok_latency)
357 apmc |= PCIE_ASPM_L0S;
358
359 /* Enable L1 if it is within endpoint acceptable limit */
360 ok_latency = (devcap & PCI_EXP_DEVCAP_L1) >> 9;
361 exit_latency = pciexp_aspm_latency(root, root_cap, endp, endp_cap,
362 PCIE_ASPM_L1);
363 if (exit_latency >= 0 && exit_latency <= ok_latency)
364 apmc |= PCIE_ASPM_L1;
365
366 if (apmc != PCIE_ASPM_NONE) {
367 /* Set APMC in root port first */
368 lnkctl = pci_read_config16(root, root_cap + PCI_EXP_LNKCTL);
369 lnkctl |= apmc;
370 pci_write_config16(root, root_cap + PCI_EXP_LNKCTL, lnkctl);
371
372 /* Set APMC in endpoint device next */
373 lnkctl = pci_read_config16(endp, endp_cap + PCI_EXP_LNKCTL);
374 lnkctl |= apmc;
375 pci_write_config16(endp, endp_cap + PCI_EXP_LNKCTL, lnkctl);
376 }
377
378 printk(BIOS_INFO, "ASPM: Enabled %s\n", aspm_type_str[apmc]);
379 return apmc;
380}
381#endif /* CONFIG_PCIEXP_ASPM */
382
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000383static void pciexp_tune_dev(device_t dev)
384{
Duncan Laurie90dcdd42011-10-25 14:15:11 -0700385 device_t root = dev->bus->dev;
386 unsigned int root_cap, cap;
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000387
388 cap = pci_find_capability(dev, PCI_CAP_ID_PCIE);
Uwe Hermannd453dd02010-10-18 00:00:57 +0000389 if (!cap)
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000390 return;
Uwe Hermannd453dd02010-10-18 00:00:57 +0000391
Duncan Laurie90dcdd42011-10-25 14:15:11 -0700392 root_cap = pci_find_capability(root, PCI_CAP_ID_PCIE);
393 if (!root_cap)
394 return;
Stefan Reinauerf6eb88a2010-01-17 13:54:08 +0000395
Duncan Laurie90dcdd42011-10-25 14:15:11 -0700396#if CONFIG_PCIEXP_COMMON_CLOCK
397 /* Check for and enable Common Clock */
398 pciexp_enable_common_clock(root, root_cap, dev, cap);
399#endif
Uwe Hermanne4870472010-11-04 23:23:47 +0000400
Kane Chen18cb1342014-10-01 11:13:54 +0800401#if CONFIG_PCIEXP_CLK_PM
402 /* Check if per port CLK req is supported by endpoint*/
403 pciexp_enable_clock_power_pm(dev, cap);
404#endif
405
Kenji Chen31c6e632014-10-04 01:14:44 +0800406#if CONFIG_PCIEXP_L1_SUB_STATE
407 /* Enable L1 Sub-State when both root port and endpoint support */
408 pciexp_config_L1_sub_state(root, dev);
409#endif /* CONFIG_PCIEXP_L1_SUB_STATE */
410
Duncan Laurie90dcdd42011-10-25 14:15:11 -0700411#if CONFIG_PCIEXP_ASPM
412 /* Check for and enable ASPM */
413 enum aspm_type apmc = pciexp_enable_aspm(root, root_cap, dev, cap);
414
415 if (apmc != PCIE_ASPM_NONE) {
416 /* Enable ASPM role based error reporting. */
417 u32 reg32 = pci_read_config32(dev, cap + PCI_EXP_DEVCAP);
418 reg32 |= PCI_EXP_DEVCAP_RBER;
419 pci_write_config32(dev, cap + PCI_EXP_DEVCAP, reg32);
420 }
Stefan Reinauerf6eb88a2010-01-17 13:54:08 +0000421#endif
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000422}
423
Kyösti Mälkkide271a82015-03-18 13:09:47 +0200424void pciexp_scan_bus(struct bus *bus, unsigned int min_devfn,
425 unsigned int max_devfn)
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000426{
427 device_t child;
Uwe Hermannd453dd02010-10-18 00:00:57 +0000428
Kyösti Mälkkide271a82015-03-18 13:09:47 +0200429 pci_scan_bus(bus, min_devfn, max_devfn);
Uwe Hermannd453dd02010-10-18 00:00:57 +0000430
431 for (child = bus->children; child; child = child->sibling) {
432 if ((child->path.pci.devfn < min_devfn) ||
433 (child->path.pci.devfn > max_devfn)) {
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000434 continue;
435 }
436 pciexp_tune_dev(child);
437 }
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000438}
439
Kyösti Mälkki580e7222015-03-19 21:04:23 +0200440void pciexp_scan_bridge(device_t dev)
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000441{
Kyösti Mälkki580e7222015-03-19 21:04:23 +0200442 do_pci_scan_bridge(dev, pciexp_scan_bus);
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000443}
444
445/** Default device operations for PCI Express bridges */
446static struct pci_operations pciexp_bus_ops_pci = {
447 .set_subsystem = 0,
448};
449
450struct device_operations default_pciexp_ops_bus = {
451 .read_resources = pci_bus_read_resources,
452 .set_resources = pci_dev_set_resources,
453 .enable_resources = pci_bus_enable_resources,
Uwe Hermannd453dd02010-10-18 00:00:57 +0000454 .init = 0,
455 .scan_bus = pciexp_scan_bridge,
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000456 .enable = 0,
457 .reset_bus = pci_bus_reset,
458 .ops_pci = &pciexp_bus_ops_pci,
459};