1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2d39398a3SYinghai Lu /*
3d39398a3SYinghai Lu * AMD Family 10h mmconfig enablement
4d39398a3SYinghai Lu */
5d39398a3SYinghai Lu
6d39398a3SYinghai Lu #include <linux/types.h>
7d39398a3SYinghai Lu #include <linux/mm.h>
8d39398a3SYinghai Lu #include <linux/string.h>
9d39398a3SYinghai Lu #include <linux/pci.h>
105f0b2976SYinghai Lu #include <linux/dmi.h>
1127811d8cSYinghai Lu #include <linux/range.h>
1227811d8cSYinghai Lu
13d39398a3SYinghai Lu #include <asm/pci-direct.h>
14d39398a3SYinghai Lu #include <linux/sort.h>
15d39398a3SYinghai Lu #include <asm/io.h>
16d39398a3SYinghai Lu #include <asm/msr.h>
17d39398a3SYinghai Lu #include <asm/acpi.h>
18d1097635SThomas Gleixner #include <asm/mmconfig.h>
1982487711SJaswinder Singh Rajput #include <asm/pci_x86.h>
205f0b2976SYinghai Lu
21d39398a3SYinghai Lu struct pci_hostbridge_probe {
22d39398a3SYinghai Lu u32 bus;
23d39398a3SYinghai Lu u32 slot;
24d39398a3SYinghai Lu u32 vendor;
25d39398a3SYinghai Lu u32 device;
26d39398a3SYinghai Lu };
27d39398a3SYinghai Lu
28148f9bb8SPaul Gortmaker static u64 fam10h_pci_mmconf_base;
29d39398a3SYinghai Lu
30148f9bb8SPaul Gortmaker static struct pci_hostbridge_probe pci_probes[] = {
31d39398a3SYinghai Lu { 0, 0x18, PCI_VENDOR_ID_AMD, 0x1200 },
32d39398a3SYinghai Lu { 0xff, 0, PCI_VENDOR_ID_AMD, 0x1200 },
33d39398a3SYinghai Lu };
34d39398a3SYinghai Lu
cmp_range(const void * x1,const void * x2)35148f9bb8SPaul Gortmaker static int cmp_range(const void *x1, const void *x2)
36d39398a3SYinghai Lu {
37d39398a3SYinghai Lu const struct range *r1 = x1;
38d39398a3SYinghai Lu const struct range *r2 = x2;
39d39398a3SYinghai Lu int start1, start2;
40d39398a3SYinghai Lu
41d39398a3SYinghai Lu start1 = r1->start >> 32;
42d39398a3SYinghai Lu start2 = r2->start >> 32;
43d39398a3SYinghai Lu
44d39398a3SYinghai Lu return start1 - start2;
45d39398a3SYinghai Lu }
46d39398a3SYinghai Lu
4737db6c8fSJan Beulich #define MMCONF_UNIT (1ULL << FAM10H_MMIO_CONF_BASE_SHIFT)
4837db6c8fSJan Beulich #define MMCONF_MASK (~(MMCONF_UNIT - 1))
4937db6c8fSJan Beulich #define MMCONF_SIZE (MMCONF_UNIT << 8)
5037db6c8fSJan Beulich /* need to avoid (0xfd<<32), (0xfe<<32), and (0xff<<32), ht used space */
51d39398a3SYinghai Lu #define FAM10H_PCI_MMCONF_BASE (0xfcULL<<32)
5237db6c8fSJan Beulich #define BASE_VALID(b) ((b) + MMCONF_SIZE <= (0xfdULL<<32) || (b) >= (1ULL<<40))
get_fam10h_pci_mmconf_base(void)53148f9bb8SPaul Gortmaker static void get_fam10h_pci_mmconf_base(void)
54d39398a3SYinghai Lu {
55d39398a3SYinghai Lu int i;
56d39398a3SYinghai Lu unsigned bus;
57d39398a3SYinghai Lu unsigned slot;
58d39398a3SYinghai Lu int found;
59d39398a3SYinghai Lu
60d39398a3SYinghai Lu u64 val;
61d39398a3SYinghai Lu u32 address;
62d39398a3SYinghai Lu u64 tom2;
63d39398a3SYinghai Lu u64 base = FAM10H_PCI_MMCONF_BASE;
64d39398a3SYinghai Lu
65d39398a3SYinghai Lu int hi_mmio_num;
66d39398a3SYinghai Lu struct range range[8];
67d39398a3SYinghai Lu
68d39398a3SYinghai Lu /* only try to get setting from BSP */
6937db6c8fSJan Beulich if (fam10h_pci_mmconf_base)
70d39398a3SYinghai Lu return;
71d39398a3SYinghai Lu
72d39398a3SYinghai Lu if (!early_pci_allowed())
7337db6c8fSJan Beulich return;
74d39398a3SYinghai Lu
75d39398a3SYinghai Lu found = 0;
76d39398a3SYinghai Lu for (i = 0; i < ARRAY_SIZE(pci_probes); i++) {
77d39398a3SYinghai Lu u32 id;
78d39398a3SYinghai Lu u16 device;
79d39398a3SYinghai Lu u16 vendor;
80d39398a3SYinghai Lu
81d39398a3SYinghai Lu bus = pci_probes[i].bus;
82d39398a3SYinghai Lu slot = pci_probes[i].slot;
83d39398a3SYinghai Lu id = read_pci_config(bus, slot, 0, PCI_VENDOR_ID);
84d39398a3SYinghai Lu
85d39398a3SYinghai Lu vendor = id & 0xffff;
86d39398a3SYinghai Lu device = (id>>16) & 0xffff;
87d39398a3SYinghai Lu if (pci_probes[i].vendor == vendor &&
88d39398a3SYinghai Lu pci_probes[i].device == device) {
89d39398a3SYinghai Lu found = 1;
90d39398a3SYinghai Lu break;
91d39398a3SYinghai Lu }
92d39398a3SYinghai Lu }
93d39398a3SYinghai Lu
94d39398a3SYinghai Lu if (!found)
9537db6c8fSJan Beulich return;
96d39398a3SYinghai Lu
97d39398a3SYinghai Lu /* SYS_CFG */
98*059e5c32SBrijesh Singh address = MSR_AMD64_SYSCFG;
99d39398a3SYinghai Lu rdmsrl(address, val);
100d39398a3SYinghai Lu
101d39398a3SYinghai Lu /* TOP_MEM2 is not enabled? */
102d39398a3SYinghai Lu if (!(val & (1<<21))) {
10337db6c8fSJan Beulich tom2 = 1ULL << 32;
104d39398a3SYinghai Lu } else {
105d39398a3SYinghai Lu /* TOP_MEM2 */
106d39398a3SYinghai Lu address = MSR_K8_TOP_MEM2;
107d39398a3SYinghai Lu rdmsrl(address, val);
10837db6c8fSJan Beulich tom2 = max(val & 0xffffff800000ULL, 1ULL << 32);
109d39398a3SYinghai Lu }
110d39398a3SYinghai Lu
111d39398a3SYinghai Lu if (base <= tom2)
11237db6c8fSJan Beulich base = (tom2 + 2 * MMCONF_UNIT - 1) & MMCONF_MASK;
113d39398a3SYinghai Lu
114d39398a3SYinghai Lu /*
115d39398a3SYinghai Lu * need to check if the range is in the high mmio range that is
116d39398a3SYinghai Lu * above 4G
117d39398a3SYinghai Lu */
118d39398a3SYinghai Lu hi_mmio_num = 0;
119d39398a3SYinghai Lu for (i = 0; i < 8; i++) {
120d39398a3SYinghai Lu u32 reg;
121d39398a3SYinghai Lu u64 start;
122d39398a3SYinghai Lu u64 end;
123d39398a3SYinghai Lu reg = read_pci_config(bus, slot, 1, 0x80 + (i << 3));
124d39398a3SYinghai Lu if (!(reg & 3))
125d39398a3SYinghai Lu continue;
126d39398a3SYinghai Lu
12737db6c8fSJan Beulich start = (u64)(reg & 0xffffff00) << 8; /* 39:16 on 31:8*/
128d39398a3SYinghai Lu reg = read_pci_config(bus, slot, 1, 0x84 + (i << 3));
12937db6c8fSJan Beulich end = ((u64)(reg & 0xffffff00) << 8) | 0xffff; /* 39:16 on 31:8*/
130d39398a3SYinghai Lu
13137db6c8fSJan Beulich if (end < tom2)
132d39398a3SYinghai Lu continue;
133d39398a3SYinghai Lu
134d39398a3SYinghai Lu range[hi_mmio_num].start = start;
135d39398a3SYinghai Lu range[hi_mmio_num].end = end;
136d39398a3SYinghai Lu hi_mmio_num++;
137d39398a3SYinghai Lu }
138d39398a3SYinghai Lu
139d39398a3SYinghai Lu if (!hi_mmio_num)
140d39398a3SYinghai Lu goto out;
141d39398a3SYinghai Lu
142d39398a3SYinghai Lu /* sort the range */
143d39398a3SYinghai Lu sort(range, hi_mmio_num, sizeof(struct range), cmp_range, NULL);
144d39398a3SYinghai Lu
145d39398a3SYinghai Lu if (range[hi_mmio_num - 1].end < base)
146d39398a3SYinghai Lu goto out;
14737db6c8fSJan Beulich if (range[0].start > base + MMCONF_SIZE)
148d39398a3SYinghai Lu goto out;
149d39398a3SYinghai Lu
150d39398a3SYinghai Lu /* need to find one window */
15137db6c8fSJan Beulich base = (range[0].start & MMCONF_MASK) - MMCONF_UNIT;
152d39398a3SYinghai Lu if ((base > tom2) && BASE_VALID(base))
153d39398a3SYinghai Lu goto out;
15437db6c8fSJan Beulich base = (range[hi_mmio_num - 1].end + MMCONF_UNIT) & MMCONF_MASK;
15537db6c8fSJan Beulich if (BASE_VALID(base))
156d39398a3SYinghai Lu goto out;
157d39398a3SYinghai Lu /* need to find window between ranges */
15837db6c8fSJan Beulich for (i = 1; i < hi_mmio_num; i++) {
15937db6c8fSJan Beulich base = (range[i - 1].end + MMCONF_UNIT) & MMCONF_MASK;
16037db6c8fSJan Beulich val = range[i].start & MMCONF_MASK;
16137db6c8fSJan Beulich if (val >= base + MMCONF_SIZE && BASE_VALID(base))
162d39398a3SYinghai Lu goto out;
163d39398a3SYinghai Lu }
164d39398a3SYinghai Lu return;
16537db6c8fSJan Beulich
166d39398a3SYinghai Lu out:
167d39398a3SYinghai Lu fam10h_pci_mmconf_base = base;
168d39398a3SYinghai Lu }
169d39398a3SYinghai Lu
fam10h_check_enable_mmcfg(void)170148f9bb8SPaul Gortmaker void fam10h_check_enable_mmcfg(void)
171d39398a3SYinghai Lu {
172d39398a3SYinghai Lu u64 val;
173d39398a3SYinghai Lu u32 address;
174d39398a3SYinghai Lu
1755f0b2976SYinghai Lu if (!(pci_probe & PCI_CHECK_ENABLE_AMD_MMCONF))
1765f0b2976SYinghai Lu return;
1775f0b2976SYinghai Lu
178d39398a3SYinghai Lu address = MSR_FAM10H_MMIO_CONF_BASE;
179d39398a3SYinghai Lu rdmsrl(address, val);
180d39398a3SYinghai Lu
181d39398a3SYinghai Lu /* try to make sure that AP's setting is identical to BSP setting */
182d39398a3SYinghai Lu if (val & FAM10H_MMIO_CONF_ENABLE) {
183d39398a3SYinghai Lu unsigned busnbits;
184d39398a3SYinghai Lu busnbits = (val >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) &
185d39398a3SYinghai Lu FAM10H_MMIO_CONF_BUSRANGE_MASK;
186d39398a3SYinghai Lu
187d39398a3SYinghai Lu /* only trust the one handle 256 buses, if acpi=off */
188d39398a3SYinghai Lu if (!acpi_pci_disabled || busnbits >= 8) {
18937db6c8fSJan Beulich u64 base = val & MMCONF_MASK;
19037db6c8fSJan Beulich
19137db6c8fSJan Beulich if (!fam10h_pci_mmconf_base) {
192d39398a3SYinghai Lu fam10h_pci_mmconf_base = base;
193d39398a3SYinghai Lu return;
194d39398a3SYinghai Lu } else if (fam10h_pci_mmconf_base == base)
195d39398a3SYinghai Lu return;
196d39398a3SYinghai Lu }
197d39398a3SYinghai Lu }
198d39398a3SYinghai Lu
199d39398a3SYinghai Lu /*
200d39398a3SYinghai Lu * if it is not enabled, try to enable it and assume only one segment
201d39398a3SYinghai Lu * with 256 buses
202d39398a3SYinghai Lu */
203d39398a3SYinghai Lu get_fam10h_pci_mmconf_base();
20437db6c8fSJan Beulich if (!fam10h_pci_mmconf_base) {
20537db6c8fSJan Beulich pci_probe &= ~PCI_CHECK_ENABLE_AMD_MMCONF;
206d39398a3SYinghai Lu return;
20737db6c8fSJan Beulich }
208d39398a3SYinghai Lu
209d39398a3SYinghai Lu printk(KERN_INFO "Enable MMCONFIG on AMD Family 10h\n");
210d39398a3SYinghai Lu val &= ~((FAM10H_MMIO_CONF_BASE_MASK<<FAM10H_MMIO_CONF_BASE_SHIFT) |
211d39398a3SYinghai Lu (FAM10H_MMIO_CONF_BUSRANGE_MASK<<FAM10H_MMIO_CONF_BUSRANGE_SHIFT));
212d39398a3SYinghai Lu val |= fam10h_pci_mmconf_base | (8 << FAM10H_MMIO_CONF_BUSRANGE_SHIFT) |
213d39398a3SYinghai Lu FAM10H_MMIO_CONF_ENABLE;
214d39398a3SYinghai Lu wrmsrl(address, val);
215d39398a3SYinghai Lu }
2165f0b2976SYinghai Lu
set_check_enable_amd_mmconf(const struct dmi_system_id * d)2172f62bf7dSJan Beulich static int __init set_check_enable_amd_mmconf(const struct dmi_system_id *d)
2185f0b2976SYinghai Lu {
2195f0b2976SYinghai Lu pci_probe |= PCI_CHECK_ENABLE_AMD_MMCONF;
2205f0b2976SYinghai Lu return 0;
2215f0b2976SYinghai Lu }
2225f0b2976SYinghai Lu
2232f62bf7dSJan Beulich static const struct dmi_system_id __initconst mmconf_dmi_table[] = {
2245f0b2976SYinghai Lu {
2255f0b2976SYinghai Lu .callback = set_check_enable_amd_mmconf,
2265f0b2976SYinghai Lu .ident = "Sun Microsystems Machine",
2275f0b2976SYinghai Lu .matches = {
2285f0b2976SYinghai Lu DMI_MATCH(DMI_SYS_VENDOR, "Sun Microsystems"),
2295f0b2976SYinghai Lu },
2305f0b2976SYinghai Lu },
2315f0b2976SYinghai Lu {}
2325f0b2976SYinghai Lu };
2335f0b2976SYinghai Lu
234148f9bb8SPaul Gortmaker /* Called from a non __init function, but only on the BSP. */
check_enable_amd_mmconf_dmi(void)2352f62bf7dSJan Beulich void __ref check_enable_amd_mmconf_dmi(void)
2365f0b2976SYinghai Lu {
2375f0b2976SYinghai Lu dmi_check_system(mmconf_dmi_table);
2385f0b2976SYinghai Lu }
239