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