diff --git a/sys/dev/agp/agp.c b/sys/dev/agp/agp.c new file mode 100644 index 000000000000..e1d658379281 --- /dev/null +++ b/sys/dev/agp/agp.c @@ -0,0 +1,790 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +MODULE_VERSION(agp, 1); + +MALLOC_DEFINE(M_AGP, "agp", "AGP data structures"); + +#define CDEV_MAJOR 148 + /* agp_drv.c */ +static d_open_t agp_open; +static d_close_t agp_close; +static d_ioctl_t agp_ioctl; +static d_mmap_t agp_mmap; + +static struct cdevsw agp_cdevsw = { + /* open */ agp_open, + /* close */ agp_close, + /* read */ noread, + /* write */ nowrite, + /* ioctl */ agp_ioctl, + /* poll */ nopoll, + /* mmap */ agp_mmap, + /* strategy */ nostrategy, + /* name */ "agp", + /* maj */ CDEV_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ D_TTY, + /* bmaj */ -1 +}; + +static devclass_t agp_devclass; +#define KDEV2DEV(kdev) devclass_get_device(agp_devclass, minor(kdev)) + +/* Helper functions for implementing chipset mini drivers. */ + +#ifdef __i386__ +#define agp_flush_cache() wbinvd() +#endif + +u_int8_t +agp_find_caps(device_t dev) +{ + u_int32_t status; + u_int8_t ptr, next; + + /* + * Check the CAP_LIST bit of the PCI status register first. + */ + status = pci_read_config(dev, PCIR_STATUS, 2); + if (!(status & 0x10)) + return 0; + + /* + * Traverse the capabilities list. + */ + for (ptr = pci_read_config(dev, AGP_CAPPTR, 1); + ptr != 0; + ptr = next) { + u_int32_t capid = pci_read_config(dev, ptr, 4); + next = AGP_CAPID_GET_NEXT_PTR(capid); + + /* + * If this capability entry ID is 2, then we are done. + */ + if (AGP_CAPID_GET_CAP_ID(capid) == 2) + return ptr; + } + + return 0; +} + +/* + * Find an AGP display device (if any). + */ +static device_t +agp_find_display(void) +{ + devclass_t pci = devclass_find("pci"); + device_t bus, dev = 0; + device_t *kids; + int busnum, numkids, i; + + for (busnum = 0; busnum < devclass_get_maxunit(pci); busnum++) { + bus = devclass_get_device(pci, busnum); + if (!bus) + continue; + device_get_children(bus, &kids, &numkids); + for (i = 0; i < numkids; i++) { + dev = kids[i]; + if (pci_get_class(dev) == PCIC_DISPLAY + && pci_get_subclass(dev) == PCIS_DISPLAY_VGA) + if (agp_find_caps(dev)) { + free(kids, M_TEMP); + return dev; + } + + } + free(kids, M_TEMP); + } + + return 0; +} + +struct agp_gatt * +agp_alloc_gatt(device_t dev) +{ + u_int32_t apsize = AGP_GET_APERTURE(dev); + u_int32_t entries = apsize >> AGP_PAGE_SHIFT; + struct agp_gatt *gatt; + + if (bootverbose) + device_printf(dev, + "allocating GATT for aperture of size %dM\n", + apsize / (1024*1024)); + + gatt = malloc(sizeof(struct agp_gatt), M_AGP, M_NOWAIT); + if (!gatt) + return 0; + + gatt->ag_entries = entries; + gatt->ag_virtual = contigmalloc(entries * sizeof(u_int32_t), M_AGP, 0, + 0, ~0, PAGE_SIZE, 0); + if (!gatt->ag_virtual) { + if (bootverbose) + device_printf(dev, "contiguous allocation failed\n"); + free(gatt, M_AGP); + return 0; + } + bzero(gatt->ag_virtual, entries * sizeof(u_int32_t)); + gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual); + agp_flush_cache(); + + return gatt; +} + +void +agp_free_gatt(struct agp_gatt *gatt) +{ + contigfree(gatt->ag_virtual, + gatt->ag_entries * sizeof(u_int32_t), M_AGP); + free(gatt, M_AGP); +} + +static int agp_max[][2] = { + {0, 0}, + {32, 4}, + {64, 28}, + {128, 96}, + {256, 204}, + {512, 440}, + {1024, 942}, + {2048, 1920}, + {4096, 3932} +}; +#define agp_max_size (sizeof(agp_max) / sizeof(agp_max[0])) + +int +agp_generic_attach(device_t dev) +{ + struct agp_softc *sc = device_get_softc(dev); + int rid, memsize, i; + + /* + * Find and map the aperture. + */ + rid = AGP_APBASE; + sc->as_aperture = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->as_aperture) + return ENOMEM; + + /* + * Work out an upper bound for agp memory allocation. This + * uses a heurisitc table from the Linux driver. + */ + memsize = ptoa(Maxmem) >> 20; + for (i = 0; i < agp_max_size; i++) { + if (memsize <= agp_max[i][0]) + break; + } + if (i == agp_max_size) i = agp_max_size - 1; + sc->as_maxmem = agp_max[i][1] << 20U; + + /* + * The lock is used to prevent re-entry to + * agp_generic_bind_memory() since that function can sleep. + */ + lockinit(&sc->as_lock, PZERO|PCATCH, "agplk", 0, 0); + + /* + * Initialise stuff for the userland device. + */ + agp_devclass = devclass_find("agp"); + TAILQ_INIT(&sc->as_memory); + sc->as_nextid = 1; + + sc->as_devnode = make_dev(&agp_cdevsw, + device_get_unit(dev), + UID_ROOT, + GID_WHEEL, + 0600, + "agpgart"); + + return 0; +} + +int +agp_generic_detach(device_t dev) +{ + struct agp_softc *sc = device_get_softc(dev); + bus_release_resource(dev, SYS_RES_MEMORY, AGP_APBASE, sc->as_aperture); + lockmgr(&sc->as_lock, LK_DRAIN, 0, curproc); + destroy_dev(sc->as_devnode); + agp_flush_cache(); + return 0; +} + +int +agp_generic_enable(device_t dev, u_int32_t mode) +{ + device_t mdev = agp_find_display(); + u_int32_t tstatus, mstatus; + u_int32_t command; + int rq, sba, fw, rate;; + + if (!mdev) { + AGP_DPF("can't find display\n"); + return ENXIO; + } + + tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); + mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4); + + /* Set RQ to the min of mode, tstatus and mstatus */ + rq = AGP_MODE_GET_RQ(mode); + if (AGP_MODE_GET_RQ(tstatus) < rq) + rq = AGP_MODE_GET_RQ(tstatus); + if (AGP_MODE_GET_RQ(mstatus) < rq) + rq = AGP_MODE_GET_RQ(mstatus); + + /* Set SBA if all three can deal with SBA */ + sba = (AGP_MODE_GET_SBA(tstatus) + & AGP_MODE_GET_SBA(mstatus) + & AGP_MODE_GET_SBA(mode)); + + /* Similar for FW */ + fw = (AGP_MODE_GET_FW(tstatus) + & AGP_MODE_GET_FW(mstatus) + & AGP_MODE_GET_FW(mode)); + + /* Figure out the max rate */ + rate = (AGP_MODE_GET_RATE(tstatus) + & AGP_MODE_GET_RATE(mstatus) + & AGP_MODE_GET_RATE(mode)); + if (rate & AGP_MODE_RATE_4x) + rate = AGP_MODE_RATE_4x; + else if (rate & AGP_MODE_RATE_2x) + rate = AGP_MODE_RATE_2x; + else + rate = AGP_MODE_RATE_1x; + + /* Construct the new mode word and tell the hardware */ + command = AGP_MODE_SET_RQ(0, rq); + command = AGP_MODE_SET_SBA(command, sba); + command = AGP_MODE_SET_FW(command, fw); + command = AGP_MODE_SET_RATE(command, rate); + command = AGP_MODE_SET_AGP(command, 1); + pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, command, 4); + pci_write_config(mdev, agp_find_caps(mdev) + AGP_COMMAND, command, 4); + + return 0; +} + +struct agp_memory * +agp_generic_alloc_memory(device_t dev, int type, vm_size_t size) +{ + struct agp_softc *sc = device_get_softc(dev); + struct agp_memory *mem; + + if ((size & (AGP_PAGE_SIZE - 1)) != 0) + return 0; + + if (sc->as_allocated + size > sc->as_maxmem) + return 0; + + mem = malloc(sizeof *mem, M_AGP, M_WAITOK); + mem->am_id = sc->as_nextid++; + mem->am_size = size; + mem->am_obj = vm_object_allocate(OBJT_DEFAULT, atop(round_page(size))); + mem->am_physical = 0; + mem->am_offset = 0; + mem->am_is_bound = 0; + TAILQ_INSERT_TAIL(&sc->as_memory, mem, am_link); + sc->as_allocated += size; + + return mem; +} + +int +agp_generic_free_memory(device_t dev, struct agp_memory *mem) +{ + struct agp_softc *sc = device_get_softc(dev); + + if (mem->am_is_bound) + return EBUSY; + + sc->as_allocated -= mem->am_size; + TAILQ_REMOVE(&sc->as_memory, mem, am_link); + vm_object_deallocate(mem->am_obj); + free(mem, M_AGP); + return 0; +} + +int +agp_generic_bind_memory(device_t dev, struct agp_memory *mem, + vm_offset_t offset) +{ + struct agp_softc *sc = device_get_softc(dev); + vm_offset_t i, j, k; + vm_page_t m; + int error; + + lockmgr(&sc->as_lock, LK_EXCLUSIVE, 0, curproc); + + if (mem->am_is_bound) { + device_printf(dev, "memory already bound\n"); + return EINVAL; + } + + if (offset < 0 + || (offset & (AGP_PAGE_SIZE - 1)) != 0 + || offset + mem->am_size > AGP_GET_APERTURE(dev)) { + device_printf(dev, "binding memory at bad offset %#x\n", + (int) offset); + return EINVAL; + } + + /* + * Bind the individual pages and flush the chipset's + * TLB. + * + * XXX Presumably, this needs to be the pci address on alpha + * (i.e. use alpha_XXX_dmamap()). I don't have access to any + * alpha AGP hardware to check. + */ + for (i = 0; i < mem->am_size; i += PAGE_SIZE) { + /* + * Find a page from the object and wire it + * down. This page will be mapped using one or more + * entries in the GATT (assuming that PAGE_SIZE >= + * AGP_PAGE_SIZE. If this is the first call to bind, + * the pages will be allocated and zeroed. + */ + m = vm_page_grab(mem->am_obj, OFF_TO_IDX(i), + VM_ALLOC_ZERO | VM_ALLOC_RETRY); + AGP_DPF("found page pa=%#x\n", VM_PAGE_TO_PHYS(m)); + vm_page_wire(m); + + /* + * Install entries in the GATT, making sure that if + * AGP_PAGE_SIZE < PAGE_SIZE and mem->am_size is not + * aligned to PAGE_SIZE, we don't modify too many GATT + * entries. + */ + for (j = 0; j < PAGE_SIZE && i + j < mem->am_size; + j += AGP_PAGE_SIZE) { + vm_offset_t pa = VM_PAGE_TO_PHYS(m) + j; + AGP_DPF("binding offset %#x to pa %#x\n", + offset + i + j, pa); + error = AGP_BIND_PAGE(dev, offset + i + j, pa); + if (error) { + /* + * Bail out. Reverse all the mappings + * and unwire the pages. + */ + vm_page_wakeup(m); + for (k = 0; k < i + j; k += AGP_PAGE_SIZE) + AGP_UNBIND_PAGE(dev, offset + k); + for (k = 0; k <= i; k += PAGE_SIZE) { + m = vm_page_lookup(mem->am_obj, + OFF_TO_IDX(k)); + vm_page_unwire(m, 0); + } + lockmgr(&sc->as_lock, LK_RELEASE, 0, curproc); + return error; + } + } + vm_page_wakeup(m); + } + + /* + * Flush the cpu cache since we are providing a new mapping + * for these pages. + */ + agp_flush_cache(); + + /* + * Make sure the chipset gets the new mappings. + */ + AGP_FLUSH_TLB(dev); + + mem->am_offset = offset; + mem->am_is_bound = 1; + + lockmgr(&sc->as_lock, LK_RELEASE, 0, curproc); + + return 0; +} + +int +agp_generic_unbind_memory(device_t dev, struct agp_memory *mem) +{ + struct agp_softc *sc = device_get_softc(dev); + vm_page_t m; + int i; + + lockmgr(&sc->as_lock, LK_EXCLUSIVE, 0, curproc); + + if (!mem->am_is_bound) { + device_printf(dev, "memory is not bound\n"); + return EINVAL; + } + + + /* + * Unbind the individual pages and flush the chipset's + * TLB. Unwire the pages so they can be swapped. + */ + for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) + AGP_UNBIND_PAGE(dev, mem->am_offset + i); + for (i = 0; i < mem->am_size; i += PAGE_SIZE) { + m = vm_page_lookup(mem->am_obj, atop(i)); + vm_page_unwire(m, 0); + } + + agp_flush_cache(); + AGP_FLUSH_TLB(dev); + + mem->am_offset = 0; + mem->am_is_bound = 0; + + lockmgr(&sc->as_lock, LK_RELEASE, 0, curproc); + + return 0; +} + +/* Helper functions for implementing user/kernel api */ + +static int +agp_acquire_helper(device_t dev, enum agp_acquire_state state) +{ + struct agp_softc *sc = device_get_softc(dev); + + if (sc->as_state != AGP_ACQUIRE_FREE) + return EBUSY; + sc->as_state = state; + + return 0; +} + +static int +agp_release_helper(device_t dev, enum agp_acquire_state state) +{ + struct agp_softc *sc = device_get_softc(dev); + struct agp_memory *mem; + + if (sc->as_state == AGP_ACQUIRE_FREE) + return 0; + + if (sc->as_state != state) + return EBUSY; + + /* + * Clear out the aperture and free any outstanding memory blocks. + */ + while ((mem = TAILQ_FIRST(&sc->as_memory)) != 0) { + if (mem->am_is_bound) + AGP_UNBIND_MEMORY(dev, mem); + AGP_FREE_MEMORY(dev, mem); + } + + sc->as_state = AGP_ACQUIRE_FREE; + return 0; +} + +static struct agp_memory * +agp_find_memory(device_t dev, int id) +{ + struct agp_softc *sc = device_get_softc(dev); + struct agp_memory *mem; + + AGP_DPF("searching for memory block %d\n", id); + TAILQ_FOREACH(mem, &sc->as_memory, am_link) { + AGP_DPF("considering memory block %d\n", mem->am_id); + if (mem->am_id == id) + return mem; + } + return 0; +} + +/* Implementation of the userland ioctl api */ + +static int +agp_info_user(device_t dev, agp_info *info) +{ + struct agp_softc *sc = device_get_softc(dev); + + bzero(info, sizeof *info); + info->bridge_id = pci_get_devid(dev); + info->agp_mode = + pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); + info->aper_base = rman_get_start(sc->as_aperture); + info->aper_size = AGP_GET_APERTURE(dev) >> 20; + info->pg_total = info->pg_system = sc->as_maxmem >> AGP_PAGE_SHIFT; + info->pg_used = sc->as_allocated >> AGP_PAGE_SHIFT; + + return 0; +} + +static int +agp_setup_user(device_t dev, agp_setup *setup) +{ + return AGP_ENABLE(dev, setup->agp_mode); +} + +static int +agp_allocate_user(device_t dev, agp_allocate *alloc) +{ + struct agp_memory *mem; + + mem = AGP_ALLOC_MEMORY(dev, + alloc->type, + alloc->pg_count << AGP_PAGE_SHIFT); + alloc->key = mem->am_id; + alloc->physical = mem->am_physical; + + return 0; +} + +static int +agp_deallocate_user(device_t dev, int id) +{ + struct agp_memory *mem = agp_find_memory(dev, id);; + + if (mem) { + AGP_FREE_MEMORY(dev, mem); + return 0; + } else { + return ENOENT; + } +} + +static int +agp_bind_user(device_t dev, agp_bind *bind) +{ + struct agp_memory *mem = agp_find_memory(dev, bind->key); + + if (!mem) + return ENOENT; + + return AGP_BIND_MEMORY(dev, mem, bind->pg_start << AGP_PAGE_SHIFT); +} + +static int +agp_unbind_user(device_t dev, agp_unbind *unbind) +{ + struct agp_memory *mem = agp_find_memory(dev, unbind->key); + + if (!mem) + return ENOENT; + + return AGP_UNBIND_MEMORY(dev, mem); +} + +static int +agp_open(dev_t kdev, int oflags, int devtype, struct proc *p) +{ + device_t dev = KDEV2DEV(kdev); + struct agp_softc *sc = device_get_softc(dev); + + if (!sc->as_isopen) { + sc->as_isopen = 1; + device_busy(dev); + } + + return 0; +} + +static int +agp_close(dev_t kdev, int fflag, int devtype, struct proc *p) +{ + device_t dev = KDEV2DEV(kdev); + struct agp_softc *sc = device_get_softc(dev); + + /* + * Clear the GATT and force release on last close + */ + if (sc->as_state == AGP_ACQUIRE_USER) + agp_release_helper(dev, AGP_ACQUIRE_USER); + sc->as_isopen = 0; + device_unbusy(dev); + + return 0; +} + +static int +agp_ioctl(dev_t kdev, u_long cmd, caddr_t data, int fflag, struct proc *p) +{ + device_t dev = KDEV2DEV(kdev); + + switch (cmd) { + case AGPIOC_INFO: + return agp_info_user(dev, (agp_info *) data); + + case AGPIOC_ACQUIRE: + return agp_acquire_helper(dev, AGP_ACQUIRE_USER); + + case AGPIOC_RELEASE: + return agp_release_helper(dev, AGP_ACQUIRE_USER); + + case AGPIOC_SETUP: + return agp_setup_user(dev, (agp_setup *)data); + + case AGPIOC_ALLOCATE: + return agp_allocate_user(dev, (agp_allocate *)data); + + case AGPIOC_DEALLOCATE: + return agp_deallocate_user(dev, *(int *) data); + + case AGPIOC_BIND: + return agp_bind_user(dev, (agp_bind *)data); + + case AGPIOC_UNBIND: + return agp_unbind_user(dev, (agp_unbind *)data); + + } + + return EINVAL; +} + +static int +agp_mmap(dev_t kdev, vm_offset_t offset, int prot) +{ + device_t dev = KDEV2DEV(kdev); + struct agp_softc *sc = device_get_softc(dev); + + if (offset > AGP_GET_APERTURE(dev)) + return -1; + return atop(rman_get_start(sc->as_aperture) + offset); +} + +/* Implementation of the kernel api */ + +device_t +agp_find_device() +{ + if (!agp_devclass) + return 0; + return devclass_get_device(agp_devclass, 0); +} + +enum agp_acquire_state +agp_state(device_t dev) +{ + struct agp_softc *sc = device_get_softc(dev); + return sc->as_state; +} + +void +agp_get_info(device_t dev, struct agp_info *info) +{ + struct agp_softc *sc = device_get_softc(dev); + + info->ai_mode = + pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); + info->ai_aperture_base = rman_get_start(sc->as_aperture); + info->ai_aperture_size = (rman_get_end(sc->as_aperture) + - rman_get_start(sc->as_aperture)) + 1; + info->ai_memory_allowed = sc->as_maxmem; + info->ai_memory_used = sc->as_allocated; +} + +int +agp_acquire(device_t dev) +{ + return agp_acquire_helper(dev, AGP_ACQUIRE_KERNEL); +} + +int +agp_release(device_t dev) +{ + return agp_release_helper(dev, AGP_ACQUIRE_KERNEL); +} + +int +agp_enable(device_t dev, u_int32_t mode) +{ + return AGP_ENABLE(dev, mode); +} + +void *agp_alloc_memory(device_t dev, int type, vm_size_t bytes) +{ + return (void *) AGP_ALLOC_MEMORY(dev, type, bytes); +} + +void agp_free_memory(device_t dev, void *handle) +{ + struct agp_memory *mem = (struct agp_memory *) handle; + AGP_FREE_MEMORY(dev, mem); +} + +int agp_bind_memory(device_t dev, void *handle, vm_offset_t offset) +{ + struct agp_memory *mem = (struct agp_memory *) handle; + return AGP_BIND_MEMORY(dev, mem, offset); +} + +int agp_unbind_memory(device_t dev, void *handle) +{ + struct agp_memory *mem = (struct agp_memory *) handle; + return AGP_UNBIND_MEMORY(dev, mem); +} + +void agp_memory_info(device_t dev, void *handle, struct + agp_memory_info *mi) +{ + struct agp_memory *mem = (struct agp_memory *) handle; + + mi->ami_size = mem->am_size; + mi->ami_physical = mem->am_physical; + mi->ami_offset = mem->am_offset; + mi->ami_is_bound = mem->am_is_bound; +} diff --git a/sys/dev/agp/agp_ali.c b/sys/dev/agp/agp_ali.c new file mode 100644 index 000000000000..86e070e8d2ae --- /dev/null +++ b/sys/dev/agp/agp_ali.c @@ -0,0 +1,264 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +struct agp_ali_softc { + struct agp_softc agp; + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_gatt *gatt; +}; + +static const char* +agp_ali_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_BRIDGE + || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) + return NULL; + + if (agp_find_caps(dev) == 0) + return NULL; + + switch (pci_get_devid(dev)) { + case 0x154110b9: + return ("Ali M1541 host to AGP bridge"); + }; + + if (pci_get_vendor(dev) == 0x10b9) + return ("Ali Generic host to PCI bridge"); + + return NULL; +} + +static int +agp_ali_probe(device_t dev) +{ + const char *desc; + + desc = agp_ali_match(dev); + if (desc) { + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_ali_attach(device_t dev) +{ + struct agp_ali_softc *sc = device_get_softc(dev); + struct agp_gatt *gatt; + int error; + + error = agp_generic_attach(dev); + if (error) + return error; + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + for (;;) { + gatt = agp_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) { + agp_generic_detach(dev); + return ENOMEM; + } + } + sc->gatt = gatt; + + /* Install the gatt. */ + pci_write_config(dev, AGP_ALI_ATTBASE, + (gatt->ag_physical + | (pci_read_config(dev, AGP_ALI_ATTBASE, 4) & 0xff)), + 4); + + /* Enable the TLB. */ + pci_write_config(dev, AGP_ALI_TLBCTRL, 0x10, 1); + + return 0; +} + +static int +agp_ali_detach(device_t dev) +{ + struct agp_ali_softc *sc = device_get_softc(dev); + int error; + + error = agp_generic_detach(dev); + if (error) + return error; + + /* Disable the TLB.. */ + pci_write_config(dev, AGP_ALI_TLBCTRL, 0x90, 1); + + /* Put the aperture back the way it started. */ + AGP_SET_APERTURE(dev, sc->initial_aperture); + pci_write_config(dev, AGP_ALI_ATTBASE, + pci_read_config(dev, AGP_ALI_ATTBASE, 4) & 0xff, + 4); + + agp_free_gatt(sc->gatt); + return 0; +} + +#define M 1024*1024 + +static u_int32_t agp_ali_table[] = { + 0, /* 0 - invalid */ + 1, /* 1 - invalid */ + 2, /* 2 - invalid */ + 4*M, /* 3 - invalid */ + 8*M, /* 4 - invalid */ + 0, /* 5 - invalid */ + 16*M, /* 6 - invalid */ + 32*M, /* 7 - invalid */ + 64*M, /* 8 - invalid */ + 128*M, /* 9 - invalid */ + 256*M, /* 10 - invalid */ +}; +#define agp_ali_table_size (sizeof(agp_ali_table) / sizeof(agp_ali_table[0])) + +static u_int32_t +agp_ali_get_aperture(device_t dev) +{ + /* + * The aperture size is derived from the low bits of attbase. + * I'm not sure this is correct.. + */ + int i = pci_read_config(dev, AGP_ALI_ATTBASE, 4) & 0xff; + if (i >= agp_ali_table_size) + return 0; + return agp_ali_table[i]; +} + +static int +agp_ali_set_aperture(device_t dev, u_int32_t aperture) +{ + int i; + + for (i = 0; i < agp_ali_table_size; i++) + if (agp_ali_table[i] == aperture) + break; + if (i == agp_ali_table_size) + return EINVAL; + + pci_write_config(dev, AGP_ALI_ATTBASE, + ((pci_read_config(dev, AGP_ALI_ATTBASE, 4) & ~0xff) + | i), 4); + return 0; +} + +static int +agp_ali_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_ali_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical; + return 0; +} + +static int +agp_ali_unbind_page(device_t dev, int offset) +{ + struct agp_ali_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; + return 0; +} + +static void +agp_ali_flush_tlb(device_t dev) +{ + pci_write_config(dev, AGP_ALI_TLBCTRL, 0x90, 1); + pci_write_config(dev, AGP_ALI_TLBCTRL, 0x10, 1); +} + +static device_method_t agp_ali_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_ali_probe), + DEVMETHOD(device_attach, agp_ali_attach), + DEVMETHOD(device_detach, agp_ali_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_ali_get_aperture), + DEVMETHOD(agp_set_aperture, agp_ali_set_aperture), + DEVMETHOD(agp_bind_page, agp_ali_bind_page), + DEVMETHOD(agp_unbind_page, agp_ali_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_ali_flush_tlb), + DEVMETHOD(agp_enable, agp_generic_enable), + DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), + DEVMETHOD(agp_free_memory, agp_generic_free_memory), + DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_ali_driver = { + "agp", + agp_ali_methods, + sizeof(struct agp_ali_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_ali, pci, agp_ali_driver, agp_devclass, 0, 0); diff --git a/sys/dev/agp/agp_amd.c b/sys/dev/agp/agp_amd.c new file mode 100644 index 000000000000..112918ad9aff --- /dev/null +++ b/sys/dev/agp/agp_amd.c @@ -0,0 +1,276 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define READ2(off) bus_space_read_2(sc->bst, sc->bsh, off) +#define READ4(off) bus_space_read_4(sc->bst, sc->bsh, off) +#define WRITE2(off,v) bus_space_write_2(sc->bst, sc->bsh, off, v) +#define WRITE4(off,v) bus_space_write_4(sc->bst, sc->bsh, off, v) + +struct agp_amd_softc { + struct agp_softc agp; + struct resource *regs; /* memory mapped control registers */ + bus_space_tag_t bst; /* bus_space tag */ + bus_space_handle_t bsh; /* bus_space handle */ + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_gatt *gatt; +}; + +static const char* +agp_amd_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_BRIDGE + || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) + return NULL; + + if (agp_find_caps(dev) == 0) + return NULL; + + switch (pci_get_devid(dev)) { + case 0x70061022: + return ("AMD 751 host to AGP bridge"); + }; + + return NULL; +} + +static int +agp_amd_probe(device_t dev) +{ + const char *desc; + + desc = agp_amd_match(dev); + if (desc) { + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_amd_attach(device_t dev) +{ + struct agp_amd_softc *sc = device_get_softc(dev); + struct agp_gatt *gatt; + int error, rid; + + error = agp_generic_attach(dev); + if (error) + return error; + + rid = AGP_AMD751_REGISTERS; + sc->regs = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->regs) { + agp_generic_detach(dev); + return ENOMEM; + } + + sc->bst = rman_get_bustag(sc->regs); + sc->bsh = rman_get_bushandle(sc->regs); + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + for (;;) { + gatt = agp_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) + return ENOMEM; + } + sc->gatt = gatt; + + /* Install the gatt. */ + WRITE4(AGP_AMD751_ATTBASE, gatt->ag_physical); + + /* Enable synchronisation between host and agp. */ + pci_write_config(dev, AGP_AMD751_MODECTRL, 0x80, 1); + + /* Enable the TLB and flush */ + WRITE2(AGP_AMD751_STATUS, + READ2(AGP_AMD751_STATUS) | AGP_AMD751_STATUS_GCE); + AGP_FLUSH_TLB(dev); + + return agp_generic_attach(dev); +} + +static int +agp_amd_detach(device_t dev) +{ + struct agp_amd_softc *sc = device_get_softc(dev); + + /* Disable the TLB.. */ + WRITE2(AGP_AMD751_STATUS, + READ2(AGP_AMD751_STATUS) & ~AGP_AMD751_STATUS_GCE); + + /* Disable host-agp sync */ + pci_write_config(dev, AGP_AMD751_MODECTRL, 0x00, 1); + + /* Clear the GATT base */ + WRITE4(AGP_AMD751_ATTBASE, 0); + + /* Put the aperture back the way it started. */ + AGP_SET_APERTURE(dev, sc->initial_aperture); + + agp_free_gatt(sc->gatt); + return 0; +} + +static u_int32_t +agp_amd_get_aperture(device_t dev) +{ + int vas; + + /* + * The aperture size is equal to 32M<> 1; + return (32*1024*1024) << vas; +} + +static int +agp_amd_set_aperture(device_t dev, u_int32_t aperture) +{ + int vas; + + /* + * Check for a power of two and make sure its within the + * programmable range. + */ + if (aperture & (aperture - 1) + || aperture < 32*1024*1024 + || aperture > 2U*1024*1024*1024) + return EINVAL; + + vas = ffs(aperture / 32*1024*1024) - 1; + + pci_write_config(dev, AGP_AMD751_APCTRL, + ((pci_read_config(dev, AGP_AMD751_APCTRL, 1) & ~0x06) + | vas << 1), 1); + + return 0; +} + +static int +agp_amd_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_amd_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 1; + return 0; +} + +static int +agp_amd_unbind_page(device_t dev, int offset) +{ + struct agp_amd_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; + return 0; +} + +static void +agp_amd_flush_tlb(device_t dev) +{ + struct agp_amd_softc *sc = device_get_softc(dev); + + /* Set the cache invalidate bit and wait for the chipset to clear */ + WRITE4(AGP_AMD751_TLBCTRL, 1); + do { + DELAY(1); + } while (READ4(AGP_AMD751_TLBCTRL)); +} + +static device_method_t agp_amd_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_amd_probe), + DEVMETHOD(device_attach, agp_amd_attach), + DEVMETHOD(device_detach, agp_amd_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_amd_get_aperture), + DEVMETHOD(agp_set_aperture, agp_amd_set_aperture), + DEVMETHOD(agp_bind_page, agp_amd_bind_page), + DEVMETHOD(agp_unbind_page, agp_amd_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_amd_flush_tlb), + DEVMETHOD(agp_enable, agp_generic_enable), + DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), + DEVMETHOD(agp_free_memory, agp_generic_free_memory), + DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_amd_driver = { + "agp", + agp_amd_methods, + sizeof(struct agp_amd_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_amd, pci, agp_amd_driver, agp_devclass, 0, 0); diff --git a/sys/dev/agp/agp_if.m b/sys/dev/agp/agp_if.m new file mode 100644 index 000000000000..06f64d376bab --- /dev/null +++ b/sys/dev/agp/agp_if.m @@ -0,0 +1,134 @@ +# +# Copyright (c) 2000 Doug Rabson +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +#include + +# +# The AGP interface is used internally to the agp driver to isolate the +# differences between various AGP chipsets into chipset mini drivers. It +# should not be used outside the AGP driver. The kernel api for accessing +# AGP functionality is described in +# +INTERFACE agp; + +# +# Return the current aperture size. +# +METHOD u_int32_t get_aperture { + device_t dev; +}; + +# +# Set the size of the aperture. Return EINVAL on error or 0 on success. +# +METHOD int set_aperture { + device_t dev; + u_int32_t aperture; +}; + +# +# Bind a single page in the AGP aperture to a given physical address. +# The offset is a byte offset within the aperture which must be +# aligned to an AGP page boundary. +# +METHOD int bind_page { + device_t dev; + vm_offset_t offset; + vm_offset_t physical; +}; + +# +# Unbind a single page in the AGP aperture. +# +METHOD int unbind_page { + device_t dev; + vm_offset_t offset; +}; + +# +# Flush the GATT TLB. This is used after a call to bind_page to +# ensure that any mappings cached in the chipset are discarded. +# +METHOD void flush_tlb { + device_t dev; +}; + +# +# Enable the agp hardware with the relavent mode. The mode bits are +# defined in +# +METHOD int enable { + device_t dev; + u_int32_t mode; +}; + +# +# Allocate memory of a given type. The type is a chipset-specific +# code which is used by certain integrated agp graphics chips +# (basically just the i810 for now) to access special features of +# the chipset. An opaque handle representing the memory region is +# returned and can be used as an argument to free_memory, bind_memory +# and unbind_memory. +# +# The size is specified in bytes but must be a multiple of the AGP +# page size. +# +METHOD struct agp_memory * alloc_memory { + device_t dev; + int type; + vm_size_t size; +}; + +# +# Free a memory region previously allocated with alloc_memory. Return +# EBUSY if the memory is bound. +# +METHOD int free_memory { + device_t dev; + struct agp_memory *mem; +}; + +# +# Bind a memory region to a specific byte offset within the chipset's +# AGP aperture. This effectively defines a range of contiguous +# physical address which alias the (possibly uncontiguous) pages in +# the memory region. +# +METHOD int bind_memory { + device_t dev; + struct agp_memory *mem; + vm_offset_t offset; +}; + +# +# Unbind a memory region bound with bind_memory. +# +METHOD int unbind_memory { + device_t dev; + struct agp_memory *handle; +}; diff --git a/sys/dev/agp/agp_intel.c b/sys/dev/agp/agp_intel.c new file mode 100644 index 000000000000..88d38ec027cf --- /dev/null +++ b/sys/dev/agp/agp_intel.c @@ -0,0 +1,266 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +struct agp_intel_softc { + struct agp_softc agp; + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_gatt *gatt; +}; + +static const char* +agp_intel_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_BRIDGE + || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) + return NULL; + + if (agp_find_caps(dev) == 0) + return NULL; + + switch (pci_get_devid(dev)) { + /* Intel -- vendor 0x8086 */ + case 0x71808086: + return ("Intel 82443LX (440 LX) host to PCI bridge"); + + case 0x71908086: + return ("Intel 82443BX (440 BX) host to PCI bridge"); + + case 0x71a08086: + return ("Intel 82443GX host to PCI bridge"); + + case 0x71a18086: + return ("Intel 82443GX host to AGP bridge"); + }; + + if (pci_get_vendor(dev) == 0x8086) + return ("Intel Generic host to PCI bridge"); + + return NULL; +} + +static int +agp_intel_probe(device_t dev) +{ + const char *desc; + + desc = agp_intel_match(dev); + if (desc) { + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_intel_attach(device_t dev) +{ + struct agp_intel_softc *sc = device_get_softc(dev); + struct agp_gatt *gatt; + int error; + + error = agp_generic_attach(dev); + if (error) + return error; + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + for (;;) { + gatt = agp_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) { + agp_generic_detach(dev); + return ENOMEM; + } + } + sc->gatt = gatt; + + /* Install the gatt. */ + pci_write_config(dev, AGP_INTEL_ATTBASE, gatt->ag_physical, 4); + + /* Enable things, clear errors etc. */ + pci_write_config(dev, AGP_INTEL_AGPCTRL, 0x2280, 4); + pci_write_config(dev, AGP_INTEL_NBXCFG, + (pci_read_config(dev, AGP_INTEL_NBXCFG, 4) + & ~(1 << 10)) | (1 << 9), 4); + pci_write_config(dev, AGP_INTEL_ERRSTS + 1, 7, 1); + + return 0; +} + +static int +agp_intel_detach(device_t dev) +{ + struct agp_intel_softc *sc = device_get_softc(dev); + int error; + + error = agp_generic_detach(dev); + if (error) + return error; + + printf("%s: set NBXCFG to %x\n", __FUNCTION__, + (pci_read_config(dev, AGP_INTEL_NBXCFG, 4) + & ~(1 << 9))); + pci_write_config(dev, AGP_INTEL_NBXCFG, + (pci_read_config(dev, AGP_INTEL_NBXCFG, 4) + & ~(1 << 9)), 4); + pci_write_config(dev, AGP_INTEL_ATTBASE, 0, 4); + AGP_SET_APERTURE(dev, sc->initial_aperture); + agp_free_gatt(sc->gatt); + + return 0; +} + +static u_int32_t +agp_intel_get_aperture(device_t dev) +{ + u_int32_t apsize; + + apsize = pci_read_config(dev, AGP_INTEL_APSIZE, 1) & 0x1f; + + /* + * The size is determined by the number of low bits of + * register APBASE which are forced to zero. The low 22 bits + * are always forced to zero and each zero bit in the apsize + * field just read forces the corresponding bit in the 27:22 + * to be zero. We calculate the aperture size accordingly. + */ + return (((apsize ^ 0x1f) << 22) | ((1 << 22) - 1)) + 1; +} + +static int +agp_intel_set_aperture(device_t dev, u_int32_t aperture) +{ + u_int32_t apsize; + + /* + * Reverse the magic from get_aperture. + */ + apsize = ((aperture - 1) >> 22) ^ 0x1f; + + /* + * Double check for sanity. + */ + if ((((apsize ^ 0x1f) << 22) | ((1 << 22) - 1)) + 1 != aperture) + return EINVAL; + + pci_write_config(dev, AGP_INTEL_APSIZE, apsize, 1); + + return 0; +} + +static int +agp_intel_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_intel_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 0x17; + return 0; +} + +static int +agp_intel_unbind_page(device_t dev, int offset) +{ + struct agp_intel_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; + return 0; +} + +static void +agp_intel_flush_tlb(device_t dev) +{ + pci_write_config(dev, AGP_INTEL_AGPCTRL, 0x2200, 4); + pci_write_config(dev, AGP_INTEL_AGPCTRL, 0x2280, 4); +} + +static device_method_t agp_intel_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_intel_probe), + DEVMETHOD(device_attach, agp_intel_attach), + DEVMETHOD(device_detach, agp_intel_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_intel_get_aperture), + DEVMETHOD(agp_set_aperture, agp_intel_set_aperture), + DEVMETHOD(agp_bind_page, agp_intel_bind_page), + DEVMETHOD(agp_unbind_page, agp_intel_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_intel_flush_tlb), + DEVMETHOD(agp_enable, agp_generic_enable), + DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), + DEVMETHOD(agp_free_memory, agp_generic_free_memory), + DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_intel_driver = { + "agp", + agp_intel_methods, + sizeof(struct agp_intel_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_intel, pci, agp_intel_driver, agp_devclass, 0, 0); diff --git a/sys/dev/agp/agp_sis.c b/sys/dev/agp/agp_sis.c new file mode 100644 index 000000000000..1f1a50b7eceb --- /dev/null +++ b/sys/dev/agp/agp_sis.c @@ -0,0 +1,256 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +struct agp_sis_softc { + struct agp_softc agp; + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_gatt *gatt; +}; + +static const char* +agp_sis_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_BRIDGE + || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) + return NULL; + + if (agp_find_caps(dev) == 0) + return NULL; + + switch (pci_get_devid(dev)) { + case 0x00011039: + return ("SiS 5591 host to AGP bridge"); + }; + + if (pci_get_vendor(dev) == 0x1039) + return ("SIS Generic host to PCI bridge"); + + return NULL; +} + +static int +agp_sis_probe(device_t dev) +{ + const char *desc; + + desc = agp_sis_match(dev); + if (desc) { + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_sis_attach(device_t dev) +{ + struct agp_sis_softc *sc = device_get_softc(dev); + struct agp_gatt *gatt; + int error; + + error = agp_generic_attach(dev); + if (error) + return error; + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + for (;;) { + gatt = agp_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) { + agp_generic_detach(dev); + return ENOMEM; + } + } + sc->gatt = gatt; + + /* Install the gatt. */ + pci_write_config(dev, AGP_SIS_ATTBASE, gatt->ag_physical, 4); + + /* Enable the aperture. */ + pci_write_config(dev, AGP_SIS_WINCTRL, + pci_read_config(dev, AGP_SIS_WINCTRL, 1) | 3, 1); + + /* + * Enable the TLB and make it automatically invalidate entries + * when the GATT is written. + */ + pci_write_config(dev, AGP_SIS_TLBCTRL, 0x05, 1); + + return 0; +} + +static int +agp_sis_detach(device_t dev) +{ + struct agp_sis_softc *sc = device_get_softc(dev); + int error; + + error = agp_generic_detach(dev); + if (error) + return error; + + /* Disable the aperture.. */ + pci_write_config(dev, AGP_SIS_WINCTRL, + pci_read_config(dev, AGP_SIS_WINCTRL, 1) & ~3, 1); + + /* and the TLB. */ + pci_write_config(dev, AGP_SIS_TLBCTRL, 0, 1); + + /* Put the aperture back the way it started. */ + AGP_SET_APERTURE(dev, sc->initial_aperture); + + agp_free_gatt(sc->gatt); + return 0; +} + +static u_int32_t +agp_sis_get_aperture(device_t dev) +{ + int gws; + + /* + * The aperture size is equal to 4M<> 4; + return (4*1024*1024) << gws; +} + +static int +agp_sis_set_aperture(device_t dev, u_int32_t aperture) +{ + int gws; + + /* + * Check for a power of two and make sure its within the + * programmable range. + */ + if (aperture & (aperture - 1) + || aperture < 4*1024*1024 + || aperture > 256*1024*1024) + return EINVAL; + + gws = ffs(aperture / 4*1024*1024) - 1; + + pci_write_config(dev, AGP_SIS_WINCTRL, + ((pci_read_config(dev, AGP_SIS_WINCTRL, 1) & ~0x70) + | gws << 4), 1); + + return 0; +} + +static int +agp_sis_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_sis_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical; + return 0; +} + +static int +agp_sis_unbind_page(device_t dev, int offset) +{ + struct agp_sis_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; + return 0; +} + +static void +agp_sis_flush_tlb(device_t dev) +{ + pci_write_config(dev, AGP_SIS_TLBFLUSH, 0x02, 1); +} + +static device_method_t agp_sis_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_sis_probe), + DEVMETHOD(device_attach, agp_sis_attach), + DEVMETHOD(device_detach, agp_sis_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_sis_get_aperture), + DEVMETHOD(agp_set_aperture, agp_sis_set_aperture), + DEVMETHOD(agp_bind_page, agp_sis_bind_page), + DEVMETHOD(agp_unbind_page, agp_sis_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_sis_flush_tlb), + DEVMETHOD(agp_enable, agp_generic_enable), + DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), + DEVMETHOD(agp_free_memory, agp_generic_free_memory), + DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_sis_driver = { + "agp", + agp_sis_methods, + sizeof(struct agp_sis_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_sis, pci, agp_sis_driver, agp_devclass, 0, 0); diff --git a/sys/dev/agp/agp_via.c b/sys/dev/agp/agp_via.c new file mode 100644 index 000000000000..983348ea25d4 --- /dev/null +++ b/sys/dev/agp/agp_via.c @@ -0,0 +1,253 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +struct agp_via_softc { + struct agp_softc agp; + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_gatt *gatt; +}; + +static const char* +agp_via_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_BRIDGE + || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) + return NULL; + + if (agp_find_caps(dev) == 0) + return NULL; + + switch (pci_get_devid(dev)) { + case 0x05011106: + return ("VIA 8501 (Apollo MVP4) host to PCI bridge"); + case 0x05971106: + return ("VIA 82C597 (Apollo VP3) host to PCI bridge"); + case 0x05981106: + return ("VIA 82C598 (Apollo MVP3) host to PCI bridge"); + case 0x06911106: + return ("VIA 82C691 (Apollo Pro) host to PCI bridge"); + }; + + if (pci_get_vendor(dev) == 0x1106) + return ("VIA Generic host to PCI bridge"); + + return NULL; +} + +static int +agp_via_probe(device_t dev) +{ + const char *desc; + + desc = agp_via_match(dev); + if (desc) { + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_via_attach(device_t dev) +{ + struct agp_via_softc *sc = device_get_softc(dev); + struct agp_gatt *gatt; + int error; + + error = agp_generic_attach(dev); + if (error) + return error; + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + for (;;) { + gatt = agp_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) { + agp_generic_detach(dev); + return ENOMEM; + } + } + sc->gatt = gatt; + + /* Install the gatt. */ + pci_write_config(dev, AGP_VIA_ATTBASE, gatt->ag_physical | 3, 4); + + /* Enable the aperture. */ + pci_write_config(dev, AGP_VIA_GARTCTRL, 0x0f, 4); + + return 0; +} + +static int +agp_via_detach(device_t dev) +{ + struct agp_via_softc *sc = device_get_softc(dev); + int error; + + error = agp_generic_detach(dev); + if (error) + return error; + + pci_write_config(dev, AGP_VIA_GARTCTRL, 0, 4); + pci_write_config(dev, AGP_VIA_ATTBASE, 0, 4); + AGP_SET_APERTURE(dev, sc->initial_aperture); + agp_free_gatt(sc->gatt); + + return 0; +} + +static u_int32_t +agp_via_get_aperture(device_t dev) +{ + u_int32_t apsize; + + apsize = pci_read_config(dev, AGP_VIA_APSIZE, 1) & 0x1f; + + /* + * The size is determined by the number of low bits of + * register APBASE which are forced to zero. The low 20 bits + * are always forced to zero and each zero bit in the apsize + * field just read forces the corresponding bit in the 27:20 + * to be zero. We calculate the aperture size accordingly. + */ + return (((apsize ^ 0xff) << 20) | ((1 << 20) - 1)) + 1; +} + +static int +agp_via_set_aperture(device_t dev, u_int32_t aperture) +{ + u_int32_t apsize; + + /* + * Reverse the magic from get_aperture. + */ + apsize = ((aperture - 1) >> 20) ^ 0xff; + + /* + * Double check for sanity. + */ + if ((((apsize ^ 0xff) << 20) | ((1 << 20) - 1)) + 1 != aperture) + return EINVAL; + + pci_write_config(dev, AGP_VIA_APSIZE, apsize, 1); + + return 0; +} + +static int +agp_via_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_via_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical; + return 0; +} + +static int +agp_via_unbind_page(device_t dev, int offset) +{ + struct agp_via_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; + return 0; +} + +static void +agp_via_flush_tlb(device_t dev) +{ + pci_write_config(dev, AGP_VIA_GARTCTRL, 0x8f, 4); + pci_write_config(dev, AGP_VIA_GARTCTRL, 0x0f, 4); +} + +static device_method_t agp_via_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_via_probe), + DEVMETHOD(device_attach, agp_via_attach), + DEVMETHOD(device_detach, agp_via_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_via_get_aperture), + DEVMETHOD(agp_set_aperture, agp_via_set_aperture), + DEVMETHOD(agp_bind_page, agp_via_bind_page), + DEVMETHOD(agp_unbind_page, agp_via_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_via_flush_tlb), + DEVMETHOD(agp_enable, agp_generic_enable), + DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), + DEVMETHOD(agp_free_memory, agp_generic_free_memory), + DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_via_driver = { + "agp", + agp_via_methods, + sizeof(struct agp_via_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_via, pci, agp_via_driver, agp_devclass, 0, 0); diff --git a/sys/dev/agp/agppriv.h b/sys/dev/agp/agppriv.h new file mode 100644 index 000000000000..427e68bf9d53 --- /dev/null +++ b/sys/dev/agp/agppriv.h @@ -0,0 +1,103 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PCI_AGPPRIV_H_ +#define _PCI_AGPPRIV_H_ + +/* + * This file *must not* be included by code outside the agp driver itself. + */ + +#include +#include + +#define AGP_DEBUGxx + +#ifdef AGP_DEBUG +#define AGP_DPF(x...) do { \ + printf("agp: "); \ + printf(##x); \ +} while (0) +#else +#define AGP_DPF(x...) do {} while (0) +#endif + +#include "agp_if.h" + +/* + * Data structure to describe an AGP memory allocation. + */ +TAILQ_HEAD(agp_memory_list, agp_memory); +struct agp_memory { + TAILQ_ENTRY(agp_memory) am_link; /* wiring for the tailq */ + int am_id; /* unique id for block */ + vm_size_t am_size; /* number of bytes allocated */ + struct vm_object *am_obj; /* VM object owning pages */ + vm_offset_t am_physical; /* bogus hack for i810 */ + vm_offset_t am_offset; /* page offset if bound */ + int am_is_bound; /* non-zero if bound */ +}; + +/* + * All chipset drivers must have this at the start of their softc. + */ +struct agp_softc { + struct resource *as_aperture; /* location of aperture */ + u_int32_t as_maxmem; /* allocation upper bound */ + u_int32_t as_allocated; /* amount allocated */ + enum agp_acquire_state as_state; + struct agp_memory_list as_memory; /* list of allocated memory */ + int as_nextid; /* next memory block id */ + int as_isopen; /* user device is open */ + dev_t as_devnode; /* from make_dev */ + struct lock as_lock; /* lock for access to GATT */ +}; + +struct agp_gatt { + u_int32_t ag_entries; + u_int32_t *ag_virtual; + vm_offset_t ag_physical; +}; + +u_int8_t agp_find_caps(device_t dev); +struct agp_gatt *agp_alloc_gatt(device_t dev); +void agp_free_gatt(struct agp_gatt *gatt); +int agp_generic_attach(device_t dev); +int agp_generic_detach(device_t dev); +int agp_generic_enable(device_t dev, u_int32_t mode); +struct agp_memory *agp_generic_alloc_memory(device_t dev, int type, + vm_size_t size); +int agp_generic_free_memory(device_t dev, + struct agp_memory *mem); +int agp_generic_bind_memory(device_t dev, + struct agp_memory *mem, + vm_offset_t offset); +int agp_generic_unbind_memory(device_t dev, + struct agp_memory *mem); + +#endif /* !_PCI_AGPPRIV_H_ */ diff --git a/sys/dev/agp/agpreg.h b/sys/dev/agp/agpreg.h new file mode 100644 index 000000000000..0539325b65d8 --- /dev/null +++ b/sys/dev/agp/agpreg.h @@ -0,0 +1,109 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PCI_AGPREG_H_ +#define _PCI_AGPREG_H_ + +/* + * Offsets for various AGP configuration registers. + */ +#define AGP_APBASE 0x10 +#define AGP_CAPPTR 0x34 + +/* + * Offsets from the AGP Capability pointer. + */ +#define AGP_CAPID 0x0 +#define AGP_CAPID_GET_MAJOR(x) (((x) & 0x00f00000U) >> 20) +#define AGP_CAPID_GET_MINOR(x) (((x) & 0x000f0000U) >> 16) +#define AGP_CAPID_GET_NEXT_PTR(x) (((x) & 0x0000ff00U) >> 8) +#define AGP_CAPID_GET_CAP_ID(x) (((x) & 0x000000ffU) >> 0) + +#define AGP_STATUS 0x4 +#define AGP_COMMAND 0x8 + +/* + * Config offsets for Intel AGP chipsets. + */ +#define AGP_INTEL_NBXCFG 0x50 +#define AGP_INTEL_ERRSTS 0x91 +#define AGP_INTEL_AGPCTRL 0xb0 +#define AGP_INTEL_APSIZE 0xb4 +#define AGP_INTEL_ATTBASE 0xb8 + +/* + * Config offsets for VIA AGP chipsets. + */ +#define AGP_VIA_GARTCTRL 0x80 +#define AGP_VIA_APSIZE 0x84 +#define AGP_VIA_ATTBASE 0x88 + +/* + * Config offsets for SiS AGP chipsets. + */ +#define AGP_SIS_ATTBASE 0x90 +#define AGP_SIS_WINCTRL 0x94 +#define AGP_SIS_TLBCTRL 0x97 +#define AGP_SIS_TLBFLUSH 0x98 + +/* + * Config offsets for Ali AGP chipsets. + */ +#define AGP_ALI_AGPCTRL 0xb8 +#define AGP_ALI_ATTBASE 0xbc +#define AGP_ALI_TLBCTRL 0xc0 + +/* + * Config offsets for the AMD 751 chipset. + */ +#define AGP_AMD751_REGISTERS 0x14 +#define AGP_AMD751_APCTRL 0xac +#define AGP_AMD751_MODECTRL 0xb0 + +/* + * Memory mapped register offsets for AMD 751 chipset. + */ +#define AGP_AMD751_CAPS 0x00 +#define AGP_AMD751_CAPS_EHI 0x0800 +#define AGP_AMD751_CAPS_P2P 0x0400 +#define AGP_AMD751_CAPS_MPC 0x0200 +#define AGP_AMD751_CAPS_VBE 0x0100 +#define AGP_AMD751_CAPS_REV 0x00ff +#define AGP_AMD751_STATUS 0x02 +#define AGP_AMD751_STATUS_P2PS 0x0800 +#define AGP_AMD751_STATUS_GCS 0x0400 +#define AGP_AMD751_STATUS_MPS 0x0200 +#define AGP_AMD751_STATUS_VBES 0x0100 +#define AGP_AMD751_STATUS_P2PE 0x0008 +#define AGP_AMD751_STATUS_GCE 0x0004 +#define AGP_AMD751_STATUS_VBEE 0x0001 +#define AGP_AMD751_ATTBASE 0x04 +#define AGP_AMD751_TLBCTRL 0x0c + + +#endif /* !_PCI_AGPREG_H_ */ diff --git a/sys/dev/agp/agpvar.h b/sys/dev/agp/agpvar.h new file mode 100644 index 000000000000..df3112b49d29 --- /dev/null +++ b/sys/dev/agp/agpvar.h @@ -0,0 +1,125 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PCI_AGPVAR_H_ +#define _PCI_AGPVAR_H_ + +/* + * The AGP chipset can be acquired by user or kernel code. If the + * chipset has already been acquired, it cannot be acquired by another + * user until the previous user has released it. + */ +enum agp_acquire_state { + AGP_ACQUIRE_FREE, + AGP_ACQUIRE_USER, + AGP_ACQUIRE_KERNEL +}; + +/* + * This structure is used to query the state of the AGP system. + */ +struct agp_info { + u_int32_t ai_mode; + vm_offset_t ai_aperture_base; + vm_size_t ai_aperture_size; + vm_size_t ai_memory_allowed; + vm_size_t ai_memory_used; + u_int32_t ai_devid; +}; + +struct agp_memory_info { + vm_size_t ami_size; /* size in bytes */ + vm_offset_t ami_physical; /* bogus hack for i810 */ + vm_offset_t ami_offset; /* page offset if bound */ + int ami_is_bound; /* non-zero if bound */ +}; + +/* + * Find the AGP device and return it. + */ +device_t agp_find_device(void); + +/* + * Return the current owner of the AGP chipset. + */ +enum agp_acquire_state agp_state(device_t dev); + +/* + * Query the state of the AGP system. + */ +void agp_get_info(device_t dev, struct agp_info *info); + +/* + * Acquire the AGP chipset for use by the kernel. Returns EBUSY if the + * AGP chipset is already acquired by another user. + */ +int agp_acquire(device_t dev); + +/* + * Release the AGP chipset. + */ +int agp_release(device_t dev); + +/* + * Enable the agp hardware with the relavent mode. The mode bits are + * defined in + */ +int agp_enable(device_t dev, u_int32_t mode); + +/* + * Allocate physical memory suitable for mapping into the AGP + * aperture. The value returned is an opaque handle which can be + * passed to agp_bind(), agp_unbind() or agp_deallocate(). + */ +void *agp_alloc_memory(device_t dev, int type, vm_size_t bytes); + +/* + * Free memory which was allocated with agp_allocate(). + */ +void agp_free_memory(device_t dev, void *handle); + +/* + * Bind memory allocated with agp_allocate() at a given offset within + * the AGP aperture. Returns EINVAL if the memory is already bound or + * the offset is not at an AGP page boundary. + */ +int agp_bind_memory(device_t dev, void *handle, vm_offset_t offset); + +/* + * Unbind memory from the AGP aperture. Returns EINVAL if the memory + * is not bound. + */ +int agp_unbind_memory(device_t dev, void *handle); + +/* + * Retrieve information about a memory block allocated with + * agp_alloc_memory(). + */ +void agp_memory_info(device_t dev, void *handle, struct agp_memory_info *mi); + +#endif /* !_PCI_AGPVAR_H_ */ diff --git a/sys/pci/agp.c b/sys/pci/agp.c new file mode 100644 index 000000000000..e1d658379281 --- /dev/null +++ b/sys/pci/agp.c @@ -0,0 +1,790 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +MODULE_VERSION(agp, 1); + +MALLOC_DEFINE(M_AGP, "agp", "AGP data structures"); + +#define CDEV_MAJOR 148 + /* agp_drv.c */ +static d_open_t agp_open; +static d_close_t agp_close; +static d_ioctl_t agp_ioctl; +static d_mmap_t agp_mmap; + +static struct cdevsw agp_cdevsw = { + /* open */ agp_open, + /* close */ agp_close, + /* read */ noread, + /* write */ nowrite, + /* ioctl */ agp_ioctl, + /* poll */ nopoll, + /* mmap */ agp_mmap, + /* strategy */ nostrategy, + /* name */ "agp", + /* maj */ CDEV_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ D_TTY, + /* bmaj */ -1 +}; + +static devclass_t agp_devclass; +#define KDEV2DEV(kdev) devclass_get_device(agp_devclass, minor(kdev)) + +/* Helper functions for implementing chipset mini drivers. */ + +#ifdef __i386__ +#define agp_flush_cache() wbinvd() +#endif + +u_int8_t +agp_find_caps(device_t dev) +{ + u_int32_t status; + u_int8_t ptr, next; + + /* + * Check the CAP_LIST bit of the PCI status register first. + */ + status = pci_read_config(dev, PCIR_STATUS, 2); + if (!(status & 0x10)) + return 0; + + /* + * Traverse the capabilities list. + */ + for (ptr = pci_read_config(dev, AGP_CAPPTR, 1); + ptr != 0; + ptr = next) { + u_int32_t capid = pci_read_config(dev, ptr, 4); + next = AGP_CAPID_GET_NEXT_PTR(capid); + + /* + * If this capability entry ID is 2, then we are done. + */ + if (AGP_CAPID_GET_CAP_ID(capid) == 2) + return ptr; + } + + return 0; +} + +/* + * Find an AGP display device (if any). + */ +static device_t +agp_find_display(void) +{ + devclass_t pci = devclass_find("pci"); + device_t bus, dev = 0; + device_t *kids; + int busnum, numkids, i; + + for (busnum = 0; busnum < devclass_get_maxunit(pci); busnum++) { + bus = devclass_get_device(pci, busnum); + if (!bus) + continue; + device_get_children(bus, &kids, &numkids); + for (i = 0; i < numkids; i++) { + dev = kids[i]; + if (pci_get_class(dev) == PCIC_DISPLAY + && pci_get_subclass(dev) == PCIS_DISPLAY_VGA) + if (agp_find_caps(dev)) { + free(kids, M_TEMP); + return dev; + } + + } + free(kids, M_TEMP); + } + + return 0; +} + +struct agp_gatt * +agp_alloc_gatt(device_t dev) +{ + u_int32_t apsize = AGP_GET_APERTURE(dev); + u_int32_t entries = apsize >> AGP_PAGE_SHIFT; + struct agp_gatt *gatt; + + if (bootverbose) + device_printf(dev, + "allocating GATT for aperture of size %dM\n", + apsize / (1024*1024)); + + gatt = malloc(sizeof(struct agp_gatt), M_AGP, M_NOWAIT); + if (!gatt) + return 0; + + gatt->ag_entries = entries; + gatt->ag_virtual = contigmalloc(entries * sizeof(u_int32_t), M_AGP, 0, + 0, ~0, PAGE_SIZE, 0); + if (!gatt->ag_virtual) { + if (bootverbose) + device_printf(dev, "contiguous allocation failed\n"); + free(gatt, M_AGP); + return 0; + } + bzero(gatt->ag_virtual, entries * sizeof(u_int32_t)); + gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual); + agp_flush_cache(); + + return gatt; +} + +void +agp_free_gatt(struct agp_gatt *gatt) +{ + contigfree(gatt->ag_virtual, + gatt->ag_entries * sizeof(u_int32_t), M_AGP); + free(gatt, M_AGP); +} + +static int agp_max[][2] = { + {0, 0}, + {32, 4}, + {64, 28}, + {128, 96}, + {256, 204}, + {512, 440}, + {1024, 942}, + {2048, 1920}, + {4096, 3932} +}; +#define agp_max_size (sizeof(agp_max) / sizeof(agp_max[0])) + +int +agp_generic_attach(device_t dev) +{ + struct agp_softc *sc = device_get_softc(dev); + int rid, memsize, i; + + /* + * Find and map the aperture. + */ + rid = AGP_APBASE; + sc->as_aperture = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->as_aperture) + return ENOMEM; + + /* + * Work out an upper bound for agp memory allocation. This + * uses a heurisitc table from the Linux driver. + */ + memsize = ptoa(Maxmem) >> 20; + for (i = 0; i < agp_max_size; i++) { + if (memsize <= agp_max[i][0]) + break; + } + if (i == agp_max_size) i = agp_max_size - 1; + sc->as_maxmem = agp_max[i][1] << 20U; + + /* + * The lock is used to prevent re-entry to + * agp_generic_bind_memory() since that function can sleep. + */ + lockinit(&sc->as_lock, PZERO|PCATCH, "agplk", 0, 0); + + /* + * Initialise stuff for the userland device. + */ + agp_devclass = devclass_find("agp"); + TAILQ_INIT(&sc->as_memory); + sc->as_nextid = 1; + + sc->as_devnode = make_dev(&agp_cdevsw, + device_get_unit(dev), + UID_ROOT, + GID_WHEEL, + 0600, + "agpgart"); + + return 0; +} + +int +agp_generic_detach(device_t dev) +{ + struct agp_softc *sc = device_get_softc(dev); + bus_release_resource(dev, SYS_RES_MEMORY, AGP_APBASE, sc->as_aperture); + lockmgr(&sc->as_lock, LK_DRAIN, 0, curproc); + destroy_dev(sc->as_devnode); + agp_flush_cache(); + return 0; +} + +int +agp_generic_enable(device_t dev, u_int32_t mode) +{ + device_t mdev = agp_find_display(); + u_int32_t tstatus, mstatus; + u_int32_t command; + int rq, sba, fw, rate;; + + if (!mdev) { + AGP_DPF("can't find display\n"); + return ENXIO; + } + + tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); + mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4); + + /* Set RQ to the min of mode, tstatus and mstatus */ + rq = AGP_MODE_GET_RQ(mode); + if (AGP_MODE_GET_RQ(tstatus) < rq) + rq = AGP_MODE_GET_RQ(tstatus); + if (AGP_MODE_GET_RQ(mstatus) < rq) + rq = AGP_MODE_GET_RQ(mstatus); + + /* Set SBA if all three can deal with SBA */ + sba = (AGP_MODE_GET_SBA(tstatus) + & AGP_MODE_GET_SBA(mstatus) + & AGP_MODE_GET_SBA(mode)); + + /* Similar for FW */ + fw = (AGP_MODE_GET_FW(tstatus) + & AGP_MODE_GET_FW(mstatus) + & AGP_MODE_GET_FW(mode)); + + /* Figure out the max rate */ + rate = (AGP_MODE_GET_RATE(tstatus) + & AGP_MODE_GET_RATE(mstatus) + & AGP_MODE_GET_RATE(mode)); + if (rate & AGP_MODE_RATE_4x) + rate = AGP_MODE_RATE_4x; + else if (rate & AGP_MODE_RATE_2x) + rate = AGP_MODE_RATE_2x; + else + rate = AGP_MODE_RATE_1x; + + /* Construct the new mode word and tell the hardware */ + command = AGP_MODE_SET_RQ(0, rq); + command = AGP_MODE_SET_SBA(command, sba); + command = AGP_MODE_SET_FW(command, fw); + command = AGP_MODE_SET_RATE(command, rate); + command = AGP_MODE_SET_AGP(command, 1); + pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, command, 4); + pci_write_config(mdev, agp_find_caps(mdev) + AGP_COMMAND, command, 4); + + return 0; +} + +struct agp_memory * +agp_generic_alloc_memory(device_t dev, int type, vm_size_t size) +{ + struct agp_softc *sc = device_get_softc(dev); + struct agp_memory *mem; + + if ((size & (AGP_PAGE_SIZE - 1)) != 0) + return 0; + + if (sc->as_allocated + size > sc->as_maxmem) + return 0; + + mem = malloc(sizeof *mem, M_AGP, M_WAITOK); + mem->am_id = sc->as_nextid++; + mem->am_size = size; + mem->am_obj = vm_object_allocate(OBJT_DEFAULT, atop(round_page(size))); + mem->am_physical = 0; + mem->am_offset = 0; + mem->am_is_bound = 0; + TAILQ_INSERT_TAIL(&sc->as_memory, mem, am_link); + sc->as_allocated += size; + + return mem; +} + +int +agp_generic_free_memory(device_t dev, struct agp_memory *mem) +{ + struct agp_softc *sc = device_get_softc(dev); + + if (mem->am_is_bound) + return EBUSY; + + sc->as_allocated -= mem->am_size; + TAILQ_REMOVE(&sc->as_memory, mem, am_link); + vm_object_deallocate(mem->am_obj); + free(mem, M_AGP); + return 0; +} + +int +agp_generic_bind_memory(device_t dev, struct agp_memory *mem, + vm_offset_t offset) +{ + struct agp_softc *sc = device_get_softc(dev); + vm_offset_t i, j, k; + vm_page_t m; + int error; + + lockmgr(&sc->as_lock, LK_EXCLUSIVE, 0, curproc); + + if (mem->am_is_bound) { + device_printf(dev, "memory already bound\n"); + return EINVAL; + } + + if (offset < 0 + || (offset & (AGP_PAGE_SIZE - 1)) != 0 + || offset + mem->am_size > AGP_GET_APERTURE(dev)) { + device_printf(dev, "binding memory at bad offset %#x\n", + (int) offset); + return EINVAL; + } + + /* + * Bind the individual pages and flush the chipset's + * TLB. + * + * XXX Presumably, this needs to be the pci address on alpha + * (i.e. use alpha_XXX_dmamap()). I don't have access to any + * alpha AGP hardware to check. + */ + for (i = 0; i < mem->am_size; i += PAGE_SIZE) { + /* + * Find a page from the object and wire it + * down. This page will be mapped using one or more + * entries in the GATT (assuming that PAGE_SIZE >= + * AGP_PAGE_SIZE. If this is the first call to bind, + * the pages will be allocated and zeroed. + */ + m = vm_page_grab(mem->am_obj, OFF_TO_IDX(i), + VM_ALLOC_ZERO | VM_ALLOC_RETRY); + AGP_DPF("found page pa=%#x\n", VM_PAGE_TO_PHYS(m)); + vm_page_wire(m); + + /* + * Install entries in the GATT, making sure that if + * AGP_PAGE_SIZE < PAGE_SIZE and mem->am_size is not + * aligned to PAGE_SIZE, we don't modify too many GATT + * entries. + */ + for (j = 0; j < PAGE_SIZE && i + j < mem->am_size; + j += AGP_PAGE_SIZE) { + vm_offset_t pa = VM_PAGE_TO_PHYS(m) + j; + AGP_DPF("binding offset %#x to pa %#x\n", + offset + i + j, pa); + error = AGP_BIND_PAGE(dev, offset + i + j, pa); + if (error) { + /* + * Bail out. Reverse all the mappings + * and unwire the pages. + */ + vm_page_wakeup(m); + for (k = 0; k < i + j; k += AGP_PAGE_SIZE) + AGP_UNBIND_PAGE(dev, offset + k); + for (k = 0; k <= i; k += PAGE_SIZE) { + m = vm_page_lookup(mem->am_obj, + OFF_TO_IDX(k)); + vm_page_unwire(m, 0); + } + lockmgr(&sc->as_lock, LK_RELEASE, 0, curproc); + return error; + } + } + vm_page_wakeup(m); + } + + /* + * Flush the cpu cache since we are providing a new mapping + * for these pages. + */ + agp_flush_cache(); + + /* + * Make sure the chipset gets the new mappings. + */ + AGP_FLUSH_TLB(dev); + + mem->am_offset = offset; + mem->am_is_bound = 1; + + lockmgr(&sc->as_lock, LK_RELEASE, 0, curproc); + + return 0; +} + +int +agp_generic_unbind_memory(device_t dev, struct agp_memory *mem) +{ + struct agp_softc *sc = device_get_softc(dev); + vm_page_t m; + int i; + + lockmgr(&sc->as_lock, LK_EXCLUSIVE, 0, curproc); + + if (!mem->am_is_bound) { + device_printf(dev, "memory is not bound\n"); + return EINVAL; + } + + + /* + * Unbind the individual pages and flush the chipset's + * TLB. Unwire the pages so they can be swapped. + */ + for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) + AGP_UNBIND_PAGE(dev, mem->am_offset + i); + for (i = 0; i < mem->am_size; i += PAGE_SIZE) { + m = vm_page_lookup(mem->am_obj, atop(i)); + vm_page_unwire(m, 0); + } + + agp_flush_cache(); + AGP_FLUSH_TLB(dev); + + mem->am_offset = 0; + mem->am_is_bound = 0; + + lockmgr(&sc->as_lock, LK_RELEASE, 0, curproc); + + return 0; +} + +/* Helper functions for implementing user/kernel api */ + +static int +agp_acquire_helper(device_t dev, enum agp_acquire_state state) +{ + struct agp_softc *sc = device_get_softc(dev); + + if (sc->as_state != AGP_ACQUIRE_FREE) + return EBUSY; + sc->as_state = state; + + return 0; +} + +static int +agp_release_helper(device_t dev, enum agp_acquire_state state) +{ + struct agp_softc *sc = device_get_softc(dev); + struct agp_memory *mem; + + if (sc->as_state == AGP_ACQUIRE_FREE) + return 0; + + if (sc->as_state != state) + return EBUSY; + + /* + * Clear out the aperture and free any outstanding memory blocks. + */ + while ((mem = TAILQ_FIRST(&sc->as_memory)) != 0) { + if (mem->am_is_bound) + AGP_UNBIND_MEMORY(dev, mem); + AGP_FREE_MEMORY(dev, mem); + } + + sc->as_state = AGP_ACQUIRE_FREE; + return 0; +} + +static struct agp_memory * +agp_find_memory(device_t dev, int id) +{ + struct agp_softc *sc = device_get_softc(dev); + struct agp_memory *mem; + + AGP_DPF("searching for memory block %d\n", id); + TAILQ_FOREACH(mem, &sc->as_memory, am_link) { + AGP_DPF("considering memory block %d\n", mem->am_id); + if (mem->am_id == id) + return mem; + } + return 0; +} + +/* Implementation of the userland ioctl api */ + +static int +agp_info_user(device_t dev, agp_info *info) +{ + struct agp_softc *sc = device_get_softc(dev); + + bzero(info, sizeof *info); + info->bridge_id = pci_get_devid(dev); + info->agp_mode = + pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); + info->aper_base = rman_get_start(sc->as_aperture); + info->aper_size = AGP_GET_APERTURE(dev) >> 20; + info->pg_total = info->pg_system = sc->as_maxmem >> AGP_PAGE_SHIFT; + info->pg_used = sc->as_allocated >> AGP_PAGE_SHIFT; + + return 0; +} + +static int +agp_setup_user(device_t dev, agp_setup *setup) +{ + return AGP_ENABLE(dev, setup->agp_mode); +} + +static int +agp_allocate_user(device_t dev, agp_allocate *alloc) +{ + struct agp_memory *mem; + + mem = AGP_ALLOC_MEMORY(dev, + alloc->type, + alloc->pg_count << AGP_PAGE_SHIFT); + alloc->key = mem->am_id; + alloc->physical = mem->am_physical; + + return 0; +} + +static int +agp_deallocate_user(device_t dev, int id) +{ + struct agp_memory *mem = agp_find_memory(dev, id);; + + if (mem) { + AGP_FREE_MEMORY(dev, mem); + return 0; + } else { + return ENOENT; + } +} + +static int +agp_bind_user(device_t dev, agp_bind *bind) +{ + struct agp_memory *mem = agp_find_memory(dev, bind->key); + + if (!mem) + return ENOENT; + + return AGP_BIND_MEMORY(dev, mem, bind->pg_start << AGP_PAGE_SHIFT); +} + +static int +agp_unbind_user(device_t dev, agp_unbind *unbind) +{ + struct agp_memory *mem = agp_find_memory(dev, unbind->key); + + if (!mem) + return ENOENT; + + return AGP_UNBIND_MEMORY(dev, mem); +} + +static int +agp_open(dev_t kdev, int oflags, int devtype, struct proc *p) +{ + device_t dev = KDEV2DEV(kdev); + struct agp_softc *sc = device_get_softc(dev); + + if (!sc->as_isopen) { + sc->as_isopen = 1; + device_busy(dev); + } + + return 0; +} + +static int +agp_close(dev_t kdev, int fflag, int devtype, struct proc *p) +{ + device_t dev = KDEV2DEV(kdev); + struct agp_softc *sc = device_get_softc(dev); + + /* + * Clear the GATT and force release on last close + */ + if (sc->as_state == AGP_ACQUIRE_USER) + agp_release_helper(dev, AGP_ACQUIRE_USER); + sc->as_isopen = 0; + device_unbusy(dev); + + return 0; +} + +static int +agp_ioctl(dev_t kdev, u_long cmd, caddr_t data, int fflag, struct proc *p) +{ + device_t dev = KDEV2DEV(kdev); + + switch (cmd) { + case AGPIOC_INFO: + return agp_info_user(dev, (agp_info *) data); + + case AGPIOC_ACQUIRE: + return agp_acquire_helper(dev, AGP_ACQUIRE_USER); + + case AGPIOC_RELEASE: + return agp_release_helper(dev, AGP_ACQUIRE_USER); + + case AGPIOC_SETUP: + return agp_setup_user(dev, (agp_setup *)data); + + case AGPIOC_ALLOCATE: + return agp_allocate_user(dev, (agp_allocate *)data); + + case AGPIOC_DEALLOCATE: + return agp_deallocate_user(dev, *(int *) data); + + case AGPIOC_BIND: + return agp_bind_user(dev, (agp_bind *)data); + + case AGPIOC_UNBIND: + return agp_unbind_user(dev, (agp_unbind *)data); + + } + + return EINVAL; +} + +static int +agp_mmap(dev_t kdev, vm_offset_t offset, int prot) +{ + device_t dev = KDEV2DEV(kdev); + struct agp_softc *sc = device_get_softc(dev); + + if (offset > AGP_GET_APERTURE(dev)) + return -1; + return atop(rman_get_start(sc->as_aperture) + offset); +} + +/* Implementation of the kernel api */ + +device_t +agp_find_device() +{ + if (!agp_devclass) + return 0; + return devclass_get_device(agp_devclass, 0); +} + +enum agp_acquire_state +agp_state(device_t dev) +{ + struct agp_softc *sc = device_get_softc(dev); + return sc->as_state; +} + +void +agp_get_info(device_t dev, struct agp_info *info) +{ + struct agp_softc *sc = device_get_softc(dev); + + info->ai_mode = + pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); + info->ai_aperture_base = rman_get_start(sc->as_aperture); + info->ai_aperture_size = (rman_get_end(sc->as_aperture) + - rman_get_start(sc->as_aperture)) + 1; + info->ai_memory_allowed = sc->as_maxmem; + info->ai_memory_used = sc->as_allocated; +} + +int +agp_acquire(device_t dev) +{ + return agp_acquire_helper(dev, AGP_ACQUIRE_KERNEL); +} + +int +agp_release(device_t dev) +{ + return agp_release_helper(dev, AGP_ACQUIRE_KERNEL); +} + +int +agp_enable(device_t dev, u_int32_t mode) +{ + return AGP_ENABLE(dev, mode); +} + +void *agp_alloc_memory(device_t dev, int type, vm_size_t bytes) +{ + return (void *) AGP_ALLOC_MEMORY(dev, type, bytes); +} + +void agp_free_memory(device_t dev, void *handle) +{ + struct agp_memory *mem = (struct agp_memory *) handle; + AGP_FREE_MEMORY(dev, mem); +} + +int agp_bind_memory(device_t dev, void *handle, vm_offset_t offset) +{ + struct agp_memory *mem = (struct agp_memory *) handle; + return AGP_BIND_MEMORY(dev, mem, offset); +} + +int agp_unbind_memory(device_t dev, void *handle) +{ + struct agp_memory *mem = (struct agp_memory *) handle; + return AGP_UNBIND_MEMORY(dev, mem); +} + +void agp_memory_info(device_t dev, void *handle, struct + agp_memory_info *mi) +{ + struct agp_memory *mem = (struct agp_memory *) handle; + + mi->ami_size = mem->am_size; + mi->ami_physical = mem->am_physical; + mi->ami_offset = mem->am_offset; + mi->ami_is_bound = mem->am_is_bound; +} diff --git a/sys/pci/agp_ali.c b/sys/pci/agp_ali.c new file mode 100644 index 000000000000..86e070e8d2ae --- /dev/null +++ b/sys/pci/agp_ali.c @@ -0,0 +1,264 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +struct agp_ali_softc { + struct agp_softc agp; + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_gatt *gatt; +}; + +static const char* +agp_ali_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_BRIDGE + || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) + return NULL; + + if (agp_find_caps(dev) == 0) + return NULL; + + switch (pci_get_devid(dev)) { + case 0x154110b9: + return ("Ali M1541 host to AGP bridge"); + }; + + if (pci_get_vendor(dev) == 0x10b9) + return ("Ali Generic host to PCI bridge"); + + return NULL; +} + +static int +agp_ali_probe(device_t dev) +{ + const char *desc; + + desc = agp_ali_match(dev); + if (desc) { + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_ali_attach(device_t dev) +{ + struct agp_ali_softc *sc = device_get_softc(dev); + struct agp_gatt *gatt; + int error; + + error = agp_generic_attach(dev); + if (error) + return error; + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + for (;;) { + gatt = agp_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) { + agp_generic_detach(dev); + return ENOMEM; + } + } + sc->gatt = gatt; + + /* Install the gatt. */ + pci_write_config(dev, AGP_ALI_ATTBASE, + (gatt->ag_physical + | (pci_read_config(dev, AGP_ALI_ATTBASE, 4) & 0xff)), + 4); + + /* Enable the TLB. */ + pci_write_config(dev, AGP_ALI_TLBCTRL, 0x10, 1); + + return 0; +} + +static int +agp_ali_detach(device_t dev) +{ + struct agp_ali_softc *sc = device_get_softc(dev); + int error; + + error = agp_generic_detach(dev); + if (error) + return error; + + /* Disable the TLB.. */ + pci_write_config(dev, AGP_ALI_TLBCTRL, 0x90, 1); + + /* Put the aperture back the way it started. */ + AGP_SET_APERTURE(dev, sc->initial_aperture); + pci_write_config(dev, AGP_ALI_ATTBASE, + pci_read_config(dev, AGP_ALI_ATTBASE, 4) & 0xff, + 4); + + agp_free_gatt(sc->gatt); + return 0; +} + +#define M 1024*1024 + +static u_int32_t agp_ali_table[] = { + 0, /* 0 - invalid */ + 1, /* 1 - invalid */ + 2, /* 2 - invalid */ + 4*M, /* 3 - invalid */ + 8*M, /* 4 - invalid */ + 0, /* 5 - invalid */ + 16*M, /* 6 - invalid */ + 32*M, /* 7 - invalid */ + 64*M, /* 8 - invalid */ + 128*M, /* 9 - invalid */ + 256*M, /* 10 - invalid */ +}; +#define agp_ali_table_size (sizeof(agp_ali_table) / sizeof(agp_ali_table[0])) + +static u_int32_t +agp_ali_get_aperture(device_t dev) +{ + /* + * The aperture size is derived from the low bits of attbase. + * I'm not sure this is correct.. + */ + int i = pci_read_config(dev, AGP_ALI_ATTBASE, 4) & 0xff; + if (i >= agp_ali_table_size) + return 0; + return agp_ali_table[i]; +} + +static int +agp_ali_set_aperture(device_t dev, u_int32_t aperture) +{ + int i; + + for (i = 0; i < agp_ali_table_size; i++) + if (agp_ali_table[i] == aperture) + break; + if (i == agp_ali_table_size) + return EINVAL; + + pci_write_config(dev, AGP_ALI_ATTBASE, + ((pci_read_config(dev, AGP_ALI_ATTBASE, 4) & ~0xff) + | i), 4); + return 0; +} + +static int +agp_ali_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_ali_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical; + return 0; +} + +static int +agp_ali_unbind_page(device_t dev, int offset) +{ + struct agp_ali_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; + return 0; +} + +static void +agp_ali_flush_tlb(device_t dev) +{ + pci_write_config(dev, AGP_ALI_TLBCTRL, 0x90, 1); + pci_write_config(dev, AGP_ALI_TLBCTRL, 0x10, 1); +} + +static device_method_t agp_ali_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_ali_probe), + DEVMETHOD(device_attach, agp_ali_attach), + DEVMETHOD(device_detach, agp_ali_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_ali_get_aperture), + DEVMETHOD(agp_set_aperture, agp_ali_set_aperture), + DEVMETHOD(agp_bind_page, agp_ali_bind_page), + DEVMETHOD(agp_unbind_page, agp_ali_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_ali_flush_tlb), + DEVMETHOD(agp_enable, agp_generic_enable), + DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), + DEVMETHOD(agp_free_memory, agp_generic_free_memory), + DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_ali_driver = { + "agp", + agp_ali_methods, + sizeof(struct agp_ali_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_ali, pci, agp_ali_driver, agp_devclass, 0, 0); diff --git a/sys/pci/agp_amd.c b/sys/pci/agp_amd.c new file mode 100644 index 000000000000..112918ad9aff --- /dev/null +++ b/sys/pci/agp_amd.c @@ -0,0 +1,276 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define READ2(off) bus_space_read_2(sc->bst, sc->bsh, off) +#define READ4(off) bus_space_read_4(sc->bst, sc->bsh, off) +#define WRITE2(off,v) bus_space_write_2(sc->bst, sc->bsh, off, v) +#define WRITE4(off,v) bus_space_write_4(sc->bst, sc->bsh, off, v) + +struct agp_amd_softc { + struct agp_softc agp; + struct resource *regs; /* memory mapped control registers */ + bus_space_tag_t bst; /* bus_space tag */ + bus_space_handle_t bsh; /* bus_space handle */ + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_gatt *gatt; +}; + +static const char* +agp_amd_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_BRIDGE + || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) + return NULL; + + if (agp_find_caps(dev) == 0) + return NULL; + + switch (pci_get_devid(dev)) { + case 0x70061022: + return ("AMD 751 host to AGP bridge"); + }; + + return NULL; +} + +static int +agp_amd_probe(device_t dev) +{ + const char *desc; + + desc = agp_amd_match(dev); + if (desc) { + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_amd_attach(device_t dev) +{ + struct agp_amd_softc *sc = device_get_softc(dev); + struct agp_gatt *gatt; + int error, rid; + + error = agp_generic_attach(dev); + if (error) + return error; + + rid = AGP_AMD751_REGISTERS; + sc->regs = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->regs) { + agp_generic_detach(dev); + return ENOMEM; + } + + sc->bst = rman_get_bustag(sc->regs); + sc->bsh = rman_get_bushandle(sc->regs); + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + for (;;) { + gatt = agp_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) + return ENOMEM; + } + sc->gatt = gatt; + + /* Install the gatt. */ + WRITE4(AGP_AMD751_ATTBASE, gatt->ag_physical); + + /* Enable synchronisation between host and agp. */ + pci_write_config(dev, AGP_AMD751_MODECTRL, 0x80, 1); + + /* Enable the TLB and flush */ + WRITE2(AGP_AMD751_STATUS, + READ2(AGP_AMD751_STATUS) | AGP_AMD751_STATUS_GCE); + AGP_FLUSH_TLB(dev); + + return agp_generic_attach(dev); +} + +static int +agp_amd_detach(device_t dev) +{ + struct agp_amd_softc *sc = device_get_softc(dev); + + /* Disable the TLB.. */ + WRITE2(AGP_AMD751_STATUS, + READ2(AGP_AMD751_STATUS) & ~AGP_AMD751_STATUS_GCE); + + /* Disable host-agp sync */ + pci_write_config(dev, AGP_AMD751_MODECTRL, 0x00, 1); + + /* Clear the GATT base */ + WRITE4(AGP_AMD751_ATTBASE, 0); + + /* Put the aperture back the way it started. */ + AGP_SET_APERTURE(dev, sc->initial_aperture); + + agp_free_gatt(sc->gatt); + return 0; +} + +static u_int32_t +agp_amd_get_aperture(device_t dev) +{ + int vas; + + /* + * The aperture size is equal to 32M<> 1; + return (32*1024*1024) << vas; +} + +static int +agp_amd_set_aperture(device_t dev, u_int32_t aperture) +{ + int vas; + + /* + * Check for a power of two and make sure its within the + * programmable range. + */ + if (aperture & (aperture - 1) + || aperture < 32*1024*1024 + || aperture > 2U*1024*1024*1024) + return EINVAL; + + vas = ffs(aperture / 32*1024*1024) - 1; + + pci_write_config(dev, AGP_AMD751_APCTRL, + ((pci_read_config(dev, AGP_AMD751_APCTRL, 1) & ~0x06) + | vas << 1), 1); + + return 0; +} + +static int +agp_amd_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_amd_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 1; + return 0; +} + +static int +agp_amd_unbind_page(device_t dev, int offset) +{ + struct agp_amd_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; + return 0; +} + +static void +agp_amd_flush_tlb(device_t dev) +{ + struct agp_amd_softc *sc = device_get_softc(dev); + + /* Set the cache invalidate bit and wait for the chipset to clear */ + WRITE4(AGP_AMD751_TLBCTRL, 1); + do { + DELAY(1); + } while (READ4(AGP_AMD751_TLBCTRL)); +} + +static device_method_t agp_amd_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_amd_probe), + DEVMETHOD(device_attach, agp_amd_attach), + DEVMETHOD(device_detach, agp_amd_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_amd_get_aperture), + DEVMETHOD(agp_set_aperture, agp_amd_set_aperture), + DEVMETHOD(agp_bind_page, agp_amd_bind_page), + DEVMETHOD(agp_unbind_page, agp_amd_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_amd_flush_tlb), + DEVMETHOD(agp_enable, agp_generic_enable), + DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), + DEVMETHOD(agp_free_memory, agp_generic_free_memory), + DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_amd_driver = { + "agp", + agp_amd_methods, + sizeof(struct agp_amd_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_amd, pci, agp_amd_driver, agp_devclass, 0, 0); diff --git a/sys/pci/agp_if.m b/sys/pci/agp_if.m new file mode 100644 index 000000000000..06f64d376bab --- /dev/null +++ b/sys/pci/agp_if.m @@ -0,0 +1,134 @@ +# +# Copyright (c) 2000 Doug Rabson +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +#include + +# +# The AGP interface is used internally to the agp driver to isolate the +# differences between various AGP chipsets into chipset mini drivers. It +# should not be used outside the AGP driver. The kernel api for accessing +# AGP functionality is described in +# +INTERFACE agp; + +# +# Return the current aperture size. +# +METHOD u_int32_t get_aperture { + device_t dev; +}; + +# +# Set the size of the aperture. Return EINVAL on error or 0 on success. +# +METHOD int set_aperture { + device_t dev; + u_int32_t aperture; +}; + +# +# Bind a single page in the AGP aperture to a given physical address. +# The offset is a byte offset within the aperture which must be +# aligned to an AGP page boundary. +# +METHOD int bind_page { + device_t dev; + vm_offset_t offset; + vm_offset_t physical; +}; + +# +# Unbind a single page in the AGP aperture. +# +METHOD int unbind_page { + device_t dev; + vm_offset_t offset; +}; + +# +# Flush the GATT TLB. This is used after a call to bind_page to +# ensure that any mappings cached in the chipset are discarded. +# +METHOD void flush_tlb { + device_t dev; +}; + +# +# Enable the agp hardware with the relavent mode. The mode bits are +# defined in +# +METHOD int enable { + device_t dev; + u_int32_t mode; +}; + +# +# Allocate memory of a given type. The type is a chipset-specific +# code which is used by certain integrated agp graphics chips +# (basically just the i810 for now) to access special features of +# the chipset. An opaque handle representing the memory region is +# returned and can be used as an argument to free_memory, bind_memory +# and unbind_memory. +# +# The size is specified in bytes but must be a multiple of the AGP +# page size. +# +METHOD struct agp_memory * alloc_memory { + device_t dev; + int type; + vm_size_t size; +}; + +# +# Free a memory region previously allocated with alloc_memory. Return +# EBUSY if the memory is bound. +# +METHOD int free_memory { + device_t dev; + struct agp_memory *mem; +}; + +# +# Bind a memory region to a specific byte offset within the chipset's +# AGP aperture. This effectively defines a range of contiguous +# physical address which alias the (possibly uncontiguous) pages in +# the memory region. +# +METHOD int bind_memory { + device_t dev; + struct agp_memory *mem; + vm_offset_t offset; +}; + +# +# Unbind a memory region bound with bind_memory. +# +METHOD int unbind_memory { + device_t dev; + struct agp_memory *handle; +}; diff --git a/sys/pci/agp_intel.c b/sys/pci/agp_intel.c new file mode 100644 index 000000000000..88d38ec027cf --- /dev/null +++ b/sys/pci/agp_intel.c @@ -0,0 +1,266 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +struct agp_intel_softc { + struct agp_softc agp; + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_gatt *gatt; +}; + +static const char* +agp_intel_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_BRIDGE + || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) + return NULL; + + if (agp_find_caps(dev) == 0) + return NULL; + + switch (pci_get_devid(dev)) { + /* Intel -- vendor 0x8086 */ + case 0x71808086: + return ("Intel 82443LX (440 LX) host to PCI bridge"); + + case 0x71908086: + return ("Intel 82443BX (440 BX) host to PCI bridge"); + + case 0x71a08086: + return ("Intel 82443GX host to PCI bridge"); + + case 0x71a18086: + return ("Intel 82443GX host to AGP bridge"); + }; + + if (pci_get_vendor(dev) == 0x8086) + return ("Intel Generic host to PCI bridge"); + + return NULL; +} + +static int +agp_intel_probe(device_t dev) +{ + const char *desc; + + desc = agp_intel_match(dev); + if (desc) { + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_intel_attach(device_t dev) +{ + struct agp_intel_softc *sc = device_get_softc(dev); + struct agp_gatt *gatt; + int error; + + error = agp_generic_attach(dev); + if (error) + return error; + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + for (;;) { + gatt = agp_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) { + agp_generic_detach(dev); + return ENOMEM; + } + } + sc->gatt = gatt; + + /* Install the gatt. */ + pci_write_config(dev, AGP_INTEL_ATTBASE, gatt->ag_physical, 4); + + /* Enable things, clear errors etc. */ + pci_write_config(dev, AGP_INTEL_AGPCTRL, 0x2280, 4); + pci_write_config(dev, AGP_INTEL_NBXCFG, + (pci_read_config(dev, AGP_INTEL_NBXCFG, 4) + & ~(1 << 10)) | (1 << 9), 4); + pci_write_config(dev, AGP_INTEL_ERRSTS + 1, 7, 1); + + return 0; +} + +static int +agp_intel_detach(device_t dev) +{ + struct agp_intel_softc *sc = device_get_softc(dev); + int error; + + error = agp_generic_detach(dev); + if (error) + return error; + + printf("%s: set NBXCFG to %x\n", __FUNCTION__, + (pci_read_config(dev, AGP_INTEL_NBXCFG, 4) + & ~(1 << 9))); + pci_write_config(dev, AGP_INTEL_NBXCFG, + (pci_read_config(dev, AGP_INTEL_NBXCFG, 4) + & ~(1 << 9)), 4); + pci_write_config(dev, AGP_INTEL_ATTBASE, 0, 4); + AGP_SET_APERTURE(dev, sc->initial_aperture); + agp_free_gatt(sc->gatt); + + return 0; +} + +static u_int32_t +agp_intel_get_aperture(device_t dev) +{ + u_int32_t apsize; + + apsize = pci_read_config(dev, AGP_INTEL_APSIZE, 1) & 0x1f; + + /* + * The size is determined by the number of low bits of + * register APBASE which are forced to zero. The low 22 bits + * are always forced to zero and each zero bit in the apsize + * field just read forces the corresponding bit in the 27:22 + * to be zero. We calculate the aperture size accordingly. + */ + return (((apsize ^ 0x1f) << 22) | ((1 << 22) - 1)) + 1; +} + +static int +agp_intel_set_aperture(device_t dev, u_int32_t aperture) +{ + u_int32_t apsize; + + /* + * Reverse the magic from get_aperture. + */ + apsize = ((aperture - 1) >> 22) ^ 0x1f; + + /* + * Double check for sanity. + */ + if ((((apsize ^ 0x1f) << 22) | ((1 << 22) - 1)) + 1 != aperture) + return EINVAL; + + pci_write_config(dev, AGP_INTEL_APSIZE, apsize, 1); + + return 0; +} + +static int +agp_intel_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_intel_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 0x17; + return 0; +} + +static int +agp_intel_unbind_page(device_t dev, int offset) +{ + struct agp_intel_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; + return 0; +} + +static void +agp_intel_flush_tlb(device_t dev) +{ + pci_write_config(dev, AGP_INTEL_AGPCTRL, 0x2200, 4); + pci_write_config(dev, AGP_INTEL_AGPCTRL, 0x2280, 4); +} + +static device_method_t agp_intel_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_intel_probe), + DEVMETHOD(device_attach, agp_intel_attach), + DEVMETHOD(device_detach, agp_intel_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_intel_get_aperture), + DEVMETHOD(agp_set_aperture, agp_intel_set_aperture), + DEVMETHOD(agp_bind_page, agp_intel_bind_page), + DEVMETHOD(agp_unbind_page, agp_intel_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_intel_flush_tlb), + DEVMETHOD(agp_enable, agp_generic_enable), + DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), + DEVMETHOD(agp_free_memory, agp_generic_free_memory), + DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_intel_driver = { + "agp", + agp_intel_methods, + sizeof(struct agp_intel_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_intel, pci, agp_intel_driver, agp_devclass, 0, 0); diff --git a/sys/pci/agp_sis.c b/sys/pci/agp_sis.c new file mode 100644 index 000000000000..1f1a50b7eceb --- /dev/null +++ b/sys/pci/agp_sis.c @@ -0,0 +1,256 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +struct agp_sis_softc { + struct agp_softc agp; + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_gatt *gatt; +}; + +static const char* +agp_sis_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_BRIDGE + || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) + return NULL; + + if (agp_find_caps(dev) == 0) + return NULL; + + switch (pci_get_devid(dev)) { + case 0x00011039: + return ("SiS 5591 host to AGP bridge"); + }; + + if (pci_get_vendor(dev) == 0x1039) + return ("SIS Generic host to PCI bridge"); + + return NULL; +} + +static int +agp_sis_probe(device_t dev) +{ + const char *desc; + + desc = agp_sis_match(dev); + if (desc) { + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_sis_attach(device_t dev) +{ + struct agp_sis_softc *sc = device_get_softc(dev); + struct agp_gatt *gatt; + int error; + + error = agp_generic_attach(dev); + if (error) + return error; + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + for (;;) { + gatt = agp_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) { + agp_generic_detach(dev); + return ENOMEM; + } + } + sc->gatt = gatt; + + /* Install the gatt. */ + pci_write_config(dev, AGP_SIS_ATTBASE, gatt->ag_physical, 4); + + /* Enable the aperture. */ + pci_write_config(dev, AGP_SIS_WINCTRL, + pci_read_config(dev, AGP_SIS_WINCTRL, 1) | 3, 1); + + /* + * Enable the TLB and make it automatically invalidate entries + * when the GATT is written. + */ + pci_write_config(dev, AGP_SIS_TLBCTRL, 0x05, 1); + + return 0; +} + +static int +agp_sis_detach(device_t dev) +{ + struct agp_sis_softc *sc = device_get_softc(dev); + int error; + + error = agp_generic_detach(dev); + if (error) + return error; + + /* Disable the aperture.. */ + pci_write_config(dev, AGP_SIS_WINCTRL, + pci_read_config(dev, AGP_SIS_WINCTRL, 1) & ~3, 1); + + /* and the TLB. */ + pci_write_config(dev, AGP_SIS_TLBCTRL, 0, 1); + + /* Put the aperture back the way it started. */ + AGP_SET_APERTURE(dev, sc->initial_aperture); + + agp_free_gatt(sc->gatt); + return 0; +} + +static u_int32_t +agp_sis_get_aperture(device_t dev) +{ + int gws; + + /* + * The aperture size is equal to 4M<> 4; + return (4*1024*1024) << gws; +} + +static int +agp_sis_set_aperture(device_t dev, u_int32_t aperture) +{ + int gws; + + /* + * Check for a power of two and make sure its within the + * programmable range. + */ + if (aperture & (aperture - 1) + || aperture < 4*1024*1024 + || aperture > 256*1024*1024) + return EINVAL; + + gws = ffs(aperture / 4*1024*1024) - 1; + + pci_write_config(dev, AGP_SIS_WINCTRL, + ((pci_read_config(dev, AGP_SIS_WINCTRL, 1) & ~0x70) + | gws << 4), 1); + + return 0; +} + +static int +agp_sis_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_sis_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical; + return 0; +} + +static int +agp_sis_unbind_page(device_t dev, int offset) +{ + struct agp_sis_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; + return 0; +} + +static void +agp_sis_flush_tlb(device_t dev) +{ + pci_write_config(dev, AGP_SIS_TLBFLUSH, 0x02, 1); +} + +static device_method_t agp_sis_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_sis_probe), + DEVMETHOD(device_attach, agp_sis_attach), + DEVMETHOD(device_detach, agp_sis_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_sis_get_aperture), + DEVMETHOD(agp_set_aperture, agp_sis_set_aperture), + DEVMETHOD(agp_bind_page, agp_sis_bind_page), + DEVMETHOD(agp_unbind_page, agp_sis_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_sis_flush_tlb), + DEVMETHOD(agp_enable, agp_generic_enable), + DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), + DEVMETHOD(agp_free_memory, agp_generic_free_memory), + DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_sis_driver = { + "agp", + agp_sis_methods, + sizeof(struct agp_sis_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_sis, pci, agp_sis_driver, agp_devclass, 0, 0); diff --git a/sys/pci/agp_via.c b/sys/pci/agp_via.c new file mode 100644 index 000000000000..983348ea25d4 --- /dev/null +++ b/sys/pci/agp_via.c @@ -0,0 +1,253 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_bus.h" +#include "opt_pci.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +struct agp_via_softc { + struct agp_softc agp; + u_int32_t initial_aperture; /* aperture size at startup */ + struct agp_gatt *gatt; +}; + +static const char* +agp_via_match(device_t dev) +{ + if (pci_get_class(dev) != PCIC_BRIDGE + || pci_get_subclass(dev) != PCIS_BRIDGE_HOST) + return NULL; + + if (agp_find_caps(dev) == 0) + return NULL; + + switch (pci_get_devid(dev)) { + case 0x05011106: + return ("VIA 8501 (Apollo MVP4) host to PCI bridge"); + case 0x05971106: + return ("VIA 82C597 (Apollo VP3) host to PCI bridge"); + case 0x05981106: + return ("VIA 82C598 (Apollo MVP3) host to PCI bridge"); + case 0x06911106: + return ("VIA 82C691 (Apollo Pro) host to PCI bridge"); + }; + + if (pci_get_vendor(dev) == 0x1106) + return ("VIA Generic host to PCI bridge"); + + return NULL; +} + +static int +agp_via_probe(device_t dev) +{ + const char *desc; + + desc = agp_via_match(dev); + if (desc) { + device_verbose(dev); + device_set_desc(dev, desc); + return 0; + } + + return ENXIO; +} + +static int +agp_via_attach(device_t dev) +{ + struct agp_via_softc *sc = device_get_softc(dev); + struct agp_gatt *gatt; + int error; + + error = agp_generic_attach(dev); + if (error) + return error; + + sc->initial_aperture = AGP_GET_APERTURE(dev); + + for (;;) { + gatt = agp_alloc_gatt(dev); + if (gatt) + break; + + /* + * Probably contigmalloc failure. Try reducing the + * aperture so that the gatt size reduces. + */ + if (AGP_SET_APERTURE(dev, AGP_GET_APERTURE(dev) / 2)) { + agp_generic_detach(dev); + return ENOMEM; + } + } + sc->gatt = gatt; + + /* Install the gatt. */ + pci_write_config(dev, AGP_VIA_ATTBASE, gatt->ag_physical | 3, 4); + + /* Enable the aperture. */ + pci_write_config(dev, AGP_VIA_GARTCTRL, 0x0f, 4); + + return 0; +} + +static int +agp_via_detach(device_t dev) +{ + struct agp_via_softc *sc = device_get_softc(dev); + int error; + + error = agp_generic_detach(dev); + if (error) + return error; + + pci_write_config(dev, AGP_VIA_GARTCTRL, 0, 4); + pci_write_config(dev, AGP_VIA_ATTBASE, 0, 4); + AGP_SET_APERTURE(dev, sc->initial_aperture); + agp_free_gatt(sc->gatt); + + return 0; +} + +static u_int32_t +agp_via_get_aperture(device_t dev) +{ + u_int32_t apsize; + + apsize = pci_read_config(dev, AGP_VIA_APSIZE, 1) & 0x1f; + + /* + * The size is determined by the number of low bits of + * register APBASE which are forced to zero. The low 20 bits + * are always forced to zero and each zero bit in the apsize + * field just read forces the corresponding bit in the 27:20 + * to be zero. We calculate the aperture size accordingly. + */ + return (((apsize ^ 0xff) << 20) | ((1 << 20) - 1)) + 1; +} + +static int +agp_via_set_aperture(device_t dev, u_int32_t aperture) +{ + u_int32_t apsize; + + /* + * Reverse the magic from get_aperture. + */ + apsize = ((aperture - 1) >> 20) ^ 0xff; + + /* + * Double check for sanity. + */ + if ((((apsize ^ 0xff) << 20) | ((1 << 20) - 1)) + 1 != aperture) + return EINVAL; + + pci_write_config(dev, AGP_VIA_APSIZE, apsize, 1); + + return 0; +} + +static int +agp_via_bind_page(device_t dev, int offset, vm_offset_t physical) +{ + struct agp_via_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical; + return 0; +} + +static int +agp_via_unbind_page(device_t dev, int offset) +{ + struct agp_via_softc *sc = device_get_softc(dev); + + if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) + return EINVAL; + + sc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; + return 0; +} + +static void +agp_via_flush_tlb(device_t dev) +{ + pci_write_config(dev, AGP_VIA_GARTCTRL, 0x8f, 4); + pci_write_config(dev, AGP_VIA_GARTCTRL, 0x0f, 4); +} + +static device_method_t agp_via_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, agp_via_probe), + DEVMETHOD(device_attach, agp_via_attach), + DEVMETHOD(device_detach, agp_via_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* AGP interface */ + DEVMETHOD(agp_get_aperture, agp_via_get_aperture), + DEVMETHOD(agp_set_aperture, agp_via_set_aperture), + DEVMETHOD(agp_bind_page, agp_via_bind_page), + DEVMETHOD(agp_unbind_page, agp_via_unbind_page), + DEVMETHOD(agp_flush_tlb, agp_via_flush_tlb), + DEVMETHOD(agp_enable, agp_generic_enable), + DEVMETHOD(agp_alloc_memory, agp_generic_alloc_memory), + DEVMETHOD(agp_free_memory, agp_generic_free_memory), + DEVMETHOD(agp_bind_memory, agp_generic_bind_memory), + DEVMETHOD(agp_unbind_memory, agp_generic_unbind_memory), + + { 0, 0 } +}; + +static driver_t agp_via_driver = { + "agp", + agp_via_methods, + sizeof(struct agp_via_softc), +}; + +static devclass_t agp_devclass; + +DRIVER_MODULE(agp_via, pci, agp_via_driver, agp_devclass, 0, 0); diff --git a/sys/pci/agppriv.h b/sys/pci/agppriv.h new file mode 100644 index 000000000000..427e68bf9d53 --- /dev/null +++ b/sys/pci/agppriv.h @@ -0,0 +1,103 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PCI_AGPPRIV_H_ +#define _PCI_AGPPRIV_H_ + +/* + * This file *must not* be included by code outside the agp driver itself. + */ + +#include +#include + +#define AGP_DEBUGxx + +#ifdef AGP_DEBUG +#define AGP_DPF(x...) do { \ + printf("agp: "); \ + printf(##x); \ +} while (0) +#else +#define AGP_DPF(x...) do {} while (0) +#endif + +#include "agp_if.h" + +/* + * Data structure to describe an AGP memory allocation. + */ +TAILQ_HEAD(agp_memory_list, agp_memory); +struct agp_memory { + TAILQ_ENTRY(agp_memory) am_link; /* wiring for the tailq */ + int am_id; /* unique id for block */ + vm_size_t am_size; /* number of bytes allocated */ + struct vm_object *am_obj; /* VM object owning pages */ + vm_offset_t am_physical; /* bogus hack for i810 */ + vm_offset_t am_offset; /* page offset if bound */ + int am_is_bound; /* non-zero if bound */ +}; + +/* + * All chipset drivers must have this at the start of their softc. + */ +struct agp_softc { + struct resource *as_aperture; /* location of aperture */ + u_int32_t as_maxmem; /* allocation upper bound */ + u_int32_t as_allocated; /* amount allocated */ + enum agp_acquire_state as_state; + struct agp_memory_list as_memory; /* list of allocated memory */ + int as_nextid; /* next memory block id */ + int as_isopen; /* user device is open */ + dev_t as_devnode; /* from make_dev */ + struct lock as_lock; /* lock for access to GATT */ +}; + +struct agp_gatt { + u_int32_t ag_entries; + u_int32_t *ag_virtual; + vm_offset_t ag_physical; +}; + +u_int8_t agp_find_caps(device_t dev); +struct agp_gatt *agp_alloc_gatt(device_t dev); +void agp_free_gatt(struct agp_gatt *gatt); +int agp_generic_attach(device_t dev); +int agp_generic_detach(device_t dev); +int agp_generic_enable(device_t dev, u_int32_t mode); +struct agp_memory *agp_generic_alloc_memory(device_t dev, int type, + vm_size_t size); +int agp_generic_free_memory(device_t dev, + struct agp_memory *mem); +int agp_generic_bind_memory(device_t dev, + struct agp_memory *mem, + vm_offset_t offset); +int agp_generic_unbind_memory(device_t dev, + struct agp_memory *mem); + +#endif /* !_PCI_AGPPRIV_H_ */ diff --git a/sys/pci/agpreg.h b/sys/pci/agpreg.h new file mode 100644 index 000000000000..0539325b65d8 --- /dev/null +++ b/sys/pci/agpreg.h @@ -0,0 +1,109 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PCI_AGPREG_H_ +#define _PCI_AGPREG_H_ + +/* + * Offsets for various AGP configuration registers. + */ +#define AGP_APBASE 0x10 +#define AGP_CAPPTR 0x34 + +/* + * Offsets from the AGP Capability pointer. + */ +#define AGP_CAPID 0x0 +#define AGP_CAPID_GET_MAJOR(x) (((x) & 0x00f00000U) >> 20) +#define AGP_CAPID_GET_MINOR(x) (((x) & 0x000f0000U) >> 16) +#define AGP_CAPID_GET_NEXT_PTR(x) (((x) & 0x0000ff00U) >> 8) +#define AGP_CAPID_GET_CAP_ID(x) (((x) & 0x000000ffU) >> 0) + +#define AGP_STATUS 0x4 +#define AGP_COMMAND 0x8 + +/* + * Config offsets for Intel AGP chipsets. + */ +#define AGP_INTEL_NBXCFG 0x50 +#define AGP_INTEL_ERRSTS 0x91 +#define AGP_INTEL_AGPCTRL 0xb0 +#define AGP_INTEL_APSIZE 0xb4 +#define AGP_INTEL_ATTBASE 0xb8 + +/* + * Config offsets for VIA AGP chipsets. + */ +#define AGP_VIA_GARTCTRL 0x80 +#define AGP_VIA_APSIZE 0x84 +#define AGP_VIA_ATTBASE 0x88 + +/* + * Config offsets for SiS AGP chipsets. + */ +#define AGP_SIS_ATTBASE 0x90 +#define AGP_SIS_WINCTRL 0x94 +#define AGP_SIS_TLBCTRL 0x97 +#define AGP_SIS_TLBFLUSH 0x98 + +/* + * Config offsets for Ali AGP chipsets. + */ +#define AGP_ALI_AGPCTRL 0xb8 +#define AGP_ALI_ATTBASE 0xbc +#define AGP_ALI_TLBCTRL 0xc0 + +/* + * Config offsets for the AMD 751 chipset. + */ +#define AGP_AMD751_REGISTERS 0x14 +#define AGP_AMD751_APCTRL 0xac +#define AGP_AMD751_MODECTRL 0xb0 + +/* + * Memory mapped register offsets for AMD 751 chipset. + */ +#define AGP_AMD751_CAPS 0x00 +#define AGP_AMD751_CAPS_EHI 0x0800 +#define AGP_AMD751_CAPS_P2P 0x0400 +#define AGP_AMD751_CAPS_MPC 0x0200 +#define AGP_AMD751_CAPS_VBE 0x0100 +#define AGP_AMD751_CAPS_REV 0x00ff +#define AGP_AMD751_STATUS 0x02 +#define AGP_AMD751_STATUS_P2PS 0x0800 +#define AGP_AMD751_STATUS_GCS 0x0400 +#define AGP_AMD751_STATUS_MPS 0x0200 +#define AGP_AMD751_STATUS_VBES 0x0100 +#define AGP_AMD751_STATUS_P2PE 0x0008 +#define AGP_AMD751_STATUS_GCE 0x0004 +#define AGP_AMD751_STATUS_VBEE 0x0001 +#define AGP_AMD751_ATTBASE 0x04 +#define AGP_AMD751_TLBCTRL 0x0c + + +#endif /* !_PCI_AGPREG_H_ */ diff --git a/sys/pci/agpvar.h b/sys/pci/agpvar.h new file mode 100644 index 000000000000..df3112b49d29 --- /dev/null +++ b/sys/pci/agpvar.h @@ -0,0 +1,125 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PCI_AGPVAR_H_ +#define _PCI_AGPVAR_H_ + +/* + * The AGP chipset can be acquired by user or kernel code. If the + * chipset has already been acquired, it cannot be acquired by another + * user until the previous user has released it. + */ +enum agp_acquire_state { + AGP_ACQUIRE_FREE, + AGP_ACQUIRE_USER, + AGP_ACQUIRE_KERNEL +}; + +/* + * This structure is used to query the state of the AGP system. + */ +struct agp_info { + u_int32_t ai_mode; + vm_offset_t ai_aperture_base; + vm_size_t ai_aperture_size; + vm_size_t ai_memory_allowed; + vm_size_t ai_memory_used; + u_int32_t ai_devid; +}; + +struct agp_memory_info { + vm_size_t ami_size; /* size in bytes */ + vm_offset_t ami_physical; /* bogus hack for i810 */ + vm_offset_t ami_offset; /* page offset if bound */ + int ami_is_bound; /* non-zero if bound */ +}; + +/* + * Find the AGP device and return it. + */ +device_t agp_find_device(void); + +/* + * Return the current owner of the AGP chipset. + */ +enum agp_acquire_state agp_state(device_t dev); + +/* + * Query the state of the AGP system. + */ +void agp_get_info(device_t dev, struct agp_info *info); + +/* + * Acquire the AGP chipset for use by the kernel. Returns EBUSY if the + * AGP chipset is already acquired by another user. + */ +int agp_acquire(device_t dev); + +/* + * Release the AGP chipset. + */ +int agp_release(device_t dev); + +/* + * Enable the agp hardware with the relavent mode. The mode bits are + * defined in + */ +int agp_enable(device_t dev, u_int32_t mode); + +/* + * Allocate physical memory suitable for mapping into the AGP + * aperture. The value returned is an opaque handle which can be + * passed to agp_bind(), agp_unbind() or agp_deallocate(). + */ +void *agp_alloc_memory(device_t dev, int type, vm_size_t bytes); + +/* + * Free memory which was allocated with agp_allocate(). + */ +void agp_free_memory(device_t dev, void *handle); + +/* + * Bind memory allocated with agp_allocate() at a given offset within + * the AGP aperture. Returns EINVAL if the memory is already bound or + * the offset is not at an AGP page boundary. + */ +int agp_bind_memory(device_t dev, void *handle, vm_offset_t offset); + +/* + * Unbind memory from the AGP aperture. Returns EINVAL if the memory + * is not bound. + */ +int agp_unbind_memory(device_t dev, void *handle); + +/* + * Retrieve information about a memory block allocated with + * agp_alloc_memory(). + */ +void agp_memory_info(device_t dev, void *handle, struct agp_memory_info *mi); + +#endif /* !_PCI_AGPVAR_H_ */ diff --git a/sys/sys/agpio.h b/sys/sys/agpio.h new file mode 100644 index 000000000000..ebdc35216847 --- /dev/null +++ b/sys/sys/agpio.h @@ -0,0 +1,128 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SYS_AGPIO_H_ +#define _SYS_AGPIO_H_ + +/* + * The AGP gatt uses 4k pages irrespective of the host page size. + */ +#define AGP_PAGE_SIZE 4096 +#define AGP_PAGE_SHIFT 12 + +/* + * Macros to manipulate AGP mode words. + */ +#define AGP_MODE_GET_RQ(x) (((x) & 0xff000000U) >> 24) +#define AGP_MODE_GET_SBA(x) (((x) & 0x00000200U) >> 9) +#define AGP_MODE_GET_AGP(x) (((x) & 0x00000100U) >> 8) +#define AGP_MODE_GET_4G(x) (((x) & 0x00000020U) >> 5) +#define AGP_MODE_GET_FW(x) (((x) & 0x00000010U) >> 4) +#define AGP_MODE_GET_RATE(x) ((x) & 0x00000003U) +#define AGP_MODE_SET_RQ(x,v) (((x) & ~0xff000000U) | ((v) << 24)) +#define AGP_MODE_SET_SBA(x,v) (((x) & ~0x00000200U) | ((v) << 9)) +#define AGP_MODE_SET_AGP(x,v) (((x) & ~0x00000100U) | ((v) << 8)) +#define AGP_MODE_SET_4G(x,v) (((x) & ~0x00000020U) | ((v) << 5)) +#define AGP_MODE_SET_FW(x,v) (((x) & ~0x00000010U) | ((v) << 4)) +#define AGP_MODE_SET_RATE(x,v) (((x) & ~0x00000003U) | (v)) +#define AGP_MODE_RATE_1x 0x00000001 +#define AGP_MODE_RATE_2x 0x00000002 +#define AGP_MODE_RATE_4x 0x00000004 + +#define AGPIOC_BASE 'A' +#define AGPIOC_INFO _IOR (AGPIOC_BASE, 0, agp_info) +#define AGPIOC_ACQUIRE _IO (AGPIOC_BASE, 1) +#define AGPIOC_RELEASE _IO (AGPIOC_BASE, 2) +#define AGPIOC_SETUP _IOW (AGPIOC_BASE, 3, agp_setup) +#if 0 +#define AGPIOC_RESERVE _IOW (AGPIOC_BASE, 4, agp_region) +#define AGPIOC_PROTECT _IOW (AGPIOC_BASE, 5, agp_region) +#endif +#define AGPIOC_ALLOCATE _IOWR(AGPIOC_BASE, 6, agp_allocate) +#define AGPIOC_DEALLOCATE _IOW (AGPIOC_BASE, 7, int) +#define AGPIOC_BIND _IOW (AGPIOC_BASE, 8, agp_bind) +#define AGPIOC_UNBIND _IOW (AGPIOC_BASE, 9, agp_unbind) + +typedef struct _agp_version { + u_int16_t major; + u_int16_t minor; +} agp_version; + +typedef struct _agp_info { + agp_version version; /* version of the driver */ + u_int32_t bridge_id; /* bridge vendor/device */ + u_int32_t agp_mode; /* mode info of bridge */ + off_t aper_base; /* base of aperture */ + size_t aper_size; /* size of aperture */ + size_t pg_total; /* max pages (swap + system) */ + size_t pg_system; /* max pages (system) */ + size_t pg_used; /* current pages used */ +} agp_info; + +typedef struct _agp_setup { + u_int32_t agp_mode; /* mode info of bridge */ +} agp_setup; + +#if 0 +/* + * The "prot" down below needs still a "sleep" flag somehow ... + */ +typedef struct _agp_segment { + off_t pg_start; /* starting page to populate */ + size_t pg_count; /* number of pages */ + int prot; /* prot flags for mmap */ +} agp_segment; + +typedef struct _agp_region { + pid_t pid; /* pid of process */ + size_t seg_count; /* number of segments */ + struct _agp_segment *seg_list; +} agp_region; +#endif + +typedef struct _agp_allocate { + int key; /* tag of allocation */ + size_t pg_count; /* number of pages */ + u_int32_t type; /* 0 == normal, other devspec */ + u_int32_t physical; /* device specific (some devices + * need a phys address of the + * actual page behind the gatt + * table) */ +} agp_allocate; + +typedef struct _agp_bind { + int key; /* tag of allocation */ + off_t pg_start; /* starting page to populate */ +} agp_bind; + +typedef struct _agp_unbind { + int key; /* tag of allocation */ + u_int32_t priority; /* priority for paging out */ +} agp_unbind; + +#endif /* !_SYS_AGPIO_H_ */