1*371e2336Sjsg /* $OpenBSD: agp.c,v 1.41 2014/03/17 04:10:59 jsg Exp $ */ 20139788eSmatthieu /*- 30139788eSmatthieu * Copyright (c) 2000 Doug Rabson 40139788eSmatthieu * All rights reserved. 50139788eSmatthieu * 60139788eSmatthieu * Redistribution and use in source and binary forms, with or without 70139788eSmatthieu * modification, are permitted provided that the following conditions 80139788eSmatthieu * are met: 90139788eSmatthieu * 1. Redistributions of source code must retain the above copyright 100139788eSmatthieu * notice, this list of conditions and the following disclaimer. 110139788eSmatthieu * 2. Redistributions in binary form must reproduce the above copyright 120139788eSmatthieu * notice, this list of conditions and the following disclaimer in the 130139788eSmatthieu * documentation and/or other materials provided with the distribution. 140139788eSmatthieu * 150139788eSmatthieu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 160139788eSmatthieu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 170139788eSmatthieu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 180139788eSmatthieu * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 190139788eSmatthieu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 200139788eSmatthieu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 210139788eSmatthieu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 220139788eSmatthieu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 230139788eSmatthieu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 240139788eSmatthieu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 250139788eSmatthieu * SUCH DAMAGE. 260139788eSmatthieu * 270139788eSmatthieu * $FreeBSD: src/sys/pci/agp.c,v 1.12 2001/05/19 01:28:07 alfred Exp $ 280139788eSmatthieu */ 290139788eSmatthieu 300139788eSmatthieu #include <sys/param.h> 310139788eSmatthieu #include <sys/malloc.h> 320139788eSmatthieu #include <sys/agpio.h> 330139788eSmatthieu #include <sys/fcntl.h> 340139788eSmatthieu #include <sys/ioctl.h> 350139788eSmatthieu 360139788eSmatthieu #include <uvm/uvm.h> 370139788eSmatthieu 380139788eSmatthieu #include <dev/pci/pcivar.h> 3940d7c10bSoga #include <dev/pci/pcidevs.h> 400139788eSmatthieu 410139788eSmatthieu #include <dev/ic/mc6845reg.h> 420139788eSmatthieu #include <dev/ic/pcdisplayvar.h> 430139788eSmatthieu #include <dev/ic/vgareg.h> 440139788eSmatthieu #include <dev/ic/vgavar.h> 450139788eSmatthieu 460139788eSmatthieu #include <dev/pci/agpvar.h> 470139788eSmatthieu #include <dev/pci/agpreg.h> 480139788eSmatthieu 498f6e6e82Soga /* 508f6e6e82Soga * the enable and {alloc, free, bind, unbind} memory routines have default 518f6e6e82Soga * fallbacks, these macros do the right thing. The callbacks with no fallback 528f6e6e82Soga * are called directly. These are mostly hacks around the weirdness of intel 538f6e6e82Soga * integrated graphics, since they are not technically a true agp chipset, 548f6e6e82Soga * but provide an almost identical interface. 558f6e6e82Soga */ 568f6e6e82Soga int agp_generic_enable(struct agp_softc *, u_int32_t); 578f6e6e82Soga struct agp_memory * 588f6e6e82Soga agp_generic_alloc_memory(struct agp_softc *, int, vsize_t size); 598f6e6e82Soga int agp_generic_free_memory(struct agp_softc *, struct agp_memory *); 608f6e6e82Soga void agp_attach(struct device *, struct device *, void *); 618f6e6e82Soga int agp_probe(struct device *, void *, void *); 620139788eSmatthieu 634ad474a9Smpi struct agp_memory *agp_find_memory(struct agp_softc *, int); 644ad474a9Smpi struct agp_memory *agp_lookup_memory(struct agp_softc *, off_t); 654ad474a9Smpi 668f6e6e82Soga int agpvga_match(struct pci_attach_args *); 6770456743Soga int agp_acquire_helper(void *dev, enum agp_acquire_state state); 6870456743Soga int agp_release_helper(void *dev, enum agp_acquire_state state); 6940d7c10bSoga 708f6e6e82Soga int 718f6e6e82Soga agpdev_print(void *aux, const char *pnp) 728f6e6e82Soga { 738f6e6e82Soga if (pnp) { 748f6e6e82Soga printf("agp at %s", pnp); 758f6e6e82Soga } 768f6e6e82Soga return (UNCONF); 778f6e6e82Soga } 7840d7c10bSoga 798f6e6e82Soga int 808f6e6e82Soga agpbus_probe(struct agp_attach_args *aa) 818f6e6e82Soga { 828f6e6e82Soga struct pci_attach_args *pa = aa->aa_pa; 838f6e6e82Soga 848f6e6e82Soga if (strncmp(aa->aa_busname, "agp", 3) == 0 && 858f6e6e82Soga PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE && 868f6e6e82Soga PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_HOST) 878f6e6e82Soga return (1); 888f6e6e82Soga return (0); 898f6e6e82Soga } 908f6e6e82Soga 918f6e6e82Soga /* 928f6e6e82Soga * Find the video card hanging off the agp bus XXX assumes only one bus 938f6e6e82Soga */ 948f6e6e82Soga int 958f6e6e82Soga agpvga_match(struct pci_attach_args *pa) 968f6e6e82Soga { 978f6e6e82Soga if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY && 988f6e6e82Soga PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_VGA) { 998f6e6e82Soga if (pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_AGP, 1008f6e6e82Soga NULL, NULL)) 1018f6e6e82Soga return (1); 1028f6e6e82Soga } 1038f6e6e82Soga return (0); 1048f6e6e82Soga } 1058f6e6e82Soga 1068f6e6e82Soga struct device * 1078f6e6e82Soga agp_attach_bus(struct pci_attach_args *pa, const struct agp_methods *methods, 108b113917fSoga bus_addr_t apaddr, bus_size_t apsize, struct device *dev) 1098f6e6e82Soga { 1108f6e6e82Soga struct agpbus_attach_args arg; 1118f6e6e82Soga 1128f6e6e82Soga arg.aa_methods = methods; 1138f6e6e82Soga arg.aa_pa = pa; 114d8cdf4a2Soga arg.aa_apaddr = apaddr; 115b113917fSoga arg.aa_apsize = apsize; 1168f6e6e82Soga 1178f6e6e82Soga printf("\n"); /* newline from the driver that called us */ 1188f6e6e82Soga return (config_found(dev, &arg, agpdev_print)); 1198f6e6e82Soga } 12040d7c10bSoga 12170456743Soga int 12240d7c10bSoga agp_probe(struct device *parent, void *match, void *aux) 12340d7c10bSoga { 1248f6e6e82Soga /* 1258f6e6e82Soga * we don't do any checking here, driver we're attaching this 1268f6e6e82Soga * interface to should have already done it. 1278f6e6e82Soga */ 12840d7c10bSoga return (1); 12940d7c10bSoga } 1300139788eSmatthieu 1310139788eSmatthieu void 1320139788eSmatthieu agp_attach(struct device *parent, struct device *self, void *aux) 1330139788eSmatthieu { 1348f6e6e82Soga struct agpbus_attach_args *aa = aux; 1358f6e6e82Soga struct pci_attach_args *pa = aa->aa_pa; 13640d7c10bSoga struct agp_softc *sc = (struct agp_softc *)self; 1370139788eSmatthieu u_int memsize; 1388f6e6e82Soga int i; 1390139788eSmatthieu 1408f6e6e82Soga sc->sc_chipc = parent; 1418f6e6e82Soga sc->sc_methods = aa->aa_methods; 142d8cdf4a2Soga sc->sc_apaddr = aa->aa_apaddr; 143b113917fSoga sc->sc_apsize = aa->aa_apsize; 1448f6e6e82Soga 1450139788eSmatthieu static const int agp_max[][2] = { 1460139788eSmatthieu {0, 0}, 1470139788eSmatthieu {32, 4}, 1480139788eSmatthieu {64, 28}, 1490139788eSmatthieu {128, 96}, 1500139788eSmatthieu {256, 204}, 1510139788eSmatthieu {512, 440}, 1520139788eSmatthieu {1024, 942}, 1530139788eSmatthieu {2048, 1920}, 1540139788eSmatthieu {4096, 3932} 1550139788eSmatthieu }; 1560139788eSmatthieu 1570139788eSmatthieu /* 1580139788eSmatthieu * Work out an upper bound for agp memory allocation. This 1590139788eSmatthieu * uses a heuristic table from the Linux driver. 1600139788eSmatthieu */ 1610139788eSmatthieu memsize = ptoa(physmem) >> 20; 1620139788eSmatthieu 1639aa97624Sjasper for (i = 0; i < nitems(agp_max) && memsize > agp_max[i][0]; i++) 1640139788eSmatthieu ; 1659aa97624Sjasper if (i == nitems(agp_max)) 1669aa97624Sjasper i = nitems(agp_max) - 1; 1670139788eSmatthieu sc->sc_maxmem = agp_max[i][1] << 20; 1680139788eSmatthieu 1690139788eSmatthieu /* 1700139788eSmatthieu * The lock is used to prevent re-entry to 1710139788eSmatthieu * agp_generic_bind_memory() since that function can sleep. 1720139788eSmatthieu */ 173f0c692fcSoga rw_init(&sc->sc_lock, "agplk"); 1740139788eSmatthieu 1750139788eSmatthieu TAILQ_INIT(&sc->sc_memory); 1760139788eSmatthieu 1770139788eSmatthieu sc->sc_pcitag = pa->pa_tag; 1780139788eSmatthieu sc->sc_pc = pa->pa_pc; 1790139788eSmatthieu sc->sc_id = pa->pa_id; 1800139788eSmatthieu sc->sc_dmat = pa->pa_dmat; 1814ad474a9Smpi sc->sc_memt = pa->pa_memt; 1820139788eSmatthieu 1830139788eSmatthieu pci_get_capability(sc->sc_pc, sc->sc_pcitag, PCI_CAP_AGP, 1840139788eSmatthieu &sc->sc_capoff, NULL); 1850139788eSmatthieu 186d8cdf4a2Soga printf(": aperture at 0x%lx, size 0x%lx\n", (u_long)sc->sc_apaddr, 187b113917fSoga (u_long)sc->sc_apsize); 1880139788eSmatthieu } 1890139788eSmatthieu 19040d7c10bSoga struct cfattach agp_ca = { 19140d7c10bSoga sizeof(struct agp_softc), agp_probe, agp_attach, 19240d7c10bSoga NULL, NULL 19340d7c10bSoga }; 19440d7c10bSoga 19540d7c10bSoga struct cfdriver agp_cd = { 19640d7c10bSoga NULL, "agp", DV_DULL 19740d7c10bSoga }; 19840d7c10bSoga 1990139788eSmatthieu struct agp_memory * 20040d7c10bSoga agp_find_memory(struct agp_softc *sc, int id) 2010139788eSmatthieu { 2020139788eSmatthieu struct agp_memory *mem; 2030139788eSmatthieu 2040139788eSmatthieu AGP_DPF("searching for memory block %d\n", id); 2050139788eSmatthieu TAILQ_FOREACH(mem, &sc->sc_memory, am_link) { 2060139788eSmatthieu AGP_DPF("considering memory block %d\n", mem->am_id); 2070139788eSmatthieu if (mem->am_id == id) 2080139788eSmatthieu return (mem); 2090139788eSmatthieu } 2104ad474a9Smpi return (NULL); 2114ad474a9Smpi } 2124ad474a9Smpi 2134ad474a9Smpi 2144ad474a9Smpi struct agp_memory * 2154ad474a9Smpi agp_lookup_memory(struct agp_softc *sc, off_t off) 2164ad474a9Smpi { 2174ad474a9Smpi struct agp_memory* mem; 2184ad474a9Smpi 2194ad474a9Smpi AGP_DPF("searching for memory offset 0x%lx\n", (unsigned long)off); 2204ad474a9Smpi TAILQ_FOREACH(mem, &sc->sc_memory, am_link) { 2214ad474a9Smpi if (mem->am_is_bound == 0) 2224ad474a9Smpi continue; 2234ad474a9Smpi if (off >= mem->am_offset && 2244ad474a9Smpi off < (mem->am_offset + mem->am_size)) 2254ad474a9Smpi return (mem); 2264ad474a9Smpi } 2274ad474a9Smpi return (NULL); 2280139788eSmatthieu } 2290139788eSmatthieu 2300139788eSmatthieu struct agp_gatt * 2318f6e6e82Soga agp_alloc_gatt(bus_dma_tag_t dmat, u_int32_t apsize) 2320139788eSmatthieu { 2330139788eSmatthieu struct agp_gatt *gatt; 2348f6e6e82Soga u_int32_t entries = apsize >> AGP_PAGE_SHIFT; 2350139788eSmatthieu 23640d7c10bSoga gatt = malloc(sizeof(*gatt), M_AGP, M_NOWAIT | M_ZERO); 2370139788eSmatthieu if (!gatt) 2380139788eSmatthieu return (NULL); 2390139788eSmatthieu gatt->ag_entries = entries; 240d4ab52b1Soga gatt->ag_size = entries * sizeof(u_int32_t); 2410139788eSmatthieu 242d4ab52b1Soga if (agp_alloc_dmamem(dmat, gatt->ag_size, &gatt->ag_dmamap, 243*371e2336Sjsg &gatt->ag_physical, &gatt->ag_dmaseg) != 0) { 244*371e2336Sjsg free(gatt, M_AGP); 24540d7c10bSoga return (NULL); 246*371e2336Sjsg } 2470139788eSmatthieu 248d4ab52b1Soga if (bus_dmamem_map(dmat, &gatt->ag_dmaseg, 1, gatt->ag_size, 249d4ab52b1Soga (caddr_t *)&gatt->ag_virtual, BUS_DMA_NOWAIT) != 0) { 250d4ab52b1Soga agp_free_dmamem(dmat, gatt->ag_size, gatt->ag_dmamap, 251d4ab52b1Soga &gatt->ag_dmaseg); 252*371e2336Sjsg free(gatt, M_AGP); 253d4ab52b1Soga return (NULL); 254d4ab52b1Soga } 255d4ab52b1Soga 2560139788eSmatthieu agp_flush_cache(); 2570139788eSmatthieu 25840d7c10bSoga return (gatt); 2590139788eSmatthieu } 2600139788eSmatthieu 2610139788eSmatthieu void 2628f6e6e82Soga agp_free_gatt(bus_dma_tag_t dmat, struct agp_gatt *gatt) 2630139788eSmatthieu { 264d4ab52b1Soga bus_dmamem_unmap(dmat, (caddr_t)gatt->ag_virtual, gatt->ag_size); 265d4ab52b1Soga agp_free_dmamem(dmat, gatt->ag_size, gatt->ag_dmamap, &gatt->ag_dmaseg); 26640d7c10bSoga free(gatt, M_AGP); 2670139788eSmatthieu } 2680139788eSmatthieu 2690139788eSmatthieu int 27040d7c10bSoga agp_generic_enable(struct agp_softc *sc, u_int32_t mode) 2710139788eSmatthieu { 2728f6e6e82Soga struct pci_attach_args pa; 2738f6e6e82Soga pcireg_t tstatus, mstatus, command; 2740139788eSmatthieu int rq, sba, fw, rate, capoff; 2750139788eSmatthieu 2768f6e6e82Soga if (pci_find_device(&pa, agpvga_match) == 0 || 2778f6e6e82Soga pci_get_capability(pa.pa_pc, pa.pa_tag, PCI_CAP_AGP, 2780139788eSmatthieu &capoff, NULL) == 0) { 2790139788eSmatthieu printf("agp_generic_enable: not an AGP capable device\n"); 28040d7c10bSoga return (-1); 2810139788eSmatthieu } 2820139788eSmatthieu 2830139788eSmatthieu tstatus = pci_conf_read(sc->sc_pc, sc->sc_pcitag, 2840139788eSmatthieu sc->sc_capoff + AGP_STATUS); 28560d13d3bSoga /* display agp mode */ 2868f6e6e82Soga mstatus = pci_conf_read(pa.pa_pc, pa.pa_tag, 2870139788eSmatthieu capoff + AGP_STATUS); 2880139788eSmatthieu 2890139788eSmatthieu /* Set RQ to the min of mode, tstatus and mstatus */ 2900139788eSmatthieu rq = AGP_MODE_GET_RQ(mode); 2910139788eSmatthieu if (AGP_MODE_GET_RQ(tstatus) < rq) 2920139788eSmatthieu rq = AGP_MODE_GET_RQ(tstatus); 2930139788eSmatthieu if (AGP_MODE_GET_RQ(mstatus) < rq) 2940139788eSmatthieu rq = AGP_MODE_GET_RQ(mstatus); 2950139788eSmatthieu 2960139788eSmatthieu /* Set SBA if all three can deal with SBA */ 2970139788eSmatthieu sba = (AGP_MODE_GET_SBA(tstatus) 2980139788eSmatthieu & AGP_MODE_GET_SBA(mstatus) 2990139788eSmatthieu & AGP_MODE_GET_SBA(mode)); 3000139788eSmatthieu 3010139788eSmatthieu /* Similar for FW */ 3020139788eSmatthieu fw = (AGP_MODE_GET_FW(tstatus) 3030139788eSmatthieu & AGP_MODE_GET_FW(mstatus) 3040139788eSmatthieu & AGP_MODE_GET_FW(mode)); 3050139788eSmatthieu 3060139788eSmatthieu /* Figure out the max rate */ 3070139788eSmatthieu rate = (AGP_MODE_GET_RATE(tstatus) 3080139788eSmatthieu & AGP_MODE_GET_RATE(mstatus) 3090139788eSmatthieu & AGP_MODE_GET_RATE(mode)); 3100139788eSmatthieu if (rate & AGP_MODE_RATE_4x) 3110139788eSmatthieu rate = AGP_MODE_RATE_4x; 3120139788eSmatthieu else if (rate & AGP_MODE_RATE_2x) 3130139788eSmatthieu rate = AGP_MODE_RATE_2x; 3140139788eSmatthieu else 3150139788eSmatthieu rate = AGP_MODE_RATE_1x; 3160139788eSmatthieu 3170139788eSmatthieu /* Construct the new mode word and tell the hardware */ 3180139788eSmatthieu command = AGP_MODE_SET_RQ(0, rq); 3190139788eSmatthieu command = AGP_MODE_SET_SBA(command, sba); 3200139788eSmatthieu command = AGP_MODE_SET_FW(command, fw); 3210139788eSmatthieu command = AGP_MODE_SET_RATE(command, rate); 3220139788eSmatthieu command = AGP_MODE_SET_AGP(command, 1); 32360d13d3bSoga 3240139788eSmatthieu pci_conf_write(sc->sc_pc, sc->sc_pcitag, 3250139788eSmatthieu sc->sc_capoff + AGP_COMMAND, command); 3268f6e6e82Soga pci_conf_write(pa.pa_pc, pa.pa_tag, capoff + AGP_COMMAND, command); 32740d7c10bSoga return (0); 3280139788eSmatthieu } 3290139788eSmatthieu 3300139788eSmatthieu struct agp_memory * 33140d7c10bSoga agp_generic_alloc_memory(struct agp_softc *sc, int type, vsize_t size) 3320139788eSmatthieu { 3330139788eSmatthieu struct agp_memory *mem; 3340139788eSmatthieu 3350139788eSmatthieu if (type != 0) { 3360139788eSmatthieu printf("agp_generic_alloc_memory: unsupported type %d\n", type); 33740d7c10bSoga return (0); 3380139788eSmatthieu } 3390139788eSmatthieu 34040d7c10bSoga mem = malloc(sizeof *mem, M_AGP, M_WAITOK | M_ZERO); 3410139788eSmatthieu 3420139788eSmatthieu if (bus_dmamap_create(sc->sc_dmat, size, size / PAGE_SIZE + 1, 3430139788eSmatthieu size, 0, BUS_DMA_NOWAIT, &mem->am_dmamap) != 0) { 34440d7c10bSoga free(mem, M_AGP); 34540d7c10bSoga return (NULL); 3460139788eSmatthieu } 3470139788eSmatthieu 3480139788eSmatthieu mem->am_id = sc->sc_nextid++; 3490139788eSmatthieu mem->am_size = size; 3500139788eSmatthieu TAILQ_INSERT_TAIL(&sc->sc_memory, mem, am_link); 3510139788eSmatthieu sc->sc_allocated += size; 3520139788eSmatthieu 35340d7c10bSoga return (mem); 3540139788eSmatthieu } 3550139788eSmatthieu 3560139788eSmatthieu int 35740d7c10bSoga agp_generic_free_memory(struct agp_softc *sc, struct agp_memory *mem) 3580139788eSmatthieu { 3590139788eSmatthieu if (mem->am_is_bound) 36040d7c10bSoga return (EBUSY); 3610139788eSmatthieu 3620139788eSmatthieu sc->sc_allocated -= mem->am_size; 3630139788eSmatthieu TAILQ_REMOVE(&sc->sc_memory, mem, am_link); 3640139788eSmatthieu bus_dmamap_destroy(sc->sc_dmat, mem->am_dmamap); 36540d7c10bSoga free(mem, M_AGP); 36640d7c10bSoga return (0); 3670139788eSmatthieu } 3680139788eSmatthieu 3690139788eSmatthieu int 37040d7c10bSoga agp_generic_bind_memory(struct agp_softc *sc, struct agp_memory *mem, 37107fa6f2aSoga bus_size_t offset) 3720139788eSmatthieu { 3730139788eSmatthieu bus_dma_segment_t *segs, *seg; 37407fa6f2aSoga bus_addr_t apaddr = sc->sc_apaddr + offset; 37507fa6f2aSoga bus_size_t done, i, j; 376eabb5cdaSdim int nseg, error; 3770139788eSmatthieu 378f0c692fcSoga rw_enter_write(&sc->sc_lock); 3790139788eSmatthieu 3800139788eSmatthieu if (mem->am_is_bound) { 3810139788eSmatthieu printf("AGP: memory already bound\n"); 382f0c692fcSoga rw_exit_write(&sc->sc_lock); 38340d7c10bSoga return (EINVAL); 3840139788eSmatthieu } 3850139788eSmatthieu 386b3d58ac7Sbrad if ((offset & (AGP_PAGE_SIZE - 1)) != 0 || 387b113917fSoga offset + mem->am_size > sc->sc_apsize) { 3880139788eSmatthieu printf("AGP: binding memory at bad offset %#lx\n", 3890139788eSmatthieu (unsigned long) offset); 390f0c692fcSoga rw_exit_write(&sc->sc_lock); 39140d7c10bSoga return (EINVAL); 3920139788eSmatthieu } 3930139788eSmatthieu 3940139788eSmatthieu /* 3950139788eSmatthieu * The memory here needs to be directly accessable from the 3960139788eSmatthieu * AGP video card, so it should be allocated using bus_dma. 3970139788eSmatthieu * However, it need not be contiguous, since individual pages 3980139788eSmatthieu * are translated using the GATT. 3990139788eSmatthieu */ 4000139788eSmatthieu 401eabb5cdaSdim nseg = (mem->am_size + PAGE_SIZE - 1) / PAGE_SIZE; 40240d7c10bSoga segs = malloc(nseg * sizeof *segs, M_AGP, M_WAITOK); 4030139788eSmatthieu if ((error = bus_dmamem_alloc(sc->sc_dmat, mem->am_size, PAGE_SIZE, 0, 404d4ab52b1Soga segs, nseg, &mem->am_nseg, BUS_DMA_ZERO | BUS_DMA_WAITOK)) != 0) { 40540d7c10bSoga free(segs, M_AGP); 406f0c692fcSoga rw_exit_write(&sc->sc_lock); 4070139788eSmatthieu AGP_DPF("bus_dmamem_alloc failed %d\n", error); 40840d7c10bSoga return (error); 4090139788eSmatthieu } 410d4ab52b1Soga if ((error = bus_dmamap_load_raw(sc->sc_dmat, mem->am_dmamap, segs, 411d4ab52b1Soga mem->am_nseg, mem->am_size, BUS_DMA_WAITOK)) != 0) { 4120139788eSmatthieu bus_dmamem_free(sc->sc_dmat, segs, mem->am_nseg); 41340d7c10bSoga free(segs, M_AGP); 414f0c692fcSoga rw_exit_write(&sc->sc_lock); 4150139788eSmatthieu AGP_DPF("bus_dmamap_load failed %d\n", error); 41640d7c10bSoga return (error); 4170139788eSmatthieu } 4180139788eSmatthieu mem->am_dmaseg = segs; 4190139788eSmatthieu 4200139788eSmatthieu /* 42107fa6f2aSoga * Install entries in the GATT, making sure that if 42207fa6f2aSoga * AGP_PAGE_SIZE < PAGE_SIZE and mem->am_size is not 42307fa6f2aSoga * aligned to PAGE_SIZE, we don't modify too many GATT 42407fa6f2aSoga * entries. Flush chipset tlb when done. 4250139788eSmatthieu */ 4260139788eSmatthieu done = 0; 4270139788eSmatthieu for (i = 0; i < mem->am_dmamap->dm_nsegs; i++) { 4280139788eSmatthieu seg = &mem->am_dmamap->dm_segs[i]; 4290139788eSmatthieu for (j = 0; j < seg->ds_len && (done + j) < mem->am_size; 4300139788eSmatthieu j += AGP_PAGE_SIZE) { 4310139788eSmatthieu AGP_DPF("binding offset %#lx to pa %#lx\n", 4320139788eSmatthieu (unsigned long)(offset + done + j), 43307fa6f2aSoga (unsigned long)seg->ds_addr + j); 43407fa6f2aSoga sc->sc_methods->bind_page(sc->sc_chipc, 43507fa6f2aSoga apaddr + done + j, seg->ds_addr + j, 0); 4360139788eSmatthieu } 4370139788eSmatthieu done += seg->ds_len; 4380139788eSmatthieu } 4390139788eSmatthieu 4400139788eSmatthieu /* 4410139788eSmatthieu * Flush the cpu cache since we are providing a new mapping 4420139788eSmatthieu * for these pages. 4430139788eSmatthieu */ 4440139788eSmatthieu agp_flush_cache(); 4450139788eSmatthieu 4460139788eSmatthieu /* 4470139788eSmatthieu * Make sure the chipset gets the new mappings. 4480139788eSmatthieu */ 4498f6e6e82Soga sc->sc_methods->flush_tlb(sc->sc_chipc); 4500139788eSmatthieu 4510139788eSmatthieu mem->am_offset = offset; 4520139788eSmatthieu mem->am_is_bound = 1; 4530139788eSmatthieu 454f0c692fcSoga rw_exit_write(&sc->sc_lock); 4550139788eSmatthieu 45640d7c10bSoga return (0); 4570139788eSmatthieu } 4580139788eSmatthieu 4590139788eSmatthieu int 46040d7c10bSoga agp_generic_unbind_memory(struct agp_softc *sc, struct agp_memory *mem) 4610139788eSmatthieu { 46207fa6f2aSoga bus_addr_t apaddr = sc->sc_apaddr + mem->am_offset; 46307fa6f2aSoga bus_size_t i; 4640139788eSmatthieu 465f0c692fcSoga rw_enter_write(&sc->sc_lock); 4660139788eSmatthieu 467678c000dSoga if (mem->am_is_bound == 0) { 4680139788eSmatthieu printf("AGP: memory is not bound\n"); 469f0c692fcSoga rw_exit_write(&sc->sc_lock); 47040d7c10bSoga return (EINVAL); 4710139788eSmatthieu } 4720139788eSmatthieu 4734ad474a9Smpi if (mem->am_mapref > 0) { 4744ad474a9Smpi printf("AGP: memory is mapped\n"); 4754ad474a9Smpi rw_exit_write(&sc->sc_lock); 4764ad474a9Smpi return (EINVAL); 4774ad474a9Smpi } 4780139788eSmatthieu 4790139788eSmatthieu /* 4800139788eSmatthieu * Unbind the individual pages and flush the chipset's 4810139788eSmatthieu * TLB. Unwire the pages so they can be swapped. 4820139788eSmatthieu */ 4830139788eSmatthieu for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) 48407fa6f2aSoga sc->sc_methods->unbind_page(sc->sc_chipc, apaddr + i); 4850139788eSmatthieu 4860139788eSmatthieu agp_flush_cache(); 4878f6e6e82Soga sc->sc_methods->flush_tlb(sc->sc_chipc); 4880139788eSmatthieu 4890139788eSmatthieu bus_dmamap_unload(sc->sc_dmat, mem->am_dmamap); 4900139788eSmatthieu bus_dmamem_free(sc->sc_dmat, mem->am_dmaseg, mem->am_nseg); 4910139788eSmatthieu 49240d7c10bSoga free(mem->am_dmaseg, M_AGP); 4930139788eSmatthieu 4940139788eSmatthieu mem->am_offset = 0; 4950139788eSmatthieu mem->am_is_bound = 0; 4960139788eSmatthieu 497f0c692fcSoga rw_exit_write(&sc->sc_lock); 4980139788eSmatthieu 49940d7c10bSoga return (0); 5000139788eSmatthieu } 5010139788eSmatthieu 502d4ab52b1Soga /* 503d4ab52b1Soga * Allocates a single-segment block of zeroed, wired dma memory. 504d4ab52b1Soga */ 5050139788eSmatthieu int 506d4ab52b1Soga agp_alloc_dmamem(bus_dma_tag_t tag, size_t size, bus_dmamap_t *mapp, 507d4ab52b1Soga bus_addr_t *baddr, bus_dma_segment_t *seg) 5080139788eSmatthieu { 509d4ab52b1Soga int error, level = 0, nseg; 5100139788eSmatthieu 5110139788eSmatthieu if ((error = bus_dmamem_alloc(tag, size, PAGE_SIZE, 0, 512d4ab52b1Soga seg, 1, &nseg, BUS_DMA_NOWAIT | BUS_DMA_ZERO)) != 0) 5130139788eSmatthieu goto out; 5140139788eSmatthieu level++; 5150139788eSmatthieu 516d4ab52b1Soga if ((error = bus_dmamap_create(tag, size, nseg, size, 0, 5170139788eSmatthieu BUS_DMA_NOWAIT, mapp)) != 0) 5180139788eSmatthieu goto out; 5190139788eSmatthieu level++; 5200139788eSmatthieu 521d4ab52b1Soga if ((error = bus_dmamap_load_raw(tag, *mapp, seg, nseg, size, 5220139788eSmatthieu BUS_DMA_NOWAIT)) != 0) 5230139788eSmatthieu goto out; 5240139788eSmatthieu 5250139788eSmatthieu *baddr = (*mapp)->dm_segs[0].ds_addr; 5260139788eSmatthieu 52740d7c10bSoga return (0); 5280139788eSmatthieu out: 5290139788eSmatthieu switch (level) { 530d4ab52b1Soga case 2: 5310139788eSmatthieu bus_dmamap_destroy(tag, *mapp); 5320139788eSmatthieu /* FALLTHROUGH */ 5330139788eSmatthieu case 1: 534d4ab52b1Soga bus_dmamem_free(tag, seg, nseg); 5350139788eSmatthieu break; 5360139788eSmatthieu default: 5370139788eSmatthieu break; 5380139788eSmatthieu } 5390139788eSmatthieu 54040d7c10bSoga return (error); 5410139788eSmatthieu } 5420139788eSmatthieu 5430139788eSmatthieu void 5440139788eSmatthieu agp_free_dmamem(bus_dma_tag_t tag, size_t size, bus_dmamap_t map, 545d4ab52b1Soga bus_dma_segment_t *seg) 5460139788eSmatthieu { 5470139788eSmatthieu bus_dmamap_unload(tag, map); 5480139788eSmatthieu bus_dmamap_destroy(tag, map); 549d4ab52b1Soga bus_dmamem_free(tag, seg, 1); 5500139788eSmatthieu } 55140d7c10bSoga 55240d7c10bSoga /* Helper functions used in both user and kernel APIs */ 55340d7c10bSoga 55470456743Soga int 55540d7c10bSoga agp_acquire_helper(void *dev, enum agp_acquire_state state) 55640d7c10bSoga { 55740d7c10bSoga struct agp_softc *sc = (struct agp_softc *)dev; 55840d7c10bSoga 5599d238d47Soga if (sc->sc_chipc == NULL) 5609d238d47Soga return (EINVAL); 5619d238d47Soga 56240d7c10bSoga if (sc->sc_state != AGP_ACQUIRE_FREE) 56340d7c10bSoga return (EBUSY); 56440d7c10bSoga sc->sc_state = state; 56540d7c10bSoga 56640d7c10bSoga return (0); 56740d7c10bSoga } 56840d7c10bSoga 56970456743Soga int 57040d7c10bSoga agp_release_helper(void *dev, enum agp_acquire_state state) 57140d7c10bSoga { 57240d7c10bSoga struct agp_softc *sc = (struct agp_softc *)dev; 57340d7c10bSoga struct agp_memory* mem; 57440d7c10bSoga 57540d7c10bSoga if (sc->sc_state == AGP_ACQUIRE_FREE) 57640d7c10bSoga return (0); 57740d7c10bSoga 57840d7c10bSoga if (sc->sc_state != state) 57940d7c10bSoga return (EBUSY); 58040d7c10bSoga 58140d7c10bSoga /* 58240d7c10bSoga * Clear out the aperture and free any 58340d7c10bSoga * outstanding memory blocks. 58440d7c10bSoga */ 58540d7c10bSoga TAILQ_FOREACH(mem, &sc->sc_memory, am_link) { 58640d7c10bSoga if (mem->am_is_bound) { 58740d7c10bSoga printf("agp_release_helper: mem %d is bound\n", 58840d7c10bSoga mem->am_id); 589678c000dSoga agp_unbind_memory(sc, mem); 59040d7c10bSoga } 59140d7c10bSoga } 59240d7c10bSoga sc->sc_state = AGP_ACQUIRE_FREE; 59340d7c10bSoga return (0); 59440d7c10bSoga } 59540d7c10bSoga 59640d7c10bSoga /* Implementation of the kernel api */ 59740d7c10bSoga 59840d7c10bSoga void * 59940d7c10bSoga agp_find_device(int unit) 60040d7c10bSoga { 6018f8e4ea1Soga if (unit >= agp_cd.cd_ndevs || unit < 0) 6028f8e4ea1Soga return (NULL); 6038f8e4ea1Soga return (agp_cd.cd_devs[unit]); 60440d7c10bSoga } 60540d7c10bSoga 60640d7c10bSoga enum agp_acquire_state 60740d7c10bSoga agp_state(void *dev) 60840d7c10bSoga { 60940d7c10bSoga struct agp_softc *sc = (struct agp_softc *) dev; 61040d7c10bSoga return (sc->sc_state); 61140d7c10bSoga } 61240d7c10bSoga 61340d7c10bSoga void 61440d7c10bSoga agp_get_info(void *dev, struct agp_info *info) 61540d7c10bSoga { 61640d7c10bSoga struct agp_softc *sc = (struct agp_softc *)dev; 61740d7c10bSoga 6188f6e6e82Soga if (sc->sc_capoff != 0) 61940d7c10bSoga info->ai_mode = pci_conf_read(sc->sc_pc, sc->sc_pcitag, 6208f6e6e82Soga AGP_STATUS + sc->sc_capoff); 6218f6e6e82Soga else 6228f6e6e82Soga info->ai_mode = 0; /* i810 doesn't have real AGP */ 62340d7c10bSoga info->ai_aperture_base = sc->sc_apaddr; 624b113917fSoga info->ai_aperture_size = sc->sc_apsize; 62540d7c10bSoga info->ai_memory_allowed = sc->sc_maxmem; 62640d7c10bSoga info->ai_memory_used = sc->sc_allocated; 62785ff9379Sjsg info->ai_devid = sc->sc_id; 62840d7c10bSoga } 62940d7c10bSoga 63040d7c10bSoga int 63140d7c10bSoga agp_acquire(void *dev) 63240d7c10bSoga { 63340d7c10bSoga struct agp_softc *sc = (struct agp_softc *)dev; 634e47997a5Soga 63540d7c10bSoga return (agp_acquire_helper(sc, AGP_ACQUIRE_KERNEL)); 63640d7c10bSoga } 63740d7c10bSoga 63840d7c10bSoga int 63940d7c10bSoga agp_release(void *dev) 64040d7c10bSoga { 64140d7c10bSoga struct agp_softc *sc = (struct agp_softc *)dev; 642e47997a5Soga 64340d7c10bSoga return (agp_release_helper(sc, AGP_ACQUIRE_KERNEL)); 64440d7c10bSoga } 64540d7c10bSoga 64640d7c10bSoga int 64740d7c10bSoga agp_enable(void *dev, u_int32_t mode) 64840d7c10bSoga { 649678c000dSoga struct agp_softc *sc = dev; 650678c000dSoga int ret; 651e47997a5Soga 652678c000dSoga if (sc->sc_methods->enable != NULL) { 653678c000dSoga ret = sc->sc_methods->enable(sc->sc_chipc, mode); 654678c000dSoga } else { 655678c000dSoga ret = agp_generic_enable(sc, mode); 656678c000dSoga } 657678c000dSoga return (ret); 65840d7c10bSoga } 65940d7c10bSoga 660e47997a5Soga void * 661e47997a5Soga agp_alloc_memory(void *dev, int type, vsize_t bytes) 66240d7c10bSoga { 663678c000dSoga struct agp_softc *sc = dev; 664678c000dSoga struct agp_memory *mem; 665e47997a5Soga 666678c000dSoga if (sc->sc_methods->alloc_memory != NULL) { 667678c000dSoga mem = sc->sc_methods->alloc_memory(sc->sc_chipc, type, bytes); 668678c000dSoga } else { 669678c000dSoga mem = agp_generic_alloc_memory(sc, type, bytes); 670678c000dSoga } 671678c000dSoga return (mem); 67240d7c10bSoga } 67340d7c10bSoga 674e47997a5Soga void 675e47997a5Soga agp_free_memory(void *dev, void *handle) 67640d7c10bSoga { 677678c000dSoga struct agp_softc *sc = dev; 678678c000dSoga struct agp_memory *mem = handle; 679e47997a5Soga 680678c000dSoga if (sc->sc_methods->free_memory != NULL) { 681678c000dSoga sc->sc_methods->free_memory(sc->sc_chipc, mem); 682678c000dSoga } else { 683678c000dSoga agp_generic_free_memory(sc, mem); 684678c000dSoga } 68540d7c10bSoga } 68640d7c10bSoga 687e47997a5Soga int 688e47997a5Soga agp_bind_memory(void *dev, void *handle, off_t offset) 68940d7c10bSoga { 690678c000dSoga struct agp_softc *sc = dev; 691678c000dSoga struct agp_memory *mem = handle; 692678c000dSoga int ret; 693e47997a5Soga 694678c000dSoga if (sc->sc_methods->bind_memory != NULL) { 695678c000dSoga ret = sc->sc_methods->bind_memory(sc->sc_chipc, mem, offset); 696678c000dSoga } else { 697678c000dSoga ret = agp_generic_bind_memory(sc, mem, offset); 698678c000dSoga } 699678c000dSoga return (ret); 70040d7c10bSoga } 70140d7c10bSoga 702e47997a5Soga int 703e47997a5Soga agp_unbind_memory(void *dev, void *handle) 70440d7c10bSoga { 705678c000dSoga struct agp_softc *sc = dev; 706678c000dSoga struct agp_memory *mem = handle; 707678c000dSoga int ret; 708e47997a5Soga 709678c000dSoga if (sc->sc_methods->unbind_memory != NULL) { 710678c000dSoga ret = sc->sc_methods->unbind_memory(sc->sc_chipc, mem); 711678c000dSoga } else { 712678c000dSoga ret = agp_generic_unbind_memory(sc, mem); 713678c000dSoga } 714678c000dSoga return (ret); 71540d7c10bSoga } 71640d7c10bSoga 717e47997a5Soga void 7189d238d47Soga agp_memory_info(void *dev, void *handle, struct agp_memory_info *mi) 71940d7c10bSoga { 72040d7c10bSoga struct agp_memory *mem = (struct agp_memory *) handle; 72140d7c10bSoga 72240d7c10bSoga mi->ami_size = mem->am_size; 72340d7c10bSoga mi->ami_physical = mem->am_physical; 72440d7c10bSoga mi->ami_offset = mem->am_offset; 72540d7c10bSoga mi->ami_is_bound = mem->am_is_bound; 72640d7c10bSoga } 7274ad474a9Smpi 7284ad474a9Smpi void * 7294ad474a9Smpi agp_map(struct agp_softc *sc, bus_size_t address, bus_size_t size, 7304ad474a9Smpi bus_space_handle_t *memh) 7314ad474a9Smpi { 7324ad474a9Smpi struct agp_memory* mem; 7334ad474a9Smpi 7344ad474a9Smpi if (sc->sc_chipc == NULL) 7354ad474a9Smpi return (NULL); 7364ad474a9Smpi 7374ad474a9Smpi if (address >= sc->sc_apsize) 7384ad474a9Smpi return (NULL); 7394ad474a9Smpi 7404ad474a9Smpi if (sc->sc_apaddr) { 7414ad474a9Smpi if (bus_space_map(sc->sc_memt, sc->sc_apaddr + address, size, 7424ad474a9Smpi BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, memh)) 7434ad474a9Smpi return (NULL); 7444ad474a9Smpi } else { 7454ad474a9Smpi /* 7464ad474a9Smpi * If the aperture base address is 0 assume that the AGP 7474ad474a9Smpi * bridge does not support remapping for processor accesses. 7484ad474a9Smpi */ 7494ad474a9Smpi mem = agp_lookup_memory(sc, address); 7504ad474a9Smpi if (mem == NULL) 7514ad474a9Smpi return (NULL); 7524ad474a9Smpi 7534ad474a9Smpi /* 7544ad474a9Smpi * Map the whole memory region because it is easier to 7554ad474a9Smpi * do so and it is improbable that only a part of it 7564ad474a9Smpi * will be used. 7574ad474a9Smpi */ 7584ad474a9Smpi if (mem->am_mapref == 0) 7594ad474a9Smpi if (bus_dmamem_map(sc->sc_dmat, mem->am_dmaseg, 7604ad474a9Smpi mem->am_nseg, mem->am_size, &mem->am_kva, 7614ad474a9Smpi BUS_DMA_NOWAIT | BUS_DMA_NOCACHE)) 7624ad474a9Smpi return (NULL); 7634ad474a9Smpi 7644ad474a9Smpi mem->am_mapref++; 7654ad474a9Smpi 7664ad474a9Smpi /* 7674ad474a9Smpi * XXX Fake a bus handle even if it is managed memory, 7684ad474a9Smpi * this is needed at least by radeondrm(4). 7694ad474a9Smpi */ 7704ad474a9Smpi *memh = (bus_space_handle_t)(mem->am_kva + address); 7714ad474a9Smpi } 7724ad474a9Smpi 7734ad474a9Smpi return bus_space_vaddr(sc->sc_memt, *memh); 7744ad474a9Smpi } 7754ad474a9Smpi 7764ad474a9Smpi void 7774ad474a9Smpi agp_unmap(struct agp_softc *sc, void *address, size_t size, 7784ad474a9Smpi bus_space_handle_t memh) 7794ad474a9Smpi { 7804ad474a9Smpi struct agp_memory* mem; 7814ad474a9Smpi caddr_t kva; 7824ad474a9Smpi 7834ad474a9Smpi if (sc->sc_apaddr) 7844ad474a9Smpi return bus_space_unmap(sc->sc_memt, memh, size); 7854ad474a9Smpi 7864ad474a9Smpi kva = (caddr_t)address; 7874ad474a9Smpi TAILQ_FOREACH(mem, &sc->sc_memory, am_link) { 7884ad474a9Smpi if (mem->am_is_bound == 0) 7894ad474a9Smpi continue; 7904ad474a9Smpi 7914ad474a9Smpi if (kva >= mem->am_kva && kva < (mem->am_kva + mem->am_size)) { 7924ad474a9Smpi mem->am_mapref--; 7934ad474a9Smpi 7944ad474a9Smpi if (mem->am_mapref == 0) { 7954ad474a9Smpi bus_dmamem_unmap(sc->sc_dmat, mem->am_kva, 7964ad474a9Smpi mem->am_size); 7974ad474a9Smpi mem->am_kva = 0; 7984ad474a9Smpi } 7994ad474a9Smpi break; 8004ad474a9Smpi } 8014ad474a9Smpi } 8024ad474a9Smpi } 8034ad474a9Smpi 8044ad474a9Smpi paddr_t 8054ad474a9Smpi agp_mmap(struct agp_softc *sc, off_t off, int prot) 8064ad474a9Smpi { 8074ad474a9Smpi struct agp_memory* mem; 8084ad474a9Smpi 8094ad474a9Smpi if (sc->sc_chipc == NULL) 8104ad474a9Smpi return (-1); 8114ad474a9Smpi 8124ad474a9Smpi if (off >= sc->sc_apsize) 8134ad474a9Smpi return (-1); 8144ad474a9Smpi 8154ad474a9Smpi if (sc->sc_apaddr) 8164ad474a9Smpi return bus_space_mmap(sc->sc_memt, sc->sc_apaddr, off, prot, 0); 8174ad474a9Smpi 8184ad474a9Smpi mem = agp_lookup_memory(sc, off); 8194ad474a9Smpi if (mem == NULL) 8204ad474a9Smpi return (-1); 8214ad474a9Smpi 8224ad474a9Smpi return bus_dmamem_mmap(sc->sc_dmat, mem->am_dmaseg, mem->am_nseg, off, 8234ad474a9Smpi prot, BUS_DMA_NOCACHE); 8244ad474a9Smpi } 825