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 27116192Sobrien#include <sys/cdefs.h> 28116192Sobrien__FBSDID("$FreeBSD: releng/10.2/sys/dev/agp/agp_amd.c 275406 2014-12-02 13:46:13Z tijl $"); 29116192Sobrien 3061452Sdfr#include <sys/param.h> 3161452Sdfr#include <sys/systm.h> 3261452Sdfr#include <sys/malloc.h> 3361452Sdfr#include <sys/kernel.h> 34129878Sphk#include <sys/module.h> 3561452Sdfr#include <sys/bus.h> 3661452Sdfr#include <sys/lock.h> 3776827Salfred#include <sys/mutex.h> 3879339Sjhb#include <sys/proc.h> 3961452Sdfr 40173573Sjhb#include <dev/agp/agppriv.h> 41173573Sjhb#include <dev/agp/agpreg.h> 42119288Simp#include <dev/pci/pcivar.h> 43119288Simp#include <dev/pci/pcireg.h> 4461452Sdfr 4561452Sdfr#include <vm/vm.h> 46275406Stijl#include <vm/vm_extern.h> 47275406Stijl#include <vm/vm_kern.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 { 70133851Sobrien struct agp_softc agp; 71133851Sobrien struct resource *regs; /* memory mapped control registers */ 72133851Sobrien bus_space_tag_t bst; /* bus_space tag */ 73133851Sobrien bus_space_handle_t bsh; /* bus_space handle */ 74133851Sobrien u_int32_t initial_aperture; /* aperture size at startup */ 75133851Sobrien 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 97275406Stijl * gatt so we don't need to use kmem_alloc_contig. 98275406Stijl * Allocate individual GATT pages and map them into the page 9987479Scokane * directory. 10061501Sdfr */ 10161501Sdfr gatt->ag_entries = entries; 102275406Stijl gatt->ag_virtual = (void *)kmem_alloc_attr(kernel_arena, 103275406Stijl entries * sizeof(u_int32_t), M_NOWAIT | M_ZERO, 0, ~0, 104275406Stijl VM_MEMATTR_WRITE_COMBINING); 10561501Sdfr if (!gatt->ag_virtual) { 10661501Sdfr if (bootverbose) 10761501Sdfr device_printf(dev, "allocation failed\n"); 10861501Sdfr free(gatt, M_AGP); 10961501Sdfr return 0; 11061501Sdfr } 11161501Sdfr 11261501Sdfr /* 11361501Sdfr * Allocate the page directory. 11461501Sdfr */ 115275406Stijl gatt->ag_vdir = (void *)kmem_alloc_attr(kernel_arena, AGP_PAGE_SIZE, 116275406Stijl M_NOWAIT | M_ZERO, 0, ~0, VM_MEMATTR_WRITE_COMBINING); 11761501Sdfr if (!gatt->ag_vdir) { 11861501Sdfr if (bootverbose) 11961501Sdfr device_printf(dev, 12061501Sdfr "failed to allocate page directory\n"); 121275406Stijl kmem_free(kernel_arena, (vm_offset_t)gatt->ag_virtual, 122275406Stijl entries * sizeof(u_int32_t)); 12361501Sdfr free(gatt, M_AGP); 12461501Sdfr return 0; 12561501Sdfr } 12694790Scokane 12761501Sdfr gatt->ag_pdir = vtophys((vm_offset_t) gatt->ag_vdir); 12887479Scokane if(bootverbose) 129105145Smarcel device_printf(dev, "gatt -> ag_pdir %#lx\n", 130105145Smarcel (u_long)gatt->ag_pdir); 13187479Scokane /* 13287479Scokane * Allocate the gatt pages 13387479Scokane */ 13487479Scokane gatt->ag_entries = entries; 13587479Scokane if(bootverbose) 13687479Scokane device_printf(dev, "allocating GATT for %d AGP page entries\n", 13787479Scokane gatt->ag_entries); 13894790Scokane 13987479Scokane gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual); 14061501Sdfr 14161501Sdfr /* 14261501Sdfr * Map the pages of the GATT into the page directory. 14387479Scokane * 14487479Scokane * The GATT page addresses are mapped into the directory offset by 14587479Scokane * an amount dependent on the base address of the aperture. This 14687479Scokane * is and offset into the page directory, not an offset added to 14787479Scokane * the addresses of the gatt pages. 14861501Sdfr */ 14987479Scokane 15087479Scokane pdir_offset = pci_read_config(dev, AGP_AMD751_APBASE, 4) >> 22; 15187479Scokane 15261501Sdfr npages = ((entries * sizeof(u_int32_t) + AGP_PAGE_SIZE - 1) 15361501Sdfr >> AGP_PAGE_SHIFT); 15487479Scokane 15561501Sdfr for (i = 0; i < npages; i++) { 15661501Sdfr vm_offset_t va; 15761501Sdfr vm_offset_t pa; 15861501Sdfr 15961501Sdfr va = ((vm_offset_t) gatt->ag_virtual) + i * AGP_PAGE_SIZE; 16061501Sdfr pa = vtophys(va); 16187479Scokane gatt->ag_vdir[i + pdir_offset] = pa | 1; 16261501Sdfr } 16361501Sdfr 16461501Sdfr return gatt; 16561501Sdfr} 16661501Sdfr 16761501Sdfrstatic void 16861501Sdfragp_amd_free_gatt(struct agp_amd_gatt *gatt) 16961501Sdfr{ 170275406Stijl kmem_free(kernel_arena, (vm_offset_t)gatt->ag_vdir, AGP_PAGE_SIZE); 171275406Stijl kmem_free(kernel_arena, (vm_offset_t)gatt->ag_virtual, 172275406Stijl gatt->ag_entries * sizeof(u_int32_t)); 17361501Sdfr free(gatt, M_AGP); 17461501Sdfr} 17561501Sdfr 17661452Sdfrstatic const char* 17761452Sdfragp_amd_match(device_t dev) 17861452Sdfr{ 17961452Sdfr if (pci_get_class(dev) != PCIC_BRIDGE 18061452Sdfr || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) 18161452Sdfr return NULL; 18261452Sdfr 18361452Sdfr if (agp_find_caps(dev) == 0) 18461452Sdfr return NULL; 18561452Sdfr 18661452Sdfr switch (pci_get_devid(dev)) { 187133851Sobrien case 0x70061022: 188133851Sobrien return ("AMD 751 host to AGP bridge"); 18983699Scokane case 0x700e1022: 19083699Scokane return ("AMD 761 host to AGP bridge"); 19187479Scokane case 0x700c1022: 19287479Scokane return ("AMD 762 host to AGP bridge"); 193244926Santoine } 19461452Sdfr 19561452Sdfr return NULL; 19661452Sdfr} 19761452Sdfr 19861452Sdfrstatic int 19961452Sdfragp_amd_probe(device_t dev) 20061452Sdfr{ 20161452Sdfr const char *desc; 20261452Sdfr 203241885Seadler if (resource_disabled("agp", device_get_unit(dev))) 204241885Seadler return (ENXIO); 20561452Sdfr desc = agp_amd_match(dev); 20661452Sdfr if (desc) { 20761452Sdfr device_set_desc(dev, desc); 208142398Simp return BUS_PROBE_DEFAULT; 20961452Sdfr } 21061452Sdfr 21161452Sdfr return ENXIO; 21261452Sdfr} 21361452Sdfr 21461452Sdfrstatic int 21561452Sdfragp_amd_attach(device_t dev) 21661452Sdfr{ 21761452Sdfr struct agp_amd_softc *sc = device_get_softc(dev); 21861501Sdfr struct agp_amd_gatt *gatt; 21961452Sdfr int error, rid; 22061452Sdfr 22161452Sdfr error = agp_generic_attach(dev); 22261452Sdfr if (error) 22361452Sdfr return error; 22461452Sdfr 22561452Sdfr rid = AGP_AMD751_REGISTERS; 226127135Snjl sc->regs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 227127135Snjl RF_ACTIVE); 22861452Sdfr if (!sc->regs) { 22961452Sdfr agp_generic_detach(dev); 23061452Sdfr return ENOMEM; 23161452Sdfr } 23261452Sdfr 23361452Sdfr sc->bst = rman_get_bustag(sc->regs); 23461452Sdfr sc->bsh = rman_get_bushandle(sc->regs); 23561452Sdfr 23661452Sdfr sc->initial_aperture = AGP_GET_APERTURE(dev); 23761452Sdfr 23861452Sdfr for (;;) { 23961501Sdfr gatt = agp_amd_alloc_gatt(dev); 24061452Sdfr if (gatt) 24161452Sdfr break; 24261452Sdfr 24361452Sdfr /* 24461452Sdfr * Probably contigmalloc failure. Try reducing the 24561452Sdfr * aperture so that the gatt size reduces. 24661452Sdfr */ 24761452Sdfr if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) 24861452Sdfr return ENOMEM; 24961452Sdfr } 25061452Sdfr sc->gatt = gatt; 25161452Sdfr 25261452Sdfr /* Install the gatt. */ 25361501Sdfr WRITE4(AGP_AMD751_ATTBASE, gatt->ag_pdir); 25461452Sdfr 25561452Sdfr /* Enable synchronisation between host and agp. */ 25661501Sdfr pci_write_config(dev, 25761501Sdfr AGP_AMD751_MODECTRL, 25861501Sdfr AGP_AMD751_MODECTRL_SYNEN, 1); 25961452Sdfr 26061501Sdfr /* Set indexing mode for two-level and enable page dir cache */ 26161501Sdfr pci_write_config(dev, 26261501Sdfr AGP_AMD751_MODECTRL2, 26361501Sdfr AGP_AMD751_MODECTRL2_GPDCE, 1); 26461501Sdfr 26561452Sdfr /* Enable the TLB and flush */ 26661452Sdfr WRITE2(AGP_AMD751_STATUS, 26761452Sdfr READ2(AGP_AMD751_STATUS) | AGP_AMD751_STATUS_GCE); 26861452Sdfr AGP_FLUSH_TLB(dev); 26961452Sdfr 27061501Sdfr return 0; 27161452Sdfr} 27261452Sdfr 27361452Sdfrstatic int 27461452Sdfragp_amd_detach(device_t dev) 27561452Sdfr{ 27661452Sdfr struct agp_amd_softc *sc = device_get_softc(dev); 27761452Sdfr 278173203Sjhb agp_free_cdev(dev); 27961503Sdfr 28061452Sdfr /* Disable the TLB.. */ 28161452Sdfr WRITE2(AGP_AMD751_STATUS, 28261452Sdfr READ2(AGP_AMD751_STATUS) & ~AGP_AMD751_STATUS_GCE); 28361452Sdfr 28461452Sdfr /* Disable host-agp sync */ 28561452Sdfr pci_write_config(dev, AGP_AMD751_MODECTRL, 0x00, 1); 28661452Sdfr 28761452Sdfr /* Clear the GATT base */ 28861452Sdfr WRITE4(AGP_AMD751_ATTBASE, 0); 28961452Sdfr 29061452Sdfr /* Put the aperture back the way it started. */ 29161452Sdfr AGP_SET_APERTURE(dev, sc->initial_aperture); 29261452Sdfr 29361501Sdfr agp_amd_free_gatt(sc->gatt); 294173203Sjhb agp_free_res(dev); 29561503Sdfr 29661503Sdfr bus_release_resource(dev, SYS_RES_MEMORY, 29761503Sdfr AGP_AMD751_REGISTERS, sc->regs); 29861503Sdfr 29961452Sdfr return 0; 30061452Sdfr} 30161452Sdfr 30261452Sdfrstatic u_int32_t 30361452Sdfragp_amd_get_aperture(device_t dev) 30461452Sdfr{ 30561452Sdfr int vas; 30661452Sdfr 30761452Sdfr /* 30861452Sdfr * The aperture size is equal to 32M<<vas. 30961452Sdfr */ 31061452Sdfr vas = (pci_read_config(dev, AGP_AMD751_APCTRL, 1) & 0x06) >> 1; 31161452Sdfr return (32*1024*1024) << vas; 31261452Sdfr} 31361452Sdfr 31461452Sdfrstatic int 31561452Sdfragp_amd_set_aperture(device_t dev, u_int32_t aperture) 31661452Sdfr{ 31761452Sdfr int vas; 31861452Sdfr 31961452Sdfr /* 32061452Sdfr * Check for a power of two and make sure its within the 32161452Sdfr * programmable range. 32261452Sdfr */ 32361452Sdfr if (aperture & (aperture - 1) 32461452Sdfr || aperture < 32*1024*1024 32561452Sdfr || aperture > 2U*1024*1024*1024) 32661452Sdfr return EINVAL; 32761452Sdfr 32861452Sdfr vas = ffs(aperture / 32*1024*1024) - 1; 32961452Sdfr 33087479Scokane /* 33187479Scokane * While the size register is bits 1-3 of APCTRL, bit 0 must be 33287479Scokane * set for the size value to be 'valid' 33387479Scokane */ 33461452Sdfr pci_write_config(dev, AGP_AMD751_APCTRL, 33587479Scokane (((pci_read_config(dev, AGP_AMD751_APCTRL, 1) & ~0x06) 33687479Scokane | ((vas << 1) | 1))), 1); 33761452Sdfr 33861452Sdfr return 0; 33961452Sdfr} 34061452Sdfr 34161452Sdfrstatic int 342194017Savgagp_amd_bind_page(device_t dev, vm_offset_t offset, vm_offset_t physical) 34361452Sdfr{ 34461452Sdfr struct agp_amd_softc *sc = device_get_softc(dev); 34561452Sdfr 346194017Savg if (offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) 34761452Sdfr return EINVAL; 34861452Sdfr 34961452Sdfr sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 1; 35061452Sdfr return 0; 35161452Sdfr} 35261452Sdfr 35361452Sdfrstatic int 354194017Savgagp_amd_unbind_page(device_t dev, vm_offset_t offset) 35561452Sdfr{ 35661452Sdfr struct agp_amd_softc *sc = device_get_softc(dev); 35761452Sdfr 358194017Savg if (offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) 35961452Sdfr return EINVAL; 36061452Sdfr 36161452Sdfr sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; 36261452Sdfr return 0; 36361452Sdfr} 36461452Sdfr 36561452Sdfrstatic void 36661452Sdfragp_amd_flush_tlb(device_t dev) 36761452Sdfr{ 36861452Sdfr struct agp_amd_softc *sc = device_get_softc(dev); 36961452Sdfr 37061452Sdfr /* Set the cache invalidate bit and wait for the chipset to clear */ 37161452Sdfr WRITE4(AGP_AMD751_TLBCTRL, 1); 37261452Sdfr do { 37361452Sdfr DELAY(1); 37461452Sdfr } while (READ4(AGP_AMD751_TLBCTRL)); 37561452Sdfr} 37661452Sdfr 37761452Sdfrstatic device_method_t agp_amd_methods[] = { 37861452Sdfr /* Device interface */ 37961452Sdfr DEVMETHOD(device_probe, agp_amd_probe), 38061452Sdfr DEVMETHOD(device_attach, agp_amd_attach), 38161452Sdfr DEVMETHOD(device_detach, agp_amd_detach), 38261452Sdfr DEVMETHOD(device_shutdown, bus_generic_shutdown), 38361452Sdfr DEVMETHOD(device_suspend, bus_generic_suspend), 38461452Sdfr DEVMETHOD(device_resume, bus_generic_resume), 38561452Sdfr 38661452Sdfr /* AGP interface */ 38761452Sdfr DEVMETHOD(agp_get_aperture, agp_amd_get_aperture), 38861452Sdfr DEVMETHOD(agp_set_aperture, agp_amd_set_aperture), 38961452Sdfr DEVMETHOD(agp_bind_page, agp_amd_bind_page), 39061452Sdfr DEVMETHOD(agp_unbind_page, agp_amd_unbind_page), 39161452Sdfr DEVMETHOD(agp_flush_tlb, agp_amd_flush_tlb), 39261452Sdfr DEVMETHOD(agp_enable, agp_generic_enable), 39361452Sdfr DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), 39461452Sdfr DEVMETHOD(agp_free_memory, agp_generic_free_memory), 39561452Sdfr DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), 39661452Sdfr DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), 39761452Sdfr 39861452Sdfr { 0, 0 } 39961452Sdfr}; 40061452Sdfr 40161452Sdfrstatic driver_t agp_amd_driver = { 40261452Sdfr "agp", 40361452Sdfr agp_amd_methods, 40461452Sdfr sizeof(struct agp_amd_softc), 40561452Sdfr}; 40661452Sdfr 40761452Sdfrstatic devclass_t agp_devclass; 40861452Sdfr 409153572SjhbDRIVER_MODULE(agp_amd, hostb, agp_amd_driver, agp_devclass, 0, 0); 410113506SmdoddMODULE_DEPEND(agp_amd, agp, 1, 1, 1); 411113506SmdoddMODULE_DEPEND(agp_amd, pci, 1, 1, 1); 412