2ef4797c2d1d905b4529c4a404cbdb0f1543cb15
[dragonfly.git] / sys / dev / agp / agp_i810.c
1 /*
2  * Copyright (c) 2000 Doug Rabson
3  * Copyright (c) 2000 Ruslan Ermilov
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  *      $FreeBSD: src/sys/pci/agp_i810.c,v 1.1.2.5 2002/09/15 08:45:41 anholt Exp $
28  *      $DragonFly: src/sys/dev/agp/agp_i810.c,v 1.11 2006/12/22 23:26:14 swildner Exp $
29  */
30
31 /*
32  * Fixes for 830/845G support: David Dawes <dawes@xfree86.org>
33  */
34
35 #include "opt_bus.h"
36 #include "opt_pci.h"
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/malloc.h>
41 #include <sys/kernel.h>
42 #include <sys/bus.h>
43 #include <sys/lock.h>
44 #include <sys/rman.h>
45
46 #include <bus/pci/pcivar.h>
47 #include <bus/pci/pcireg.h>
48 #include "agppriv.h"
49 #include "agpreg.h"
50
51 #include <vm/vm.h>
52 #include <vm/vm_object.h>
53 #include <vm/vm_page.h>
54 #include <vm/vm_pageout.h>
55 #include <vm/pmap.h>
56
57 MALLOC_DECLARE(M_AGP);
58
59 #define READ1(off)      bus_space_read_1(sc->bst, sc->bsh, off)
60 #define READ4(off)      bus_space_read_4(sc->bst, sc->bsh, off)
61 #define WRITE4(off,v)   bus_space_write_4(sc->bst, sc->bsh, off, v)
62
63 #define CHIP_I810 0     /* i810/i815 */
64 #define CHIP_I830 1     /* 830M/845G */
65 #define CHIP_I855 2     /* 852GM/855GM/865G */
66
67 struct agp_i810_softc {
68         struct agp_softc agp;
69         u_int32_t initial_aperture;     /* aperture size at startup */
70         struct agp_gatt *gatt;
71         int chiptype;                   /* i810-like or i830 */
72         u_int32_t dcache_size;          /* i810 only */
73         u_int32_t stolen;               /* number of i830/845 gtt entries for stolen memory */
74         device_t bdev;                  /* bridge device */
75         struct resource *regs;          /* memory mapped GC registers */
76         bus_space_tag_t bst;            /* bus_space tag */
77         bus_space_handle_t bsh;         /* bus_space handle */
78 };
79
80 static const char*
81 agp_i810_match(device_t dev)
82 {
83         if (pci_get_class(dev) != PCIC_DISPLAY
84             || pci_get_subclass(dev) != PCIS_DISPLAY_VGA)
85                 return NULL;
86
87         switch (pci_get_devid(dev)) {
88         case 0x71218086:
89                 return ("Intel 82810 (i810 GMCH) SVGA controller");
90
91         case 0x71238086:
92                 return ("Intel 82810-DC100 (i810-DC100 GMCH) SVGA controller");
93
94         case 0x71258086:
95                 return ("Intel 82810E (i810E GMCH) SVGA controller");
96
97         case 0x11328086:
98                 return ("Intel 82815 (i815 GMCH) SVGA controller");
99
100         case 0x35778086:
101                 return ("Intel 82830M (i830M GMCH) SVGA controller");
102
103         case 0x25628086:
104                 return ("Intel 82845G (i845 GMCH) SVGA controller");
105
106         case 0x25728086:
107                 return ("Intel 82865G (i865 GMCH) SVGA controller");
108
109         case 0x35828086:
110                 switch (pci_read_config(dev, AGP_I85X_CAPID, 1)) {
111                 case AGP_I855_GME:
112                         return ("Intel 82855GME (855GME GMCH) SVGA controller");
113
114                 case AGP_I855_GM:
115                         return ("Intel 82855GM (855GM GMCH) SVGA controller");
116
117                 case AGP_I852_GME:
118                         return ("Intel 82852GME (852GME GMCH) SVGA controller");
119
120                 case AGP_I852_GM:
121                         return ("Intel 82852GM (852GM GMCH) SVGA controller");
122
123                 default:
124                         return ("Intel 8285xM (85xGM GMCH) SVGA controller");
125                 }
126                 /* not reached */
127         };
128
129         return NULL;
130 }
131
132 /*
133  * Find bridge device.
134  */
135 static device_t
136 agp_i810_find_bridge(device_t dev)
137 {
138         device_t *children, child;
139         int nchildren, i;
140         u_int32_t devid;
141
142         /*
143          * Calculate bridge device's ID.
144          */
145         devid = pci_get_devid(dev);
146         switch (devid) {
147         case 0x71218086:
148         case 0x71238086:
149         case 0x71258086:
150                 devid -= 0x10000;
151                 break;
152
153         case 0x11328086:
154         case 0x35778086:
155         case 0x25628086:
156         case 0x25728086:
157                 devid -= 0x20000;
158                 break;
159         };
160         if (device_get_children(device_get_parent(dev), &children, &nchildren))
161                 return 0;
162
163         for (i = 0; i < nchildren; i++) {
164                 child = children[i];
165
166                 if (pci_get_devid(child) == devid) {
167                         kfree(children, M_TEMP);
168                         return child;
169                 }
170         }
171         kfree(children, M_TEMP);
172         return 0;
173 }
174
175 static int
176 agp_i810_probe(device_t dev)
177 {
178         const char *desc;
179
180         desc = agp_i810_match(dev);
181         if (desc) {
182                 device_t bdev;
183                 u_int8_t smram;
184                 unsigned int gcc1;
185                 int devid = pci_get_devid(dev);
186
187                 bdev = agp_i810_find_bridge(dev);
188                 if (!bdev) {
189                         if (bootverbose)
190                                 kprintf("I810: can't find bridge device\n");
191                         return ENXIO;
192                 }
193
194                 /*
195                  * checking whether internal graphics device has been activated.
196                  */
197                 switch(devid) {
198                 case 0x71218086:
199                 case 0x71238086:
200                 case 0x71258086:
201                 case 0x11328086:
202                         /* i810 */
203                         smram = pci_read_config(bdev, AGP_I810_SMRAM, 1);
204                         if ((smram & AGP_I810_SMRAM_GMS)
205                             == AGP_I810_SMRAM_GMS_DISABLED) {
206                                 if (bootverbose)
207                                         kprintf("I810: disabled, not probing\n");
208                                 return ENXIO;
209                         }
210                         break;
211                 case 0x35778086:
212                 case 0x35828086:
213                 case 0x25628086:
214                 case 0x25728086:
215                         /* i830 */
216                         gcc1 = pci_read_config(bdev, AGP_I830_GCC1, 1);
217                         if ((gcc1 & AGP_I830_GCC1_DEV2) == AGP_I830_GCC1_DEV2_DISABLED) {
218                                 if (bootverbose)
219                                         kprintf("I830: disabled, not probing\n");
220                                         return ENXIO;
221                         }
222                         break;
223                 default:
224                         return ENXIO;
225                 }
226
227                 device_verbose(dev);
228                 device_set_desc(dev, desc);
229                 return 0;
230         }
231
232         return ENXIO;
233 }
234
235 static int
236 agp_i810_attach(device_t dev)
237 {
238         struct agp_i810_softc *sc = device_get_softc(dev);
239         struct agp_gatt *gatt;
240         int error, rid;
241
242         sc->bdev = agp_i810_find_bridge(dev);
243         if (!sc->bdev)
244                 return ENOENT;
245
246         error = agp_generic_attach(dev);
247         if (error)
248                 return error;
249
250         switch (pci_get_devid(dev)) {
251         case 0x71218086:
252         case 0x71238086:
253         case 0x71258086:
254         case 0x11328086:
255                 sc->chiptype = CHIP_I810;
256                 break;
257         case 0x35778086:
258         case 0x25628086:
259                 sc->chiptype = CHIP_I830;
260                 break;
261         case 0x25728086:
262         case 0x35828086:
263                 sc->chiptype = CHIP_I855;
264                 break;
265         };
266
267         /* Same for i810 and i830 */
268         rid = AGP_I810_MMADR;
269         sc->regs = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
270                                       0, ~0, 1, RF_ACTIVE);
271         if (!sc->regs) {
272                 agp_generic_detach(dev);
273                 return ENOMEM;
274         }
275         sc->bst = rman_get_bustag(sc->regs);
276         sc->bsh = rman_get_bushandle(sc->regs);
277
278         sc->initial_aperture = AGP_GET_APERTURE(dev);
279         if (sc->initial_aperture == 0) {
280                 device_printf(dev, "bad initial aperture size, disabling\n");
281                 return ENXIO;
282         }
283
284         gatt = kmalloc( sizeof(struct agp_gatt), M_AGP, M_INTWAIT);
285         sc->gatt = gatt;
286
287         gatt->ag_entries = AGP_GET_APERTURE(dev) >> AGP_PAGE_SHIFT;
288
289         if ( sc->chiptype == CHIP_I810 ) {
290                 /* Some i810s have on-chip memory called dcache */
291                 if (READ1(AGP_I810_DRT) & AGP_I810_DRT_POPULATED)
292                         sc->dcache_size = 4 * 1024 * 1024;
293                 else
294                         sc->dcache_size = 0;
295
296                 /* According to the specs the gatt on the i810 must be 64k */
297                 gatt->ag_virtual = contigmalloc( 64 * 1024, M_AGP, 0, 
298                                         0, ~0, PAGE_SIZE, 0);
299                 if (!gatt->ag_virtual) {
300                         if (bootverbose)
301                                 device_printf(dev, "contiguous allocation failed\n");
302                         kfree(gatt, M_AGP);
303                         agp_generic_detach(dev);
304                         return ENOMEM;
305                 }
306                 bzero(gatt->ag_virtual, gatt->ag_entries * sizeof(u_int32_t));
307         
308                 gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual);
309                 agp_flush_cache();
310                 /* Install the GATT. */
311                 WRITE4(AGP_I810_PGTBL_CTL, gatt->ag_physical | 1);
312         } else if (sc->chiptype == CHIP_I830) {
313                 /* The i830 automatically initializes the 128k gatt on boot. */
314                 unsigned int gcc1, pgtblctl;
315                 
316                 gcc1 = pci_read_config(sc->bdev, AGP_I830_GCC1, 1);
317                 switch (gcc1 & AGP_I830_GCC1_GMS) {
318                         case AGP_I830_GCC1_GMS_STOLEN_512:
319                                 sc->stolen = (512 - 132) * 1024 / 4096;
320                                 break;
321                         case AGP_I830_GCC1_GMS_STOLEN_1024: 
322                                 sc->stolen = (1024 - 132) * 1024 / 4096;
323                                 break;
324                         case AGP_I830_GCC1_GMS_STOLEN_8192: 
325                                 sc->stolen = (8192 - 132) * 1024 / 4096;
326                                 break;
327                         default:
328                                 sc->stolen = 0;
329                                 device_printf(dev, "unknown memory configuration, disabling\n");
330                                 agp_generic_detach(dev);
331                                 return EINVAL;
332                 }
333                 if (sc->stolen > 0) {
334                         device_printf(dev,
335                                 "detected %dk stolen memory\n", sc->stolen * 4);
336                 }
337                 device_printf(dev, "aperture size is %dM\n", sc->initial_aperture / 1024 / 1024);
338
339                 /* GATT address is already in there, make sure it's enabled */
340                 pgtblctl = READ4(AGP_I810_PGTBL_CTL);
341                 pgtblctl |= 1;
342                 WRITE4(AGP_I810_PGTBL_CTL, pgtblctl);
343
344                 gatt->ag_physical = pgtblctl & ~1;
345         } else {        /* CHIP_I855 */
346                 /* The i855 automatically initializes the 128k gatt on boot. */
347                 unsigned int gcc1, pgtblctl;
348
349                 gcc1 = pci_read_config(sc->bdev, AGP_I855_GCC1, 1);
350                 switch (gcc1 & AGP_I855_GCC1_GMS) {
351                 case AGP_I855_GCC1_GMS_STOLEN_1M:
352                         sc->stolen = (1024 - 132) * 1024 / 4096;  
353                         break;
354                 case AGP_I855_GCC1_GMS_STOLEN_4M:
355                         sc->stolen = (4096 - 132) * 1024 / 4096;
356                         break;
357                 case AGP_I855_GCC1_GMS_STOLEN_8M:
358                         sc->stolen = (8192 - 132) * 1024 / 4096;
359                         break;
360                 case AGP_I855_GCC1_GMS_STOLEN_16M:
361                         sc->stolen = (16384 - 132) * 1024 / 4096;
362                         break;
363                 case AGP_I855_GCC1_GMS_STOLEN_32M:
364                         sc->stolen = (32768 - 132) * 1024 / 4096;
365                         break;
366                 default:
367                         sc->stolen = 0;
368                         device_printf(dev,
369                             "unknown memory configuration, disabling\n");
370                         agp_generic_detach(dev);
371                         return EINVAL;
372                 }
373                 if (sc->stolen > 0) {
374                         device_printf(dev, "detected %dk stolen memory\n",
375                                         sc->stolen * 4);
376                 }
377                 device_printf(dev, "aperture size is %dM\n", 
378                                 sc->initial_aperture / 1024 / 1024);
379
380                 /* GATT address is already in there, make sure it's enabled */
381                 pgtblctl = READ4(AGP_I810_PGTBL_CTL);
382                 pgtblctl |= 1;
383                 WRITE4(AGP_I810_PGTBL_CTL, pgtblctl);
384
385                 gatt->ag_physical = pgtblctl & ~1;
386         }
387         return 0;
388 }
389
390 static int
391 agp_i810_detach(device_t dev)
392 {
393         struct agp_i810_softc *sc = device_get_softc(dev);
394         int error;
395
396         error = agp_generic_detach(dev);
397         if (error)
398                 return error;
399
400         /* Clear the GATT base. */
401         if ( sc->chiptype == CHIP_I810 ) {
402                 WRITE4(AGP_I810_PGTBL_CTL, 0);
403         } else {
404                 unsigned int pgtblctl;
405                 pgtblctl = READ4(AGP_I810_PGTBL_CTL);
406                 pgtblctl &= ~1;
407                 WRITE4(AGP_I810_PGTBL_CTL, pgtblctl);
408         }
409
410         /* Put the aperture back the way it started. */
411         AGP_SET_APERTURE(dev, sc->initial_aperture);
412
413         if ( sc->chiptype == CHIP_I810 ) {
414                 contigfree(sc->gatt->ag_virtual, 64 * 1024, M_AGP);
415         }
416         kfree(sc->gatt, M_AGP);
417
418         bus_release_resource(dev, SYS_RES_MEMORY,
419                              AGP_I810_MMADR, sc->regs);
420
421         return 0;
422 }
423
424 static u_int32_t
425 agp_i810_get_aperture(device_t dev)
426 {
427         struct agp_i810_softc *sc = device_get_softc(dev);
428
429         if ( sc->chiptype == CHIP_I810 ) {
430                 u_int16_t miscc;
431                 miscc = pci_read_config(sc->bdev, AGP_I810_MISCC, 2);
432                 if ((miscc & AGP_I810_MISCC_WINSIZE) == AGP_I810_MISCC_WINSIZE_32)
433                         return 32 * 1024 * 1024;
434                 else
435                         return 64 * 1024 * 1024;
436         } else {        /* I830 */
437                 unsigned int gcc1;
438
439                 gcc1 = pci_read_config(sc->bdev, AGP_I830_GCC1, 2);
440                 if ((gcc1 & AGP_I830_GCC1_GMASIZE) == AGP_I830_GCC1_GMASIZE_64)
441                         return 64 * 1024 * 1024;
442                 else
443                         return 128 * 1024 * 1024;
444         }
445 }
446
447 static int
448 agp_i810_set_aperture(device_t dev, u_int32_t aperture)
449 {
450         struct agp_i810_softc *sc = device_get_softc(dev);
451         u_int16_t miscc;
452
453         if ( sc->chiptype == CHIP_I810 ) {
454                 /*
455                  * Double check for sanity.
456                  */
457                 if (aperture != 32 * 1024 * 1024 && aperture != 64 * 1024 * 1024) {
458                         device_printf(dev, "bad aperture size %d\n", aperture);
459                         return EINVAL;
460                 }
461         
462                 miscc = pci_read_config(sc->bdev, AGP_I810_MISCC, 2);
463                 miscc &= ~AGP_I810_MISCC_WINSIZE;
464                 if (aperture == 32 * 1024 * 1024)
465                         miscc |= AGP_I810_MISCC_WINSIZE_32;
466                 else
467                         miscc |= AGP_I810_MISCC_WINSIZE_64;
468         
469                 pci_write_config(sc->bdev, AGP_I810_MISCC, miscc, 2);
470         } else {        /* I830 */
471                 unsigned int gcc1;
472
473                 if (aperture != 64 * 1024 * 1024 && aperture != 128 * 1024 * 1024) {
474                         device_printf(dev, "bad aperture size %d\n", aperture);
475                         return EINVAL;
476                 }
477                 gcc1 = pci_read_config(sc->bdev, AGP_I830_GCC1, 2);
478                 gcc1 &= ~AGP_I830_GCC1_GMASIZE;
479                 if (aperture == 64 * 1024 * 1024)
480                         gcc1 |= AGP_I830_GCC1_GMASIZE_64;
481                 else
482                         gcc1 |= AGP_I830_GCC1_GMASIZE_128;
483
484                 pci_write_config(sc->bdev, AGP_I830_GCC1, gcc1, 2);
485         }
486
487         return 0;
488 }
489
490 static int
491 agp_i810_bind_page(device_t dev, int offset, vm_offset_t physical)
492 {
493         struct agp_i810_softc *sc = device_get_softc(dev);
494
495         if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) {
496                 device_printf(dev, "failed: offset is 0x%08x, shift is %d, entries is %d\n", offset, AGP_PAGE_SHIFT, sc->gatt->ag_entries);
497                 return EINVAL;
498         }
499
500         if ( sc->chiptype == CHIP_I830 ) {
501                 if ( (offset >> AGP_PAGE_SHIFT) < sc->stolen ) {
502                         device_printf(dev, "trying to bind into stolen memory");
503                         return EINVAL;
504                 }
505         }
506
507         WRITE4(AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4, physical | 1);
508         return 0;
509 }
510
511 static int
512 agp_i810_unbind_page(device_t dev, int offset)
513 {
514         struct agp_i810_softc *sc = device_get_softc(dev);
515
516         if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT))
517                 return EINVAL;
518
519         if ( sc->chiptype == CHIP_I830 ) {
520                 if ( (offset >> AGP_PAGE_SHIFT) < sc->stolen ) {
521                         device_printf(dev, "trying to unbind from stolen memory");
522                         return EINVAL;
523                 }
524         }
525
526         WRITE4(AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4, 0);
527         return 0;
528 }
529
530 /*
531  * Writing via memory mapped registers already flushes all TLBs.
532  */
533 static void
534 agp_i810_flush_tlb(device_t dev)
535 {
536 }
537
538 static int
539 agp_i810_enable(device_t dev, u_int32_t mode)
540 {
541
542         return 0;
543 }
544
545 static struct agp_memory *
546 agp_i810_alloc_memory(device_t dev, int type, vm_size_t size)
547 {
548         struct agp_i810_softc *sc = device_get_softc(dev);
549         struct agp_memory *mem;
550
551         if ((size & (AGP_PAGE_SIZE - 1)) != 0)
552                 return 0;
553
554         if (sc->agp.as_allocated + size > sc->agp.as_maxmem)
555                 return 0;
556
557         if (type == 1) {
558                 /*
559                  * Mapping local DRAM into GATT.
560                  */
561                 if ( sc->chiptype == CHIP_I830 )
562                         return 0;
563                 if (size != sc->dcache_size)
564                         return 0;
565         } else if (type == 2) {
566                 /*
567                  * Bogus mapping of a single page for the hardware cursor.
568                  */
569                 if (size != AGP_PAGE_SIZE)
570                         return 0;
571         }
572
573         mem = kmalloc(sizeof *mem, M_AGP, M_INTWAIT);
574         mem->am_id = sc->agp.as_nextid++;
575         mem->am_size = size;
576         mem->am_type = type;
577         if (type != 1)
578                 mem->am_obj = vm_object_allocate(OBJT_DEFAULT,
579                                                  atop(round_page(size)));
580         else
581                 mem->am_obj = 0;
582
583         if (type == 2) {
584                 /*
585                  * Allocate and wire down the page now so that we can
586                  * get its physical address.
587                  */
588                 vm_page_t m;
589                 m = vm_page_grab(mem->am_obj, 0, 
590                         VM_ALLOC_NORMAL|VM_ALLOC_ZERO|VM_ALLOC_RETRY);
591                 if ((m->flags & PG_ZERO) == 0)
592                         vm_page_zero_fill(m);
593                 vm_page_wire(m);
594                 mem->am_physical = VM_PAGE_TO_PHYS(m);
595                 vm_page_wakeup(m);
596         } else {
597                 mem->am_physical = 0;
598         }
599
600         mem->am_offset = 0;
601         mem->am_is_bound = 0;
602         TAILQ_INSERT_TAIL(&sc->agp.as_memory, mem, am_link);
603         sc->agp.as_allocated += size;
604
605         return mem;
606 }
607
608 static int
609 agp_i810_free_memory(device_t dev, struct agp_memory *mem)
610 {
611         struct agp_i810_softc *sc = device_get_softc(dev);
612
613         if (mem->am_is_bound)
614                 return EBUSY;
615
616         if (mem->am_type == 2) {
617                 /*
618                  * Unwire the page which we wired in alloc_memory.
619                  */
620                 vm_page_t m = vm_page_lookup(mem->am_obj, 0);
621                 vm_page_unwire(m, 0);
622         }
623
624         sc->agp.as_allocated -= mem->am_size;
625         TAILQ_REMOVE(&sc->agp.as_memory, mem, am_link);
626         if (mem->am_obj)
627                 vm_object_deallocate(mem->am_obj);
628         kfree(mem, M_AGP);
629         return 0;
630 }
631
632 static int
633 agp_i810_bind_memory(device_t dev, struct agp_memory *mem,
634                      vm_offset_t offset)
635 {
636         struct agp_i810_softc *sc = device_get_softc(dev);
637         vm_offset_t i;
638
639         if (mem->am_type != 1)
640                 return agp_generic_bind_memory(dev, mem, offset);
641
642         if ( sc->chiptype == CHIP_I830 )
643                 return EINVAL;
644
645         for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) {
646                 WRITE4(AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4,
647                        i | 3);
648         }
649
650         return 0;
651 }
652
653 static int
654 agp_i810_unbind_memory(device_t dev, struct agp_memory *mem)
655 {
656         struct agp_i810_softc *sc = device_get_softc(dev);
657         vm_offset_t i;
658
659         if (mem->am_type != 1)
660                 return agp_generic_unbind_memory(dev, mem);
661
662         if ( sc->chiptype == CHIP_I830 )
663                 return EINVAL;
664
665         for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE)
666                 WRITE4(AGP_I810_GTT + (i >> AGP_PAGE_SHIFT) * 4, 0);
667
668         return 0;
669 }
670
671 static device_method_t agp_i810_methods[] = {
672         /* Device interface */
673         DEVMETHOD(device_probe,         agp_i810_probe),
674         DEVMETHOD(device_attach,        agp_i810_attach),
675         DEVMETHOD(device_detach,        agp_i810_detach),
676         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
677         DEVMETHOD(device_suspend,       bus_generic_suspend),
678         DEVMETHOD(device_resume,        bus_generic_resume),
679
680         /* AGP interface */
681         DEVMETHOD(agp_get_aperture,     agp_i810_get_aperture),
682         DEVMETHOD(agp_set_aperture,     agp_i810_set_aperture),
683         DEVMETHOD(agp_bind_page,        agp_i810_bind_page),
684         DEVMETHOD(agp_unbind_page,      agp_i810_unbind_page),
685         DEVMETHOD(agp_flush_tlb,        agp_i810_flush_tlb),
686         DEVMETHOD(agp_enable,           agp_i810_enable),
687         DEVMETHOD(agp_alloc_memory,     agp_i810_alloc_memory),
688         DEVMETHOD(agp_free_memory,      agp_i810_free_memory),
689         DEVMETHOD(agp_bind_memory,      agp_i810_bind_memory),
690         DEVMETHOD(agp_unbind_memory,    agp_i810_unbind_memory),
691
692         { 0, 0 }
693 };
694
695 static driver_t agp_i810_driver = {
696         "agp",
697         agp_i810_methods,
698         sizeof(struct agp_i810_softc),
699 };
700
701 static devclass_t agp_devclass;
702
703 DRIVER_MODULE(agp_i810, pci, agp_i810_driver, agp_devclass, 0, 0);
704 MODULE_DEPEND(agp_i810, agp, 1, 1, 1);
705 MODULE_DEPEND(agp_i810, pci, 1, 1, 1);