xref: /haiku/src/add-ons/kernel/busses/agp_gart/intel_gart.cpp (revision 893988af824e65e49e55f517b157db8386e8002b)
1 /*
2  * Copyright 2008-2009, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <AreaKeeper.h>
8 #include <intel_extreme.h>
9 
10 #include <stdlib.h>
11 
12 #include <AGP.h>
13 #include <KernelExport.h>
14 #include <PCI.h>
15 
16 
17 //#define TRACE_INTEL
18 #ifdef TRACE_INTEL
19 #	define TRACE(x...) dprintf("\33[33magp-intel:\33[0m " x)
20 #else
21 #	define TRACE(x...) ;
22 #endif
23 
24 #ifndef __HAIKU__
25 #	define B_KERNEL_READ_AREA	0
26 #	define B_KERNEL_WRITE_AREA	0
27 #endif
28 
29 /* read and write to PCI config space */
30 #define get_pci_config(info, offset, size) \
31 	(sPCI->read_pci_config((info).bus, (info).device, (info).function, \
32 		(offset), (size)))
33 #define set_pci_config(info, offset, size, value) \
34 	(sPCI->write_pci_config((info).bus, (info).device, (info).function, \
35 		(offset), (size), (value)))
36 #define write32(address, data) \
37 	(*((volatile uint32 *)(address)) = (data))
38 #define read32(address) \
39 	(*((volatile uint32 *)(address)))
40 
41 
42 const struct supported_device {
43 	uint32		bridge_id;
44 	uint32		display_id;
45 	uint32		type;
46 	const char	*name;
47 } kSupportedDevices[] = {
48 	{0x3575, 0x3577, INTEL_TYPE_83x, "i830GM"},
49 	{0x2560, 0x2562, INTEL_TYPE_83x, "i845G"},
50 	{0x3580, 0x3582, INTEL_TYPE_85x, "i855G"},
51 	{0x2570, 0x2572, INTEL_TYPE_85x, "i865G"},
52 
53 //	{0x2792, INTEL_TYPE_91x, "i910"},
54 //	{0x258a, INTEL_TYPE_91x, "i915"},
55 	{0x2580, 0x2582, INTEL_TYPE_91x, "i915G"},
56 	{0x2590, 0x2592, INTEL_TYPE_91x, "i915GM"},
57 	{0x2770, 0x2772, INTEL_TYPE_945, "i945G"},
58 	{0x27a0, 0x27a2, INTEL_TYPE_945, "i945GM"},
59 	{0x27ac, 0x27ae, INTEL_TYPE_945, "i945GME"},
60 
61 	{0x2970, 0x2972, INTEL_TYPE_965, "i946GZ"},
62 	{0x2980, 0x2982, INTEL_TYPE_965, "i965G"},
63 	{0x2990, 0x2992, INTEL_TYPE_965, "i965Q"},
64 	{0x29a0, 0x29a2, INTEL_TYPE_965, "i965G"},
65 	{0x2a00, 0x2a02, INTEL_TYPE_965, "i965GM"},
66 	{0x2a10, 0x2a12, INTEL_TYPE_965, "i965GME"},
67 
68 	{0x29b0, 0x29b2, INTEL_TYPE_G33, "G33"},
69 	{0x29c0, 0x29c2, INTEL_TYPE_G33, "Q35"},
70 	{0x29d0, 0x29d2, INTEL_TYPE_G33, "Q33"},
71 };
72 
73 struct intel_info {
74 	pci_info	bridge;
75 	pci_info	display;
76 	uint32		type;
77 
78 	uint32		*gtt_base;
79 	addr_t		gtt_physical_base;
80 	area_id		gtt_area;
81 	size_t		gtt_entries;
82 	size_t		gtt_stolen_entries;
83 
84 	vuint32		*registers;
85 	area_id		registers_area;
86 
87 	addr_t		aperture_base;
88 	addr_t		aperture_physical_base;
89 	area_id		aperture_area;
90 	size_t		aperture_size;
91 	size_t		aperture_stolen_size;
92 
93 	addr_t		scratch_page;
94 	area_id		scratch_area;
95 };
96 
97 static intel_info sInfo;
98 static pci_module_info *sPCI;
99 
100 
101 static bool
102 has_display_device(pci_info &info, uint32 deviceID)
103 {
104 	for (uint32 index = 0; sPCI->get_nth_pci_info(index, &info) == B_OK;
105 			index++) {
106 		if (info.vendor_id != VENDOR_ID_INTEL
107 			|| info.device_id != deviceID
108 			|| info.class_base != PCI_display)
109 			continue;
110 
111 		return true;
112 	}
113 
114 	return false;
115 }
116 
117 
118 static void
119 determine_memory_sizes(intel_info &info, size_t &gttSize, size_t &stolenSize)
120 {
121 	// read stolen memory from the PCI configuration of the PCI bridge
122 	uint16 memoryConfig = get_pci_config(info.bridge,
123 		INTEL_GRAPHICS_MEMORY_CONTROL, 2);
124 	size_t memorySize = 1 << 20; // 1 MB
125 	gttSize = 0;
126 	stolenSize = 0;
127 
128 	if (info.type == INTEL_TYPE_965) {
129 		switch (memoryConfig & i965_GTT_MASK) {
130 			case i965_GTT_128K:
131 				gttSize = 128 << 10;
132 				break;
133 			case i965_GTT_256K:
134 				gttSize = 256 << 10;
135 				break;
136 			case i965_GTT_512K:
137 				gttSize = 512 << 10;
138 				break;
139 		}
140 	} else if (info.type == INTEL_TYPE_G33) {
141 		switch (memoryConfig & G33_GTT_MASK) {
142 			case G33_GTT_1M:
143 				gttSize = 1 << 20;
144 				break;
145 			case G33_GTT_2M:
146 				gttSize = 2 << 20;
147 				break;
148 		}
149 	} else {
150 		// older models have the GTT as large as their frame buffer mapping
151 		// TODO: check if the i9xx version works with the i8xx chips as well
152 		size_t frameBufferSize = 0;
153 		if ((info.type & INTEL_TYPE_8xx) != 0) {
154 			if (info.type == INTEL_TYPE_83x
155 				&& (memoryConfig & MEMORY_MASK) == i830_FRAME_BUFFER_64M)
156 				frameBufferSize = 64 << 20;
157 			else
158 				frameBufferSize = 128 << 20;
159 		} else if ((info.type & INTEL_TYPE_9xx) != 0)
160 			frameBufferSize = info.display.u.h0.base_register_sizes[2];
161 
162 		gttSize = frameBufferSize / 1024;
163 	}
164 
165 	// TODO: test with different models!
166 
167 	if (info.type == INTEL_TYPE_83x) {
168 		switch (memoryConfig & STOLEN_MEMORY_MASK) {
169 			case i830_LOCAL_MEMORY_ONLY:
170 				// TODO: determine its size!
171 				dprintf("intel_gart: getting local memory size not implemented.\n");
172 				break;
173 			case i830_STOLEN_512K:
174 				memorySize >>= 1;
175 				break;
176 			case i830_STOLEN_8M:
177 				memorySize *= 8;
178 				break;
179 		}
180 	} else if (info.type == INTEL_TYPE_85x
181 		|| (info.type & INTEL_TYPE_9xx) != 0) {
182 		switch (memoryConfig & STOLEN_MEMORY_MASK) {
183 			case i855_STOLEN_MEMORY_4M:
184 				memorySize *= 4;
185 				break;
186 			case i855_STOLEN_MEMORY_8M:
187 				memorySize *= 8;
188 				break;
189 			case i855_STOLEN_MEMORY_16M:
190 				memorySize *= 16;
191 				break;
192 			case i855_STOLEN_MEMORY_32M:
193 				memorySize *= 32;
194 				break;
195 			case i855_STOLEN_MEMORY_48M:
196 				memorySize *= 48;
197 				break;
198 			case i855_STOLEN_MEMORY_64M:
199 				memorySize *= 64;
200 				break;
201 			case i855_STOLEN_MEMORY_128M:
202 				memorySize *= 128;
203 				break;
204 			case i855_STOLEN_MEMORY_256M:
205 				memorySize *= 256;
206 				break;
207 		}
208 	}
209 
210 	stolenSize = memorySize - 4096;
211 }
212 
213 
214 static void
215 set_gtt_entry(intel_info &info, uint32 offset, addr_t physicalAddress)
216 {
217 	write32(info.gtt_base + (offset >> GTT_PAGE_SHIFT),
218 		(uint32)physicalAddress | GTT_ENTRY_VALID);
219 }
220 
221 
222 static void
223 intel_unmap(intel_info &info)
224 {
225 	delete_area(info.registers_area);
226 	delete_area(info.gtt_area);
227 	delete_area(info.scratch_area);
228 	delete_area(info.aperture_area);
229 	info.aperture_size = 0;
230 }
231 
232 
233 static status_t
234 intel_map(intel_info &info)
235 {
236 	int fbIndex = 0;
237 	int mmioIndex = 1;
238 	if ((info.type & INTEL_TYPE_FAMILY_MASK) == INTEL_TYPE_9xx) {
239 		// for some reason Intel saw the need to change the order of the mappings
240 		// with the introduction of the i9xx family
241 		mmioIndex = 0;
242 		fbIndex = 2;
243 	}
244 
245 	AreaKeeper mmioMapper;
246 	info.registers_area = mmioMapper.Map("intel GMCH mmio",
247 		(void *)info.display.u.h0.base_registers[mmioIndex],
248 		info.display.u.h0.base_register_sizes[mmioIndex], B_ANY_KERNEL_ADDRESS,
249 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void **)&info.registers);
250 	if (mmioMapper.InitCheck() < B_OK) {
251 		dprintf("agp_intel: could not map memory I/O!\n");
252 		return info.registers_area;
253 	}
254 
255 	// make sure bus master, memory-mapped I/O, and frame buffer is enabled
256 	set_pci_config(info.display, PCI_command, 2,
257 		get_pci_config(info.display, PCI_command, 2)
258 			| PCI_command_io | PCI_command_memory | PCI_command_master);
259 
260 	void *scratchAddress;
261 	AreaKeeper scratchCreator;
262 	info.scratch_area = scratchCreator.Create("intel GMCH scratch",
263 		&scratchAddress, B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_FULL_LOCK,
264 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
265 	if (scratchCreator.InitCheck() < B_OK) {
266 		dprintf("agp_intel: could not create scratch page!\n");
267 		return info.scratch_area;
268 	}
269 
270 	physical_entry entry;
271 	if (get_memory_map(scratchAddress, B_PAGE_SIZE, &entry, 1) != B_OK)
272 		return B_ERROR;
273 
274 	if ((info.type & INTEL_TYPE_FAMILY_MASK) == INTEL_TYPE_9xx)
275 		info.gtt_physical_base = get_pci_config(info.display, i915_GTT_BASE, 4);
276 	else {
277 		info.gtt_physical_base = read32(info.registers
278 			+ INTEL_PAGE_TABLE_CONTROL) & ~PAGE_TABLE_ENABLED;
279 		if (info.gtt_physical_base == 0) {
280 			// TODO: not sure how this is supposed to work under Linux/FreeBSD,
281 			// but on my i865, this code is needed for Haiku.
282 			dprintf("intel_gart: Use GTT address fallback.\n");
283 			info.gtt_physical_base = info.display.u.h0.base_registers[mmioIndex]
284 				+ i830_GTT_BASE;
285 		}
286 	}
287 
288 	size_t gttSize, stolenSize;
289 	determine_memory_sizes(info, gttSize, stolenSize);
290 
291 	info.gtt_entries = gttSize / 4096;
292 	info.gtt_stolen_entries = stolenSize / 4096;
293 
294 	TRACE("GTT base %lx, size %lu, entries %lu, stolen %lu\n", info.gtt_physical_base,
295 		gttSize, info.gtt_entries, stolenSize);
296 
297 	AreaKeeper gttMapper;
298 	info.gtt_area = gttMapper.Map("intel GMCH gtt",
299 		(void *)info.gtt_physical_base, gttSize, B_ANY_KERNEL_ADDRESS,
300 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void **)&info.gtt_base);
301 	if (gttMapper.InitCheck() < B_OK) {
302 		dprintf("intel_gart: could not map GTT!\n");
303 		return info.gtt_area;
304 	}
305 
306 	info.aperture_physical_base = info.display.u.h0.base_registers[fbIndex];
307 	info.aperture_stolen_size = stolenSize;
308 	if (info.aperture_size == 0)
309 		info.aperture_size = info.display.u.h0.base_register_sizes[fbIndex];
310 
311 	dprintf("intel_gart: detected %ld MB of stolen memory, aperture "
312 		"size %ld MB, GTT size %ld KB\n", (stolenSize + (1023 << 10)) >> 20,
313 		info.aperture_size >> 20, gttSize >> 10);
314 
315 	AreaKeeper apertureMapper;
316 	info.aperture_area = apertureMapper.Map("intel graphics aperture",
317 		(void *)info.aperture_physical_base, info.aperture_size,
318 		B_ANY_KERNEL_BLOCK_ADDRESS | B_MTR_WC,
319 		B_READ_AREA | B_WRITE_AREA, (void **)&info.aperture_base);
320 	if (apertureMapper.InitCheck() < B_OK) {
321 		// try again without write combining
322 		dprintf(DEVICE_NAME ": enabling write combined mode failed.\n");
323 
324 		info.aperture_area = apertureMapper.Map("intel graphics aperture",
325 			(void *)info.aperture_physical_base, info.aperture_size,
326 			B_ANY_KERNEL_BLOCK_ADDRESS, B_READ_AREA | B_WRITE_AREA,
327 			(void **)&info.aperture_base);
328 	}
329 	if (apertureMapper.InitCheck() < B_OK) {
330 		dprintf(DEVICE_NAME ": could not map graphics aperture!\n");
331 		return info.aperture_area;
332 	}
333 
334 	info.scratch_page = (addr_t)entry.address;
335 
336 	gttMapper.Detach();
337 	mmioMapper.Detach();
338 	scratchCreator.Detach();
339 	apertureMapper.Detach();
340 
341 	return B_OK;
342 }
343 
344 
345 //	#pragma mark - module interface
346 
347 
348 status_t
349 intel_create_aperture(uint8 bus, uint8 device, uint8 function, size_t size,
350 	void **_aperture)
351 {
352 	// TODO: we currently only support a single AGP bridge!
353 	if ((bus != sInfo.bridge.bus || device != sInfo.bridge.device
354 			|| function != sInfo.bridge.function)
355 		&& (bus != sInfo.display.bus || device != sInfo.display.device
356 			|| function != sInfo.display.function))
357 		return B_BAD_VALUE;
358 
359 	sInfo.aperture_size = size;
360 
361 	if (intel_map(sInfo) < B_OK)
362 		return B_ERROR;
363 
364 	uint16 gmchControl = get_pci_config(sInfo.bridge,
365 		INTEL_GRAPHICS_MEMORY_CONTROL, 2) | MEMORY_CONTROL_ENABLED;
366 	set_pci_config(sInfo.bridge, INTEL_GRAPHICS_MEMORY_CONTROL, 2, gmchControl);
367 
368 	write32(sInfo.registers + INTEL_PAGE_TABLE_CONTROL,
369 		sInfo.gtt_physical_base | PAGE_TABLE_ENABLED);
370 	read32(sInfo.registers + INTEL_PAGE_TABLE_CONTROL);
371 
372 	if (sInfo.scratch_page != 0) {
373 		for (size_t i = sInfo.gtt_stolen_entries; i < sInfo.gtt_entries; i++) {
374 			set_gtt_entry(sInfo, i << GTT_PAGE_SHIFT, sInfo.scratch_page);
375 		}
376 		read32(sInfo.gtt_base + sInfo.gtt_entries - 1);
377 	}
378 
379 	asm("wbinvd;");
380 
381 	*_aperture = NULL;
382 	return B_OK;
383 }
384 
385 
386 void
387 intel_delete_aperture(void *aperture)
388 {
389 	intel_unmap(sInfo);
390 }
391 
392 
393 static status_t
394 intel_get_aperture_info(void *aperture, aperture_info *info)
395 {
396 	if (info == NULL)
397 		return B_BAD_VALUE;
398 
399 	info->base = sInfo.aperture_base;
400 	info->physical_base = sInfo.aperture_physical_base;
401 	info->size = sInfo.aperture_size;
402 	info->reserved_size = sInfo.aperture_stolen_size;
403 
404 	return B_OK;
405 }
406 
407 
408 status_t
409 intel_set_aperture_size(void *aperture, size_t size)
410 {
411 	return B_ERROR;
412 }
413 
414 
415 static status_t
416 intel_bind_page(void *aperture, uint32 offset, addr_t physicalAddress)
417 {
418 	//TRACE("bind_page(offset %lx, physical %lx)\n", offset, physicalAddress);
419 
420 	set_gtt_entry(sInfo, offset, physicalAddress);
421 	return B_OK;
422 }
423 
424 
425 static status_t
426 intel_unbind_page(void *aperture, uint32 offset)
427 {
428 	//TRACE("unbind_page(offset %lx)\n", offset);
429 
430 	if (sInfo.scratch_page != 0)
431 		set_gtt_entry(sInfo, offset, sInfo.scratch_page);
432 
433 	return B_OK;
434 }
435 
436 
437 void
438 intel_flush_tlbs(void *aperture)
439 {
440 	read32(sInfo.gtt_base + sInfo.gtt_entries - 1);
441 	asm("wbinvd;");
442 }
443 
444 
445 //	#pragma mark -
446 
447 
448 static status_t
449 intel_init()
450 {
451 	TRACE("bus manager init\n");
452 
453 	if (get_module(B_PCI_MODULE_NAME, (module_info **)&sPCI) != B_OK)
454 		return B_ERROR;
455 
456 	bool found = false;
457 
458 	for (uint32 index = 0; sPCI->get_nth_pci_info(index, &sInfo.bridge) == B_OK;
459 			index++) {
460 		if (sInfo.bridge.vendor_id != VENDOR_ID_INTEL
461 			|| sInfo.bridge.class_base != PCI_bridge)
462 			continue;
463 
464 		// check device
465 		for (uint32 i = 0; i < sizeof(kSupportedDevices)
466 				/ sizeof(kSupportedDevices[0]); i++) {
467 			if (sInfo.bridge.device_id == kSupportedDevices[i].bridge_id) {
468 				sInfo.type = kSupportedDevices[i].type;
469 				found = has_display_device(sInfo.display,
470 					kSupportedDevices[i].display_id);
471 				break;
472 			}
473 		}
474 
475 		if (found)
476 			break;
477 	}
478 
479 	if (!found)
480 		return ENODEV;
481 
482 	TRACE("found intel bridge\n");
483 	return B_OK;
484 }
485 
486 
487 static void
488 intel_uninit()
489 {
490 }
491 
492 
493 static int32
494 intel_std_ops(int32 op, ...)
495 {
496 	switch (op) {
497 		case B_MODULE_INIT:
498 			return intel_init();
499 		case B_MODULE_UNINIT:
500 			intel_uninit();
501 			return B_OK;
502 	}
503 
504 	return B_BAD_VALUE;
505 }
506 
507 
508 static struct agp_gart_bus_module_info sIntelModuleInfo = {
509 	{
510 		"busses/agp_gart/intel/v0",
511 		0,
512 		intel_std_ops
513 	},
514 
515 	intel_create_aperture,
516 	intel_delete_aperture,
517 
518 	intel_get_aperture_info,
519 	intel_set_aperture_size,
520 	intel_bind_page,
521 	intel_unbind_page,
522 	intel_flush_tlbs
523 };
524 
525 module_info *modules[] = {
526 	(module_info *)&sIntelModuleInfo,
527 	NULL
528 };
529