intel-agp.c 66.5 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
/*
 * Intel AGPGART routines.
 */

#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
8
#include <linux/kernel.h>
Linus Torvalds's avatar
Linus Torvalds committed
9
10
11
12
#include <linux/pagemap.h>
#include <linux/agp_backend.h>
#include "agp.h"

13
14
#define PCI_DEVICE_ID_INTEL_E7221_HB	0x2588
#define PCI_DEVICE_ID_INTEL_E7221_IG	0x258a
15
16
#define PCI_DEVICE_ID_INTEL_82946GZ_HB      0x2970
#define PCI_DEVICE_ID_INTEL_82946GZ_IG      0x2972
17
18
#define PCI_DEVICE_ID_INTEL_82G35_HB     0x2980
#define PCI_DEVICE_ID_INTEL_82G35_IG     0x2982
19
20
21
22
#define PCI_DEVICE_ID_INTEL_82965Q_HB       0x2990
#define PCI_DEVICE_ID_INTEL_82965Q_IG       0x2992
#define PCI_DEVICE_ID_INTEL_82965G_HB       0x29A0
#define PCI_DEVICE_ID_INTEL_82965G_IG       0x29A2
23
24
#define PCI_DEVICE_ID_INTEL_82965GM_HB      0x2A00
#define PCI_DEVICE_ID_INTEL_82965GM_IG      0x2A02
Zhenyu Wang's avatar
Zhenyu Wang committed
25
#define PCI_DEVICE_ID_INTEL_82965GME_HB     0x2A10
26
#define PCI_DEVICE_ID_INTEL_82965GME_IG     0x2A12
Zhenyu Wang's avatar
Zhenyu Wang committed
27
#define PCI_DEVICE_ID_INTEL_82945GME_HB     0x27AC
28
#define PCI_DEVICE_ID_INTEL_82945GME_IG     0x27AE
29
30
31
32
33
34
#define PCI_DEVICE_ID_INTEL_G33_HB          0x29C0
#define PCI_DEVICE_ID_INTEL_G33_IG          0x29C2
#define PCI_DEVICE_ID_INTEL_Q35_HB          0x29B0
#define PCI_DEVICE_ID_INTEL_Q35_IG          0x29B2
#define PCI_DEVICE_ID_INTEL_Q33_HB          0x29D0
#define PCI_DEVICE_ID_INTEL_Q33_IG          0x29D2
Zhenyu Wang's avatar
Zhenyu Wang committed
35
36
#define PCI_DEVICE_ID_INTEL_IGD_HB          0x2A40
#define PCI_DEVICE_ID_INTEL_IGD_IG          0x2A42
37

38
39
40
41
42
43
44
45
/* cover 915 and 945 variants */
#define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \
		 agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915G_HB || \
		 agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915GM_HB || \
		 agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945G_HB || \
		 agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GM_HB || \
		 agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GME_HB)

46
#define IS_I965 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82946GZ_HB || \
47
48
49
50
		 agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82G35_HB || \
		 agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965Q_HB || \
		 agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965G_HB || \
		 agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965GM_HB || \
Zhenyu Wang's avatar
Zhenyu Wang committed
51
52
		 agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965GME_HB || \
		 agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGD_HB)
53

54
55
56
#define IS_G33 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G33_HB || \
		agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q35_HB || \
		agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q33_HB)
57

58
59
60
extern int agp_memory_reserved;


Linus Torvalds's avatar
Linus Torvalds committed
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/* Intel 815 register */
#define INTEL_815_APCONT	0x51
#define INTEL_815_ATTBASE_MASK	~0x1FFFFFFF

/* Intel i820 registers */
#define INTEL_I820_RDCR		0x51
#define INTEL_I820_ERRSTS	0xc8

/* Intel i840 registers */
#define INTEL_I840_MCHCFG	0x50
#define INTEL_I840_ERRSTS	0xc8

/* Intel i850 registers */
#define INTEL_I850_MCHCFG	0x50
#define INTEL_I850_ERRSTS	0xc8

/* intel 915G registers */
#define I915_GMADDR	0x18
#define I915_MMADDR	0x10
#define I915_PTEADDR	0x1C
#define I915_GMCH_GMS_STOLEN_48M	(0x6 << 4)
#define I915_GMCH_GMS_STOLEN_64M	(0x7 << 4)
83
84
#define G33_GMCH_GMS_STOLEN_128M       (0x8 << 4)
#define G33_GMCH_GMS_STOLEN_256M       (0x9 << 4)
85
#define I915_IFPADDR    0x60
Linus Torvalds's avatar
Linus Torvalds committed
86

87
88
/* Intel 965G registers */
#define I965_MSAC 0x62
89
#define I965_IFPADDR    0x70
Linus Torvalds's avatar
Linus Torvalds committed
90
91
92
93
94
95
96
97
98
99

/* Intel 7505 registers */
#define INTEL_I7505_APSIZE	0x74
#define INTEL_I7505_NCAPID	0x60
#define INTEL_I7505_NISTAT	0x6c
#define INTEL_I7505_ATTBASE	0x78
#define INTEL_I7505_ERRSTS	0x42
#define INTEL_I7505_AGPCTRL	0x70
#define INTEL_I7505_MCHCFG	0x50

Dave Jones's avatar
Dave Jones committed
100
static const struct aper_size_info_fixed intel_i810_sizes[] =
Linus Torvalds's avatar
Linus Torvalds committed
101
102
103
104
105
106
107
108
{
	{64, 16384, 4},
	/* The 32M mode still requires a 64k gatt */
	{32, 8192, 4}
};

#define AGP_DCACHE_MEMORY	1
#define AGP_PHYS_MEMORY		2
109
#define INTEL_AGP_CACHED_MEMORY 3
Linus Torvalds's avatar
Linus Torvalds committed
110
111
112
113
114

static struct gatt_mask intel_i810_masks[] =
{
	{.mask = I810_PTE_VALID, .type = 0},
	{.mask = (I810_PTE_VALID | I810_PTE_LOCAL), .type = AGP_DCACHE_MEMORY},
115
116
117
	{.mask = I810_PTE_VALID, .type = 0},
	{.mask = I810_PTE_VALID | I830_PTE_SYSTEM_CACHED,
	 .type = INTEL_AGP_CACHED_MEMORY}
Linus Torvalds's avatar
Linus Torvalds committed
118
119
};

120
121
122
123
static struct _intel_private {
	struct pci_dev *pcidev;	/* device one */
	u8 __iomem *registers;
	u32 __iomem *gtt;		/* I915G */
Linus Torvalds's avatar
Linus Torvalds committed
124
	int num_dcache_entries;
125
126
127
128
129
130
	/* gtt_entries is the number of gtt entries that are already mapped
	 * to stolen memory.  Stolen memory is larger than the memory mapped
	 * through gtt_entries, as it includes some reserved space for the BIOS
	 * popup and for the GTT.
	 */
	int gtt_entries;			/* i830+ */
131
132
133
134
135
	union {
		void __iomem *i9xx_flush_page;
		void *i8xx_flush_page;
	};
	struct page *i8xx_page;
136
	struct resource ifp_resource;
137
	int resource_valid;
138
} intel_private;
Linus Torvalds's avatar
Linus Torvalds committed
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174

static int intel_i810_fetch_size(void)
{
	u32 smram_miscc;
	struct aper_size_info_fixed *values;

	pci_read_config_dword(agp_bridge->dev, I810_SMRAM_MISCC, &smram_miscc);
	values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes);

	if ((smram_miscc & I810_GMS) == I810_GMS_DISABLE) {
		printk(KERN_WARNING PFX "i810 is disabled\n");
		return 0;
	}
	if ((smram_miscc & I810_GFX_MEM_WIN_SIZE) == I810_GFX_MEM_WIN_32M) {
		agp_bridge->previous_size =
			agp_bridge->current_size = (void *) (values + 1);
		agp_bridge->aperture_size_idx = 1;
		return values[1].size;
	} else {
		agp_bridge->previous_size =
			agp_bridge->current_size = (void *) (values);
		agp_bridge->aperture_size_idx = 0;
		return values[0].size;
	}

	return 0;
}

static int intel_i810_configure(void)
{
	struct aper_size_info_fixed *current_size;
	u32 temp;
	int i;

	current_size = A_SIZE_FIX(agp_bridge->current_size);

175
176
	if (!intel_private.registers) {
		pci_read_config_dword(intel_private.pcidev, I810_MMADDR, &temp);
177
178
		temp &= 0xfff80000;

179
180
		intel_private.registers = ioremap(temp, 128 * 4096);
		if (!intel_private.registers) {
181
182
183
			printk(KERN_ERR PFX "Unable to remap memory.\n");
			return -ENOMEM;
		}
Linus Torvalds's avatar
Linus Torvalds committed
184
185
	}

186
	if ((readl(intel_private.registers+I810_DRAM_CTL)
Linus Torvalds's avatar
Linus Torvalds committed
187
188
189
		& I810_DRAM_ROW_0) == I810_DRAM_ROW_0_SDRAM) {
		/* This will need to be dynamically assigned */
		printk(KERN_INFO PFX "detected 4MB dedicated video ram.\n");
190
		intel_private.num_dcache_entries = 1024;
Linus Torvalds's avatar
Linus Torvalds committed
191
	}
192
	pci_read_config_dword(intel_private.pcidev, I810_GMADDR, &temp);
Linus Torvalds's avatar
Linus Torvalds committed
193
	agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
194
195
	writel(agp_bridge->gatt_bus_addr | I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL);
	readl(intel_private.registers+I810_PGETBL_CTL);	/* PCI Posting. */
Linus Torvalds's avatar
Linus Torvalds committed
196
197
198

	if (agp_bridge->driver->needs_scratch_page) {
		for (i = 0; i < current_size->num_entries; i++) {
199
200
			writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4));
			readl(intel_private.registers+I810_PTE_BASE+(i*4));	/* PCI posting. */
Linus Torvalds's avatar
Linus Torvalds committed
201
202
203
204
205
206
207
208
		}
	}
	global_cache_flush();
	return 0;
}

static void intel_i810_cleanup(void)
{
209
210
211
	writel(0, intel_private.registers+I810_PGETBL_CTL);
	readl(intel_private.registers);	/* PCI Posting. */
	iounmap(intel_private.registers);
Linus Torvalds's avatar
Linus Torvalds committed
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
}

static void intel_i810_tlbflush(struct agp_memory *mem)
{
	return;
}

static void intel_i810_agp_enable(struct agp_bridge_data *bridge, u32 mode)
{
	return;
}

/* Exists to support ARGB cursors */
static void *i8xx_alloc_pages(void)
{
227
	struct page *page;
Linus Torvalds's avatar
Linus Torvalds committed
228

229
	page = alloc_pages(GFP_KERNEL | GFP_DMA32, 2);
Linus Torvalds's avatar
Linus Torvalds committed
230
231
232
	if (page == NULL)
		return NULL;

233
234
	if (set_pages_uc(page, 4) < 0) {
		set_pages_wb(page, 4);
Jan Beulich's avatar
Jan Beulich committed
235
		__free_pages(page, 2);
Linus Torvalds's avatar
Linus Torvalds committed
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
		return NULL;
	}
	get_page(page);
	atomic_inc(&agp_bridge->current_memory_agp);
	return page_address(page);
}

static void i8xx_destroy_pages(void *addr)
{
	struct page *page;

	if (addr == NULL)
		return;

	page = virt_to_page(addr);
251
	set_pages_wb(page, 4);
Linus Torvalds's avatar
Linus Torvalds committed
252
	put_page(page);
Jan Beulich's avatar
Jan Beulich committed
253
	__free_pages(page, 2);
Linus Torvalds's avatar
Linus Torvalds committed
254
255
256
	atomic_dec(&agp_bridge->current_memory_agp);
}

257
258
259
260
261
262
263
264
265
266
267
static int intel_i830_type_to_mask_type(struct agp_bridge_data *bridge,
					int type)
{
	if (type < AGP_USER_TYPES)
		return type;
	else if (type == AGP_USER_CACHED_MEMORY)
		return INTEL_AGP_CACHED_MEMORY;
	else
		return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
268
269
270
271
272
static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start,
				int type)
{
	int i, j, num_entries;
	void *temp;
273
274
	int ret = -EINVAL;
	int mask_type;
Linus Torvalds's avatar
Linus Torvalds committed
275

276
	if (mem->page_count == 0)
277
		goto out;
278

Linus Torvalds's avatar
Linus Torvalds committed
279
280
281
	temp = agp_bridge->current_size;
	num_entries = A_SIZE_FIX(temp)->num_entries;

282
	if ((pg_start + mem->page_count) > num_entries)
283
		goto out_err;
284

Linus Torvalds's avatar
Linus Torvalds committed
285

286
287
288
289
	for (j = pg_start; j < (pg_start + mem->page_count); j++) {
		if (!PGE_EMPTY(agp_bridge, readl(agp_bridge->gatt_table+j))) {
			ret = -EBUSY;
			goto out_err;
Linus Torvalds's avatar
Linus Torvalds committed
290
291
292
		}
	}

293
294
	if (type != mem->type)
		goto out_err;
295

296
297
298
299
300
301
302
303
	mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type);

	switch (mask_type) {
	case AGP_DCACHE_MEMORY:
		if (!mem->is_flushed)
			global_cache_flush();
		for (i = pg_start; i < (pg_start + mem->page_count); i++) {
			writel((i*4096)|I810_PTE_LOCAL|I810_PTE_VALID,
304
			       intel_private.registers+I810_PTE_BASE+(i*4));
305
		}
306
		readl(intel_private.registers+I810_PTE_BASE+((i-1)*4));
307
308
309
310
311
312
313
314
315
		break;
	case AGP_PHYS_MEMORY:
	case AGP_NORMAL_MEMORY:
		if (!mem->is_flushed)
			global_cache_flush();
		for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
			writel(agp_bridge->driver->mask_memory(agp_bridge,
							       mem->memory[i],
							       mask_type),
316
			       intel_private.registers+I810_PTE_BASE+(j*4));
317
		}
318
		readl(intel_private.registers+I810_PTE_BASE+((j-1)*4));
319
320
321
		break;
	default:
		goto out_err;
Linus Torvalds's avatar
Linus Torvalds committed
322
323
324
	}

	agp_bridge->driver->tlb_flush(mem);
325
326
327
328
329
out:
	ret = 0;
out_err:
	mem->is_flushed = 1;
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
330
331
332
333
334
335
336
}

static int intel_i810_remove_entries(struct agp_memory *mem, off_t pg_start,
				int type)
{
	int i;

337
338
339
	if (mem->page_count == 0)
		return 0;

Linus Torvalds's avatar
Linus Torvalds committed
340
	for (i = pg_start; i < (mem->page_count + pg_start); i++) {
341
		writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4));
Linus Torvalds's avatar
Linus Torvalds committed
342
	}
343
	readl(intel_private.registers+I810_PTE_BASE+((i-1)*4));
Linus Torvalds's avatar
Linus Torvalds committed
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376

	agp_bridge->driver->tlb_flush(mem);
	return 0;
}

/*
 * The i810/i830 requires a physical address to program its mouse
 * pointer into hardware.
 * However the Xserver still writes to it through the agp aperture.
 */
static struct agp_memory *alloc_agpphysmem_i8xx(size_t pg_count, int type)
{
	struct agp_memory *new;
	void *addr;

	switch (pg_count) {
	case 1: addr = agp_bridge->driver->agp_alloc_page(agp_bridge);
		break;
	case 4:
		/* kludge to get 4 physical pages for ARGB cursor */
		addr = i8xx_alloc_pages();
		break;
	default:
		return NULL;
	}

	if (addr == NULL)
		return NULL;

	new = agp_create_memory(pg_count);
	if (new == NULL)
		return NULL;

Keir Fraser's avatar
Keir Fraser committed
377
	new->memory[0] = virt_to_gart(addr);
Linus Torvalds's avatar
Linus Torvalds committed
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
	if (pg_count == 4) {
		/* kludge to get 4 physical pages for ARGB cursor */
		new->memory[1] = new->memory[0] + PAGE_SIZE;
		new->memory[2] = new->memory[1] + PAGE_SIZE;
		new->memory[3] = new->memory[2] + PAGE_SIZE;
	}
	new->page_count = pg_count;
	new->num_scratch_pages = pg_count;
	new->type = AGP_PHYS_MEMORY;
	new->physical = new->memory[0];
	return new;
}

static struct agp_memory *intel_i810_alloc_by_type(size_t pg_count, int type)
{
	struct agp_memory *new;

	if (type == AGP_DCACHE_MEMORY) {
396
		if (pg_count != intel_private.num_dcache_entries)
Linus Torvalds's avatar
Linus Torvalds committed
397
398
399
400
401
402
403
404
405
			return NULL;

		new = agp_create_memory(1);
		if (new == NULL)
			return NULL;

		new->type = AGP_DCACHE_MEMORY;
		new->page_count = pg_count;
		new->num_scratch_pages = 0;
406
		agp_free_page_array(new);
Linus Torvalds's avatar
Linus Torvalds committed
407
408
409
410
411
412
413
414
415
416
		return new;
	}
	if (type == AGP_PHYS_MEMORY)
		return alloc_agpphysmem_i8xx(pg_count, type);
	return NULL;
}

static void intel_i810_free_by_type(struct agp_memory *curr)
{
	agp_free_key(curr->key);
417
	if (curr->type == AGP_PHYS_MEMORY) {
Linus Torvalds's avatar
Linus Torvalds committed
418
		if (curr->page_count == 4)
Keir Fraser's avatar
Keir Fraser committed
419
			i8xx_destroy_pages(gart_to_virt(curr->memory[0]));
Alan Hourihane's avatar
Alan Hourihane committed
420
		else {
421
422
423
			void *va = gart_to_virt(curr->memory[0]);

			agp_bridge->driver->agp_destroy_page(va,
424
							     AGP_PAGE_DESTROY_UNMAP);
425
			agp_bridge->driver->agp_destroy_page(va,
426
							     AGP_PAGE_DESTROY_FREE);
Alan Hourihane's avatar
Alan Hourihane committed
427
		}
428
		agp_free_page_array(curr);
Linus Torvalds's avatar
Linus Torvalds committed
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
	}
	kfree(curr);
}

static unsigned long intel_i810_mask_memory(struct agp_bridge_data *bridge,
	unsigned long addr, int type)
{
	/* Type checking must be done elsewhere */
	return addr | bridge->driver->masks[type].mask;
}

static struct aper_size_info_fixed intel_i830_sizes[] =
{
	{128, 32768, 5},
	/* The 64M mode still requires a 128k gatt */
	{64, 16384, 5},
	{256, 65536, 6},
446
	{512, 131072, 7},
Linus Torvalds's avatar
Linus Torvalds committed
447
448
449
450
451
452
453
454
455
};

static void intel_i830_init_gtt_entries(void)
{
	u16 gmch_ctrl;
	int gtt_entries;
	u8 rdct;
	int local = 0;
	static const int ddt[4] = { 0, 16, 32, 64 };
456
	int size; /* reserved space (in kb) at the top of stolen memory */
Linus Torvalds's avatar
Linus Torvalds committed
457

458
	pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl);
Linus Torvalds's avatar
Linus Torvalds committed
459

460
461
	if (IS_I965) {
		u32 pgetbl_ctl;
462
		pgetbl_ctl = readl(intel_private.registers+I810_PGETBL_CTL);
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477

		/* The 965 has a field telling us the size of the GTT,
		 * which may be larger than what is necessary to map the
		 * aperture.
		 */
		switch (pgetbl_ctl & I965_PGETBL_SIZE_MASK) {
		case I965_PGETBL_SIZE_128KB:
			size = 128;
			break;
		case I965_PGETBL_SIZE_256KB:
			size = 256;
			break;
		case I965_PGETBL_SIZE_512KB:
			size = 512;
			break;
Zhenyu Wang's avatar
Zhenyu Wang committed
478
479
480
481
482
483
484
485
486
		case I965_PGETBL_SIZE_1MB:
			size = 1024;
			break;
		case I965_PGETBL_SIZE_2MB:
			size = 2048;
			break;
		case I965_PGETBL_SIZE_1_5MB:
			size = 1024 + 512;
			break;
487
488
489
490
491
492
		default:
			printk(KERN_INFO PFX "Unknown page table size, "
			       "assuming 512KB\n");
			size = 512;
		}
		size += 4; /* add in BIOS popup space */
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
	} else if (IS_G33) {
	/* G33's GTT size defined in gmch_ctrl */
		switch (gmch_ctrl & G33_PGETBL_SIZE_MASK) {
		case G33_PGETBL_SIZE_1M:
			size = 1024;
			break;
		case G33_PGETBL_SIZE_2M:
			size = 2048;
			break;
		default:
			printk(KERN_INFO PFX "Unknown page table size 0x%x, "
				"assuming 512KB\n",
				(gmch_ctrl & G33_PGETBL_SIZE_MASK));
			size = 512;
		}
		size += 4;
509
510
511
512
513
514
	} else {
		/* On previous hardware, the GTT size was just what was
		 * required to map the aperture.
		 */
		size = agp_bridge->driver->fetch_size() + 4;
	}
Linus Torvalds's avatar
Linus Torvalds committed
515
516
517
518
519
520
521
522
523
524
525
526
527
528

	if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82830_HB ||
	    agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82845G_HB) {
		switch (gmch_ctrl & I830_GMCH_GMS_MASK) {
		case I830_GMCH_GMS_STOLEN_512:
			gtt_entries = KB(512) - KB(size);
			break;
		case I830_GMCH_GMS_STOLEN_1024:
			gtt_entries = MB(1) - KB(size);
			break;
		case I830_GMCH_GMS_STOLEN_8192:
			gtt_entries = MB(8) - KB(size);
			break;
		case I830_GMCH_GMS_LOCAL:
529
			rdct = readb(intel_private.registers+I830_RDRAM_CHANNEL_TYPE);
Linus Torvalds's avatar
Linus Torvalds committed
530
531
532
533
534
535
536
537
538
			gtt_entries = (I830_RDRAM_ND(rdct) + 1) *
					MB(ddt[I830_RDRAM_DDT(rdct)]);
			local = 1;
			break;
		default:
			gtt_entries = 0;
			break;
		}
	} else {
539
		switch (gmch_ctrl & I855_GMCH_GMS_MASK) {
Linus Torvalds's avatar
Linus Torvalds committed
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
		case I855_GMCH_GMS_STOLEN_1M:
			gtt_entries = MB(1) - KB(size);
			break;
		case I855_GMCH_GMS_STOLEN_4M:
			gtt_entries = MB(4) - KB(size);
			break;
		case I855_GMCH_GMS_STOLEN_8M:
			gtt_entries = MB(8) - KB(size);
			break;
		case I855_GMCH_GMS_STOLEN_16M:
			gtt_entries = MB(16) - KB(size);
			break;
		case I855_GMCH_GMS_STOLEN_32M:
			gtt_entries = MB(32) - KB(size);
			break;
		case I915_GMCH_GMS_STOLEN_48M:
			/* Check it's really I915G */
557
			if (IS_I915 || IS_I965 || IS_G33)
Linus Torvalds's avatar
Linus Torvalds committed
558
559
560
561
562
563
				gtt_entries = MB(48) - KB(size);
			else
				gtt_entries = 0;
			break;
		case I915_GMCH_GMS_STOLEN_64M:
			/* Check it's really I915G */
564
			if (IS_I915 || IS_I965 || IS_G33)
Linus Torvalds's avatar
Linus Torvalds committed
565
566
567
				gtt_entries = MB(64) - KB(size);
			else
				gtt_entries = 0;
568
569
570
571
572
573
574
575
576
577
578
579
580
			break;
		case G33_GMCH_GMS_STOLEN_128M:
			if (IS_G33)
				gtt_entries = MB(128) - KB(size);
			else
				gtt_entries = 0;
			break;
		case G33_GMCH_GMS_STOLEN_256M:
			if (IS_G33)
				gtt_entries = MB(256) - KB(size);
			else
				gtt_entries = 0;
			break;
Linus Torvalds's avatar
Linus Torvalds committed
581
582
583
584
585
586
587
588
589
590
591
592
593
		default:
			gtt_entries = 0;
			break;
		}
	}
	if (gtt_entries > 0)
		printk(KERN_INFO PFX "Detected %dK %s memory.\n",
		       gtt_entries / KB(1), local ? "local" : "stolen");
	else
		printk(KERN_INFO PFX
		       "No pre-allocated video memory detected.\n");
	gtt_entries /= KB(4);

594
	intel_private.gtt_entries = gtt_entries;
Linus Torvalds's avatar
Linus Torvalds committed
595
596
}

597
598
599
600
601
602
603
static void intel_i830_fini_flush(void)
{
	kunmap(intel_private.i8xx_page);
	intel_private.i8xx_flush_page = NULL;
	unmap_page_from_agp(intel_private.i8xx_page);

	__free_page(intel_private.i8xx_page);
604
	intel_private.i8xx_page = NULL;
605
606
607
608
}

static void intel_i830_setup_flush(void)
{
609
610
611
	/* return if we've already set the flush mechanism up */
	if (intel_private.i8xx_page)
		return;
612
613

	intel_private.i8xx_page = alloc_page(GFP_KERNEL | __GFP_ZERO | GFP_DMA32);
614
	if (!intel_private.i8xx_page)
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
		return;

	/* make page uncached */
	map_page_into_agp(intel_private.i8xx_page);

	intel_private.i8xx_flush_page = kmap(intel_private.i8xx_page);
	if (!intel_private.i8xx_flush_page)
		intel_i830_fini_flush();
}

static void intel_i830_chipset_flush(struct agp_bridge_data *bridge)
{
	unsigned int *pg = intel_private.i8xx_flush_page;
	int i;

630
	for (i = 0; i < 256; i += 2)
631
		*(pg + i) = i;
632

633
634
635
	wmb();
}

Linus Torvalds's avatar
Linus Torvalds committed
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
/* The intel i830 automatically initializes the agp aperture during POST.
 * Use the memory already set aside for in the GTT.
 */
static int intel_i830_create_gatt_table(struct agp_bridge_data *bridge)
{
	int page_order;
	struct aper_size_info_fixed *size;
	int num_entries;
	u32 temp;

	size = agp_bridge->current_size;
	page_order = size->page_order;
	num_entries = size->num_entries;
	agp_bridge->gatt_table_real = NULL;

651
	pci_read_config_dword(intel_private.pcidev, I810_MMADDR, &temp);
Linus Torvalds's avatar
Linus Torvalds committed
652
653
	temp &= 0xfff80000;

654
	intel_private.registers = ioremap(temp, 128 * 4096);
655
	if (!intel_private.registers)
Linus Torvalds's avatar
Linus Torvalds committed
656
657
		return -ENOMEM;

658
	temp = readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000;
Linus Torvalds's avatar
Linus Torvalds committed
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
	global_cache_flush();	/* FIXME: ?? */

	/* we have to call this as early as possible after the MMIO base address is known */
	intel_i830_init_gtt_entries();

	agp_bridge->gatt_table = NULL;

	agp_bridge->gatt_bus_addr = temp;

	return 0;
}

/* Return the gatt table to a sane state. Use the top of stolen
 * memory for the GTT.
 */
static int intel_i830_free_gatt_table(struct agp_bridge_data *bridge)
{
	return 0;
}

static int intel_i830_fetch_size(void)
{
	u16 gmch_ctrl;
	struct aper_size_info_fixed *values;

	values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes);

	if (agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82830_HB &&
	    agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82845G_HB) {
		/* 855GM/852GM/865G has 128MB aperture size */
		agp_bridge->previous_size = agp_bridge->current_size = (void *) values;
		agp_bridge->aperture_size_idx = 0;
		return values[0].size;
	}

694
	pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl);
Linus Torvalds's avatar
Linus Torvalds committed
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717

	if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_128M) {
		agp_bridge->previous_size = agp_bridge->current_size = (void *) values;
		agp_bridge->aperture_size_idx = 0;
		return values[0].size;
	} else {
		agp_bridge->previous_size = agp_bridge->current_size = (void *) (values + 1);
		agp_bridge->aperture_size_idx = 1;
		return values[1].size;
	}

	return 0;
}

static int intel_i830_configure(void)
{
	struct aper_size_info_fixed *current_size;
	u32 temp;
	u16 gmch_ctrl;
	int i;

	current_size = A_SIZE_FIX(agp_bridge->current_size);

718
	pci_read_config_dword(intel_private.pcidev, I810_GMADDR, &temp);
Linus Torvalds's avatar
Linus Torvalds committed
719
720
	agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);

721
	pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl);
Linus Torvalds's avatar
Linus Torvalds committed
722
	gmch_ctrl |= I830_GMCH_ENABLED;
723
	pci_write_config_word(agp_bridge->dev, I830_GMCH_CTRL, gmch_ctrl);
Linus Torvalds's avatar
Linus Torvalds committed
724

725
726
	writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL);
	readl(intel_private.registers+I810_PGETBL_CTL);	/* PCI Posting. */
Linus Torvalds's avatar
Linus Torvalds committed
727
728

	if (agp_bridge->driver->needs_scratch_page) {
729
730
731
		for (i = intel_private.gtt_entries; i < current_size->num_entries; i++) {
			writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4));
			readl(intel_private.registers+I810_PTE_BASE+(i*4));	/* PCI Posting. */
Linus Torvalds's avatar
Linus Torvalds committed
732
733
734
735
		}
	}

	global_cache_flush();
736
737

	intel_i830_setup_flush();
Linus Torvalds's avatar
Linus Torvalds committed
738
739
740
741
742
	return 0;
}

static void intel_i830_cleanup(void)
{
743
	iounmap(intel_private.registers);
Linus Torvalds's avatar
Linus Torvalds committed
744
745
}

746
747
static int intel_i830_insert_entries(struct agp_memory *mem, off_t pg_start,
				     int type)
Linus Torvalds's avatar
Linus Torvalds committed
748
{
749
	int i, j, num_entries;
Linus Torvalds's avatar
Linus Torvalds committed
750
	void *temp;
751
752
	int ret = -EINVAL;
	int mask_type;
Linus Torvalds's avatar
Linus Torvalds committed
753

754
	if (mem->page_count == 0)
755
		goto out;
756

Linus Torvalds's avatar
Linus Torvalds committed
757
758
759
	temp = agp_bridge->current_size;
	num_entries = A_SIZE_FIX(temp)->num_entries;

760
	if (pg_start < intel_private.gtt_entries) {
761
762
		printk(KERN_DEBUG PFX "pg_start == 0x%.8lx,intel_private.gtt_entries == 0x%.8x\n",
				pg_start, intel_private.gtt_entries);
Linus Torvalds's avatar
Linus Torvalds committed
763

764
		printk(KERN_INFO PFX "Trying to insert into local/stolen memory\n");
765
		goto out_err;
Linus Torvalds's avatar
Linus Torvalds committed
766
767
768
	}

	if ((pg_start + mem->page_count) > num_entries)
769
		goto out_err;
Linus Torvalds's avatar
Linus Torvalds committed
770
771
772
773
774

	/* The i830 can't check the GTT for entries since its read only,
	 * depend on the caller to make the correct offset decisions.
	 */

775
776
777
778
	if (type != mem->type)
		goto out_err;

	mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type);
Linus Torvalds's avatar
Linus Torvalds committed
779

780
781
782
783
784
	if (mask_type != 0 && mask_type != AGP_PHYS_MEMORY &&
	    mask_type != INTEL_AGP_CACHED_MEMORY)
		goto out_err;

	if (!mem->is_flushed)
785
		global_cache_flush();
Linus Torvalds's avatar
Linus Torvalds committed
786
787
788

	for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
		writel(agp_bridge->driver->mask_memory(agp_bridge,
789
						       mem->memory[i], mask_type),
790
		       intel_private.registers+I810_PTE_BASE+(j*4));
Linus Torvalds's avatar
Linus Torvalds committed
791
	}
792
	readl(intel_private.registers+I810_PTE_BASE+((j-1)*4));
Linus Torvalds's avatar
Linus Torvalds committed
793
	agp_bridge->driver->tlb_flush(mem);
794
795
796
797
798
799

out:
	ret = 0;
out_err:
	mem->is_flushed = 1;
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
800
801
}

802
803
static int intel_i830_remove_entries(struct agp_memory *mem, off_t pg_start,
				     int type)
Linus Torvalds's avatar
Linus Torvalds committed
804
805
806
{
	int i;

807
808
	if (mem->page_count == 0)
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
809

810
	if (pg_start < intel_private.gtt_entries) {
811
		printk(KERN_INFO PFX "Trying to disable local/stolen memory\n");
Linus Torvalds's avatar
Linus Torvalds committed
812
813
814
815
		return -EINVAL;
	}

	for (i = pg_start; i < (mem->page_count + pg_start); i++) {
816
		writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4));
Linus Torvalds's avatar
Linus Torvalds committed
817
	}
818
	readl(intel_private.registers+I810_PTE_BASE+((i-1)*4));
Linus Torvalds's avatar
Linus Torvalds committed
819
820
821
822
823

	agp_bridge->driver->tlb_flush(mem);
	return 0;
}

824
static struct agp_memory *intel_i830_alloc_by_type(size_t pg_count, int type)
Linus Torvalds's avatar
Linus Torvalds committed
825
826
827
828
829
830
831
{
	if (type == AGP_PHYS_MEMORY)
		return alloc_agpphysmem_i8xx(pg_count, type);
	/* always return NULL for other allocation types for now */
	return NULL;
}

832
833
834
835
836
837
838
static int intel_alloc_chipset_flush_resource(void)
{
	int ret;
	ret = pci_bus_alloc_resource(agp_bridge->dev->bus, &intel_private.ifp_resource, PAGE_SIZE,
				     PAGE_SIZE, PCIBIOS_MIN_MEM, 0,
				     pcibios_align_resource, agp_bridge->dev);

839
	return ret;
840
841
842
843
844
845
846
847
848
849
}

static void intel_i915_setup_chipset_flush(void)
{
	int ret;
	u32 temp;

	pci_read_config_dword(agp_bridge->dev, I915_IFPADDR, &temp);
	if (!(temp & 0x1)) {
		intel_alloc_chipset_flush_resource();
850
		intel_private.resource_valid = 1;
851
852
853
854
		pci_write_config_dword(agp_bridge->dev, I915_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1);
	} else {
		temp &= ~1;

855
		intel_private.resource_valid = 1;
856
857
858
		intel_private.ifp_resource.start = temp;
		intel_private.ifp_resource.end = temp + PAGE_SIZE;
		ret = request_resource(&iomem_resource, &intel_private.ifp_resource);
859
860
861
		/* some BIOSes reserve this area in a pnp some don't */
		if (ret)
			intel_private.resource_valid = 0;
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
	}
}

static void intel_i965_g33_setup_chipset_flush(void)
{
	u32 temp_hi, temp_lo;
	int ret;

	pci_read_config_dword(agp_bridge->dev, I965_IFPADDR + 4, &temp_hi);
	pci_read_config_dword(agp_bridge->dev, I965_IFPADDR, &temp_lo);

	if (!(temp_lo & 0x1)) {

		intel_alloc_chipset_flush_resource();

877
		intel_private.resource_valid = 1;
Andrew Morton's avatar
Andrew Morton committed
878
879
		pci_write_config_dword(agp_bridge->dev, I965_IFPADDR + 4,
			upper_32_bits(intel_private.ifp_resource.start));
880
881
882
		pci_write_config_dword(agp_bridge->dev, I965_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1);
	} else {
		u64 l64;
883

884
885
886
		temp_lo &= ~0x1;
		l64 = ((u64)temp_hi << 32) | temp_lo;

887
		intel_private.resource_valid = 1;
888
889
890
		intel_private.ifp_resource.start = l64;
		intel_private.ifp_resource.end = l64 + PAGE_SIZE;
		ret = request_resource(&iomem_resource, &intel_private.ifp_resource);
891
892
893
		/* some BIOSes reserve this area in a pnp some don't */
		if (ret)
			intel_private.resource_valid = 0;
894
895
896
	}
}

897
898
static void intel_i9xx_setup_flush(void)
{
899
900
901
	/* return if already configured */
	if (intel_private.ifp_resource.start)
		return;
902

903
	/* setup a resource for this object */
904
905
906
907
908
909
910
911
912
913
914
915
916
	intel_private.ifp_resource.name = "Intel Flush Page";
	intel_private.ifp_resource.flags = IORESOURCE_MEM;

	/* Setup chipset flush for 915 */
	if (IS_I965 || IS_G33) {
		intel_i965_g33_setup_chipset_flush();
	} else {
		intel_i915_setup_chipset_flush();
	}

	if (intel_private.ifp_resource.start) {
		intel_private.i9xx_flush_page = ioremap_nocache(intel_private.ifp_resource.start, PAGE_SIZE);
		if (!intel_private.i9xx_flush_page)
917
			printk(KERN_INFO "unable to ioremap flush  page - no chipset flushing");
918
919
920
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
921
922
923
924
925
926
927
928
929
static int intel_i915_configure(void)
{
	struct aper_size_info_fixed *current_size;
	u32 temp;
	u16 gmch_ctrl;
	int i;

	current_size = A_SIZE_FIX(agp_bridge->current_size);

930
	pci_read_config_dword(intel_private.pcidev, I915_GMADDR, &temp);
Linus Torvalds's avatar
Linus Torvalds committed
931
932
933

	agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);

934
	pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl);
Linus Torvalds's avatar
Linus Torvalds committed
935
	gmch_ctrl |= I830_GMCH_ENABLED;
936
	pci_write_config_word(agp_bridge->dev, I830_GMCH_CTRL, gmch_ctrl);
Linus Torvalds's avatar
Linus Torvalds committed
937

938
939
	writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL);
	readl(intel_private.registers+I810_PGETBL_CTL);	/* PCI Posting. */
Linus Torvalds's avatar
Linus Torvalds committed
940
941

	if (agp_bridge->driver->needs_scratch_page) {
942
943
944
		for (i = intel_private.gtt_entries; i < current_size->num_entries; i++) {
			writel(agp_bridge->scratch_page, intel_private.gtt+i);
			readl(intel_private.gtt+i);	/* PCI Posting. */
Linus Torvalds's avatar
Linus Torvalds committed
945
946
947
948
		}
	}

	global_cache_flush();
949

950
	intel_i9xx_setup_flush();
951

Linus Torvalds's avatar
Linus Torvalds committed
952
953
954
955
956
	return 0;
}

static void intel_i915_cleanup(void)
{
957
958
	if (intel_private.i9xx_flush_page)
		iounmap(intel_private.i9xx_flush_page);
959
960
961
962
	if (intel_private.resource_valid)
		release_resource(&intel_private.ifp_resource);
	intel_private.ifp_resource.start = 0;
	intel_private.resource_valid = 0;
963
964
	iounmap(intel_private.gtt);
	iounmap(intel_private.registers);
Linus Torvalds's avatar
Linus Torvalds committed
965
966
}

967
968
static void intel_i915_chipset_flush(struct agp_bridge_data *bridge)
{
969
970
	if (intel_private.i9xx_flush_page)
		writel(1, intel_private.i9xx_flush_page);
971
972
}

973
974
static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start,
				     int type)
Linus Torvalds's avatar
Linus Torvalds committed
975
{
976
	int i, j, num_entries;
Linus Torvalds's avatar
Linus Torvalds committed
977
	void *temp;
978
979
	int ret = -EINVAL;
	int mask_type;
Linus Torvalds's avatar
Linus Torvalds committed
980

981
	if (mem->page_count == 0)
982
		goto out;
983

Linus Torvalds's avatar
Linus Torvalds committed
984
985
986
	temp = agp_bridge->current_size;
	num_entries = A_SIZE_FIX(temp)->num_entries;

987
	if (pg_start < intel_private.gtt_entries) {
988
989
		printk(KERN_DEBUG PFX "pg_start == 0x%.8lx,intel_private.gtt_entries == 0x%.8x\n",
				pg_start, intel_private.gtt_entries);
Linus Torvalds's avatar
Linus Torvalds committed
990

991
		printk(KERN_INFO PFX "Trying to insert into local/stolen memory\n");
992
		goto out_err;
Linus Torvalds's avatar
Linus Torvalds committed
993
994
995
	}

	if ((pg_start + mem->page_count) > num_entries)
996
		goto out_err;
Linus Torvalds's avatar
Linus Torvalds committed
997

998
	/* The i915 can't check the GTT for entries since its read only,
Linus Torvalds's avatar
Linus Torvalds committed
999
1000
	 * depend on the caller to make the correct offset decisions.
	 */