agp_amd.c revision 129878
151078Speter/*- 251078Speter * Copyright (c) 2000 Doug Rabson 351078Speter * All rights reserved. 451078Speter * 551078Speter * Redistribution and use in source and binary forms, with or without 651078Speter * modification, are permitted provided that the following conditions 751078Speter * are met: 851078Speter * 1. Redistributions of source code must retain the above copyright 951078Speter * notice, this list of conditions and the following disclaimer. 1051078Speter * 2. Redistributions in binary form must reproduce the above copyright 1151078Speter * notice, this list of conditions and the following disclaimer in the 1251078Speter * documentation and/or other materials provided with the distribution. 1351078Speter * 1451078Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1551078Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1651078Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1751078Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1851078Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1951078Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2051078Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2151078Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2251078Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2351078Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2451078Speter * SUCH DAMAGE. 2551078Speter */ 2651078Speter 2751078Speter#include <sys/cdefs.h> 2851078Speter__FBSDID("$FreeBSD: head/sys/dev/agp/agp_amd.c 129878 2004-05-30 20:00:41Z phk $"); 2951078Speter 3051078Speter#include "opt_bus.h" 3151078Speter 3251078Speter#include <sys/param.h> 33119419Sobrien#include <sys/systm.h> 34119419Sobrien#include <sys/malloc.h> 35119419Sobrien#include <sys/kernel.h> 3651078Speter#include <sys/module.h> 3751078Speter#include <sys/bus.h> 3851078Speter#include <sys/lock.h> 3951078Speter#include <sys/mutex.h> 4051078Speter#include <sys/proc.h> 4151078Speter 4251078Speter#include <dev/pci/pcivar.h> 4351078Speter#include <dev/pci/pcireg.h> 4451078Speter#include <pci/agppriv.h> 4551078Speter#include <pci/agpreg.h> 4651078Speter 4751078Speter#include <vm/vm.h> 4851078Speter#include <vm/vm_object.h> 4951078Speter#include <vm/pmap.h> 5051078Speter#include <machine/bus.h> 5176166Smarkm#include <machine/resource.h> 5265822Sjhb#include <sys/rman.h> 5351078Speter 5451078SpeterMALLOC_DECLARE(M_AGP); 5551078Speter 5651078Speter#define READ2(off) bus_space_read_2(sc->bst, sc->bsh, off) 57114216Skan#define READ4(off) bus_space_read_4(sc->bst, sc->bsh, off) 5876166Smarkm#define WRITE2(off,v) bus_space_write_2(sc->bst, sc->bsh, off, v) 5976166Smarkm#define WRITE4(off,v) bus_space_write_4(sc->bst, sc->bsh, off, v) 6076166Smarkm 6176166Smarkmstruct agp_amd_gatt { 6276166Smarkm u_int32_t ag_entries; 6376166Smarkm u_int32_t *ag_virtual; /* virtual address of gatt */ 6476166Smarkm vm_offset_t ag_physical; 6551078Speter u_int32_t *ag_vdir; /* virtual address of page dir */ 6676166Smarkm vm_offset_t ag_pdir; /* physical address of page dir */ 6760471Snyan}; 6851078Speter 6951078Speterstruct agp_amd_softc { 7051078Speter struct agp_softc agp; 7193466Sbde struct resource *regs; /* memory mapped control registers */ 72119485Snjl bus_space_tag_t bst; /* bus_space tag */ 73119485Snjl bus_space_handle_t bsh; /* bus_space handle */ 74119485Snjl u_int32_t initial_aperture; /* aperture size at startup */ 75119485Snjl struct agp_amd_gatt *gatt; 7651078Speter}; 7786909Simp 7886909Simpstatic struct agp_amd_gatt * 7951078Speteragp_amd_alloc_gatt(device_t dev) 8051078Speter{ 8185302Simp u_int32_t apsize = AGP_GET_APERTURE(dev); 8285365Simp u_int32_t entries = apsize >> AGP_PAGE_SHIFT; 8351078Speter struct agp_amd_gatt *gatt; 8451078Speter int i, npages, pdir_offset; 8577726Sjoerg 8651078Speter if (bootverbose) 8777726Sjoerg device_printf(dev, 8851078Speter "allocating GATT for aperture of size %dM\n", 8951078Speter apsize / (1024*1024)); 9051078Speter 9151078Speter gatt = malloc(sizeof(struct agp_amd_gatt), M_AGP, M_NOWAIT); 9251078Speter if (!gatt) 9351078Speter return 0; 9451078Speter 9551078Speter /* 9693470Sbde * The AMD751 uses a page directory to map a non-contiguous 9793470Sbde * gatt so we don't need to use contigmalloc. 9893470Sbde * Malloc individual gatt pages and map them into the page 9993470Sbde * directory. 10051078Speter */ 10151078Speter gatt->ag_entries = entries; 10251078Speter gatt->ag_virtual = malloc(entries * sizeof(u_int32_t), 10351078Speter M_AGP, M_NOWAIT); 10451078Speter if (!gatt->ag_virtual) { 10551078Speter if (bootverbose) 10651078Speter device_printf(dev, "allocation failed\n"); 10751078Speter free(gatt, M_AGP); 108104067Sphk return 0; 109104067Sphk } 11051078Speter bzero(gatt->ag_virtual, entries * sizeof(u_int32_t)); 11151078Speter 112120175Sbde /* 11351078Speter * Allocate the page directory. 114120175Sbde */ 115120175Sbde gatt->ag_vdir = malloc(AGP_PAGE_SIZE, M_AGP, M_NOWAIT); 11651078Speter if (!gatt->ag_vdir) { 117120175Sbde if (bootverbose) 11851078Speter device_printf(dev, 11951078Speter "failed to allocate page directory\n"); 120120175Sbde free(gatt->ag_virtual, M_AGP); 121120175Sbde free(gatt, M_AGP); 122120175Sbde return 0; 123111613Sphk } 124120175Sbde bzero(gatt->ag_vdir, AGP_PAGE_SIZE); 125112384Ssobomax 12651078Speter gatt->ag_pdir = vtophys((vm_offset_t) gatt->ag_vdir); 12760471Snyan if(bootverbose) 12860471Snyan device_printf(dev, "gatt -> ag_pdir %#lx\n", 12960471Snyan (u_long)gatt->ag_pdir); 13060471Snyan /* 13160471Snyan * Allocate the gatt pages 13251078Speter */ 13351078Speter gatt->ag_entries = entries; 13451078Speter if(bootverbose) 13551078Speter device_printf(dev, "allocating GATT for %d AGP page entries\n", 13651078Speter gatt->ag_entries); 13751078Speter 13851078Speter gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual); 13951078Speter 14053344Speter /* 14151078Speter * Map the pages of the GATT into the page directory. 14251078Speter * 14351078Speter * The GATT page addresses are mapped into the directory offset by 14451078Speter * an amount dependent on the base address of the aperture. This 14551078Speter * is and offset into the page directory, not an offset added to 14651078Speter * the addresses of the gatt pages. 14751078Speter */ 14851078Speter 14951078Speter pdir_offset = pci_read_config(dev, AGP_AMD751_APBASE, 4) >> 22; 15051078Speter 15151078Speter npages = ((entries * sizeof(u_int32_t) + AGP_PAGE_SIZE - 1) 15251078Speter >> AGP_PAGE_SHIFT); 15351078Speter 15451078Speter for (i = 0; i < npages; i++) { 15551078Speter vm_offset_t va; 15651078Speter vm_offset_t pa; 15751078Speter 15851078Speter va = ((vm_offset_t) gatt->ag_virtual) + i * AGP_PAGE_SIZE; 15951078Speter pa = vtophys(va); 16051078Speter gatt->ag_vdir[i + pdir_offset] = pa | 1; 16151078Speter } 16251078Speter 16351078Speter /* 16451078Speter * Make sure the chipset can see everything. 16551078Speter */ 16651078Speter agp_flush_cache(); 16786909Simp 16851078Speter return gatt; 16951078Speter} 17086909Simp 17186909Simpstatic void 17286909Simpagp_amd_free_gatt(struct agp_amd_gatt *gatt) 17386909Simp{ 17486909Simp free(gatt->ag_virtual, M_AGP); 17586909Simp free(gatt->ag_vdir, M_AGP); 17686909Simp free(gatt, M_AGP); 17786909Simp} 17886909Simp 17986909Simpstatic const char* 18086909Simpagp_amd_match(device_t dev) 18186909Simp{ 18286909Simp if (pci_get_class(dev) != PCIC_BRIDGE 18386909Simp || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) 18486909Simp return NULL; 18586909Simp 18686909Simp if (agp_find_caps(dev) == 0) 18751078Speter return NULL; 18886909Simp 18986909Simp switch (pci_get_devid(dev)) { 19086909Simp 19186909Simp case 0x700e1022: 19286909Simp return ("AMD 761 host to AGP bridge"); 19386909Simp 19486909Simp case 0x70061022: 19586909Simp return ("AMD 751 host to AGP bridge"); 19686909Simp 19786909Simp case 0x700c1022: 19886909Simp return ("AMD 762 host to AGP bridge"); 19986909Simp 20086909Simp }; 20186909Simp 202120175Sbde return NULL; 20386909Simp} 20486909Simp 205120189Sbdestatic int 20686909Simpagp_amd_probe(device_t dev) 20786909Simp{ 20886909Simp const char *desc; 20986909Simp 21086909Simp if (resource_disabled("agp", device_get_unit(dev))) 21186909Simp return (ENXIO); 21286909Simp desc = agp_amd_match(dev); 21386909Simp if (desc) { 21486909Simp device_verbose(dev); 21586909Simp device_set_desc(dev, desc); 21686909Simp return 0; 21786909Simp } 21886909Simp 21986909Simp return ENXIO; 22086909Simp} 22186909Simp 22286909Simpstatic int 22386909Simpagp_amd_attach(device_t dev) 22486909Simp{ 22586909Simp struct agp_amd_softc *sc = device_get_softc(dev); 22686909Simp struct agp_amd_gatt *gatt; 22786909Simp int error, rid; 22886909Simp 22986909Simp error = agp_generic_attach(dev); 23086909Simp if (error) 23186909Simp return error; 23286909Simp 23386909Simp rid = AGP_AMD751_REGISTERS; 23486909Simp sc->regs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 23586909Simp RF_ACTIVE); 23686909Simp if (!sc->regs) { 237120189Sbde agp_generic_detach(dev); 23886909Simp return ENOMEM; 23986909Simp } 24086909Simp 24186909Simp sc->bst = rman_get_bustag(sc->regs); 24286909Simp sc->bsh = rman_get_bushandle(sc->regs); 24386909Simp 24486909Simp sc->initial_aperture = AGP_GET_APERTURE(dev); 24586909Simp 24686909Simp for (;;) { 24786909Simp gatt = agp_amd_alloc_gatt(dev); 24886909Simp if (gatt) 24986909Simp break; 25086909Simp 25186909Simp /* 25286909Simp * Probably contigmalloc failure. Try reducing the 25386909Simp * aperture so that the gatt size reduces. 25486909Simp */ 25586909Simp if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) 25686909Simp return ENOMEM; 25786909Simp } 258111613Sphk sc->gatt = gatt; 259119485Snjl 260119485Snjl /* Install the gatt. */ 261119485Snjl WRITE4(AGP_AMD751_ATTBASE, gatt->ag_pdir); 26286909Simp 26386909Simp /* Enable synchronisation between host and agp. */ 26486909Simp pci_write_config(dev, 26586909Simp AGP_AMD751_MODECTRL, 26686909Simp AGP_AMD751_MODECTRL_SYNEN, 1); 26786909Simp 26889986Sjhay /* Set indexing mode for two-level and enable page dir cache */ 26989986Sjhay pci_write_config(dev, 27086909Simp AGP_AMD751_MODECTRL2, 27186909Simp AGP_AMD751_MODECTRL2_GPDCE, 1); 272116120Sscottl 273116120Sscottl /* Enable the TLB and flush */ 274116120Sscottl WRITE2(AGP_AMD751_STATUS, 27586909Simp READ2(AGP_AMD751_STATUS) | AGP_AMD751_STATUS_GCE); 27686909Simp AGP_FLUSH_TLB(dev); 27786909Simp 27886909Simp return 0; 27986909Simp} 28086909Simp 28186909Simpstatic int 28286909Simpagp_amd_detach(device_t dev) 28386909Simp{ 28486909Simp struct agp_amd_softc *sc = device_get_softc(dev); 28593010Sbde int error; 28651078Speter 28751078Speter error = agp_generic_detach(dev); 28851078Speter if (error) 28993010Sbde return error; 29051078Speter 29193010Sbde /* Disable the TLB.. */ 29293010Sbde WRITE2(AGP_AMD751_STATUS, 29393010Sbde READ2(AGP_AMD751_STATUS) & ~AGP_AMD751_STATUS_GCE); 29493010Sbde 29593010Sbde /* Disable host-agp sync */ 29693010Sbde pci_write_config(dev, AGP_AMD751_MODECTRL, 0x00, 1); 29793010Sbde 29893010Sbde /* Clear the GATT base */ 29993010Sbde WRITE4(AGP_AMD751_ATTBASE, 0); 30093010Sbde 30193010Sbde /* Put the aperture back the way it started. */ 30251078Speter AGP_SET_APERTURE(dev, sc->initial_aperture); 30393010Sbde 30493010Sbde agp_amd_free_gatt(sc->gatt); 30551078Speter 30685365Simp bus_release_resource(dev, SYS_RES_MEMORY, 30770174Sjhb AGP_AMD751_REGISTERS, sc->regs); 30870174Sjhb 30951078Speter return 0; 31051078Speter} 31185365Simp 31251078Speterstatic u_int32_t 31386909Simpagp_amd_get_aperture(device_t dev) 31451078Speter{ 31551078Speter int vas; 31651078Speter 31751078Speter /* 31851078Speter * The aperture size is equal to 32M<<vas. 31951078Speter */ 32051078Speter vas = (pci_read_config(dev, AGP_AMD751_APCTRL, 1) & 0x06) >> 1; 32151078Speter return (32*1024*1024) << vas; 322126080Sphk} 323111815Sphk 324111815Sphkstatic int 325111815Sphkagp_amd_set_aperture(device_t dev, u_int32_t aperture) 326111815Sphk{ 327111815Sphk int vas; 328111815Sphk 329126080Sphk /* 33051078Speter * Check for a power of two and make sure its within the 33151078Speter * programmable range. 332114722Sobrien */ 33351078Speter if (aperture & (aperture - 1) 33489986Sjhay || aperture < 32*1024*1024 33589986Sjhay || aperture > 2U*1024*1024*1024) 33698401Sn_hibma return EINVAL; 33798401Sn_hibma 33898401Sn_hibma vas = ffs(aperture / 32*1024*1024) - 1; 33951078Speter 34051078Speter /* 34198401Sn_hibma * While the size register is bits 1-3 of APCTRL, bit 0 must be 34251078Speter * set for the size value to be 'valid' 34351078Speter */ 34472238Sjhb pci_write_config(dev, AGP_AMD751_APCTRL, 34572238Sjhb (((pci_read_config(dev, AGP_AMD751_APCTRL, 1) & ~0x06) 34651078Speter | ((vas << 1) | 1))), 1); 34751078Speter 34851078Speter return 0; 34951078Speter} 35053344Speter 35151078Speterstatic int 35251078Speteragp_amd_bind_page(device_t dev, int offset, vm_offset_t physical) 35351078Speter{ 35486909Simp struct agp_amd_softc *sc = device_get_softc(dev); 35551078Speter 35651078Speter if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) 35751078Speter return EINVAL; 35851078Speter 35951078Speter sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 1; 36051078Speter 36151078Speter /* invalidate the cache */ 36251078Speter AGP_FLUSH_TLB(dev); 36351078Speter return 0; 36451078Speter} 36551078Speter 36651078Speterstatic int 36751078Speteragp_amd_unbind_page(device_t dev, int offset) 36851078Speter{ 36951078Speter struct agp_amd_softc *sc = device_get_softc(dev); 37062573Sphk 37151078Speter if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) 37251078Speter return EINVAL; 37351078Speter 37451078Speter sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; 37551078Speter return 0; 37651078Speter} 37751078Speter 37851078Speterstatic void 37951078Speteragp_amd_flush_tlb(device_t dev) 38051078Speter{ 38151078Speter struct agp_amd_softc *sc = device_get_softc(dev); 38251078Speter 38351078Speter /* Set the cache invalidate bit and wait for the chipset to clear */ 38451078Speter WRITE4(AGP_AMD751_TLBCTRL, 1); 38551078Speter do { 38651078Speter DELAY(1); 38751078Speter } while (READ4(AGP_AMD751_TLBCTRL)); 38851078Speter} 38957915Simp 39051078Speterstatic device_method_t agp_amd_methods[] = { 39151078Speter /* Device interface */ 39251078Speter DEVMETHOD(device_probe, agp_amd_probe), 39351078Speter DEVMETHOD(device_attach, agp_amd_attach), 39451078Speter DEVMETHOD(device_detach, agp_amd_detach), 39551078Speter DEVMETHOD(device_shutdown, bus_generic_shutdown), 39651078Speter DEVMETHOD(device_suspend, bus_generic_suspend), 39751078Speter DEVMETHOD(device_resume, bus_generic_resume), 39851078Speter 39951078Speter /* AGP interface */ 40051078Speter DEVMETHOD(agp_get_aperture, agp_amd_get_aperture), 40151078Speter DEVMETHOD(agp_set_aperture, agp_amd_set_aperture), 40251078Speter DEVMETHOD(agp_bind_page, agp_amd_bind_page), 40351078Speter DEVMETHOD(agp_unbind_page, agp_amd_unbind_page), 40451078Speter DEVMETHOD(agp_flush_tlb, agp_amd_flush_tlb), 40551078Speter DEVMETHOD(agp_enable, agp_generic_enable), 40651078Speter DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), 40751078Speter DEVMETHOD(agp_free_memory, agp_generic_free_memory), 40851078Speter DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), 40951078Speter DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), 41051078Speter 41151078Speter { 0, 0 } 41251078Speter}; 41351078Speter 41451078Speterstatic driver_t agp_amd_driver = { 41551078Speter "agp", 41651078Speter agp_amd_methods, 41751078Speter sizeof(struct agp_amd_softc), 41891280Simp}; 41951078Speter 42086909Simpstatic devclass_t agp_devclass; 42186909Simp 42286909SimpDRIVER_MODULE(agp_amd, pci, agp_amd_driver, agp_devclass, 0, 0); 42386909SimpMODULE_DEPEND(agp_amd, agp, 1, 1, 1); 42486909SimpMODULE_DEPEND(agp_amd, pci, 1, 1, 1); 42586909Simp