blob: a375b844777ff3193fe6f22e3129805dbe49d8d1 [file] [log] [blame]
Raul E Rangel789aefc2020-05-11 16:26:35 -06001/* SPDX-License-Identifier: GPL-2.0-only */
2
3#include <console/console.h>
4#include <cpu/x86/lapic_def.h>
5#include <device/pci_ops.h>
6#include <soc/data_fabric.h>
7#include <soc/iomap.h>
8#include <soc/pci_devs.h>
9#include <stdbool.h>
10
11static void disable_mmio_reg(int reg)
12{
13 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_CONTROL(reg),
14 IOMS0_FABRIC_ID << MMIO_DST_FABRIC_ID_SHIFT);
15 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_BASE(reg), 0);
16 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_LIMIT(reg), 0);
17}
18
19static bool is_mmio_reg_disabled(int reg)
20{
21 uint32_t val = pci_read_config32(SOC_DF_F0_DEV, NB_MMIO_CONTROL(reg));
22 return !(val & ((MMIO_WE | MMIO_RE)));
23}
24
25static int find_unused_mmio_reg(void)
26{
27 unsigned int i;
28
29 for (i = 0; i < NUM_NB_MMIO_REGS; i++) {
30 if (is_mmio_reg_disabled(i))
31 return i;
32 }
33 return -1;
34}
35
36void data_fabric_set_mmio_np(void)
37{
38 /*
39 * Mark region from HPET-LAPIC or 0xfed00000-0xfee00000-1 as NP.
40 *
41 * AGESA has already programmed the NB MMIO routing, however nothing
42 * is yet marked as non-posted.
43 *
44 * If there exists an overlapping routing base/limit pair, trim its
45 * base or limit to avoid the new NP region. If any pair exists
46 * completely within HPET-LAPIC range, remove it. If any pair surrounds
47 * HPET-LAPIC, it must be split into two regions.
48 *
49 * TODO(b/156296146): Remove the settings from AGESA and allow coreboot
50 * to own everything. If not practical, consider erasing all settings
51 * and have coreboot reprogram them. At that time, make the source
52 * below more flexible.
53 * * Note that the code relies on the granularity of the HPET and
54 * LAPIC addresses being sufficiently large that the shifted limits
55 * +/-1 are always equivalent to the non-shifted values +/-1.
56 */
57
58 unsigned int i;
59 int reg;
60 uint32_t base, limit, ctrl;
61 const uint32_t np_bot = HPET_BASE_ADDRESS >> D18F0_MMIO_SHIFT;
62 const uint32_t np_top = (LOCAL_APIC_ADDR - 1) >> D18F0_MMIO_SHIFT;
63
64 for (i = 0; i < NUM_NB_MMIO_REGS; i++) {
65 /* Adjust all registers that overlap */
66 ctrl = pci_read_config32(SOC_DF_F0_DEV, NB_MMIO_CONTROL(i));
67 if (!(ctrl & (MMIO_WE | MMIO_RE)))
68 continue; /* not enabled */
69
70 base = pci_read_config32(SOC_DF_F0_DEV, NB_MMIO_BASE(i));
71 limit = pci_read_config32(SOC_DF_F0_DEV, NB_MMIO_LIMIT(i));
72
73 if (base > np_top || limit < np_bot)
74 continue; /* no overlap at all */
75
76 if (base >= np_bot && limit <= np_top) {
77 disable_mmio_reg(i); /* 100% within, so remove */
78 continue;
79 }
80
81 if (base < np_bot && limit > np_top) {
82 /* Split the configured region */
83 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_LIMIT(i), np_bot - 1);
84 reg = find_unused_mmio_reg();
85 if (reg < 0) {
86 /* Although a pair could be freed later, this condition is
87 * very unusual and deserves analysis. Flag an error and
88 * leave the topmost part unconfigured. */
89 printk(BIOS_ERR,
90 "Error: Not enough NB MMIO routing registers\n");
91 continue;
92 }
93 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_BASE(reg), np_top + 1);
94 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_LIMIT(reg), limit);
95 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_CONTROL(reg), ctrl);
96 continue;
97 }
98
99 /* If still here, adjust only the base or limit */
100 if (base <= np_bot)
101 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_LIMIT(i), np_bot - 1);
102 else
103 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_BASE(i), np_top + 1);
104 }
105
106 reg = find_unused_mmio_reg();
107 if (reg < 0) {
108 printk(BIOS_ERR, "Error: cannot configure region as NP\n");
109 return;
110 }
111
112 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_BASE(reg), np_bot);
113 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_LIMIT(reg), np_top);
114 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_CONTROL(reg),
115 (IOMS0_FABRIC_ID << MMIO_DST_FABRIC_ID_SHIFT) | MMIO_NP | MMIO_WE
116 | MMIO_RE);
117}