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