diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/agp/i460-agp.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/agp/i460-agp.c')
-rw-r--r-- | drivers/char/agp/i460-agp.c | 642 |
1 files changed, 642 insertions, 0 deletions
diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c new file mode 100644 index 000000000000..adbea896c0d2 --- /dev/null +++ b/drivers/char/agp/i460-agp.c | |||
@@ -0,0 +1,642 @@ | |||
1 | /* | ||
2 | * For documentation on the i460 AGP interface, see Chapter 7 (AGP Subsystem) of | ||
3 | * the "Intel 460GTX Chipset Software Developer's Manual": | ||
4 | * http://developer.intel.com/design/itanium/downloads/24870401s.htm | ||
5 | */ | ||
6 | /* | ||
7 | * 460GX support by Chris Ahna <christopher.j.ahna@intel.com> | ||
8 | * Clean up & simplification by David Mosberger-Tang <davidm@hpl.hp.com> | ||
9 | */ | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/pci.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/agp_backend.h> | ||
14 | |||
15 | #include "agp.h" | ||
16 | |||
17 | #define INTEL_I460_BAPBASE 0x98 | ||
18 | #define INTEL_I460_GXBCTL 0xa0 | ||
19 | #define INTEL_I460_AGPSIZ 0xa2 | ||
20 | #define INTEL_I460_ATTBASE 0xfe200000 | ||
21 | #define INTEL_I460_GATT_VALID (1UL << 24) | ||
22 | #define INTEL_I460_GATT_COHERENT (1UL << 25) | ||
23 | |||
24 | /* | ||
25 | * The i460 can operate with large (4MB) pages, but there is no sane way to support this | ||
26 | * within the current kernel/DRM environment, so we disable the relevant code for now. | ||
27 | * See also comments in ia64_alloc_page()... | ||
28 | */ | ||
29 | #define I460_LARGE_IO_PAGES 0 | ||
30 | |||
31 | #if I460_LARGE_IO_PAGES | ||
32 | # define I460_IO_PAGE_SHIFT i460.io_page_shift | ||
33 | #else | ||
34 | # define I460_IO_PAGE_SHIFT 12 | ||
35 | #endif | ||
36 | |||
37 | #define I460_IOPAGES_PER_KPAGE (PAGE_SIZE >> I460_IO_PAGE_SHIFT) | ||
38 | #define I460_KPAGES_PER_IOPAGE (1 << (I460_IO_PAGE_SHIFT - PAGE_SHIFT)) | ||
39 | #define I460_SRAM_IO_DISABLE (1 << 4) | ||
40 | #define I460_BAPBASE_ENABLE (1 << 3) | ||
41 | #define I460_AGPSIZ_MASK 0x7 | ||
42 | #define I460_4M_PS (1 << 1) | ||
43 | |||
44 | /* Control bits for Out-Of-GART coherency and Burst Write Combining */ | ||
45 | #define I460_GXBCTL_OOG (1UL << 0) | ||
46 | #define I460_GXBCTL_BWC (1UL << 2) | ||
47 | |||
48 | /* | ||
49 | * gatt_table entries are 32-bits wide on the i460; the generic code ought to declare the | ||
50 | * gatt_table and gatt_table_real pointers a "void *"... | ||
51 | */ | ||
52 | #define RD_GATT(index) readl((u32 *) i460.gatt + (index)) | ||
53 | #define WR_GATT(index, val) writel((val), (u32 *) i460.gatt + (index)) | ||
54 | /* | ||
55 | * The 460 spec says we have to read the last location written to make sure that all | ||
56 | * writes have taken effect | ||
57 | */ | ||
58 | #define WR_FLUSH_GATT(index) RD_GATT(index) | ||
59 | |||
60 | #define log2(x) ffz(~(x)) | ||
61 | |||
62 | static struct { | ||
63 | void *gatt; /* ioremap'd GATT area */ | ||
64 | |||
65 | /* i460 supports multiple GART page sizes, so GART pageshift is dynamic: */ | ||
66 | u8 io_page_shift; | ||
67 | |||
68 | /* BIOS configures chipset to one of 2 possible apbase values: */ | ||
69 | u8 dynamic_apbase; | ||
70 | |||
71 | /* structure for tracking partial use of 4MB GART pages: */ | ||
72 | struct lp_desc { | ||
73 | unsigned long *alloced_map; /* bitmap of kernel-pages in use */ | ||
74 | int refcount; /* number of kernel pages using the large page */ | ||
75 | u64 paddr; /* physical address of large page */ | ||
76 | } *lp_desc; | ||
77 | } i460; | ||
78 | |||
79 | static struct aper_size_info_8 i460_sizes[3] = | ||
80 | { | ||
81 | /* | ||
82 | * The 32GB aperture is only available with a 4M GART page size. Due to the | ||
83 | * dynamic GART page size, we can't figure out page_order or num_entries until | ||
84 | * runtime. | ||
85 | */ | ||
86 | {32768, 0, 0, 4}, | ||
87 | {1024, 0, 0, 2}, | ||
88 | {256, 0, 0, 1} | ||
89 | }; | ||
90 | |||
91 | static struct gatt_mask i460_masks[] = | ||
92 | { | ||
93 | { | ||
94 | .mask = INTEL_I460_GATT_VALID | INTEL_I460_GATT_COHERENT, | ||
95 | .type = 0 | ||
96 | } | ||
97 | }; | ||
98 | |||
99 | static int i460_fetch_size (void) | ||
100 | { | ||
101 | int i; | ||
102 | u8 temp; | ||
103 | struct aper_size_info_8 *values; | ||
104 | |||
105 | /* Determine the GART page size */ | ||
106 | pci_read_config_byte(agp_bridge->dev, INTEL_I460_GXBCTL, &temp); | ||
107 | i460.io_page_shift = (temp & I460_4M_PS) ? 22 : 12; | ||
108 | pr_debug("i460_fetch_size: io_page_shift=%d\n", i460.io_page_shift); | ||
109 | |||
110 | if (i460.io_page_shift != I460_IO_PAGE_SHIFT) { | ||
111 | printk(KERN_ERR PFX | ||
112 | "I/O (GART) page-size %ZuKB doesn't match expected size %ZuKB\n", | ||
113 | 1UL << (i460.io_page_shift - 10), 1UL << (I460_IO_PAGE_SHIFT)); | ||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | values = A_SIZE_8(agp_bridge->driver->aperture_sizes); | ||
118 | |||
119 | pci_read_config_byte(agp_bridge->dev, INTEL_I460_AGPSIZ, &temp); | ||
120 | |||
121 | /* Exit now if the IO drivers for the GART SRAMS are turned off */ | ||
122 | if (temp & I460_SRAM_IO_DISABLE) { | ||
123 | printk(KERN_ERR PFX "GART SRAMS disabled on 460GX chipset\n"); | ||
124 | printk(KERN_ERR PFX "AGPGART operation not possible\n"); | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | /* Make sure we don't try to create an 2 ^ 23 entry GATT */ | ||
129 | if ((i460.io_page_shift == 0) && ((temp & I460_AGPSIZ_MASK) == 4)) { | ||
130 | printk(KERN_ERR PFX "We can't have a 32GB aperture with 4KB GART pages\n"); | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | /* Determine the proper APBASE register */ | ||
135 | if (temp & I460_BAPBASE_ENABLE) | ||
136 | i460.dynamic_apbase = INTEL_I460_BAPBASE; | ||
137 | else | ||
138 | i460.dynamic_apbase = AGP_APBASE; | ||
139 | |||
140 | for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { | ||
141 | /* | ||
142 | * Dynamically calculate the proper num_entries and page_order values for | ||
143 | * the define aperture sizes. Take care not to shift off the end of | ||
144 | * values[i].size. | ||
145 | */ | ||
146 | values[i].num_entries = (values[i].size << 8) >> (I460_IO_PAGE_SHIFT - 12); | ||
147 | values[i].page_order = log2((sizeof(u32)*values[i].num_entries) >> PAGE_SHIFT); | ||
148 | } | ||
149 | |||
150 | for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { | ||
151 | /* Neglect control bits when matching up size_value */ | ||
152 | if ((temp & I460_AGPSIZ_MASK) == values[i].size_value) { | ||
153 | agp_bridge->previous_size = agp_bridge->current_size = (void *) (values + i); | ||
154 | agp_bridge->aperture_size_idx = i; | ||
155 | return values[i].size; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | /* There isn't anything to do here since 460 has no GART TLB. */ | ||
163 | static void i460_tlb_flush (struct agp_memory *mem) | ||
164 | { | ||
165 | return; | ||
166 | } | ||
167 | |||
168 | /* | ||
169 | * This utility function is needed to prevent corruption of the control bits | ||
170 | * which are stored along with the aperture size in 460's AGPSIZ register | ||
171 | */ | ||
172 | static void i460_write_agpsiz (u8 size_value) | ||
173 | { | ||
174 | u8 temp; | ||
175 | |||
176 | pci_read_config_byte(agp_bridge->dev, INTEL_I460_AGPSIZ, &temp); | ||
177 | pci_write_config_byte(agp_bridge->dev, INTEL_I460_AGPSIZ, | ||
178 | ((temp & ~I460_AGPSIZ_MASK) | size_value)); | ||
179 | } | ||
180 | |||
181 | static void i460_cleanup (void) | ||
182 | { | ||
183 | struct aper_size_info_8 *previous_size; | ||
184 | |||
185 | previous_size = A_SIZE_8(agp_bridge->previous_size); | ||
186 | i460_write_agpsiz(previous_size->size_value); | ||
187 | |||
188 | if (I460_IO_PAGE_SHIFT > PAGE_SHIFT) | ||
189 | kfree(i460.lp_desc); | ||
190 | } | ||
191 | |||
192 | static int i460_configure (void) | ||
193 | { | ||
194 | union { | ||
195 | u32 small[2]; | ||
196 | u64 large; | ||
197 | } temp; | ||
198 | size_t size; | ||
199 | u8 scratch; | ||
200 | struct aper_size_info_8 *current_size; | ||
201 | |||
202 | temp.large = 0; | ||
203 | |||
204 | current_size = A_SIZE_8(agp_bridge->current_size); | ||
205 | i460_write_agpsiz(current_size->size_value); | ||
206 | |||
207 | /* | ||
208 | * Do the necessary rigmarole to read all eight bytes of APBASE. | ||
209 | * This has to be done since the AGP aperture can be above 4GB on | ||
210 | * 460 based systems. | ||
211 | */ | ||
212 | pci_read_config_dword(agp_bridge->dev, i460.dynamic_apbase, &(temp.small[0])); | ||
213 | pci_read_config_dword(agp_bridge->dev, i460.dynamic_apbase + 4, &(temp.small[1])); | ||
214 | |||
215 | /* Clear BAR control bits */ | ||
216 | agp_bridge->gart_bus_addr = temp.large & ~((1UL << 3) - 1); | ||
217 | |||
218 | pci_read_config_byte(agp_bridge->dev, INTEL_I460_GXBCTL, &scratch); | ||
219 | pci_write_config_byte(agp_bridge->dev, INTEL_I460_GXBCTL, | ||
220 | (scratch & 0x02) | I460_GXBCTL_OOG | I460_GXBCTL_BWC); | ||
221 | |||
222 | /* | ||
223 | * Initialize partial allocation trackers if a GART page is bigger than a kernel | ||
224 | * page. | ||
225 | */ | ||
226 | if (I460_IO_PAGE_SHIFT > PAGE_SHIFT) { | ||
227 | size = current_size->num_entries * sizeof(i460.lp_desc[0]); | ||
228 | i460.lp_desc = kmalloc(size, GFP_KERNEL); | ||
229 | if (!i460.lp_desc) | ||
230 | return -ENOMEM; | ||
231 | memset(i460.lp_desc, 0, size); | ||
232 | } | ||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | static int i460_create_gatt_table (struct agp_bridge_data *bridge) | ||
237 | { | ||
238 | int page_order, num_entries, i; | ||
239 | void *temp; | ||
240 | |||
241 | /* | ||
242 | * Load up the fixed address of the GART SRAMS which hold our GATT table. | ||
243 | */ | ||
244 | temp = agp_bridge->current_size; | ||
245 | page_order = A_SIZE_8(temp)->page_order; | ||
246 | num_entries = A_SIZE_8(temp)->num_entries; | ||
247 | |||
248 | i460.gatt = ioremap(INTEL_I460_ATTBASE, PAGE_SIZE << page_order); | ||
249 | |||
250 | /* These are no good, the should be removed from the agp_bridge strucure... */ | ||
251 | agp_bridge->gatt_table_real = NULL; | ||
252 | agp_bridge->gatt_table = NULL; | ||
253 | agp_bridge->gatt_bus_addr = 0; | ||
254 | |||
255 | for (i = 0; i < num_entries; ++i) | ||
256 | WR_GATT(i, 0); | ||
257 | WR_FLUSH_GATT(i - 1); | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static int i460_free_gatt_table (struct agp_bridge_data *bridge) | ||
262 | { | ||
263 | int num_entries, i; | ||
264 | void *temp; | ||
265 | |||
266 | temp = agp_bridge->current_size; | ||
267 | |||
268 | num_entries = A_SIZE_8(temp)->num_entries; | ||
269 | |||
270 | for (i = 0; i < num_entries; ++i) | ||
271 | WR_GATT(i, 0); | ||
272 | WR_FLUSH_GATT(num_entries - 1); | ||
273 | |||
274 | iounmap(i460.gatt); | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | /* | ||
279 | * The following functions are called when the I/O (GART) page size is smaller than | ||
280 | * PAGE_SIZE. | ||
281 | */ | ||
282 | |||
283 | static int i460_insert_memory_small_io_page (struct agp_memory *mem, | ||
284 | off_t pg_start, int type) | ||
285 | { | ||
286 | unsigned long paddr, io_pg_start, io_page_size; | ||
287 | int i, j, k, num_entries; | ||
288 | void *temp; | ||
289 | |||
290 | pr_debug("i460_insert_memory_small_io_page(mem=%p, pg_start=%ld, type=%d, paddr0=0x%lx)\n", | ||
291 | mem, pg_start, type, mem->memory[0]); | ||
292 | |||
293 | io_pg_start = I460_IOPAGES_PER_KPAGE * pg_start; | ||
294 | |||
295 | temp = agp_bridge->current_size; | ||
296 | num_entries = A_SIZE_8(temp)->num_entries; | ||
297 | |||
298 | if ((io_pg_start + I460_IOPAGES_PER_KPAGE * mem->page_count) > num_entries) { | ||
299 | printk(KERN_ERR PFX "Looks like we're out of AGP memory\n"); | ||
300 | return -EINVAL; | ||
301 | } | ||
302 | |||
303 | j = io_pg_start; | ||
304 | while (j < (io_pg_start + I460_IOPAGES_PER_KPAGE * mem->page_count)) { | ||
305 | if (!PGE_EMPTY(agp_bridge, RD_GATT(j))) { | ||
306 | pr_debug("i460_insert_memory_small_io_page: GATT[%d]=0x%x is busy\n", | ||
307 | j, RD_GATT(j)); | ||
308 | return -EBUSY; | ||
309 | } | ||
310 | j++; | ||
311 | } | ||
312 | |||
313 | io_page_size = 1UL << I460_IO_PAGE_SHIFT; | ||
314 | for (i = 0, j = io_pg_start; i < mem->page_count; i++) { | ||
315 | paddr = mem->memory[i]; | ||
316 | for (k = 0; k < I460_IOPAGES_PER_KPAGE; k++, j++, paddr += io_page_size) | ||
317 | WR_GATT(j, agp_bridge->driver->mask_memory(agp_bridge, | ||
318 | paddr, mem->type)); | ||
319 | } | ||
320 | WR_FLUSH_GATT(j - 1); | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static int i460_remove_memory_small_io_page(struct agp_memory *mem, | ||
325 | off_t pg_start, int type) | ||
326 | { | ||
327 | int i; | ||
328 | |||
329 | pr_debug("i460_remove_memory_small_io_page(mem=%p, pg_start=%ld, type=%d)\n", | ||
330 | mem, pg_start, type); | ||
331 | |||
332 | pg_start = I460_IOPAGES_PER_KPAGE * pg_start; | ||
333 | |||
334 | for (i = pg_start; i < (pg_start + I460_IOPAGES_PER_KPAGE * mem->page_count); i++) | ||
335 | WR_GATT(i, 0); | ||
336 | WR_FLUSH_GATT(i - 1); | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | #if I460_LARGE_IO_PAGES | ||
341 | |||
342 | /* | ||
343 | * These functions are called when the I/O (GART) page size exceeds PAGE_SIZE. | ||
344 | * | ||
345 | * This situation is interesting since AGP memory allocations that are smaller than a | ||
346 | * single GART page are possible. The i460.lp_desc array tracks partial allocation of the | ||
347 | * large GART pages to work around this issue. | ||
348 | * | ||
349 | * i460.lp_desc[pg_num].refcount tracks the number of kernel pages in use within GART page | ||
350 | * pg_num. i460.lp_desc[pg_num].paddr is the physical address of the large page and | ||
351 | * i460.lp_desc[pg_num].alloced_map is a bitmap of kernel pages that are in use (allocated). | ||
352 | */ | ||
353 | |||
354 | static int i460_alloc_large_page (struct lp_desc *lp) | ||
355 | { | ||
356 | unsigned long order = I460_IO_PAGE_SHIFT - PAGE_SHIFT; | ||
357 | size_t map_size; | ||
358 | void *lpage; | ||
359 | |||
360 | lpage = (void *) __get_free_pages(GFP_KERNEL, order); | ||
361 | if (!lpage) { | ||
362 | printk(KERN_ERR PFX "Couldn't alloc 4M GART page...\n"); | ||
363 | return -ENOMEM; | ||
364 | } | ||
365 | |||
366 | map_size = ((I460_KPAGES_PER_IOPAGE + BITS_PER_LONG - 1) & -BITS_PER_LONG)/8; | ||
367 | lp->alloced_map = kmalloc(map_size, GFP_KERNEL); | ||
368 | if (!lp->alloced_map) { | ||
369 | free_pages((unsigned long) lpage, order); | ||
370 | printk(KERN_ERR PFX "Out of memory, we're in trouble...\n"); | ||
371 | return -ENOMEM; | ||
372 | } | ||
373 | memset(lp->alloced_map, 0, map_size); | ||
374 | |||
375 | lp->paddr = virt_to_phys(lpage); | ||
376 | lp->refcount = 0; | ||
377 | atomic_add(I460_KPAGES_PER_IOPAGE, &agp_bridge->current_memory_agp); | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | static void i460_free_large_page (struct lp_desc *lp) | ||
382 | { | ||
383 | kfree(lp->alloced_map); | ||
384 | lp->alloced_map = NULL; | ||
385 | |||
386 | free_pages((unsigned long) phys_to_virt(lp->paddr), I460_IO_PAGE_SHIFT - PAGE_SHIFT); | ||
387 | atomic_sub(I460_KPAGES_PER_IOPAGE, &agp_bridge->current_memory_agp); | ||
388 | } | ||
389 | |||
390 | static int i460_insert_memory_large_io_page (struct agp_memory *mem, | ||
391 | off_t pg_start, int type) | ||
392 | { | ||
393 | int i, start_offset, end_offset, idx, pg, num_entries; | ||
394 | struct lp_desc *start, *end, *lp; | ||
395 | void *temp; | ||
396 | |||
397 | temp = agp_bridge->current_size; | ||
398 | num_entries = A_SIZE_8(temp)->num_entries; | ||
399 | |||
400 | /* Figure out what pg_start means in terms of our large GART pages */ | ||
401 | start = &i460.lp_desc[pg_start / I460_KPAGES_PER_IOPAGE]; | ||
402 | end = &i460.lp_desc[(pg_start + mem->page_count - 1) / I460_KPAGES_PER_IOPAGE]; | ||
403 | start_offset = pg_start % I460_KPAGES_PER_IOPAGE; | ||
404 | end_offset = (pg_start + mem->page_count - 1) % I460_KPAGES_PER_IOPAGE; | ||
405 | |||
406 | if (end > i460.lp_desc + num_entries) { | ||
407 | printk(KERN_ERR PFX "Looks like we're out of AGP memory\n"); | ||
408 | return -EINVAL; | ||
409 | } | ||
410 | |||
411 | /* Check if the requested region of the aperture is free */ | ||
412 | for (lp = start; lp <= end; ++lp) { | ||
413 | if (!lp->alloced_map) | ||
414 | continue; /* OK, the entire large page is available... */ | ||
415 | |||
416 | for (idx = ((lp == start) ? start_offset : 0); | ||
417 | idx < ((lp == end) ? (end_offset + 1) : I460_KPAGES_PER_IOPAGE); | ||
418 | idx++) | ||
419 | { | ||
420 | if (test_bit(idx, lp->alloced_map)) | ||
421 | return -EBUSY; | ||
422 | } | ||
423 | } | ||
424 | |||
425 | for (lp = start, i = 0; lp <= end; ++lp) { | ||
426 | if (!lp->alloced_map) { | ||
427 | /* Allocate new GART pages... */ | ||
428 | if (i460_alloc_large_page(lp) < 0) | ||
429 | return -ENOMEM; | ||
430 | pg = lp - i460.lp_desc; | ||
431 | WR_GATT(pg, agp_bridge->driver->mask_memory(agp_bridge, | ||
432 | lp->paddr, 0)); | ||
433 | WR_FLUSH_GATT(pg); | ||
434 | } | ||
435 | |||
436 | for (idx = ((lp == start) ? start_offset : 0); | ||
437 | idx < ((lp == end) ? (end_offset + 1) : I460_KPAGES_PER_IOPAGE); | ||
438 | idx++, i++) | ||
439 | { | ||
440 | mem->memory[i] = lp->paddr + idx*PAGE_SIZE; | ||
441 | __set_bit(idx, lp->alloced_map); | ||
442 | ++lp->refcount; | ||
443 | } | ||
444 | } | ||
445 | return 0; | ||
446 | } | ||
447 | |||
448 | static int i460_remove_memory_large_io_page (struct agp_memory *mem, | ||
449 | off_t pg_start, int type) | ||
450 | { | ||
451 | int i, pg, start_offset, end_offset, idx, num_entries; | ||
452 | struct lp_desc *start, *end, *lp; | ||
453 | void *temp; | ||
454 | |||
455 | temp = agp_bridge->driver->current_size; | ||
456 | num_entries = A_SIZE_8(temp)->num_entries; | ||
457 | |||
458 | /* Figure out what pg_start means in terms of our large GART pages */ | ||
459 | start = &i460.lp_desc[pg_start / I460_KPAGES_PER_IOPAGE]; | ||
460 | end = &i460.lp_desc[(pg_start + mem->page_count - 1) / I460_KPAGES_PER_IOPAGE]; | ||
461 | start_offset = pg_start % I460_KPAGES_PER_IOPAGE; | ||
462 | end_offset = (pg_start + mem->page_count - 1) % I460_KPAGES_PER_IOPAGE; | ||
463 | |||
464 | for (i = 0, lp = start; lp <= end; ++lp) { | ||
465 | for (idx = ((lp == start) ? start_offset : 0); | ||
466 | idx < ((lp == end) ? (end_offset + 1) : I460_KPAGES_PER_IOPAGE); | ||
467 | idx++, i++) | ||
468 | { | ||
469 | mem->memory[i] = 0; | ||
470 | __clear_bit(idx, lp->alloced_map); | ||
471 | --lp->refcount; | ||
472 | } | ||
473 | |||
474 | /* Free GART pages if they are unused */ | ||
475 | if (lp->refcount == 0) { | ||
476 | pg = lp - i460.lp_desc; | ||
477 | WR_GATT(pg, 0); | ||
478 | WR_FLUSH_GATT(pg); | ||
479 | i460_free_large_page(lp); | ||
480 | } | ||
481 | } | ||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | /* Wrapper routines to call the approriate {small_io_page,large_io_page} function */ | ||
486 | |||
487 | static int i460_insert_memory (struct agp_memory *mem, | ||
488 | off_t pg_start, int type) | ||
489 | { | ||
490 | if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) | ||
491 | return i460_insert_memory_small_io_page(mem, pg_start, type); | ||
492 | else | ||
493 | return i460_insert_memory_large_io_page(mem, pg_start, type); | ||
494 | } | ||
495 | |||
496 | static int i460_remove_memory (struct agp_memory *mem, | ||
497 | off_t pg_start, int type) | ||
498 | { | ||
499 | if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) | ||
500 | return i460_remove_memory_small_io_page(mem, pg_start, type); | ||
501 | else | ||
502 | return i460_remove_memory_large_io_page(mem, pg_start, type); | ||
503 | } | ||
504 | |||
505 | /* | ||
506 | * If the I/O (GART) page size is bigger than the kernel page size, we don't want to | ||
507 | * allocate memory until we know where it is to be bound in the aperture (a | ||
508 | * multi-kernel-page alloc might fit inside of an already allocated GART page). | ||
509 | * | ||
510 | * Let's just hope nobody counts on the allocated AGP memory being there before bind time | ||
511 | * (I don't think current drivers do)... | ||
512 | */ | ||
513 | static void *i460_alloc_page (struct agp_bridge_data *bridge) | ||
514 | { | ||
515 | void *page; | ||
516 | |||
517 | if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) | ||
518 | page = agp_generic_alloc_page(agp_bridge); | ||
519 | else | ||
520 | /* Returning NULL would cause problems */ | ||
521 | /* AK: really dubious code. */ | ||
522 | page = (void *)~0UL; | ||
523 | return page; | ||
524 | } | ||
525 | |||
526 | static void i460_destroy_page (void *page) | ||
527 | { | ||
528 | if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) | ||
529 | agp_generic_destroy_page(page); | ||
530 | } | ||
531 | |||
532 | #endif /* I460_LARGE_IO_PAGES */ | ||
533 | |||
534 | static unsigned long i460_mask_memory (struct agp_bridge_data *bridge, | ||
535 | unsigned long addr, int type) | ||
536 | { | ||
537 | /* Make sure the returned address is a valid GATT entry */ | ||
538 | return bridge->driver->masks[0].mask | ||
539 | | (((addr & ~((1 << I460_IO_PAGE_SHIFT) - 1)) & 0xffffff000) >> 12); | ||
540 | } | ||
541 | |||
542 | struct agp_bridge_driver intel_i460_driver = { | ||
543 | .owner = THIS_MODULE, | ||
544 | .aperture_sizes = i460_sizes, | ||
545 | .size_type = U8_APER_SIZE, | ||
546 | .num_aperture_sizes = 3, | ||
547 | .configure = i460_configure, | ||
548 | .fetch_size = i460_fetch_size, | ||
549 | .cleanup = i460_cleanup, | ||
550 | .tlb_flush = i460_tlb_flush, | ||
551 | .mask_memory = i460_mask_memory, | ||
552 | .masks = i460_masks, | ||
553 | .agp_enable = agp_generic_enable, | ||
554 | .cache_flush = global_cache_flush, | ||
555 | .create_gatt_table = i460_create_gatt_table, | ||
556 | .free_gatt_table = i460_free_gatt_table, | ||
557 | #if I460_LARGE_IO_PAGES | ||
558 | .insert_memory = i460_insert_memory, | ||
559 | .remove_memory = i460_remove_memory, | ||
560 | .agp_alloc_page = i460_alloc_page, | ||
561 | .agp_destroy_page = i460_destroy_page, | ||
562 | #else | ||
563 | .insert_memory = i460_insert_memory_small_io_page, | ||
564 | .remove_memory = i460_remove_memory_small_io_page, | ||
565 | .agp_alloc_page = agp_generic_alloc_page, | ||
566 | .agp_destroy_page = agp_generic_destroy_page, | ||
567 | #endif | ||
568 | .alloc_by_type = agp_generic_alloc_by_type, | ||
569 | .free_by_type = agp_generic_free_by_type, | ||
570 | .cant_use_aperture = 1, | ||
571 | }; | ||
572 | |||
573 | static int __devinit agp_intel_i460_probe(struct pci_dev *pdev, | ||
574 | const struct pci_device_id *ent) | ||
575 | { | ||
576 | struct agp_bridge_data *bridge; | ||
577 | u8 cap_ptr; | ||
578 | |||
579 | cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); | ||
580 | if (!cap_ptr) | ||
581 | return -ENODEV; | ||
582 | |||
583 | bridge = agp_alloc_bridge(); | ||
584 | if (!bridge) | ||
585 | return -ENOMEM; | ||
586 | |||
587 | bridge->driver = &intel_i460_driver; | ||
588 | bridge->dev = pdev; | ||
589 | bridge->capndx = cap_ptr; | ||
590 | |||
591 | printk(KERN_INFO PFX "Detected Intel 460GX chipset\n"); | ||
592 | |||
593 | pci_set_drvdata(pdev, bridge); | ||
594 | return agp_add_bridge(bridge); | ||
595 | } | ||
596 | |||
597 | static void __devexit agp_intel_i460_remove(struct pci_dev *pdev) | ||
598 | { | ||
599 | struct agp_bridge_data *bridge = pci_get_drvdata(pdev); | ||
600 | |||
601 | agp_remove_bridge(bridge); | ||
602 | agp_put_bridge(bridge); | ||
603 | } | ||
604 | |||
605 | static struct pci_device_id agp_intel_i460_pci_table[] = { | ||
606 | { | ||
607 | .class = (PCI_CLASS_BRIDGE_HOST << 8), | ||
608 | .class_mask = ~0, | ||
609 | .vendor = PCI_VENDOR_ID_INTEL, | ||
610 | .device = PCI_DEVICE_ID_INTEL_84460GX, | ||
611 | .subvendor = PCI_ANY_ID, | ||
612 | .subdevice = PCI_ANY_ID, | ||
613 | }, | ||
614 | { } | ||
615 | }; | ||
616 | |||
617 | MODULE_DEVICE_TABLE(pci, agp_intel_i460_pci_table); | ||
618 | |||
619 | static struct pci_driver agp_intel_i460_pci_driver = { | ||
620 | .name = "agpgart-intel-i460", | ||
621 | .id_table = agp_intel_i460_pci_table, | ||
622 | .probe = agp_intel_i460_probe, | ||
623 | .remove = __devexit_p(agp_intel_i460_remove), | ||
624 | }; | ||
625 | |||
626 | static int __init agp_intel_i460_init(void) | ||
627 | { | ||
628 | if (agp_off) | ||
629 | return -EINVAL; | ||
630 | return pci_register_driver(&agp_intel_i460_pci_driver); | ||
631 | } | ||
632 | |||
633 | static void __exit agp_intel_i460_cleanup(void) | ||
634 | { | ||
635 | pci_unregister_driver(&agp_intel_i460_pci_driver); | ||
636 | } | ||
637 | |||
638 | module_init(agp_intel_i460_init); | ||
639 | module_exit(agp_intel_i460_cleanup); | ||
640 | |||
641 | MODULE_AUTHOR("Chris Ahna <Christopher.J.Ahna@intel.com>"); | ||
642 | MODULE_LICENSE("GPL and additional rights"); | ||