xref: /linux/arch/x86/kernel/mmconf-fam10h_64.c (revision 19a519ca87b59a0031e1295674b1af0d6da83f70)
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>
12*9221222cSJuergen Gross #include <linux/acpi.h>
1327811d8cSYinghai Lu 
14d39398a3SYinghai Lu #include <asm/pci-direct.h>
15d39398a3SYinghai Lu #include <linux/sort.h>
16d39398a3SYinghai Lu #include <asm/io.h>
17d39398a3SYinghai Lu #include <asm/msr.h>
18d39398a3SYinghai Lu #include <asm/acpi.h>
19d1097635SThomas Gleixner #include <asm/mmconfig.h>
2082487711SJaswinder Singh Rajput #include <asm/pci_x86.h>
215f0b2976SYinghai Lu 
22d39398a3SYinghai Lu struct pci_hostbridge_probe {
23d39398a3SYinghai Lu 	u32 bus;
24d39398a3SYinghai Lu 	u32 slot;
25d39398a3SYinghai Lu 	u32 vendor;
26d39398a3SYinghai Lu 	u32 device;
27d39398a3SYinghai Lu };
28d39398a3SYinghai Lu 
29148f9bb8SPaul Gortmaker static u64 fam10h_pci_mmconf_base;
30d39398a3SYinghai Lu 
31148f9bb8SPaul Gortmaker static struct pci_hostbridge_probe pci_probes[] = {
32d39398a3SYinghai Lu 	{ 0, 0x18, PCI_VENDOR_ID_AMD, 0x1200 },
33d39398a3SYinghai Lu 	{ 0xff, 0, PCI_VENDOR_ID_AMD, 0x1200 },
34d39398a3SYinghai Lu };
35d39398a3SYinghai Lu 
36148f9bb8SPaul Gortmaker static int cmp_range(const void *x1, const void *x2)
37d39398a3SYinghai Lu {
38d39398a3SYinghai Lu 	const struct range *r1 = x1;
39d39398a3SYinghai Lu 	const struct range *r2 = x2;
40d39398a3SYinghai Lu 	int start1, start2;
41d39398a3SYinghai Lu 
42d39398a3SYinghai Lu 	start1 = r1->start >> 32;
43d39398a3SYinghai Lu 	start2 = r2->start >> 32;
44d39398a3SYinghai Lu 
45d39398a3SYinghai Lu 	return start1 - start2;
46d39398a3SYinghai Lu }
47d39398a3SYinghai Lu 
4837db6c8fSJan Beulich #define MMCONF_UNIT (1ULL << FAM10H_MMIO_CONF_BASE_SHIFT)
4937db6c8fSJan Beulich #define MMCONF_MASK (~(MMCONF_UNIT - 1))
5037db6c8fSJan Beulich #define MMCONF_SIZE (MMCONF_UNIT << 8)
5137db6c8fSJan Beulich /* need to avoid (0xfd<<32), (0xfe<<32), and (0xff<<32), ht used space */
52d39398a3SYinghai Lu #define FAM10H_PCI_MMCONF_BASE (0xfcULL<<32)
5337db6c8fSJan Beulich #define BASE_VALID(b) ((b) + MMCONF_SIZE <= (0xfdULL<<32) || (b) >= (1ULL<<40))
54148f9bb8SPaul Gortmaker static void get_fam10h_pci_mmconf_base(void)
55d39398a3SYinghai Lu {
56d39398a3SYinghai Lu 	int i;
57d39398a3SYinghai Lu 	unsigned bus;
58d39398a3SYinghai Lu 	unsigned slot;
59d39398a3SYinghai Lu 	int found;
60d39398a3SYinghai Lu 
61d39398a3SYinghai Lu 	u64 val;
62d39398a3SYinghai Lu 	u32 address;
63d39398a3SYinghai Lu 	u64 tom2;
64d39398a3SYinghai Lu 	u64 base = FAM10H_PCI_MMCONF_BASE;
65d39398a3SYinghai Lu 
66d39398a3SYinghai Lu 	int hi_mmio_num;
67d39398a3SYinghai Lu 	struct range range[8];
68d39398a3SYinghai Lu 
69d39398a3SYinghai Lu 	/* only try to get setting from BSP */
7037db6c8fSJan Beulich 	if (fam10h_pci_mmconf_base)
71d39398a3SYinghai Lu 		return;
72d39398a3SYinghai Lu 
73d39398a3SYinghai Lu 	if (!early_pci_allowed())
7437db6c8fSJan Beulich 		return;
75d39398a3SYinghai Lu 
76d39398a3SYinghai Lu 	found = 0;
77d39398a3SYinghai Lu 	for (i = 0; i < ARRAY_SIZE(pci_probes); i++) {
78d39398a3SYinghai Lu 		u32 id;
79d39398a3SYinghai Lu 		u16 device;
80d39398a3SYinghai Lu 		u16 vendor;
81d39398a3SYinghai Lu 
82d39398a3SYinghai Lu 		bus = pci_probes[i].bus;
83d39398a3SYinghai Lu 		slot = pci_probes[i].slot;
84d39398a3SYinghai Lu 		id = read_pci_config(bus, slot, 0, PCI_VENDOR_ID);
85d39398a3SYinghai Lu 
86d39398a3SYinghai Lu 		vendor = id & 0xffff;
87d39398a3SYinghai Lu 		device = (id>>16) & 0xffff;
88d39398a3SYinghai Lu 		if (pci_probes[i].vendor == vendor &&
89d39398a3SYinghai Lu 		    pci_probes[i].device == device) {
90d39398a3SYinghai Lu 			found = 1;
91d39398a3SYinghai Lu 			break;
92d39398a3SYinghai Lu 		}
93d39398a3SYinghai Lu 	}
94d39398a3SYinghai Lu 
95d39398a3SYinghai Lu 	if (!found)
9637db6c8fSJan Beulich 		return;
97d39398a3SYinghai Lu 
98d39398a3SYinghai Lu 	/* SYS_CFG */
99059e5c32SBrijesh Singh 	address = MSR_AMD64_SYSCFG;
100d39398a3SYinghai Lu 	rdmsrl(address, val);
101d39398a3SYinghai Lu 
102d39398a3SYinghai Lu 	/* TOP_MEM2 is not enabled? */
103d39398a3SYinghai Lu 	if (!(val & (1<<21))) {
10437db6c8fSJan Beulich 		tom2 = 1ULL << 32;
105d39398a3SYinghai Lu 	} else {
106d39398a3SYinghai Lu 		/* TOP_MEM2 */
107d39398a3SYinghai Lu 		address = MSR_K8_TOP_MEM2;
108d39398a3SYinghai Lu 		rdmsrl(address, val);
10937db6c8fSJan Beulich 		tom2 = max(val & 0xffffff800000ULL, 1ULL << 32);
110d39398a3SYinghai Lu 	}
111d39398a3SYinghai Lu 
112d39398a3SYinghai Lu 	if (base <= tom2)
11337db6c8fSJan Beulich 		base = (tom2 + 2 * MMCONF_UNIT - 1) & MMCONF_MASK;
114d39398a3SYinghai Lu 
115d39398a3SYinghai Lu 	/*
116d39398a3SYinghai Lu 	 * need to check if the range is in the high mmio range that is
117d39398a3SYinghai Lu 	 * above 4G
118d39398a3SYinghai Lu 	 */
119d39398a3SYinghai Lu 	hi_mmio_num = 0;
120d39398a3SYinghai Lu 	for (i = 0; i < 8; i++) {
121d39398a3SYinghai Lu 		u32 reg;
122d39398a3SYinghai Lu 		u64 start;
123d39398a3SYinghai Lu 		u64 end;
124d39398a3SYinghai Lu 		reg = read_pci_config(bus, slot, 1, 0x80 + (i << 3));
125d39398a3SYinghai Lu 		if (!(reg & 3))
126d39398a3SYinghai Lu 			continue;
127d39398a3SYinghai Lu 
12837db6c8fSJan Beulich 		start = (u64)(reg & 0xffffff00) << 8; /* 39:16 on 31:8*/
129d39398a3SYinghai Lu 		reg = read_pci_config(bus, slot, 1, 0x84 + (i << 3));
13037db6c8fSJan Beulich 		end = ((u64)(reg & 0xffffff00) << 8) | 0xffff; /* 39:16 on 31:8*/
131d39398a3SYinghai Lu 
13237db6c8fSJan Beulich 		if (end < tom2)
133d39398a3SYinghai Lu 			continue;
134d39398a3SYinghai Lu 
135d39398a3SYinghai Lu 		range[hi_mmio_num].start = start;
136d39398a3SYinghai Lu 		range[hi_mmio_num].end = end;
137d39398a3SYinghai Lu 		hi_mmio_num++;
138d39398a3SYinghai Lu 	}
139d39398a3SYinghai Lu 
140d39398a3SYinghai Lu 	if (!hi_mmio_num)
141d39398a3SYinghai Lu 		goto out;
142d39398a3SYinghai Lu 
143d39398a3SYinghai Lu 	/* sort the range */
144d39398a3SYinghai Lu 	sort(range, hi_mmio_num, sizeof(struct range), cmp_range, NULL);
145d39398a3SYinghai Lu 
146d39398a3SYinghai Lu 	if (range[hi_mmio_num - 1].end < base)
147d39398a3SYinghai Lu 		goto out;
14837db6c8fSJan Beulich 	if (range[0].start > base + MMCONF_SIZE)
149d39398a3SYinghai Lu 		goto out;
150d39398a3SYinghai Lu 
151d39398a3SYinghai Lu 	/* need to find one window */
15237db6c8fSJan Beulich 	base = (range[0].start & MMCONF_MASK) - MMCONF_UNIT;
153d39398a3SYinghai Lu 	if ((base > tom2) && BASE_VALID(base))
154d39398a3SYinghai Lu 		goto out;
15537db6c8fSJan Beulich 	base = (range[hi_mmio_num - 1].end + MMCONF_UNIT) & MMCONF_MASK;
15637db6c8fSJan Beulich 	if (BASE_VALID(base))
157d39398a3SYinghai Lu 		goto out;
158d39398a3SYinghai Lu 	/* need to find window between ranges */
15937db6c8fSJan Beulich 	for (i = 1; i < hi_mmio_num; i++) {
16037db6c8fSJan Beulich 		base = (range[i - 1].end + MMCONF_UNIT) & MMCONF_MASK;
16137db6c8fSJan Beulich 		val = range[i].start & MMCONF_MASK;
16237db6c8fSJan Beulich 		if (val >= base + MMCONF_SIZE && BASE_VALID(base))
163d39398a3SYinghai Lu 			goto out;
164d39398a3SYinghai Lu 	}
165d39398a3SYinghai Lu 	return;
16637db6c8fSJan Beulich 
167d39398a3SYinghai Lu out:
168d39398a3SYinghai Lu 	fam10h_pci_mmconf_base = base;
169d39398a3SYinghai Lu }
170d39398a3SYinghai Lu 
171148f9bb8SPaul Gortmaker void fam10h_check_enable_mmcfg(void)
172d39398a3SYinghai Lu {
173d39398a3SYinghai Lu 	u64 val;
174d39398a3SYinghai Lu 	u32 address;
175d39398a3SYinghai Lu 
1765f0b2976SYinghai Lu 	if (!(pci_probe & PCI_CHECK_ENABLE_AMD_MMCONF))
1775f0b2976SYinghai Lu 		return;
1785f0b2976SYinghai Lu 
179d39398a3SYinghai Lu 	address = MSR_FAM10H_MMIO_CONF_BASE;
180d39398a3SYinghai Lu 	rdmsrl(address, val);
181d39398a3SYinghai Lu 
182d39398a3SYinghai Lu 	/* try to make sure that AP's setting is identical to BSP setting */
183d39398a3SYinghai Lu 	if (val & FAM10H_MMIO_CONF_ENABLE) {
184d39398a3SYinghai Lu 		unsigned busnbits;
185d39398a3SYinghai Lu 		busnbits = (val >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) &
186d39398a3SYinghai Lu 			FAM10H_MMIO_CONF_BUSRANGE_MASK;
187d39398a3SYinghai Lu 
188d39398a3SYinghai Lu 		/* only trust the one handle 256 buses, if acpi=off */
189d39398a3SYinghai Lu 		if (!acpi_pci_disabled || busnbits >= 8) {
19037db6c8fSJan Beulich 			u64 base = val & MMCONF_MASK;
19137db6c8fSJan Beulich 
19237db6c8fSJan Beulich 			if (!fam10h_pci_mmconf_base) {
193d39398a3SYinghai Lu 				fam10h_pci_mmconf_base = base;
194d39398a3SYinghai Lu 				return;
195d39398a3SYinghai Lu 			} else if (fam10h_pci_mmconf_base ==  base)
196d39398a3SYinghai Lu 				return;
197d39398a3SYinghai Lu 		}
198d39398a3SYinghai Lu 	}
199d39398a3SYinghai Lu 
200d39398a3SYinghai Lu 	/*
201d39398a3SYinghai Lu 	 * if it is not enabled, try to enable it and assume only one segment
202d39398a3SYinghai Lu 	 * with 256 buses
203d39398a3SYinghai Lu 	 */
204d39398a3SYinghai Lu 	get_fam10h_pci_mmconf_base();
20537db6c8fSJan Beulich 	if (!fam10h_pci_mmconf_base) {
20637db6c8fSJan Beulich 		pci_probe &= ~PCI_CHECK_ENABLE_AMD_MMCONF;
207d39398a3SYinghai Lu 		return;
20837db6c8fSJan Beulich 	}
209d39398a3SYinghai Lu 
210d39398a3SYinghai Lu 	printk(KERN_INFO "Enable MMCONFIG on AMD Family 10h\n");
211d39398a3SYinghai Lu 	val &= ~((FAM10H_MMIO_CONF_BASE_MASK<<FAM10H_MMIO_CONF_BASE_SHIFT) |
212d39398a3SYinghai Lu 	     (FAM10H_MMIO_CONF_BUSRANGE_MASK<<FAM10H_MMIO_CONF_BUSRANGE_SHIFT));
213d39398a3SYinghai Lu 	val |= fam10h_pci_mmconf_base | (8 << FAM10H_MMIO_CONF_BUSRANGE_SHIFT) |
214d39398a3SYinghai Lu 	       FAM10H_MMIO_CONF_ENABLE;
215d39398a3SYinghai Lu 	wrmsrl(address, val);
216d39398a3SYinghai Lu }
2175f0b2976SYinghai Lu 
2182f62bf7dSJan Beulich static int __init set_check_enable_amd_mmconf(const struct dmi_system_id *d)
2195f0b2976SYinghai Lu {
2205f0b2976SYinghai Lu         pci_probe |= PCI_CHECK_ENABLE_AMD_MMCONF;
2215f0b2976SYinghai Lu         return 0;
2225f0b2976SYinghai Lu }
2235f0b2976SYinghai Lu 
2242f62bf7dSJan Beulich static const struct dmi_system_id __initconst mmconf_dmi_table[] = {
2255f0b2976SYinghai Lu         {
2265f0b2976SYinghai Lu                 .callback = set_check_enable_amd_mmconf,
2275f0b2976SYinghai Lu                 .ident = "Sun Microsystems Machine",
2285f0b2976SYinghai Lu                 .matches = {
2295f0b2976SYinghai Lu                         DMI_MATCH(DMI_SYS_VENDOR, "Sun Microsystems"),
2305f0b2976SYinghai Lu                 },
2315f0b2976SYinghai Lu         },
2325f0b2976SYinghai Lu 	{}
2335f0b2976SYinghai Lu };
2345f0b2976SYinghai Lu 
235148f9bb8SPaul Gortmaker /* Called from a non __init function, but only on the BSP. */
2362f62bf7dSJan Beulich void __ref check_enable_amd_mmconf_dmi(void)
2375f0b2976SYinghai Lu {
2385f0b2976SYinghai Lu 	dmi_check_system(mmconf_dmi_table);
2395f0b2976SYinghai Lu }
240