1119418Sobrien/*- 274534Scokane * Copyright (c) 2000-2001 by Coleman Kane <cokane@FreeBSD.org> 361931Scokane * All rights reserved. 461931Scokane * 561931Scokane * Redistribution and use in source and binary forms, with or without 661931Scokane * modification, are permitted provided that the following conditions 761931Scokane * are met: 861931Scokane * 1. Redistributions of source code must retain the above copyright 961931Scokane * notice, this list of conditions and the following disclaimer. 1061931Scokane * 2. Redistributions in binary form must reproduce the above copyright 1161931Scokane * notice, this list of conditions and the following disclaimer in the 1261931Scokane * documentation and/or other materials provided with the distribution. 1361931Scokane * 3. All advertising materials mentioning features or use of this software 1461931Scokane * must display the following acknowledgement: 1561931Scokane * This product includes software developed by Gardner Buchanan. 1661931Scokane * 4. The name of Gardner Buchanan may not be used to endorse or promote 1761931Scokane * products derived from this software without specific prior written 1861931Scokane * permission. 1961931Scokane * 2061931Scokane * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2161931Scokane * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2261931Scokane * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2361931Scokane * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2461931Scokane * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2561931Scokane * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2661931Scokane * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2761931Scokane * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2861931Scokane * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2961931Scokane * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3061931Scokane */ 3161931Scokane 32119418Sobrien#include <sys/cdefs.h> 33119418Sobrien__FBSDID("$FreeBSD$"); 34119418Sobrien 3561911Scokane/* 3dfx driver for FreeBSD 4.x - Finished 11 May 2000, 12:25AM ET 3661911Scokane * 3774534Scokane * Copyright (C) 2000-2001, by Coleman Kane <cokane@FreeBSD.org>, 3861911Scokane * based upon the 3dfx driver written for linux, by Daryll Straus, Jon Taylor, 3961911Scokane * and Jens Axboe, located at http://linux.3dfx.com. 4061911Scokane */ 4161911Scokane 4261911Scokane#include <sys/param.h> 4361911Scokane 4461911Scokane#include <sys/bus.h> 4561911Scokane#include <sys/conf.h> 4661911Scokane#include <sys/fcntl.h> 4761911Scokane#include <sys/file.h> 4861911Scokane#include <sys/filedesc.h> 4961911Scokane#include <sys/filio.h> 5061911Scokane#include <sys/ioccom.h> 5161911Scokane#include <sys/kernel.h> 52129879Sphk#include <sys/module.h> 53129879Sphk#include <sys/malloc.h> 5461911Scokane#include <sys/mman.h> 5561911Scokane#include <sys/signalvar.h> 5661911Scokane#include <sys/systm.h> 5761911Scokane#include <sys/uio.h> 5861911Scokane 59119287Simp#include <dev/pci/pcivar.h> 60119287Simp#include <dev/pci/pcireg.h> 6161911Scokane 6261911Scokane#include <vm/vm.h> 6361911Scokane#include <vm/vm_kern.h> 6461911Scokane#include <vm/pmap.h> 6561911Scokane#include <vm/vm_extern.h> 6661911Scokane 6761911Scokane/* rman.h depends on machine/bus.h */ 6861911Scokane#include <machine/resource.h> 6961911Scokane#include <machine/bus.h> 7061911Scokane#include <sys/rman.h> 7162028Scokane 7261911Scokane#include <dev/tdfx/tdfx_io.h> 7361911Scokane#include <dev/tdfx/tdfx_vars.h> 7461911Scokane#include <dev/tdfx/tdfx_pci.h> 7561911Scokane 7661911Scokane 7761911Scokanestatic devclass_t tdfx_devclass; 7861911Scokane 7961911Scokane 8061911Scokanestatic int tdfx_count = 0; 8161911Scokane 8261911Scokane 8361911Scokane/* Set up the boot probe/attach routines */ 8461911Scokanestatic device_method_t tdfx_methods[] = { 8561911Scokane DEVMETHOD(device_probe, tdfx_probe), 8661911Scokane DEVMETHOD(device_attach, tdfx_attach), 8761911Scokane DEVMETHOD(device_detach, tdfx_detach), 8861911Scokane DEVMETHOD(device_shutdown, tdfx_shutdown), 8961911Scokane { 0, 0 } 9061911Scokane}; 9161911Scokane 92249132Smavstatic MALLOC_DEFINE(M_TDFX,"tdfx_driver","3DFX Graphics[/2D]/3D Accelerators"); 9361911Scokane 9461911Scokane/* Char. Dev. file operations structure */ 9561911Scokanestatic struct cdevsw tdfx_cdev = { 96126080Sphk .d_version = D_VERSION, 97126080Sphk .d_flags = D_NEEDGIANT, 98111815Sphk .d_open = tdfx_open, 99111815Sphk .d_close = tdfx_close, 100111815Sphk .d_ioctl = tdfx_ioctl, 101111815Sphk .d_mmap = tdfx_mmap, 102111815Sphk .d_name = "tdfx", 10361911Scokane}; 10461911Scokane 10561911Scokanestatic int 10661911Scokanetdfx_probe(device_t dev) 10761911Scokane{ 10861911Scokane /* 10961911Scokane * probe routine called on kernel boot to register supported devices. We get 11061911Scokane * a device structure to work with, and we can test the VENDOR/DEVICE IDs to 111142880Simp * see if this PCI device is one that we support. Return BUS_PRROBE_DEFAULT 112142880Simp * if yes, ENXIO if not. 11361911Scokane */ 11461911Scokane switch(pci_get_devid(dev)) { 11561911Scokane case PCI_DEVICE_ALLIANCE_AT3D: 11661911Scokane device_set_desc(dev, "ProMotion At3D 3D Accelerator"); 117142880Simp return BUS_PROBE_DEFAULT; 11861911Scokane case PCI_DEVICE_3DFX_VOODOO2: 11961911Scokane device_set_desc(dev, "3DFX Voodoo II 3D Accelerator"); 120142880Simp return BUS_PROBE_DEFAULT; 12165146Scokane /*case PCI_DEVICE_3DFX_BANSHEE: 12261911Scokane device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator"); 123142880Simp return BUS_PROBE_DEFAULT; 12461911Scokane case PCI_DEVICE_3DFX_VOODOO3: 12561911Scokane device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator"); 126142880Simp return BUS_PROBE_DEFAULT;*/ 12761911Scokane case PCI_DEVICE_3DFX_VOODOO1: 12861911Scokane device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator"); 129142880Simp return BUS_PROBE_DEFAULT; 13061911Scokane }; 13161911Scokane 13261911Scokane return ENXIO; 13361911Scokane} 13461911Scokane 13561911Scokanestatic int 13661911Scokanetdfx_attach(device_t dev) { 13761911Scokane /* 13861911Scokane * The attach routine is called after the probe routine successfully says it 13961911Scokane * supports a given card. We now proceed to initialize this card for use with 14061911Scokane * the system. I want to map the device memory for userland allocation and 14161911Scokane * fill an information structure with information on this card. I'd also like 14261911Scokane * to set Write Combining with the MTRR code so that we can hopefully speed 14361911Scokane * up memory writes. The last thing is to register the character device 14461911Scokane * interface to the card, so we can open it from /dev/3dfxN, where N is a 14561911Scokane * small, whole number. 14661911Scokane */ 14761911Scokane struct tdfx_softc *tdfx_info; 14861911Scokane /* rid value tells bus_alloc_resource where to find the addresses of ports or 14961911Scokane * of memory ranges in the PCI config space*/ 150119690Sjhb int rid = PCIR_BAR(0); 15161911Scokane 15261911Scokane /* Increment the card counter (for the ioctl code) */ 15361911Scokane tdfx_count++; 15461911Scokane 15561911Scokane /* Fill the soft config struct with info about this device*/ 15661911Scokane tdfx_info = device_get_softc(dev); 15761911Scokane tdfx_info->dev = dev; 15861911Scokane tdfx_info->vendor = pci_get_vendor(dev); 15961911Scokane tdfx_info->type = pci_get_devid(dev) >> 16; 16061911Scokane tdfx_info->bus = pci_get_bus(dev); 16161911Scokane tdfx_info->dv = pci_get_slot(dev); 16261911Scokane tdfx_info->curFile = NULL; 16361911Scokane 16461911Scokane /* 16561911Scokane * Get the Memory Location from the PCI Config, mask out lower word, since 16661911Scokane * the config space register is only one word long (this is nicer than a 16761911Scokane * bitshift). 16861911Scokane */ 16961911Scokane tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000); 17061931Scokane#ifdef DEBUG 17161911Scokane device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0); 17261911Scokane#endif 17361911Scokane /* Notify the VM that we will be mapping some memory later */ 174127135Snjl tdfx_info->memrange = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 175127135Snjl &rid, RF_ACTIVE | RF_SHAREABLE); 17661911Scokane if(tdfx_info->memrange == NULL) { 17761931Scokane#ifdef DEBUG 17861911Scokane device_printf(dev, "Error mapping mem, won't be able to use mmap()\n"); 17961911Scokane#endif 18061911Scokane tdfx_info->memrid = 0; 18161911Scokane } 18261911Scokane else { 18361911Scokane tdfx_info->memrid = rid; 18461931Scokane#ifdef DEBUG 18561911Scokane device_printf(dev, "Mapped to: 0x%x\n", 18661911Scokane (unsigned int)rman_get_start(tdfx_info->memrange)); 18761911Scokane#endif 18861911Scokane } 18961911Scokane 19063488Scokane /* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */ 19163488Scokane if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 || 19263488Scokane pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) { 19364085Scokane rid = 0x14; /* 2nd mem map */ 19463488Scokane tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000); 19563488Scokane#ifdef DEBUG 19663488Scokane device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1); 19763488Scokane#endif 198127135Snjl tdfx_info->memrange2 = bus_alloc_resource_any(dev, 199127135Snjl SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE); 20063488Scokane if(tdfx_info->memrange2 == NULL) { 20163488Scokane#ifdef DEBUG 20263488Scokane device_printf(dev, "Mem1 couldn't be allocated, glide may not work."); 20363488Scokane#endif 20463488Scokane tdfx_info->memrid2 = 0; 20563488Scokane } 20663488Scokane else { 20763488Scokane tdfx_info->memrid2 = rid; 20863488Scokane } 20963488Scokane /* Now to map the PIO stuff */ 21065146Scokane rid = PCIR_IOBASE0_2; 21165146Scokane tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2); 21265146Scokane tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0; 213127135Snjl tdfx_info->piorange = bus_alloc_resource_any(dev, 214127135Snjl SYS_RES_IOPORT, &rid, RF_ACTIVE | RF_SHAREABLE); 21563488Scokane if(tdfx_info->piorange == NULL) { 21663488Scokane#ifdef DEBUG 21763488Scokane device_printf(dev, "Couldn't map PIO range."); 21863488Scokane#endif 21963488Scokane tdfx_info->piorid = 0; 22063488Scokane } 22163488Scokane else { 22263488Scokane tdfx_info->piorid = rid; 22365146Scokane } 22463488Scokane } else { 22563488Scokane tdfx_info->addr1 = 0; 22663488Scokane tdfx_info->memrange2 = NULL; 22763488Scokane tdfx_info->piorange = NULL; 22863488Scokane } 22963488Scokane 23061911Scokane /* 23161911Scokane * Set Writecombining, or at least Uncacheable for the memory region, if we 23261911Scokane * are able to 23361911Scokane */ 23461911Scokane 23561911Scokane if(tdfx_setmtrr(dev) != 0) { 23661931Scokane#ifdef DEBUG 23761911Scokane device_printf(dev, "Some weird error setting MTRRs"); 23861911Scokane#endif 23961911Scokane return -1; 24061911Scokane } 24163488Scokane 24261911Scokane /* 24361911Scokane * make_dev registers the cdev to access the 3dfx card from /dev 24461911Scokane * use hex here for the dev num, simply to provide better support if > 10 245215334Sdougb * voodoo cards, for the mad. The user must set the link. 24661911Scokane * Why would we want that many voodoo cards anyhow? 24761911Scokane */ 248104111Sphk tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev), 249108323Srwatson UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev)); 250191056Sed tdfx_info->devt->si_drv1 = tdfx_info; 25161911Scokane 25261911Scokane return 0; 25361911Scokane} 25461911Scokane 25561911Scokanestatic int 25661911Scokanetdfx_detach(device_t dev) { 25761911Scokane struct tdfx_softc* tdfx_info; 25861911Scokane int retval; 25961911Scokane tdfx_info = device_get_softc(dev); 26061911Scokane 26161911Scokane /* Delete allocated resource, of course */ 26263488Scokane bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid, 26361911Scokane tdfx_info->memrange); 26463488Scokane 26563488Scokane /* Release extended Voodoo3/Banshee resources */ 26663488Scokane if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE || 26763488Scokane pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) { 26863488Scokane if(tdfx_info->memrange2 != NULL) 26963488Scokane bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2, 27063488Scokane tdfx_info->memrange); 27164085Scokane /* if(tdfx_info->piorange != NULL) 27263488Scokane bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid, 27364085Scokane tdfx_info->piorange);*/ 27463488Scokane } 27563488Scokane 27661911Scokane /* Though it is safe to leave the WRCOMB support since the 27761911Scokane mem driver checks for it, we should remove it in order 27861911Scokane to free an MTRR for another device */ 27961911Scokane retval = tdfx_clrmtrr(dev); 28061931Scokane#ifdef DEBUG 28161911Scokane if(retval != 0) 28261911Scokane printf("tdfx: For some reason, I couldn't clear the mtrr\n"); 28361911Scokane#endif 28461989Scokane /* Remove device entry when it can no longer be accessed */ 28561989Scokane destroy_dev(tdfx_info->devt); 28661911Scokane return(0); 28761911Scokane} 28861911Scokane 28961911Scokanestatic int 29061911Scokanetdfx_shutdown(device_t dev) { 29161931Scokane#ifdef DEBUG 29261911Scokane device_printf(dev, "tdfx: Device Shutdown\n"); 29361911Scokane#endif 29461911Scokane return 0; 29561911Scokane} 29661911Scokane 29761911Scokanestatic int 29861911Scokanetdfx_clrmtrr(device_t dev) { 29961911Scokane /* This function removes the MTRR set by the attach call, so it can be used 30061911Scokane * in the future by other drivers. 30161911Scokane */ 30261911Scokane int retval, act; 30361911Scokane struct tdfx_softc *tdfx_info = device_get_softc(dev); 30461911Scokane 30561911Scokane act = MEMRANGE_SET_REMOVE; 30661911Scokane retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 30761911Scokane return retval; 30861911Scokane} 30961911Scokane 31061911Scokanestatic int 31161911Scokanetdfx_setmtrr(device_t dev) { 31261911Scokane /* 31361911Scokane * This is the MTRR setting function for the 3dfx card. It is called from 31461911Scokane * tdfx_attach. If we can't set the MTRR properly, it's not the end of the 31561911Scokane * world. We can still continue, just with slightly (very slightly) degraded 31661911Scokane * performance. 31761911Scokane */ 31861911Scokane int retval = 0, act; 31961911Scokane struct tdfx_softc *tdfx_info = device_get_softc(dev); 32061911Scokane 32161911Scokane /* The older Voodoo cards have a shorter memrange than the newer ones */ 32261911Scokane if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) == 32363488Scokane PCI_DEVICE_3DFX_VOODOO2)) { 32461911Scokane tdfx_info->mrdesc.mr_len = 0x400000; 32563488Scokane 32663488Scokane /* The memory descriptor is described as the top 15 bits of the real 32763488Scokane address */ 32863488Scokane tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000; 32963488Scokane } 33061911Scokane else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) || 33163488Scokane (pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) { 33261911Scokane tdfx_info->mrdesc.mr_len = 0x1000000; 33363488Scokane /* The Voodoo3 and Banshee LFB is the second memory address */ 33463488Scokane /* The memory descriptor is described as the top 15 bits of the real 33563488Scokane address */ 33663488Scokane tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000; 33763488Scokane } 33863488Scokane else 33963488Scokane return 0; 34061911Scokane /* 34161911Scokane * The Alliance Pro Motion AT3D was not mentioned in the linux 34261911Scokane * driver as far as MTRR support goes, so I just won't put the 34361911Scokane * code in here for it. This is where it should go, though. 34461911Scokane */ 34561911Scokane 34661911Scokane /* Firstly, try to set write combining */ 34761911Scokane tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE; 34861911Scokane bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4); 34961911Scokane act = MEMRANGE_SET_UPDATE; 35061911Scokane retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 35161911Scokane 35261911Scokane if(retval == 0) { 35361931Scokane#ifdef DEBUG 35461911Scokane device_printf(dev, "MTRR Set Correctly for tdfx\n"); 35561911Scokane#endif 35661911Scokane } else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) || 35761911Scokane (pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) { 35861911Scokane /* if, for some reason we can't set the WRCOMB range with the V1/V2, we 35961911Scokane * can still possibly use the UNCACHEABLE region for it instead, and help 36061911Scokane * out in a small way */ 36161911Scokane tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE; 36261911Scokane /* This length of 1000h was taken from the linux device driver... */ 36361911Scokane tdfx_info->mrdesc.mr_len = 0x1000; 36461911Scokane 36561911Scokane /* 36661911Scokane * If, for some reason, we can't set the MTRR (N/A?) we may still continue 36761911Scokane */ 36861931Scokane#ifdef DEBUG 369142253Ssam device_printf(dev, "MTRR Set Type Uncacheable %x\n", 370142253Ssam (u_int32_t)tdfx_info->mrdesc.mr_base); 37161911Scokane#endif 37261911Scokane } 37361931Scokane#ifdef DEBUG 37461911Scokane else { 37561911Scokane device_printf(dev, "Couldn't Set MTRR\n"); 37661911Scokane return 0; 37761911Scokane } 37861911Scokane#endif 37961911Scokane return 0; 38061911Scokane} 38161911Scokane 38261911Scokanestatic int 383130585Sphktdfx_open(struct cdev *dev, int flags, int fmt, struct thread *td) 38461911Scokane{ 38561911Scokane /* 38661911Scokane * The open cdev method handles open(2) calls to /dev/3dfx[n] 38761911Scokane * We can pretty much allow any opening of the device. 38861911Scokane */ 389191056Sed struct tdfx_softc *tdfx_info = dev->si_drv1; 39061911Scokane if(tdfx_info->busy != 0) return EBUSY; 39161931Scokane#ifdef DEBUG 39283366Sjulian printf("3dfx: Opened by #%d\n", td->td_proc->p_pid); 39361911Scokane#endif 39461911Scokane /* Set the driver as busy */ 39561911Scokane tdfx_info->busy++; 39661911Scokane return 0; 39761911Scokane} 39861911Scokane 39961911Scokanestatic int 400130585Sphktdfx_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 40161911Scokane{ 40261911Scokane /* 40361911Scokane * The close cdev method handles close(2) calls to /dev/3dfx[n] 40461911Scokane * We'll always want to close the device when it's called. 40561911Scokane */ 406191056Sed struct tdfx_softc *tdfx_info = dev->si_drv1; 40761911Scokane if(tdfx_info->busy == 0) return EBADF; 40861911Scokane tdfx_info->busy = 0; 40961931Scokane#ifdef DEBUG 41083366Sjulian printf("Closed by #%d\n", td->td_proc->p_pid); 41161911Scokane#endif 41261911Scokane return 0; 41361911Scokane} 41461911Scokane 41561911Scokanestatic int 416201223Srnolandtdfx_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, 417201223Srnoland int nprot, vm_memattr_t *memattr) 41861911Scokane{ 41961911Scokane /* 42061911Scokane * mmap(2) is called by a user process to request that an area of memory 42161911Scokane * associated with this device be mapped for the process to work with. Nprot 42261911Scokane * holds the protections requested, PROT_READ, PROT_WRITE, or both. 42361911Scokane */ 42466910Scokane 42566910Scokane /**** OLD GET CONFIG ****/ 42666910Scokane /* struct tdfx_softc* tdfx_info; */ 42761911Scokane 42861911Scokane /* Get the configuration for our card XXX*/ 429191056Sed /*tdfx_info = dev->si_drv1; */ 43066910Scokane /************************/ 43166910Scokane 43266910Scokane struct tdfx_softc* tdfx_info[2]; 43361911Scokane 43466910Scokane tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0); 43566910Scokane 43661911Scokane /* If, for some reason, its not configured, we bail out */ 43766910Scokane if(tdfx_info[0] == NULL) { 43861931Scokane#ifdef DEBUG 43961911Scokane printf("tdfx: tdfx_info (softc) is NULL\n"); 44061911Scokane#endif 44161911Scokane return -1; 44261911Scokane } 44366910Scokane 44461911Scokane /* We must stay within the bound of our address space */ 44566910Scokane if((offset & 0xff000000) == tdfx_info[0]->addr0) { 44661911Scokane offset &= 0xffffff; 447111462Smux *paddr = rman_get_start(tdfx_info[0]->memrange) + offset; 448111462Smux return 0; 44966910Scokane } 45066910Scokane 45166910Scokane if(tdfx_count > 1) { 45266910Scokane tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1); 45366910Scokane if((offset & 0xff000000) == tdfx_info[1]->addr0) { 45466910Scokane offset &= 0xffffff; 455111462Smux *paddr = rman_get_start(tdfx_info[1]->memrange) + 456111462Smux offset; 457111462Smux return 0; 45866910Scokane } 45966910Scokane } 46063488Scokane 46163488Scokane /* See if the Banshee/V3 LFB is being requested */ 46266910Scokane /*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) == 46364085Scokane tdfx_info->addr1) { 46463488Scokane offset &= 0xffffff; 46566910Scokane return atop(rman_get_start(tdfx_info[1]->memrange2) + offset); 46666910Scokane }*/ /* VoodooNG code */ 46763488Scokane 46866910Scokane /* The ret call */ 46961911Scokane /* atop -> address to page 47061911Scokane * rman_get_start, get the (struct resource*)->r_start member, 47161911Scokane * the mapping base address. 47261911Scokane */ 47366910Scokane return -1; 47461911Scokane} 47561911Scokane 47661911Scokanestatic int 47761911Scokanetdfx_query_boards(void) { 47861911Scokane /* 47961911Scokane * This returns the number of installed tdfx cards, we have been keeping 48061911Scokane * count, look at tdfx_attach 48161911Scokane */ 48261911Scokane return tdfx_count; 48361911Scokane} 48461911Scokane 48561911Scokanestatic int 48661911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod) 48761911Scokane{ 48861911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 48961911Scokane /* Various return values 8bit-32bit */ 49061911Scokane u_int8_t ret_byte; 49161911Scokane u_int16_t ret_word; 49261911Scokane u_int32_t ret_dword; 49361911Scokane struct tdfx_softc* tdfx_info = NULL; 49461911Scokane 49561911Scokane /* This one depend on the tdfx_* structs being properly initialized */ 49661911Scokane 49761911Scokane /*piod->device &= 0xf;*/ 49861911Scokane if((piod == NULL) ||(tdfx_count <= piod->device) || 49961911Scokane (piod->device < 0)) { 50061931Scokane#ifdef DEBUG 50161911Scokane printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n"); 50261911Scokane#endif 50361911Scokane return -EINVAL; 50461911Scokane } 50561911Scokane 50661911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 50761911Scokane piod->device); 50861911Scokane 50961911Scokane if(tdfx_info == NULL) return -ENXIO; 51061911Scokane 51161911Scokane /* We must restrict the size reads from the port, since to high or low of a 51261911Scokane * size witll result in wrong data being passed, and that's bad */ 51361911Scokane /* A few of these were pulled during the attach phase */ 51461911Scokane switch(piod->port) { 51561911Scokane case PCI_VENDOR_ID_FREEBSD: 51661911Scokane if(piod->size != 2) return -EINVAL; 51761911Scokane copyout(&tdfx_info->vendor, piod->value, piod->size); 51861911Scokane return 0; 51961911Scokane case PCI_DEVICE_ID_FREEBSD: 52061911Scokane if(piod->size != 2) return -EINVAL; 52161911Scokane copyout(&tdfx_info->type, piod->value, piod->size); 52261911Scokane return 0; 52361911Scokane case PCI_BASE_ADDRESS_0_FREEBSD: 52461911Scokane if(piod->size != 4) return -EINVAL; 52561911Scokane copyout(&tdfx_info->addr0, piod->value, piod->size); 52661911Scokane return 0; 52765146Scokane case PCI_BASE_ADDRESS_1_FREEBSD: 52865146Scokane if(piod->size != 4) return -EINVAL; 52965146Scokane copyout(&tdfx_info->addr1, piod->value, piod->size); 53065146Scokane return 0; 53165146Scokane case PCI_PRIBUS_FREEBSD: 53265146Scokane if(piod->size != 1) return -EINVAL; 53365146Scokane break; 53465146Scokane case PCI_IOBASE_0_FREEBSD: 53565146Scokane if(piod->size != 2) return -EINVAL; 53665146Scokane break; 53765146Scokane case PCI_IOLIMIT_0_FREEBSD: 53865146Scokane if(piod->size != 2) return -EINVAL; 53965146Scokane break; 54061911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 54161911Scokane if(piod->size != 4) return -EINVAL; 54261911Scokane break; 54361911Scokane case PCI_REVISION_ID_FREEBSD: 54461911Scokane if(piod->size != 1) return -EINVAL; 54561911Scokane break; 54661911Scokane case SST1_PCI_SPECIAL4_FREEBSD: 54761911Scokane if(piod->size != 4) return -EINVAL; 54861911Scokane break; 54961911Scokane default: 55061911Scokane return -EINVAL; 55161911Scokane } 55261911Scokane 55361911Scokane 55461911Scokane /* Read the value and return */ 55561911Scokane switch(piod->size) { 55661911Scokane case 1: 55761911Scokane ret_byte = pci_read_config(tdfx_info[piod->device].dev, 55861911Scokane piod->port, 1); 55961911Scokane copyout(&ret_byte, piod->value, 1); 56061911Scokane break; 56161911Scokane case 2: 56261911Scokane ret_word = pci_read_config(tdfx_info[piod->device].dev, 56361911Scokane piod->port, 2); 56461911Scokane copyout(&ret_word, piod->value, 2); 56561911Scokane break; 56661911Scokane case 4: 56761911Scokane ret_dword = pci_read_config(tdfx_info[piod->device].dev, 56861911Scokane piod->port, 4); 56961911Scokane copyout(&ret_dword, piod->value, 4); 57061911Scokane break; 57161911Scokane default: 57261911Scokane return -EINVAL; 57361911Scokane } 57461911Scokane return 0; 57561911Scokane} 57661911Scokane 57761911Scokanestatic int 57861911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod) 57961911Scokane{ 58061911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 58161911Scokane /* Return vals */ 58261911Scokane u_int8_t ret_byte; 58361911Scokane u_int16_t ret_word; 58461911Scokane u_int32_t ret_dword; 58561911Scokane 58661911Scokane /* Port vals, mask */ 58761911Scokane u_int32_t retval, preval, mask; 58861911Scokane struct tdfx_softc* tdfx_info = NULL; 58961911Scokane 59061911Scokane 59161911Scokane if((piod == NULL) || (piod->device >= (tdfx_count & 59261911Scokane 0xf))) { 59361931Scokane#ifdef DEBUG 59461911Scokane printf("tdfx: Bad struct or device in tdfx_query_update\n"); 59561911Scokane#endif 59661911Scokane return -EINVAL; 59761911Scokane } 59861911Scokane 59961911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 60061911Scokane piod->device); 60161911Scokane if(tdfx_info == NULL) return -ENXIO; 60261911Scokane /* Code below this line in the fuction was taken from the 60361911Scokane * Linux driver and converted for freebsd. */ 60461911Scokane 60561911Scokane /* Check the size for all the ports, to make sure stuff doesn't get messed up 60661911Scokane * by poorly written clients */ 60761911Scokane 60861911Scokane switch(piod->port) { 60961911Scokane case PCI_COMMAND_FREEBSD: 61061911Scokane if(piod->size != 2) return -EINVAL; 61161911Scokane break; 61261911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 61361911Scokane if(piod->size != 4) return -EINVAL; 61461911Scokane break; 61561911Scokane case SST1_PCI_SPECIAL2_FREEBSD: 61661911Scokane if(piod->size != 4) return -EINVAL; 61761911Scokane break; 61861911Scokane case SST1_PCI_SPECIAL3_FREEBSD: 61961911Scokane if(piod->size != 4) return -EINVAL; 62061911Scokane break; 62161911Scokane case SST1_PCI_SPECIAL4_FREEBSD: 62261911Scokane if(piod->size != 4) return -EINVAL; 62361911Scokane break; 62461911Scokane default: 62561911Scokane return -EINVAL; 62661911Scokane } 62761911Scokane /* Read the current value */ 62861911Scokane retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4); 62961911Scokane 63061911Scokane /* These set up a mask to use, since apparently they wanted to write 4 bytes 63161911Scokane * at once to the ports */ 63261911Scokane switch (piod->size) { 63361911Scokane case 1: 63461911Scokane copyin(piod->value, &ret_byte, 1); 63561911Scokane preval = ret_byte << (8 * (piod->port & 0x3)); 63661911Scokane mask = 0xff << (8 * (piod->port & 0x3)); 63761911Scokane break; 63861911Scokane case 2: 63961911Scokane copyin(piod->value, &ret_word, 2); 64061911Scokane preval = ret_word << (8 * (piod->port & 0x3)); 64161911Scokane mask = 0xffff << (8 * (piod->port & 0x3)); 64261911Scokane break; 64361911Scokane case 4: 64461911Scokane copyin(piod->value, &ret_dword, 4); 64561911Scokane preval = ret_dword; 64661911Scokane mask = ~0; 64761911Scokane break; 64861911Scokane default: 64961911Scokane return -EINVAL; 65061911Scokane } 65161911Scokane /* Finally, combine the values and write it to the port */ 65261911Scokane retval = (retval & ~mask) | preval; 65361911Scokane pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4); 65461911Scokane 65561911Scokane return 0; 65661911Scokane} 65761911Scokane 65863488Scokane/* For both of these, I added a variable named workport of type u_int so 65963488Scokane * that I could eliminate the warning about my data type size. The 66063488Scokane * applications expect the port to be of type short, so I needed to change 66163488Scokane * this within the function */ 66261911Scokanestatic int 66361911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod) 66461911Scokane{ 66561911Scokane /* Return val */ 66661911Scokane u_int8_t ret_byte; 66763488Scokane u_int workport; 66865146Scokane struct tdfx_softc *tdfx_info = 66965146Scokane (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 67065146Scokane 67161911Scokane /* Restricts the access of ports other than those we use */ 67265146Scokane if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) || 67365146Scokane (piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) && 67465146Scokane (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 67561911Scokane return -EPERM; 67661911Scokane 67761911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 67861911Scokane if(piod->size != 1) { 67961911Scokane return -EINVAL; 68061911Scokane } 68161911Scokane 68261911Scokane /* Write the data to the intended port */ 68363488Scokane workport = piod->port; 68463488Scokane ret_byte = inb(workport); 68561911Scokane copyout(&ret_byte, piod->value, sizeof(u_int8_t)); 68661911Scokane return 0; 68761911Scokane} 68861911Scokane 68961911Scokanestatic int 69061911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod) 69161911Scokane{ 69261911Scokane /* return val */ 69361911Scokane u_int8_t ret_byte; 69463488Scokane u_int workport; 69565146Scokane struct tdfx_softc *tdfx_info = (struct 69665146Scokane tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 69761911Scokane /* Replace old switch w/ massive if(...) */ 69861911Scokane /* Restricts the access of ports other than those we use */ 69965146Scokane if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) && 70065146Scokane (piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ && 70165146Scokane (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 70261911Scokane return -EPERM; 70361911Scokane 70461911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 70561911Scokane if(piod->size != 1) { 70661911Scokane return -EINVAL; 70761911Scokane } 70861911Scokane 70961911Scokane /* Write the data to the intended port */ 71061911Scokane copyin(piod->value, &ret_byte, sizeof(u_int8_t)); 71163488Scokane workport = piod->port; 71263488Scokane outb(workport, ret_byte); 71361911Scokane return 0; 71461911Scokane} 71561911Scokane 71661911Scokanestatic int 71761911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod) 71861911Scokane{ 71961911Scokane /* There are three sub-commands to the query 0x33 */ 72061911Scokane switch(_IOC_NR(cmd)) { 72161911Scokane case 2: 72261911Scokane return tdfx_query_boards(); 72361911Scokane break; 72461911Scokane case 3: 72561911Scokane return tdfx_query_fetch(cmd, piod); 72661911Scokane break; 72761911Scokane case 4: 72861911Scokane return tdfx_query_update(cmd, piod); 72961911Scokane break; 73061911Scokane default: 73161911Scokane /* In case we are thrown a bogus sub-command! */ 73261931Scokane#ifdef DEBUG 73361911Scokane printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd)); 73461911Scokane#endif 73561911Scokane return -EINVAL; 736115494Sphk } 73761911Scokane} 73861911Scokane 73961911Scokanestatic int 74061911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod) 74161911Scokane{ 74261911Scokane /* Two types of PIO, INPUT and OUTPUT, as the name suggests */ 74361911Scokane switch(_IOC_DIR(cmd)) { 74461911Scokane case IOCV_OUT: 74561911Scokane return tdfx_do_pio_rd(piod); 74661911Scokane break; 74761911Scokane case IOCV_IN: 74861911Scokane return tdfx_do_pio_wt(piod); 74961911Scokane break; 75061911Scokane default: 75161911Scokane return -EINVAL; 752115494Sphk } 75361911Scokane} 75461911Scokane 75561911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO, 75661911Scokane * normally, you would read in the data pointed to by data, then write your 75761911Scokane * output to it. The ioctl *should* normally return zero if everything is 75861911Scokane * alright, but 3dfx didn't make it that way... 75961911Scokane * 76061911Scokane * For all of the ioctl code, in the event of a real error, 76161911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this 76261911Scokane * is that the ioctls actually RET information back to the program 76361911Scokane * sometimes, rather than filling it in the passed structure. We 76461911Scokane * want to distinguish errors from useful data, and maintain compatibility. 76561911Scokane * 76661911Scokane * There is this portion of the proc struct called p_retval[], we can store a 76783366Sjulian * return value in td->td_retval[0] and place the return value if it is positive 76861911Scokane * in there, then we can return 0 (good). If the return value is negative, we 76961911Scokane * can return -retval and the error should be properly handled. 77061911Scokane */ 77161911Scokanestatic int 772130585Sphktdfx_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 77361911Scokane{ 77461911Scokane int retval = 0; 77561911Scokane struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data; 77661931Scokane#ifdef DEBUG 777106578Sjhb printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd, 778106578Sjhb piod); 77961911Scokane#endif 78061911Scokane switch(_IOC_TYPE(cmd)) { 78161911Scokane /* Return the real error if negative, or simply stick the valid return 78283366Sjulian * in td->td_retval */ 78361911Scokane case 0x33: 78461911Scokane /* The '3'(0x33) type IOCTL is for querying the installed cards */ 78583366Sjulian if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval; 78661911Scokane else return -retval; 78761911Scokane break; 78861911Scokane case 0: 78961911Scokane /* The 0 type IOCTL is for programmed I/O methods */ 79083366Sjulian if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval; 79161911Scokane else return -retval; 79261911Scokane break; 79361911Scokane default: 79461911Scokane /* Technically, we won't reach this from linux emu, but when glide 79561911Scokane * finally gets ported, watch out! */ 79661931Scokane#ifdef DEBUG 79783366Sjulian printf("Bad IOCTL from #%d\n", td->td_proc->p_pid); 79861911Scokane#endif 79961911Scokane return ENXIO; 80061911Scokane } 80161911Scokane 80261911Scokane return 0; 80361911Scokane} 80461911Scokane 80561911Scokane/* This is the device driver struct. This is sent to the driver subsystem to 80661911Scokane * register the method structure and the info strcut space for this particular 80761911Scokane * instance of the driver. 80861911Scokane */ 80961911Scokanestatic driver_t tdfx_driver = { 81061911Scokane "tdfx", 81161911Scokane tdfx_methods, 81261911Scokane sizeof(struct tdfx_softc), 81361911Scokane}; 81461911Scokane 81561911Scokane/* Tell Mr. Kernel about us! */ 81661911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0); 817177147ScokaneMODULE_DEPEND(tdfx, mem, 1, 1, 1); 818156260SyarMODULE_VERSION(tdfx, 1); 819