agp_amd.c revision 76827
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 76827 2001-05-19 01:28:09Z alfred $ 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> 3876827Salfred#include <sys/mutex.h> 3961452Sdfr 4061452Sdfr#include <pci/pcivar.h> 4161452Sdfr#include <pci/pcireg.h> 4261452Sdfr#include <pci/agppriv.h> 4361452Sdfr#include <pci/agpreg.h> 4461452Sdfr 4561452Sdfr#include <vm/vm.h> 4661452Sdfr#include <vm/vm_object.h> 4761452Sdfr#include <vm/pmap.h> 4861452Sdfr#include <machine/bus.h> 4961452Sdfr#include <machine/resource.h> 5061452Sdfr#include <sys/rman.h> 5161452Sdfr 5261501SdfrMALLOC_DECLARE(M_AGP); 5361501Sdfr 5461452Sdfr#define READ2(off) bus_space_read_2(sc->bst, sc->bsh, off) 5561452Sdfr#define READ4(off) bus_space_read_4(sc->bst, sc->bsh, off) 5661452Sdfr#define WRITE2(off,v) bus_space_write_2(sc->bst, sc->bsh, off, v) 5761452Sdfr#define WRITE4(off,v) bus_space_write_4(sc->bst, sc->bsh, off, v) 5861452Sdfr 5961501Sdfrstruct agp_amd_gatt { 6061501Sdfr u_int32_t ag_entries; 6161501Sdfr u_int32_t *ag_vdir; /* virtual address of page dir */ 6261501Sdfr vm_offset_t ag_pdir; /* physical address of page dir */ 6361501Sdfr u_int32_t *ag_virtual; /* virtual address of gatt */ 6461501Sdfr}; 6561501Sdfr 6661452Sdfrstruct agp_amd_softc { 6761452Sdfr struct agp_softc agp; 6861452Sdfr struct resource *regs; /* memory mapped control registers */ 6961452Sdfr bus_space_tag_t bst; /* bus_space tag */ 7061452Sdfr bus_space_handle_t bsh; /* bus_space handle */ 7161452Sdfr u_int32_t initial_aperture; /* aperture size at startup */ 7261501Sdfr struct agp_amd_gatt *gatt; 7361452Sdfr}; 7461452Sdfr 7561501Sdfrstatic struct agp_amd_gatt * 7661501Sdfragp_amd_alloc_gatt(device_t dev) 7761501Sdfr{ 7861501Sdfr u_int32_t apsize = AGP_GET_APERTURE(dev); 7961501Sdfr u_int32_t entries = apsize >> AGP_PAGE_SHIFT; 8061501Sdfr struct agp_amd_gatt *gatt; 8161501Sdfr int i, npages; 8261501Sdfr 8361501Sdfr if (bootverbose) 8461501Sdfr device_printf(dev, 8561501Sdfr "allocating GATT for aperture of size %dM\n", 8661501Sdfr apsize / (1024*1024)); 8761501Sdfr 8861501Sdfr gatt = malloc(sizeof(struct agp_amd_gatt), M_AGP, M_NOWAIT); 8961501Sdfr if (!gatt) 9061501Sdfr return 0; 9161501Sdfr 9261501Sdfr /* 9361501Sdfr * The AMD751 uses a page directory to map a non-contiguous 9461501Sdfr * gatt so we don't need to use contigmalloc. 9561501Sdfr */ 9661501Sdfr gatt->ag_entries = entries; 9761501Sdfr gatt->ag_virtual = malloc(entries * sizeof(u_int32_t), 9861501Sdfr M_AGP, M_NOWAIT); 9961501Sdfr if (!gatt->ag_virtual) { 10061501Sdfr if (bootverbose) 10161501Sdfr device_printf(dev, "allocation failed\n"); 10261501Sdfr free(gatt, M_AGP); 10361501Sdfr return 0; 10461501Sdfr } 10561501Sdfr bzero(gatt->ag_virtual, entries * sizeof(u_int32_t)); 10661501Sdfr 10761501Sdfr /* 10861501Sdfr * Allocate the page directory. 10961501Sdfr */ 11061501Sdfr gatt->ag_vdir = malloc(AGP_PAGE_SIZE, M_AGP, M_NOWAIT); 11161501Sdfr if (!gatt->ag_vdir) { 11261501Sdfr if (bootverbose) 11361501Sdfr device_printf(dev, 11461501Sdfr "failed to allocate page directory\n"); 11561501Sdfr free(gatt->ag_virtual, M_AGP); 11661501Sdfr free(gatt, M_AGP); 11761501Sdfr return 0; 11861501Sdfr } 11961501Sdfr bzero(gatt->ag_vdir, AGP_PAGE_SIZE); 12061501Sdfr gatt->ag_pdir = vtophys((vm_offset_t) gatt->ag_vdir); 12161501Sdfr gatt->ag_pdir = vtophys(gatt->ag_virtual); 12261501Sdfr 12361501Sdfr /* 12461501Sdfr * Map the pages of the GATT into the page directory. 12561501Sdfr */ 12661501Sdfr npages = ((entries * sizeof(u_int32_t) + AGP_PAGE_SIZE - 1) 12761501Sdfr >> AGP_PAGE_SHIFT); 12861501Sdfr for (i = 0; i < npages; i++) { 12961501Sdfr vm_offset_t va; 13061501Sdfr vm_offset_t pa; 13161501Sdfr 13261501Sdfr va = ((vm_offset_t) gatt->ag_virtual) + i * AGP_PAGE_SIZE; 13361501Sdfr pa = vtophys(va); 13461501Sdfr gatt->ag_vdir[i] = pa | 1; 13561501Sdfr } 13661501Sdfr 13761501Sdfr /* 13861501Sdfr * Make sure the chipset can see everything. 13961501Sdfr */ 14061501Sdfr agp_flush_cache(); 14161501Sdfr 14261501Sdfr return gatt; 14361501Sdfr} 14461501Sdfr 14561501Sdfrstatic void 14661501Sdfragp_amd_free_gatt(struct agp_amd_gatt *gatt) 14761501Sdfr{ 14861501Sdfr free(gatt->ag_virtual, M_AGP); 14961501Sdfr free(gatt->ag_vdir, M_AGP); 15061501Sdfr free(gatt, M_AGP); 15161501Sdfr} 15261501Sdfr 15361452Sdfrstatic const char* 15461452Sdfragp_amd_match(device_t dev) 15561452Sdfr{ 15661452Sdfr if (pci_get_class(dev) != PCIC_BRIDGE 15761452Sdfr || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) 15861452Sdfr return NULL; 15961452Sdfr 16061452Sdfr if (agp_find_caps(dev) == 0) 16161452Sdfr return NULL; 16261452Sdfr 16361452Sdfr switch (pci_get_devid(dev)) { 16461452Sdfr case 0x70061022: 16561452Sdfr return ("AMD 751 host to AGP bridge"); 16661452Sdfr }; 16761452Sdfr 16861452Sdfr return NULL; 16961452Sdfr} 17061452Sdfr 17161452Sdfrstatic int 17261452Sdfragp_amd_probe(device_t dev) 17361452Sdfr{ 17461452Sdfr const char *desc; 17561452Sdfr 17661452Sdfr desc = agp_amd_match(dev); 17761452Sdfr if (desc) { 17861452Sdfr device_verbose(dev); 17961452Sdfr device_set_desc(dev, desc); 18061452Sdfr return 0; 18161452Sdfr } 18261452Sdfr 18361452Sdfr return ENXIO; 18461452Sdfr} 18561452Sdfr 18661452Sdfrstatic int 18761452Sdfragp_amd_attach(device_t dev) 18861452Sdfr{ 18961452Sdfr struct agp_amd_softc *sc = device_get_softc(dev); 19061501Sdfr struct agp_amd_gatt *gatt; 19161452Sdfr int error, rid; 19261452Sdfr 19361452Sdfr error = agp_generic_attach(dev); 19461452Sdfr if (error) 19561452Sdfr return error; 19661452Sdfr 19761452Sdfr rid = AGP_AMD751_REGISTERS; 19861452Sdfr sc->regs = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 19961452Sdfr 0, ~0, 1, RF_ACTIVE); 20061452Sdfr if (!sc->regs) { 20161452Sdfr agp_generic_detach(dev); 20261452Sdfr return ENOMEM; 20361452Sdfr } 20461452Sdfr 20561452Sdfr sc->bst = rman_get_bustag(sc->regs); 20661452Sdfr sc->bsh = rman_get_bushandle(sc->regs); 20761452Sdfr 20861452Sdfr sc->initial_aperture = AGP_GET_APERTURE(dev); 20961452Sdfr 21061452Sdfr for (;;) { 21161501Sdfr gatt = agp_amd_alloc_gatt(dev); 21261452Sdfr if (gatt) 21361452Sdfr break; 21461452Sdfr 21561452Sdfr /* 21661452Sdfr * Probably contigmalloc failure. Try reducing the 21761452Sdfr * aperture so that the gatt size reduces. 21861452Sdfr */ 21961452Sdfr if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) 22061452Sdfr return ENOMEM; 22161452Sdfr } 22261452Sdfr sc->gatt = gatt; 22361452Sdfr 22461452Sdfr /* Install the gatt. */ 22561501Sdfr WRITE4(AGP_AMD751_ATTBASE, gatt->ag_pdir); 22661452Sdfr 22761452Sdfr /* Enable synchronisation between host and agp. */ 22861501Sdfr pci_write_config(dev, 22961501Sdfr AGP_AMD751_MODECTRL, 23061501Sdfr AGP_AMD751_MODECTRL_SYNEN, 1); 23161452Sdfr 23261501Sdfr /* Set indexing mode for two-level and enable page dir cache */ 23361501Sdfr pci_write_config(dev, 23461501Sdfr AGP_AMD751_MODECTRL2, 23561501Sdfr AGP_AMD751_MODECTRL2_GPDCE, 1); 23661501Sdfr 23761452Sdfr /* Enable the TLB and flush */ 23861452Sdfr WRITE2(AGP_AMD751_STATUS, 23961452Sdfr READ2(AGP_AMD751_STATUS) | AGP_AMD751_STATUS_GCE); 24061452Sdfr AGP_FLUSH_TLB(dev); 24161452Sdfr 24261501Sdfr return 0; 24361452Sdfr} 24461452Sdfr 24561452Sdfrstatic int 24661452Sdfragp_amd_detach(device_t dev) 24761452Sdfr{ 24861452Sdfr struct agp_amd_softc *sc = device_get_softc(dev); 24961503Sdfr int error; 25061452Sdfr 25161503Sdfr error = agp_generic_detach(dev); 25261503Sdfr if (error) 25361503Sdfr return error; 25461503Sdfr 25561452Sdfr /* Disable the TLB.. */ 25661452Sdfr WRITE2(AGP_AMD751_STATUS, 25761452Sdfr READ2(AGP_AMD751_STATUS) & ~AGP_AMD751_STATUS_GCE); 25861452Sdfr 25961452Sdfr /* Disable host-agp sync */ 26061452Sdfr pci_write_config(dev, AGP_AMD751_MODECTRL, 0x00, 1); 26161452Sdfr 26261452Sdfr /* Clear the GATT base */ 26361452Sdfr WRITE4(AGP_AMD751_ATTBASE, 0); 26461452Sdfr 26561452Sdfr /* Put the aperture back the way it started. */ 26661452Sdfr AGP_SET_APERTURE(dev, sc->initial_aperture); 26761452Sdfr 26861501Sdfr agp_amd_free_gatt(sc->gatt); 26961503Sdfr 27061503Sdfr bus_release_resource(dev, SYS_RES_MEMORY, 27161503Sdfr AGP_AMD751_REGISTERS, sc->regs); 27261503Sdfr 27361452Sdfr return 0; 27461452Sdfr} 27561452Sdfr 27661452Sdfrstatic u_int32_t 27761452Sdfragp_amd_get_aperture(device_t dev) 27861452Sdfr{ 27961452Sdfr int vas; 28061452Sdfr 28161452Sdfr /* 28261452Sdfr * The aperture size is equal to 32M<<vas. 28361452Sdfr */ 28461452Sdfr vas = (pci_read_config(dev, AGP_AMD751_APCTRL, 1) & 0x06) >> 1; 28561452Sdfr return (32*1024*1024) << vas; 28661452Sdfr} 28761452Sdfr 28861452Sdfrstatic int 28961452Sdfragp_amd_set_aperture(device_t dev, u_int32_t aperture) 29061452Sdfr{ 29161452Sdfr int vas; 29261452Sdfr 29361452Sdfr /* 29461452Sdfr * Check for a power of two and make sure its within the 29561452Sdfr * programmable range. 29661452Sdfr */ 29761452Sdfr if (aperture & (aperture - 1) 29861452Sdfr || aperture < 32*1024*1024 29961452Sdfr || aperture > 2U*1024*1024*1024) 30061452Sdfr return EINVAL; 30161452Sdfr 30261452Sdfr vas = ffs(aperture / 32*1024*1024) - 1; 30361452Sdfr 30461452Sdfr pci_write_config(dev, AGP_AMD751_APCTRL, 30561452Sdfr ((pci_read_config(dev, AGP_AMD751_APCTRL, 1) & ~0x06) 30661452Sdfr | vas << 1), 1); 30761452Sdfr 30861452Sdfr return 0; 30961452Sdfr} 31061452Sdfr 31161452Sdfrstatic int 31261452Sdfragp_amd_bind_page(device_t dev, int offset, vm_offset_t physical) 31361452Sdfr{ 31461452Sdfr struct agp_amd_softc *sc = device_get_softc(dev); 31561452Sdfr 31661452Sdfr if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) 31761452Sdfr return EINVAL; 31861452Sdfr 31961452Sdfr sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 1; 32061452Sdfr return 0; 32161452Sdfr} 32261452Sdfr 32361452Sdfrstatic int 32461452Sdfragp_amd_unbind_page(device_t dev, int offset) 32561452Sdfr{ 32661452Sdfr struct agp_amd_softc *sc = device_get_softc(dev); 32761452Sdfr 32861452Sdfr if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) 32961452Sdfr return EINVAL; 33061452Sdfr 33161452Sdfr sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; 33261452Sdfr return 0; 33361452Sdfr} 33461452Sdfr 33561452Sdfrstatic void 33661452Sdfragp_amd_flush_tlb(device_t dev) 33761452Sdfr{ 33861452Sdfr struct agp_amd_softc *sc = device_get_softc(dev); 33961452Sdfr 34061452Sdfr /* Set the cache invalidate bit and wait for the chipset to clear */ 34161452Sdfr WRITE4(AGP_AMD751_TLBCTRL, 1); 34261452Sdfr do { 34361452Sdfr DELAY(1); 34461452Sdfr } while (READ4(AGP_AMD751_TLBCTRL)); 34561452Sdfr} 34661452Sdfr 34761452Sdfrstatic device_method_t agp_amd_methods[] = { 34861452Sdfr /* Device interface */ 34961452Sdfr DEVMETHOD(device_probe, agp_amd_probe), 35061452Sdfr DEVMETHOD(device_attach, agp_amd_attach), 35161452Sdfr DEVMETHOD(device_detach, agp_amd_detach), 35261452Sdfr DEVMETHOD(device_shutdown, bus_generic_shutdown), 35361452Sdfr DEVMETHOD(device_suspend, bus_generic_suspend), 35461452Sdfr DEVMETHOD(device_resume, bus_generic_resume), 35561452Sdfr 35661452Sdfr /* AGP interface */ 35761452Sdfr DEVMETHOD(agp_get_aperture, agp_amd_get_aperture), 35861452Sdfr DEVMETHOD(agp_set_aperture, agp_amd_set_aperture), 35961452Sdfr DEVMETHOD(agp_bind_page, agp_amd_bind_page), 36061452Sdfr DEVMETHOD(agp_unbind_page, agp_amd_unbind_page), 36161452Sdfr DEVMETHOD(agp_flush_tlb, agp_amd_flush_tlb), 36261452Sdfr DEVMETHOD(agp_enable, agp_generic_enable), 36361452Sdfr DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), 36461452Sdfr DEVMETHOD(agp_free_memory, agp_generic_free_memory), 36561452Sdfr DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), 36661452Sdfr DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), 36761452Sdfr 36861452Sdfr { 0, 0 } 36961452Sdfr}; 37061452Sdfr 37161452Sdfrstatic driver_t agp_amd_driver = { 37261452Sdfr "agp", 37361452Sdfr agp_amd_methods, 37461452Sdfr sizeof(struct agp_amd_softc), 37561452Sdfr}; 37661452Sdfr 37761452Sdfrstatic devclass_t agp_devclass; 37861452Sdfr 37961452SdfrDRIVER_MODULE(agp_amd, pci, agp_amd_driver, agp_devclass, 0, 0); 380