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