agp_amd.c revision 105145
161452Sdfr/*- 261452Sdfr * Copyright (c) 2000 Doug Rabson 361452Sdfr * All rights reserved. 461452Sdfr * 561452Sdfr * Redistribution and use in source and binary forms, with or without 661452Sdfr * modification, are permitted provided that the following conditions 761452Sdfr * are met: 861452Sdfr * 1. Redistributions of source code must retain the above copyright 961452Sdfr * notice, this list of conditions and the following disclaimer. 1061452Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1161452Sdfr * notice, this list of conditions and the following disclaimer in the 1261452Sdfr * documentation and/or other materials provided with the distribution. 1361452Sdfr * 1461452Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1561452Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1661452Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1761452Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1861452Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1961452Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2061452Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2161452Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2261452Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2361452Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2461452Sdfr * SUCH DAMAGE. 2561452Sdfr * 2661452Sdfr * $FreeBSD: head/sys/dev/agp/agp_amd.c 105145 2002-10-15 01:50:09Z marcel $ 2761452Sdfr */ 2861452Sdfr 2961452Sdfr#include "opt_bus.h" 3061452Sdfr#include "opt_pci.h" 3161452Sdfr 3261452Sdfr#include <sys/param.h> 3361452Sdfr#include <sys/systm.h> 3461452Sdfr#include <sys/malloc.h> 3561452Sdfr#include <sys/kernel.h> 3661452Sdfr#include <sys/bus.h> 3761452Sdfr#include <sys/lock.h> 38102480Sbde#include <sys/lockmgr.h> 3976827Salfred#include <sys/mutex.h> 4079339Sjhb#include <sys/proc.h> 4161452Sdfr 4261452Sdfr#include <pci/pcivar.h> 4361452Sdfr#include <pci/pcireg.h> 4461452Sdfr#include <pci/agppriv.h> 4561452Sdfr#include <pci/agpreg.h> 4661452Sdfr 4761452Sdfr#include <vm/vm.h> 4861452Sdfr#include <vm/vm_object.h> 4961452Sdfr#include <vm/pmap.h> 5061452Sdfr#include <machine/bus.h> 5161452Sdfr#include <machine/resource.h> 5261452Sdfr#include <sys/rman.h> 5361452Sdfr 5461501SdfrMALLOC_DECLARE(M_AGP); 5561501Sdfr 5661452Sdfr#define READ2(off) bus_space_read_2(sc->bst, sc->bsh, off) 5761452Sdfr#define READ4(off) bus_space_read_4(sc->bst, sc->bsh, off) 5861452Sdfr#define WRITE2(off,v) bus_space_write_2(sc->bst, sc->bsh, off, v) 5961452Sdfr#define WRITE4(off,v) bus_space_write_4(sc->bst, sc->bsh, off, v) 6061452Sdfr 6161501Sdfrstruct agp_amd_gatt { 6261501Sdfr u_int32_t ag_entries; 6387479Scokane u_int32_t *ag_virtual; /* virtual address of gatt */ 6487479Scokane vm_offset_t ag_physical; 6561501Sdfr u_int32_t *ag_vdir; /* virtual address of page dir */ 6661501Sdfr vm_offset_t ag_pdir; /* physical address of page dir */ 6761501Sdfr}; 6861501Sdfr 6961452Sdfrstruct agp_amd_softc { 7061452Sdfr struct agp_softc agp; 7161452Sdfr struct resource *regs; /* memory mapped control registers */ 7261452Sdfr bus_space_tag_t bst; /* bus_space tag */ 7361452Sdfr bus_space_handle_t bsh; /* bus_space handle */ 7461452Sdfr u_int32_t initial_aperture; /* aperture size at startup */ 7561501Sdfr struct agp_amd_gatt *gatt; 7661452Sdfr}; 7761452Sdfr 7861501Sdfrstatic struct agp_amd_gatt * 7961501Sdfragp_amd_alloc_gatt(device_t dev) 8061501Sdfr{ 8161501Sdfr u_int32_t apsize = AGP_GET_APERTURE(dev); 8261501Sdfr u_int32_t entries = apsize >> AGP_PAGE_SHIFT; 8361501Sdfr struct agp_amd_gatt *gatt; 8487479Scokane int i, npages, pdir_offset; 8561501Sdfr 8661501Sdfr if (bootverbose) 8761501Sdfr device_printf(dev, 8861501Sdfr "allocating GATT for aperture of size %dM\n", 8961501Sdfr apsize / (1024*1024)); 9061501Sdfr 9161501Sdfr gatt = malloc(sizeof(struct agp_amd_gatt), M_AGP, M_NOWAIT); 9261501Sdfr if (!gatt) 9361501Sdfr return 0; 9461501Sdfr 9561501Sdfr /* 9661501Sdfr * The AMD751 uses a page directory to map a non-contiguous 9761501Sdfr * gatt so we don't need to use contigmalloc. 9887479Scokane * Malloc individual gatt pages and map them into the page 9987479Scokane * directory. 10061501Sdfr */ 10161501Sdfr gatt->ag_entries = entries; 10261501Sdfr gatt->ag_virtual = malloc(entries * sizeof(u_int32_t), 10361501Sdfr M_AGP, M_NOWAIT); 10461501Sdfr if (!gatt->ag_virtual) { 10561501Sdfr if (bootverbose) 10661501Sdfr device_printf(dev, "allocation failed\n"); 10761501Sdfr free(gatt, M_AGP); 10861501Sdfr return 0; 10961501Sdfr } 11061501Sdfr bzero(gatt->ag_virtual, entries * sizeof(u_int32_t)); 11161501Sdfr 11261501Sdfr /* 11361501Sdfr * Allocate the page directory. 11461501Sdfr */ 11561501Sdfr gatt->ag_vdir = malloc(AGP_PAGE_SIZE, M_AGP, M_NOWAIT); 11661501Sdfr if (!gatt->ag_vdir) { 11761501Sdfr if (bootverbose) 11861501Sdfr device_printf(dev, 11961501Sdfr "failed to allocate page directory\n"); 12061501Sdfr free(gatt->ag_virtual, M_AGP); 12161501Sdfr free(gatt, M_AGP); 12261501Sdfr return 0; 12361501Sdfr } 12494790Scokane bzero(gatt->ag_vdir, AGP_PAGE_SIZE); 12594790Scokane 12661501Sdfr gatt->ag_pdir = vtophys((vm_offset_t) gatt->ag_vdir); 12787479Scokane if(bootverbose) 128105145Smarcel device_printf(dev, "gatt -> ag_pdir %#lx\n", 129105145Smarcel (u_long)gatt->ag_pdir); 13087479Scokane /* 13187479Scokane * Allocate the gatt pages 13287479Scokane */ 13387479Scokane gatt->ag_entries = entries; 13487479Scokane if(bootverbose) 13587479Scokane device_printf(dev, "allocating GATT for %d AGP page entries\n", 13687479Scokane gatt->ag_entries); 13794790Scokane 13887479Scokane gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual); 13961501Sdfr 14061501Sdfr /* 14161501Sdfr * Map the pages of the GATT into the page directory. 14287479Scokane * 14387479Scokane * The GATT page addresses are mapped into the directory offset by 14487479Scokane * an amount dependent on the base address of the aperture. This 14587479Scokane * is and offset into the page directory, not an offset added to 14687479Scokane * the addresses of the gatt pages. 14761501Sdfr */ 14887479Scokane 14987479Scokane pdir_offset = pci_read_config(dev, AGP_AMD751_APBASE, 4) >> 22; 15087479Scokane 15161501Sdfr npages = ((entries * sizeof(u_int32_t) + AGP_PAGE_SIZE - 1) 15261501Sdfr >> AGP_PAGE_SHIFT); 15387479Scokane 15461501Sdfr for (i = 0; i < npages; i++) { 15561501Sdfr vm_offset_t va; 15661501Sdfr vm_offset_t pa; 15761501Sdfr 15861501Sdfr va = ((vm_offset_t) gatt->ag_virtual) + i * AGP_PAGE_SIZE; 15961501Sdfr pa = vtophys(va); 16087479Scokane gatt->ag_vdir[i + pdir_offset] = pa | 1; 16161501Sdfr } 16261501Sdfr 16361501Sdfr /* 16461501Sdfr * Make sure the chipset can see everything. 16561501Sdfr */ 16661501Sdfr agp_flush_cache(); 16761501Sdfr 16861501Sdfr return gatt; 16961501Sdfr} 17061501Sdfr 17161501Sdfrstatic void 17261501Sdfragp_amd_free_gatt(struct agp_amd_gatt *gatt) 17361501Sdfr{ 17461501Sdfr free(gatt->ag_virtual, M_AGP); 17561501Sdfr free(gatt->ag_vdir, M_AGP); 17661501Sdfr free(gatt, M_AGP); 17761501Sdfr} 17861501Sdfr 17961452Sdfrstatic const char* 18061452Sdfragp_amd_match(device_t dev) 18161452Sdfr{ 18261452Sdfr if (pci_get_class(dev) != PCIC_BRIDGE 18361452Sdfr || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) 18461452Sdfr return NULL; 18561452Sdfr 18661452Sdfr if (agp_find_caps(dev) == 0) 18761452Sdfr return NULL; 18861452Sdfr 18961452Sdfr switch (pci_get_devid(dev)) { 19087479Scokane 19183699Scokane case 0x700e1022: 19283699Scokane return ("AMD 761 host to AGP bridge"); 19387479Scokane 19461452Sdfr case 0x70061022: 19561452Sdfr return ("AMD 751 host to AGP bridge"); 19687479Scokane 19787479Scokane case 0x700c1022: 19887479Scokane return ("AMD 762 host to AGP bridge"); 19987479Scokane 20061452Sdfr }; 20161452Sdfr 20261452Sdfr return NULL; 20361452Sdfr} 20461452Sdfr 20561452Sdfrstatic int 20661452Sdfragp_amd_probe(device_t dev) 20761452Sdfr{ 20861452Sdfr const char *desc; 20961452Sdfr 21061452Sdfr desc = agp_amd_match(dev); 21161452Sdfr if (desc) { 21261452Sdfr device_verbose(dev); 21361452Sdfr device_set_desc(dev, desc); 21461452Sdfr return 0; 21561452Sdfr } 21661452Sdfr 21761452Sdfr return ENXIO; 21861452Sdfr} 21961452Sdfr 22061452Sdfrstatic int 22161452Sdfragp_amd_attach(device_t dev) 22261452Sdfr{ 22361452Sdfr struct agp_amd_softc *sc = device_get_softc(dev); 22461501Sdfr struct agp_amd_gatt *gatt; 22561452Sdfr int error, rid; 22661452Sdfr 22761452Sdfr error = agp_generic_attach(dev); 22861452Sdfr if (error) 22961452Sdfr return error; 23061452Sdfr 23161452Sdfr rid = AGP_AMD751_REGISTERS; 23261452Sdfr sc->regs = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 23361452Sdfr 0, ~0, 1, RF_ACTIVE); 23461452Sdfr if (!sc->regs) { 23561452Sdfr agp_generic_detach(dev); 23661452Sdfr return ENOMEM; 23761452Sdfr } 23861452Sdfr 23961452Sdfr sc->bst = rman_get_bustag(sc->regs); 24061452Sdfr sc->bsh = rman_get_bushandle(sc->regs); 24161452Sdfr 24261452Sdfr sc->initial_aperture = AGP_GET_APERTURE(dev); 24361452Sdfr 24461452Sdfr for (;;) { 24561501Sdfr gatt = agp_amd_alloc_gatt(dev); 24661452Sdfr if (gatt) 24761452Sdfr break; 24861452Sdfr 24961452Sdfr /* 25061452Sdfr * Probably contigmalloc failure. Try reducing the 25161452Sdfr * aperture so that the gatt size reduces. 25261452Sdfr */ 25361452Sdfr if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) 25461452Sdfr return ENOMEM; 25561452Sdfr } 25661452Sdfr sc->gatt = gatt; 25761452Sdfr 25861452Sdfr /* Install the gatt. */ 25961501Sdfr WRITE4(AGP_AMD751_ATTBASE, gatt->ag_pdir); 26061452Sdfr 26161452Sdfr /* Enable synchronisation between host and agp. */ 26261501Sdfr pci_write_config(dev, 26361501Sdfr AGP_AMD751_MODECTRL, 26461501Sdfr AGP_AMD751_MODECTRL_SYNEN, 1); 26561452Sdfr 26661501Sdfr /* Set indexing mode for two-level and enable page dir cache */ 26761501Sdfr pci_write_config(dev, 26861501Sdfr AGP_AMD751_MODECTRL2, 26961501Sdfr AGP_AMD751_MODECTRL2_GPDCE, 1); 27061501Sdfr 27161452Sdfr /* Enable the TLB and flush */ 27261452Sdfr WRITE2(AGP_AMD751_STATUS, 27361452Sdfr READ2(AGP_AMD751_STATUS) | AGP_AMD751_STATUS_GCE); 27461452Sdfr AGP_FLUSH_TLB(dev); 27561452Sdfr 27661501Sdfr return 0; 27761452Sdfr} 27861452Sdfr 27961452Sdfrstatic int 28061452Sdfragp_amd_detach(device_t dev) 28161452Sdfr{ 28261452Sdfr struct agp_amd_softc *sc = device_get_softc(dev); 28361503Sdfr int error; 28461452Sdfr 28561503Sdfr error = agp_generic_detach(dev); 28661503Sdfr if (error) 28761503Sdfr return error; 28861503Sdfr 28961452Sdfr /* Disable the TLB.. */ 29061452Sdfr WRITE2(AGP_AMD751_STATUS, 29161452Sdfr READ2(AGP_AMD751_STATUS) & ~AGP_AMD751_STATUS_GCE); 29261452Sdfr 29361452Sdfr /* Disable host-agp sync */ 29461452Sdfr pci_write_config(dev, AGP_AMD751_MODECTRL, 0x00, 1); 29561452Sdfr 29661452Sdfr /* Clear the GATT base */ 29761452Sdfr WRITE4(AGP_AMD751_ATTBASE, 0); 29861452Sdfr 29961452Sdfr /* Put the aperture back the way it started. */ 30061452Sdfr AGP_SET_APERTURE(dev, sc->initial_aperture); 30161452Sdfr 30261501Sdfr agp_amd_free_gatt(sc->gatt); 30361503Sdfr 30461503Sdfr bus_release_resource(dev, SYS_RES_MEMORY, 30561503Sdfr AGP_AMD751_REGISTERS, sc->regs); 30661503Sdfr 30761452Sdfr return 0; 30861452Sdfr} 30961452Sdfr 31061452Sdfrstatic u_int32_t 31161452Sdfragp_amd_get_aperture(device_t dev) 31261452Sdfr{ 31361452Sdfr int vas; 31461452Sdfr 31561452Sdfr /* 31661452Sdfr * The aperture size is equal to 32M<<vas. 31761452Sdfr */ 31861452Sdfr vas = (pci_read_config(dev, AGP_AMD751_APCTRL, 1) & 0x06) >> 1; 31961452Sdfr return (32*1024*1024) << vas; 32061452Sdfr} 32161452Sdfr 32261452Sdfrstatic int 32361452Sdfragp_amd_set_aperture(device_t dev, u_int32_t aperture) 32461452Sdfr{ 32561452Sdfr int vas; 32661452Sdfr 32761452Sdfr /* 32861452Sdfr * Check for a power of two and make sure its within the 32961452Sdfr * programmable range. 33061452Sdfr */ 33161452Sdfr if (aperture & (aperture - 1) 33261452Sdfr || aperture < 32*1024*1024 33361452Sdfr || aperture > 2U*1024*1024*1024) 33461452Sdfr return EINVAL; 33561452Sdfr 33661452Sdfr vas = ffs(aperture / 32*1024*1024) - 1; 33761452Sdfr 33887479Scokane /* 33987479Scokane * While the size register is bits 1-3 of APCTRL, bit 0 must be 34087479Scokane * set for the size value to be 'valid' 34187479Scokane */ 34261452Sdfr pci_write_config(dev, AGP_AMD751_APCTRL, 34387479Scokane (((pci_read_config(dev, AGP_AMD751_APCTRL, 1) & ~0x06) 34487479Scokane | ((vas << 1) | 1))), 1); 34561452Sdfr 34661452Sdfr return 0; 34761452Sdfr} 34861452Sdfr 34961452Sdfrstatic int 35061452Sdfragp_amd_bind_page(device_t dev, int offset, vm_offset_t physical) 35161452Sdfr{ 35261452Sdfr struct agp_amd_softc *sc = device_get_softc(dev); 35361452Sdfr 35461452Sdfr if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) 35561452Sdfr return EINVAL; 35661452Sdfr 35761452Sdfr sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 1; 35887479Scokane 35987479Scokane /* invalidate the cache */ 36087479Scokane AGP_FLUSH_TLB(dev); 36161452Sdfr return 0; 36261452Sdfr} 36361452Sdfr 36461452Sdfrstatic int 36561452Sdfragp_amd_unbind_page(device_t dev, int offset) 36661452Sdfr{ 36761452Sdfr struct agp_amd_softc *sc = device_get_softc(dev); 36861452Sdfr 36961452Sdfr if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) 37061452Sdfr return EINVAL; 37161452Sdfr 37261452Sdfr sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; 37361452Sdfr return 0; 37461452Sdfr} 37561452Sdfr 37661452Sdfrstatic void 37761452Sdfragp_amd_flush_tlb(device_t dev) 37861452Sdfr{ 37961452Sdfr struct agp_amd_softc *sc = device_get_softc(dev); 38061452Sdfr 38161452Sdfr /* Set the cache invalidate bit and wait for the chipset to clear */ 38261452Sdfr WRITE4(AGP_AMD751_TLBCTRL, 1); 38361452Sdfr do { 38461452Sdfr DELAY(1); 38561452Sdfr } while (READ4(AGP_AMD751_TLBCTRL)); 38661452Sdfr} 38761452Sdfr 38861452Sdfrstatic device_method_t agp_amd_methods[] = { 38961452Sdfr /* Device interface */ 39061452Sdfr DEVMETHOD(device_probe, agp_amd_probe), 39161452Sdfr DEVMETHOD(device_attach, agp_amd_attach), 39261452Sdfr DEVMETHOD(device_detach, agp_amd_detach), 39361452Sdfr DEVMETHOD(device_shutdown, bus_generic_shutdown), 39461452Sdfr DEVMETHOD(device_suspend, bus_generic_suspend), 39561452Sdfr DEVMETHOD(device_resume, bus_generic_resume), 39661452Sdfr 39761452Sdfr /* AGP interface */ 39861452Sdfr DEVMETHOD(agp_get_aperture, agp_amd_get_aperture), 39961452Sdfr DEVMETHOD(agp_set_aperture, agp_amd_set_aperture), 40061452Sdfr DEVMETHOD(agp_bind_page, agp_amd_bind_page), 40161452Sdfr DEVMETHOD(agp_unbind_page, agp_amd_unbind_page), 40261452Sdfr DEVMETHOD(agp_flush_tlb, agp_amd_flush_tlb), 40361452Sdfr DEVMETHOD(agp_enable, agp_generic_enable), 40461452Sdfr DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), 40561452Sdfr DEVMETHOD(agp_free_memory, agp_generic_free_memory), 40661452Sdfr DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), 40761452Sdfr DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), 40861452Sdfr 40961452Sdfr { 0, 0 } 41061452Sdfr}; 41161452Sdfr 41261452Sdfrstatic driver_t agp_amd_driver = { 41361452Sdfr "agp", 41461452Sdfr agp_amd_methods, 41561452Sdfr sizeof(struct agp_amd_softc), 41661452Sdfr}; 41761452Sdfr 41861452Sdfrstatic devclass_t agp_devclass; 41961452Sdfr 42061452SdfrDRIVER_MODULE(agp_amd, pci, agp_amd_driver, agp_devclass, 0, 0); 421