blob: 23cb94c83747a498a7f1130e6bcf3fe32536f901 [file] [log] [blame]
Raul E Rangel789aefc2020-05-11 16:26:35 -06001/* SPDX-License-Identifier: GPL-2.0-only */
2
Furquan Shaikh0c707d42020-07-08 16:54:40 -07003#include <acpi/acpi_device.h>
Raul E Rangel789aefc2020-05-11 16:26:35 -06004#include <console/console.h>
5#include <cpu/x86/lapic_def.h>
Furquan Shaikh0c707d42020-07-08 16:54:40 -07006#include <device/device.h>
7#include <device/pci.h>
8#include <device/pci_ids.h>
Raul E Rangel789aefc2020-05-11 16:26:35 -06009#include <device/pci_ops.h>
10#include <soc/data_fabric.h>
11#include <soc/iomap.h>
12#include <soc/pci_devs.h>
13#include <stdbool.h>
14
15static void disable_mmio_reg(int reg)
16{
17 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_CONTROL(reg),
18 IOMS0_FABRIC_ID << MMIO_DST_FABRIC_ID_SHIFT);
19 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_BASE(reg), 0);
20 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_LIMIT(reg), 0);
21}
22
23static bool is_mmio_reg_disabled(int reg)
24{
25 uint32_t val = pci_read_config32(SOC_DF_F0_DEV, NB_MMIO_CONTROL(reg));
26 return !(val & ((MMIO_WE | MMIO_RE)));
27}
28
29static int find_unused_mmio_reg(void)
30{
31 unsigned int i;
32
33 for (i = 0; i < NUM_NB_MMIO_REGS; i++) {
34 if (is_mmio_reg_disabled(i))
35 return i;
36 }
37 return -1;
38}
39
40void data_fabric_set_mmio_np(void)
41{
42 /*
43 * Mark region from HPET-LAPIC or 0xfed00000-0xfee00000-1 as NP.
44 *
45 * AGESA has already programmed the NB MMIO routing, however nothing
46 * is yet marked as non-posted.
47 *
48 * If there exists an overlapping routing base/limit pair, trim its
49 * base or limit to avoid the new NP region. If any pair exists
50 * completely within HPET-LAPIC range, remove it. If any pair surrounds
51 * HPET-LAPIC, it must be split into two regions.
52 *
53 * TODO(b/156296146): Remove the settings from AGESA and allow coreboot
54 * to own everything. If not practical, consider erasing all settings
55 * and have coreboot reprogram them. At that time, make the source
56 * below more flexible.
57 * * Note that the code relies on the granularity of the HPET and
58 * LAPIC addresses being sufficiently large that the shifted limits
59 * +/-1 are always equivalent to the non-shifted values +/-1.
60 */
61
62 unsigned int i;
63 int reg;
64 uint32_t base, limit, ctrl;
65 const uint32_t np_bot = HPET_BASE_ADDRESS >> D18F0_MMIO_SHIFT;
66 const uint32_t np_top = (LOCAL_APIC_ADDR - 1) >> D18F0_MMIO_SHIFT;
67
68 for (i = 0; i < NUM_NB_MMIO_REGS; i++) {
69 /* Adjust all registers that overlap */
70 ctrl = pci_read_config32(SOC_DF_F0_DEV, NB_MMIO_CONTROL(i));
71 if (!(ctrl & (MMIO_WE | MMIO_RE)))
72 continue; /* not enabled */
73
74 base = pci_read_config32(SOC_DF_F0_DEV, NB_MMIO_BASE(i));
75 limit = pci_read_config32(SOC_DF_F0_DEV, NB_MMIO_LIMIT(i));
76
77 if (base > np_top || limit < np_bot)
78 continue; /* no overlap at all */
79
80 if (base >= np_bot && limit <= np_top) {
81 disable_mmio_reg(i); /* 100% within, so remove */
82 continue;
83 }
84
85 if (base < np_bot && limit > np_top) {
86 /* Split the configured region */
87 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_LIMIT(i), np_bot - 1);
88 reg = find_unused_mmio_reg();
89 if (reg < 0) {
90 /* Although a pair could be freed later, this condition is
91 * very unusual and deserves analysis. Flag an error and
92 * leave the topmost part unconfigured. */
93 printk(BIOS_ERR,
94 "Error: Not enough NB MMIO routing registers\n");
95 continue;
96 }
97 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_BASE(reg), np_top + 1);
98 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_LIMIT(reg), limit);
99 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_CONTROL(reg), ctrl);
100 continue;
101 }
102
103 /* If still here, adjust only the base or limit */
104 if (base <= np_bot)
105 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_LIMIT(i), np_bot - 1);
106 else
107 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_BASE(i), np_top + 1);
108 }
109
110 reg = find_unused_mmio_reg();
111 if (reg < 0) {
112 printk(BIOS_ERR, "Error: cannot configure region as NP\n");
113 return;
114 }
115
116 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_BASE(reg), np_bot);
117 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_LIMIT(reg), np_top);
118 pci_write_config32(SOC_DF_F0_DEV, NB_MMIO_CONTROL(reg),
119 (IOMS0_FABRIC_ID << MMIO_DST_FABRIC_ID_SHIFT) | MMIO_NP | MMIO_WE
120 | MMIO_RE);
121}
Furquan Shaikh0c707d42020-07-08 16:54:40 -0700122
123static const char *data_fabric_acpi_name(const struct device *dev)
124{
125 switch (dev->device) {
126 case PCI_DEVICE_ID_AMD_FAM17H_DF0:
127 return "DFD0";
128 case PCI_DEVICE_ID_AMD_FAM17H_DF1:
129 return "DFD1";
130 case PCI_DEVICE_ID_AMD_FAM17H_DF2:
131 return "DFD2";
132 case PCI_DEVICE_ID_AMD_FAM17H_DF3:
133 return "DFD3";
134 case PCI_DEVICE_ID_AMD_FAM17H_DF4:
135 return "DFD4";
136 case PCI_DEVICE_ID_AMD_FAM17H_DF5:
137 return "DFD5";
138 case PCI_DEVICE_ID_AMD_FAM17H_DF6:
139 return "DFD6";
140 default:
141 printk(BIOS_ERR, "%s: Unhandled device id 0x%x\n", __func__, dev->device);
142 }
143
144 return NULL;
145}
146
147static struct device_operations data_fabric_ops = {
148 .read_resources = noop_read_resources,
149 .set_resources = noop_set_resources,
150 .acpi_name = data_fabric_acpi_name,
151 .acpi_fill_ssdt = acpi_device_write_pci_dev,
152};
153
154static const unsigned short pci_device_ids[] = {
155 PCI_DEVICE_ID_AMD_FAM17H_DF0,
156 PCI_DEVICE_ID_AMD_FAM17H_DF1,
157 PCI_DEVICE_ID_AMD_FAM17H_DF2,
158 PCI_DEVICE_ID_AMD_FAM17H_DF3,
159 PCI_DEVICE_ID_AMD_FAM17H_DF4,
160 PCI_DEVICE_ID_AMD_FAM17H_DF5,
161 PCI_DEVICE_ID_AMD_FAM17H_DF6,
162 0
163};
164
165static const struct pci_driver data_fabric_driver __pci_driver = {
166 .ops = &data_fabric_ops,
167 .vendor = PCI_VENDOR_ID_AMD,
168 .devices = pci_device_ids,
169};