blob: f3548fcb36f26848ba790213e80adc991ea604e4 [file] [log] [blame]
Felix Helddba3fe72021-02-13 01:05:56 +01001/* SPDX-License-Identifier: GPL-2.0-only */
2
Fred Reitberger1a9ac342022-11-03 15:19:51 -04003#include <acpi/acpi_device.h>
Felix Helddba3fe72021-02-13 01:05:56 +01004#include <amdblocks/data_fabric.h>
5#include <amdblocks/pci_devs.h>
Fred Reitberger31e62982022-10-31 14:18:20 -04006#include <arch/hpet.h>
Felix Held906f9be2021-02-13 20:38:08 +01007#include <console/console.h>
Fred Reitberger31e62982022-10-31 14:18:20 -04008#include <cpu/x86/lapic_def.h>
Felix Helddba3fe72021-02-13 01:05:56 +01009#include <device/pci_ops.h>
Felix Held602f93e2021-02-13 21:10:08 +010010#include <soc/data_fabric.h>
Felix Helddba3fe72021-02-13 01:05:56 +010011#include <soc/pci_devs.h>
12#include <types.h>
Felix Helddba3fe72021-02-13 01:05:56 +010013
14static void data_fabric_set_indirect_address(uint8_t func, uint16_t reg, uint8_t instance_id)
15{
Fred Reitberger437d0112022-11-01 08:42:41 -040016 union df_ficaa ficaa = { .cfg_inst_acc_en = 1 };
17 /* convert register address to 32-bit register number */
18 ficaa.reg_num = reg >> 2;
19 ficaa.func_num = func;
20 ficaa.inst_id = instance_id;
21 pci_write_config32(SOC_DF_F4_DEV, DF_FICAA_BIOS, ficaa.raw);
Felix Helddba3fe72021-02-13 01:05:56 +010022}
23
24uint32_t data_fabric_read32(uint8_t function, uint16_t reg, uint8_t instance_id)
25{
Felix Heldf1eaa672021-02-18 20:34:21 +010026 /* Broadcast reads might return unexpected results when a register has different
27 contents in the different instances. */
Felix Helddba3fe72021-02-13 01:05:56 +010028 if (instance_id == BROADCAST_FABRIC_ID)
Felix Held789f6f72021-02-13 01:09:28 +010029 return data_fabric_broadcast_read32(function, reg);
Felix Helddba3fe72021-02-13 01:05:56 +010030
31 /* non-broadcast data fabric accesses need to be done via indirect access */
32 data_fabric_set_indirect_address(function, reg, instance_id);
33 return pci_read_config32(SOC_DF_F4_DEV, DF_FICAD_LO);
34}
Felix Held45df9c12021-02-13 01:08:14 +010035
36void data_fabric_write32(uint8_t function, uint16_t reg, uint8_t instance_id, uint32_t data)
37{
38 if (instance_id == BROADCAST_FABRIC_ID) {
Felix Heldb2d633d2021-02-17 04:47:26 +010039 data_fabric_broadcast_write32(function, reg, data);
Felix Held45df9c12021-02-13 01:08:14 +010040 return;
41 }
42
43 /* non-broadcast data fabric accesses need to be done via indirect access */
44 data_fabric_set_indirect_address(function, reg, instance_id);
45 pci_write_config32(SOC_DF_F4_DEV, DF_FICAD_LO, data);
46}
Felix Held602f93e2021-02-13 21:10:08 +010047
Felix Held906f9be2021-02-13 20:38:08 +010048void data_fabric_print_mmio_conf(void)
49{
Felix Held77128a82023-02-02 16:13:05 +010050 uint32_t control;
51 uint64_t base, limit;
Felix Held906f9be2021-02-13 20:38:08 +010052 printk(BIOS_SPEW,
53 "=== Data Fabric MMIO configuration registers ===\n"
Felix Held77128a82023-02-02 16:13:05 +010054 "idx control base limit\n");
Felix Heldb307ed62023-02-07 12:06:41 +010055 for (unsigned int i = 0; i < DF_MMIO_REG_SET_COUNT; i++) {
Felix Held77128a82023-02-02 16:13:05 +010056 control = data_fabric_broadcast_read32(0, NB_MMIO_CONTROL(i));
57 /* Base and limit address registers don't contain the lower address bits, but
58 are shifted by D18F0_MMIO_SHIFT bits */
59 base = (uint64_t)data_fabric_broadcast_read32(0, NB_MMIO_BASE(i))
60 << D18F0_MMIO_SHIFT;
61 limit = (uint64_t)data_fabric_broadcast_read32(0, NB_MMIO_LIMIT(i))
62 << D18F0_MMIO_SHIFT;
63 /* Lower D18F0_MMIO_SHIFT address limit bits are all 1 */
64 limit += (1 << D18F0_MMIO_SHIFT) - 1;
65 printk(BIOS_SPEW, " %2u %8x %16llx %16llx\n",
66 i, control, base, limit);
Felix Held906f9be2021-02-13 20:38:08 +010067 }
68}
69
Felix Held602f93e2021-02-13 21:10:08 +010070void data_fabric_disable_mmio_reg(unsigned int reg)
71{
Fred Reitberger437d0112022-11-01 08:42:41 -040072 union df_mmio_control ctrl = { .fabric_id = IOMS0_FABRIC_ID };
73 data_fabric_broadcast_write32(0, NB_MMIO_CONTROL(reg), ctrl.raw);
Felix Held602f93e2021-02-13 21:10:08 +010074 data_fabric_broadcast_write32(0, NB_MMIO_BASE(reg), 0);
75 data_fabric_broadcast_write32(0, NB_MMIO_LIMIT(reg), 0);
76}
77
78static bool is_mmio_reg_disabled(unsigned int reg)
79{
Fred Reitberger437d0112022-11-01 08:42:41 -040080 union df_mmio_control ctrl;
81 ctrl.raw = data_fabric_broadcast_read32(0, NB_MMIO_CONTROL(reg));
82 return !(ctrl.we || ctrl.re);
Felix Held602f93e2021-02-13 21:10:08 +010083}
84
85int data_fabric_find_unused_mmio_reg(void)
86{
Felix Heldb307ed62023-02-07 12:06:41 +010087 for (unsigned int i = 0; i < DF_MMIO_REG_SET_COUNT; i++) {
Felix Held602f93e2021-02-13 21:10:08 +010088 if (is_mmio_reg_disabled(i))
89 return i;
90 }
91 return -1;
92}
Fred Reitberger31e62982022-10-31 14:18:20 -040093
94void data_fabric_set_mmio_np(void)
95{
96 /*
97 * Mark region from HPET-LAPIC or 0xfed00000-0xfee00000-1 as NP.
98 *
99 * AGESA has already programmed the NB MMIO routing, however nothing
100 * is yet marked as non-posted.
101 *
102 * If there exists an overlapping routing base/limit pair, trim its
103 * base or limit to avoid the new NP region. If any pair exists
104 * completely within HPET-LAPIC range, remove it. If any pair surrounds
105 * HPET-LAPIC, it must be split into two regions.
106 *
107 * TODO(b/156296146): Remove the settings from AGESA and allow coreboot
108 * to own everything. If not practical, consider erasing all settings
109 * and have coreboot reprogram them. At that time, make the source
110 * below more flexible.
111 * * Note that the code relies on the granularity of the HPET and
112 * LAPIC addresses being sufficiently large that the shifted limits
113 * +/-1 are always equivalent to the non-shifted values +/-1.
114 */
115
116 unsigned int i;
117 int reg;
Fred Reitberger437d0112022-11-01 08:42:41 -0400118 uint32_t base, limit;
119 union df_mmio_control ctrl;
Fred Reitberger31e62982022-10-31 14:18:20 -0400120 const uint32_t np_bot = HPET_BASE_ADDRESS >> D18F0_MMIO_SHIFT;
121 const uint32_t np_top = (LAPIC_DEFAULT_BASE - 1) >> D18F0_MMIO_SHIFT;
122
123 data_fabric_print_mmio_conf();
124
Felix Heldb307ed62023-02-07 12:06:41 +0100125 for (i = 0; i < DF_MMIO_REG_SET_COUNT; i++) {
Fred Reitberger31e62982022-10-31 14:18:20 -0400126 /* Adjust all registers that overlap */
Fred Reitberger437d0112022-11-01 08:42:41 -0400127 ctrl.raw = data_fabric_broadcast_read32(0, NB_MMIO_CONTROL(i));
128 if (!(ctrl.we || ctrl.re))
Fred Reitberger31e62982022-10-31 14:18:20 -0400129 continue; /* not enabled */
130
131 base = data_fabric_broadcast_read32(0, NB_MMIO_BASE(i));
132 limit = data_fabric_broadcast_read32(0, NB_MMIO_LIMIT(i));
133
134 if (base > np_top || limit < np_bot)
135 continue; /* no overlap at all */
136
137 if (base >= np_bot && limit <= np_top) {
138 data_fabric_disable_mmio_reg(i); /* 100% within, so remove */
139 continue;
140 }
141
142 if (base < np_bot && limit > np_top) {
143 /* Split the configured region */
144 data_fabric_broadcast_write32(0, NB_MMIO_LIMIT(i), np_bot - 1);
145 reg = data_fabric_find_unused_mmio_reg();
146 if (reg < 0) {
147 /* Although a pair could be freed later, this condition is
148 * very unusual and deserves analysis. Flag an error and
149 * leave the topmost part unconfigured. */
150 printk(BIOS_ERR, "Not enough NB MMIO routing registers\n");
151 continue;
152 }
153 data_fabric_broadcast_write32(0, NB_MMIO_BASE(reg), np_top + 1);
154 data_fabric_broadcast_write32(0, NB_MMIO_LIMIT(reg), limit);
Fred Reitberger437d0112022-11-01 08:42:41 -0400155 data_fabric_broadcast_write32(0, NB_MMIO_CONTROL(reg), ctrl.raw);
Fred Reitberger31e62982022-10-31 14:18:20 -0400156 continue;
157 }
158
159 /* If still here, adjust only the base or limit */
160 if (base <= np_bot)
161 data_fabric_broadcast_write32(0, NB_MMIO_LIMIT(i), np_bot - 1);
162 else
163 data_fabric_broadcast_write32(0, NB_MMIO_BASE(i), np_top + 1);
164 }
165
166 reg = data_fabric_find_unused_mmio_reg();
167 if (reg < 0) {
168 printk(BIOS_ERR, "cannot configure region as NP\n");
169 return;
170 }
171
Fred Reitberger437d0112022-11-01 08:42:41 -0400172 union df_mmio_control np_ctrl = { .fabric_id = IOMS0_FABRIC_ID,
173 .np = 1, .we = 1, .re = 1 };
Fred Reitberger31e62982022-10-31 14:18:20 -0400174 data_fabric_broadcast_write32(0, NB_MMIO_BASE(reg), np_bot);
175 data_fabric_broadcast_write32(0, NB_MMIO_LIMIT(reg), np_top);
Fred Reitberger437d0112022-11-01 08:42:41 -0400176 data_fabric_broadcast_write32(0, NB_MMIO_CONTROL(reg), np_ctrl.raw);
Fred Reitberger31e62982022-10-31 14:18:20 -0400177
178 data_fabric_print_mmio_conf();
179}
Fred Reitberger1a9ac342022-11-03 15:19:51 -0400180
181static const char *data_fabric_acpi_name(const struct device *dev)
182{
183 const char *df_acpi_names[8] = {
184 "DFD0",
185 "DFD1",
186 "DFD2",
187 "DFD3",
188 "DFD4",
189 "DFD5",
190 "DFD6",
191 "DFD7"
192 };
193
194 if (dev->path.type == DEVICE_PATH_PCI &&
195 PCI_SLOT(dev->path.pci.devfn) == DF_DEV)
196 return df_acpi_names[PCI_FUNC(dev->path.pci.devfn)];
197
198 printk(BIOS_ERR, "%s: Unhandled device id 0x%x\n", __func__, dev->device);
199 return NULL;
200}
201
202struct device_operations amd_data_fabric_ops = {
203 .read_resources = noop_read_resources,
204 .set_resources = noop_set_resources,
205 .acpi_name = data_fabric_acpi_name,
206 .acpi_fill_ssdt = acpi_device_write_pci_dev,
207};