1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2018 Johannes Lundberg
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/bus.h>
34#include <sys/kernel.h>
35#include <vm/vm.h>
36/* XXX: enable this once the KPI is available */
37/* #include <x86/physmem.h> */
38#include <machine/pci_cfgreg.h>
39#include <machine/md_var.h>
40#include <dev/pci/pcivar.h>
41#include <dev/pci/pcireg.h>
42
43#include <x86/pci/pci_early_quirks.h>
44
45#define	MiB(v) ((unsigned long)(v) << 20)
46
47struct pci_device_id {
48	uint32_t	vendor;
49	uint32_t	device;
50	const struct intel_stolen_ops *data;
51};
52
53/*
54 * These global variables are read by LinuxKPI.
55 * LinuxKPI provide this information to the i915 driver.
56 */
57vm_paddr_t intel_graphics_stolen_base = 0;
58vm_paddr_t intel_graphics_stolen_size = 0;
59
60/*
61 * Intel early quirks functions
62 */
63static vm_paddr_t
64intel_stolen_base_gen3(int bus, int slot, int func)
65{
66	uint32_t ctrl;
67	vm_paddr_t val;
68
69	ctrl = pci_cfgregread(bus, slot, func, INTEL_BSM, 4);
70	val = ctrl & INTEL_BSM_MASK;
71	return (val);
72}
73
74static vm_paddr_t
75intel_stolen_size_gen3(int bus, int slot, int func)
76{
77	uint32_t ctrl;
78	vm_paddr_t val;
79
80	ctrl = pci_cfgregread(0, 0, 0, I830_GMCH_CTRL, 2);
81	val = ctrl & I855_GMCH_GMS_MASK;
82
83	switch (val) {
84	case I855_GMCH_GMS_STOLEN_1M:
85		return (MiB(1));
86	case I855_GMCH_GMS_STOLEN_4M:
87		return (MiB(4));
88	case I855_GMCH_GMS_STOLEN_8M:
89		return (MiB(8));
90	case I855_GMCH_GMS_STOLEN_16M:
91		return (MiB(16));
92	case I855_GMCH_GMS_STOLEN_32M:
93		return (MiB(32));
94	case I915_GMCH_GMS_STOLEN_48M:
95		return (MiB(48));
96	case I915_GMCH_GMS_STOLEN_64M:
97		return (MiB(64));
98	case G33_GMCH_GMS_STOLEN_128M:
99		return (MiB(128));
100	case G33_GMCH_GMS_STOLEN_256M:
101		return (MiB(256));
102	case INTEL_GMCH_GMS_STOLEN_96M:
103		return (MiB(96));
104	case INTEL_GMCH_GMS_STOLEN_160M:
105		return (MiB(160));
106	case INTEL_GMCH_GMS_STOLEN_224M:
107		return (MiB(224));
108	case INTEL_GMCH_GMS_STOLEN_352M:
109		return (MiB(352));
110	}
111	return (0);
112}
113
114static vm_paddr_t
115intel_stolen_size_gen6(int bus, int slot, int func)
116{
117	uint32_t ctrl;
118	vm_paddr_t val;
119
120	ctrl = pci_cfgregread(bus, slot, func, SNB_GMCH_CTRL, 2);
121	val = (ctrl >> SNB_GMCH_GMS_SHIFT) & SNB_GMCH_GMS_MASK;
122	return (val * MiB(32));
123}
124
125static vm_paddr_t
126intel_stolen_size_gen8(int bus, int slot, int func)
127{
128	uint32_t ctrl;
129	vm_paddr_t val;
130
131	ctrl = pci_cfgregread(bus, slot, func, SNB_GMCH_CTRL, 2);
132	val = (ctrl >> BDW_GMCH_GMS_SHIFT) & BDW_GMCH_GMS_MASK;
133	return (val * MiB(32));
134}
135
136static vm_paddr_t
137intel_stolen_size_chv(int bus, int slot, int func)
138{
139	uint32_t ctrl;
140	vm_paddr_t val;
141
142	ctrl = pci_cfgregread(bus, slot, func, SNB_GMCH_CTRL, 2);
143	val = (ctrl >> SNB_GMCH_GMS_SHIFT) & SNB_GMCH_GMS_MASK;
144
145	/*
146	 * 0x0  to 0x10: 32MB increments starting at 0MB
147	 * 0x11 to 0x16: 4MB increments starting at 8MB
148	 * 0x17 to 0x1d: 4MB increments start at 36MB
149	 */
150	if (val < 0x11)
151		return (val * MiB(32));
152	else if (val < 0x17)
153		return ((val - 0x11) * MiB(4) + MiB(8));
154	else
155		return ((val - 0x17) * MiB(4) + MiB(36));
156}
157
158static vm_paddr_t
159intel_stolen_size_gen9(int bus, int slot, int func)
160{
161	uint32_t ctrl;
162	vm_paddr_t val;
163
164	ctrl = pci_cfgregread(bus, slot, func, SNB_GMCH_CTRL, 2);
165	val = (ctrl >> BDW_GMCH_GMS_SHIFT) & BDW_GMCH_GMS_MASK;
166
167	/* 0x0  to 0xEF: 32MB increments starting at 0MB */
168	/* 0xF0 to 0xFE: 4MB increments starting at 4MB */
169	if (val < 0xF0)
170		return (val * MiB(32));
171	return ((val - 0xF0) * MiB(4) + MiB(4));
172}
173
174struct intel_stolen_ops {
175	vm_paddr_t (*base)(int bus, int slot, int func);
176	vm_paddr_t (*size)(int bus, int slot, int func);
177};
178
179static const struct intel_stolen_ops intel_stolen_ops_gen3 = {
180	.base = intel_stolen_base_gen3,
181	.size = intel_stolen_size_gen3,
182};
183
184static const struct intel_stolen_ops intel_stolen_ops_gen6 = {
185	.base = intel_stolen_base_gen3,
186	.size = intel_stolen_size_gen6,
187};
188
189static const struct intel_stolen_ops intel_stolen_ops_gen8 = {
190	.base = intel_stolen_base_gen3,
191	.size = intel_stolen_size_gen8,
192};
193
194static const struct intel_stolen_ops intel_stolen_ops_gen9 = {
195	.base = intel_stolen_base_gen3,
196	.size = intel_stolen_size_gen9,
197};
198
199static const struct intel_stolen_ops intel_stolen_ops_chv = {
200	.base = intel_stolen_base_gen3,
201	.size = intel_stolen_size_chv,
202};
203
204static const struct pci_device_id intel_ids[] = {
205	INTEL_I915G_IDS(&intel_stolen_ops_gen3),
206	INTEL_I915GM_IDS(&intel_stolen_ops_gen3),
207	INTEL_I945G_IDS(&intel_stolen_ops_gen3),
208	INTEL_I945GM_IDS(&intel_stolen_ops_gen3),
209	INTEL_VLV_IDS(&intel_stolen_ops_gen6),
210	INTEL_PINEVIEW_IDS(&intel_stolen_ops_gen3),
211	INTEL_I965G_IDS(&intel_stolen_ops_gen3),
212	INTEL_G33_IDS(&intel_stolen_ops_gen3),
213	INTEL_I965GM_IDS(&intel_stolen_ops_gen3),
214	INTEL_GM45_IDS(&intel_stolen_ops_gen3),
215	INTEL_G45_IDS(&intel_stolen_ops_gen3),
216	INTEL_IRONLAKE_D_IDS(&intel_stolen_ops_gen3),
217	INTEL_IRONLAKE_M_IDS(&intel_stolen_ops_gen3),
218	INTEL_SNB_D_IDS(&intel_stolen_ops_gen6),
219	INTEL_SNB_M_IDS(&intel_stolen_ops_gen6),
220	INTEL_IVB_M_IDS(&intel_stolen_ops_gen6),
221	INTEL_IVB_D_IDS(&intel_stolen_ops_gen6),
222	INTEL_HSW_IDS(&intel_stolen_ops_gen6),
223	INTEL_BDW_IDS(&intel_stolen_ops_gen8),
224	INTEL_CHV_IDS(&intel_stolen_ops_chv),
225	INTEL_SKL_IDS(&intel_stolen_ops_gen9),
226	INTEL_BXT_IDS(&intel_stolen_ops_gen9),
227	INTEL_KBL_IDS(&intel_stolen_ops_gen9),
228	INTEL_CFL_IDS(&intel_stolen_ops_gen9),
229	INTEL_GLK_IDS(&intel_stolen_ops_gen9),
230	INTEL_CNL_IDS(&intel_stolen_ops_gen9),
231};
232
233/*
234 * Buggy BIOS don't reserve memory for the GPU properly and the OS
235 * can claim it before the GPU driver is loaded. This function will
236 * check the registers for base and size of this memory and reserve
237 * it for the GPU driver.
238 * gen3 (2004) and newer devices are supported. Support for older hw
239 * can be ported from Linux if needed.
240 */
241static void
242intel_graphics_stolen(void)
243{
244	const struct intel_stolen_ops *ops;
245	uint32_t vendor, device, class;
246	int i;
247
248	/* XXX: Scan bus instead of assuming 0:2:0? */
249	const int bus = 0;
250	const int slot = 2;
251	const int func = 0;
252
253	if (pci_cfgregopen() == 0)
254		return;
255
256	vendor = pci_cfgregread(bus, slot, func, PCIR_VENDOR, 2);
257	if (vendor != PCI_VENDOR_INTEL)
258		return;
259
260	class = pci_cfgregread(bus, slot, func, PCIR_SUBCLASS, 2);
261	if (class != PCI_CLASS_VGA)
262		return;
263
264	device = pci_cfgregread(bus, slot, func, PCIR_DEVICE, 2);
265	if (device == 0xFFFF)
266		return;
267
268	for (i = 0; i < nitems(intel_ids); i++) {
269		if (intel_ids[i].device != device)
270			continue;
271		ops = intel_ids[i].data;
272		intel_graphics_stolen_base = ops->base(bus, slot, func);
273		intel_graphics_stolen_size = ops->size(bus, slot, func);
274		break;
275	}
276
277	/* XXX: enable this once the KPI is available */
278	/* phys_avail_reserve(intel_graphics_stolen_base, */
279	/*     intel_graphics_stolen_base + intel_graphics_stolen_size); */
280}
281
282void
283pci_early_quirks(void)
284{
285
286	intel_graphics_stolen();
287}
288