tdfx_pci.c revision 227293
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: head/sys/dev/tdfx/tdfx_pci.c 227293 2011-11-07 06:44:47Z ed $"); 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 92227293Sedstatic 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 u_long val; 14961911Scokane /* rid value tells bus_alloc_resource where to find the addresses of ports or 15061911Scokane * of memory ranges in the PCI config space*/ 151119690Sjhb int rid = PCIR_BAR(0); 15261911Scokane 15361911Scokane /* Increment the card counter (for the ioctl code) */ 15461911Scokane tdfx_count++; 15561911Scokane 15661911Scokane /* Enable MemMap on Voodoo */ 15761911Scokane val = pci_read_config(dev, PCIR_COMMAND, 2); 15861911Scokane val |= (PCIM_CMD_MEMEN); 15961911Scokane pci_write_config(dev, PCIR_COMMAND, val, 2); 16061911Scokane val = pci_read_config(dev, PCIR_COMMAND, 2); 16161911Scokane 16261911Scokane /* Fill the soft config struct with info about this device*/ 16361911Scokane tdfx_info = device_get_softc(dev); 16461911Scokane tdfx_info->dev = dev; 16561911Scokane tdfx_info->vendor = pci_get_vendor(dev); 16661911Scokane tdfx_info->type = pci_get_devid(dev) >> 16; 16761911Scokane tdfx_info->bus = pci_get_bus(dev); 16861911Scokane tdfx_info->dv = pci_get_slot(dev); 16961911Scokane tdfx_info->curFile = NULL; 17061911Scokane 17161911Scokane /* 17261911Scokane * Get the Memory Location from the PCI Config, mask out lower word, since 17361911Scokane * the config space register is only one word long (this is nicer than a 17461911Scokane * bitshift). 17561911Scokane */ 17661911Scokane tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000); 17761931Scokane#ifdef DEBUG 17861911Scokane device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0); 17961911Scokane#endif 18061911Scokane /* Notify the VM that we will be mapping some memory later */ 181127135Snjl tdfx_info->memrange = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 182127135Snjl &rid, RF_ACTIVE | RF_SHAREABLE); 18361911Scokane if(tdfx_info->memrange == NULL) { 18461931Scokane#ifdef DEBUG 18561911Scokane device_printf(dev, "Error mapping mem, won't be able to use mmap()\n"); 18661911Scokane#endif 18761911Scokane tdfx_info->memrid = 0; 18861911Scokane } 18961911Scokane else { 19061911Scokane tdfx_info->memrid = rid; 19161931Scokane#ifdef DEBUG 19261911Scokane device_printf(dev, "Mapped to: 0x%x\n", 19361911Scokane (unsigned int)rman_get_start(tdfx_info->memrange)); 19461911Scokane#endif 19561911Scokane } 19661911Scokane 19763488Scokane /* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */ 19863488Scokane if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 || 19963488Scokane pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) { 20064085Scokane rid = 0x14; /* 2nd mem map */ 20163488Scokane tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000); 20263488Scokane#ifdef DEBUG 20363488Scokane device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1); 20463488Scokane#endif 205127135Snjl tdfx_info->memrange2 = bus_alloc_resource_any(dev, 206127135Snjl SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE); 20763488Scokane if(tdfx_info->memrange2 == NULL) { 20863488Scokane#ifdef DEBUG 20963488Scokane device_printf(dev, "Mem1 couldn't be allocated, glide may not work."); 21063488Scokane#endif 21163488Scokane tdfx_info->memrid2 = 0; 21263488Scokane } 21363488Scokane else { 21463488Scokane tdfx_info->memrid2 = rid; 21563488Scokane } 21663488Scokane /* Now to map the PIO stuff */ 21765146Scokane rid = PCIR_IOBASE0_2; 21865146Scokane tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2); 21965146Scokane tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0; 220127135Snjl tdfx_info->piorange = bus_alloc_resource_any(dev, 221127135Snjl SYS_RES_IOPORT, &rid, RF_ACTIVE | RF_SHAREABLE); 22263488Scokane if(tdfx_info->piorange == NULL) { 22363488Scokane#ifdef DEBUG 22463488Scokane device_printf(dev, "Couldn't map PIO range."); 22563488Scokane#endif 22663488Scokane tdfx_info->piorid = 0; 22763488Scokane } 22863488Scokane else { 22963488Scokane tdfx_info->piorid = rid; 23065146Scokane } 23163488Scokane } else { 23263488Scokane tdfx_info->addr1 = 0; 23363488Scokane tdfx_info->memrange2 = NULL; 23463488Scokane tdfx_info->piorange = NULL; 23563488Scokane } 23663488Scokane 23761911Scokane /* 23861911Scokane * Set Writecombining, or at least Uncacheable for the memory region, if we 23961911Scokane * are able to 24061911Scokane */ 24161911Scokane 24261911Scokane if(tdfx_setmtrr(dev) != 0) { 24361931Scokane#ifdef DEBUG 24461911Scokane device_printf(dev, "Some weird error setting MTRRs"); 24561911Scokane#endif 24661911Scokane return -1; 24761911Scokane } 24863488Scokane 24961911Scokane /* 25061911Scokane * make_dev registers the cdev to access the 3dfx card from /dev 25161911Scokane * use hex here for the dev num, simply to provide better support if > 10 252215334Sdougb * voodoo cards, for the mad. The user must set the link. 25361911Scokane * Why would we want that many voodoo cards anyhow? 25461911Scokane */ 255104111Sphk tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev), 256108323Srwatson UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev)); 257191056Sed tdfx_info->devt->si_drv1 = tdfx_info; 25861911Scokane 25961911Scokane return 0; 26061911Scokane} 26161911Scokane 26261911Scokanestatic int 26361911Scokanetdfx_detach(device_t dev) { 26461911Scokane struct tdfx_softc* tdfx_info; 26561911Scokane int retval; 26661911Scokane tdfx_info = device_get_softc(dev); 26761911Scokane 26861911Scokane /* Delete allocated resource, of course */ 26963488Scokane bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid, 27061911Scokane tdfx_info->memrange); 27163488Scokane 27263488Scokane /* Release extended Voodoo3/Banshee resources */ 27363488Scokane if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE || 27463488Scokane pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) { 27563488Scokane if(tdfx_info->memrange2 != NULL) 27663488Scokane bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2, 27763488Scokane tdfx_info->memrange); 27864085Scokane /* if(tdfx_info->piorange != NULL) 27963488Scokane bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid, 28064085Scokane tdfx_info->piorange);*/ 28163488Scokane } 28263488Scokane 28361911Scokane /* Though it is safe to leave the WRCOMB support since the 28461911Scokane mem driver checks for it, we should remove it in order 28561911Scokane to free an MTRR for another device */ 28661911Scokane retval = tdfx_clrmtrr(dev); 28761931Scokane#ifdef DEBUG 28861911Scokane if(retval != 0) 28961911Scokane printf("tdfx: For some reason, I couldn't clear the mtrr\n"); 29061911Scokane#endif 29161989Scokane /* Remove device entry when it can no longer be accessed */ 29261989Scokane destroy_dev(tdfx_info->devt); 29361911Scokane return(0); 29461911Scokane} 29561911Scokane 29661911Scokanestatic int 29761911Scokanetdfx_shutdown(device_t dev) { 29861931Scokane#ifdef DEBUG 29961911Scokane device_printf(dev, "tdfx: Device Shutdown\n"); 30061911Scokane#endif 30161911Scokane return 0; 30261911Scokane} 30361911Scokane 30461911Scokanestatic int 30561911Scokanetdfx_clrmtrr(device_t dev) { 30661911Scokane /* This function removes the MTRR set by the attach call, so it can be used 30761911Scokane * in the future by other drivers. 30861911Scokane */ 30961911Scokane int retval, act; 31061911Scokane struct tdfx_softc *tdfx_info = device_get_softc(dev); 31161911Scokane 31261911Scokane act = MEMRANGE_SET_REMOVE; 31361911Scokane retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 31461911Scokane return retval; 31561911Scokane} 31661911Scokane 31761911Scokanestatic int 31861911Scokanetdfx_setmtrr(device_t dev) { 31961911Scokane /* 32061911Scokane * This is the MTRR setting function for the 3dfx card. It is called from 32161911Scokane * tdfx_attach. If we can't set the MTRR properly, it's not the end of the 32261911Scokane * world. We can still continue, just with slightly (very slightly) degraded 32361911Scokane * performance. 32461911Scokane */ 32561911Scokane int retval = 0, act; 32661911Scokane struct tdfx_softc *tdfx_info = device_get_softc(dev); 32761911Scokane 32861911Scokane /* The older Voodoo cards have a shorter memrange than the newer ones */ 32961911Scokane if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) == 33063488Scokane PCI_DEVICE_3DFX_VOODOO2)) { 33161911Scokane tdfx_info->mrdesc.mr_len = 0x400000; 33263488Scokane 33363488Scokane /* The memory descriptor is described as the top 15 bits of the real 33463488Scokane address */ 33563488Scokane tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000; 33663488Scokane } 33761911Scokane else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) || 33863488Scokane (pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) { 33961911Scokane tdfx_info->mrdesc.mr_len = 0x1000000; 34063488Scokane /* The Voodoo3 and Banshee LFB is the second memory address */ 34163488Scokane /* The memory descriptor is described as the top 15 bits of the real 34263488Scokane address */ 34363488Scokane tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000; 34463488Scokane } 34563488Scokane else 34663488Scokane return 0; 34761911Scokane /* 34861911Scokane * The Alliance Pro Motion AT3D was not mentioned in the linux 34961911Scokane * driver as far as MTRR support goes, so I just won't put the 35061911Scokane * code in here for it. This is where it should go, though. 35161911Scokane */ 35261911Scokane 35361911Scokane /* Firstly, try to set write combining */ 35461911Scokane tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE; 35561911Scokane bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4); 35661911Scokane act = MEMRANGE_SET_UPDATE; 35761911Scokane retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 35861911Scokane 35961911Scokane if(retval == 0) { 36061931Scokane#ifdef DEBUG 36161911Scokane device_printf(dev, "MTRR Set Correctly for tdfx\n"); 36261911Scokane#endif 36361911Scokane } else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) || 36461911Scokane (pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) { 36561911Scokane /* if, for some reason we can't set the WRCOMB range with the V1/V2, we 36661911Scokane * can still possibly use the UNCACHEABLE region for it instead, and help 36761911Scokane * out in a small way */ 36861911Scokane tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE; 36961911Scokane /* This length of 1000h was taken from the linux device driver... */ 37061911Scokane tdfx_info->mrdesc.mr_len = 0x1000; 37161911Scokane 37261911Scokane /* 37361911Scokane * If, for some reason, we can't set the MTRR (N/A?) we may still continue 37461911Scokane */ 37561931Scokane#ifdef DEBUG 376142253Ssam device_printf(dev, "MTRR Set Type Uncacheable %x\n", 377142253Ssam (u_int32_t)tdfx_info->mrdesc.mr_base); 37861911Scokane#endif 37961911Scokane } 38061931Scokane#ifdef DEBUG 38161911Scokane else { 38261911Scokane device_printf(dev, "Couldn't Set MTRR\n"); 38361911Scokane return 0; 38461911Scokane } 38561911Scokane#endif 38661911Scokane return 0; 38761911Scokane} 38861911Scokane 38961911Scokanestatic int 390130585Sphktdfx_open(struct cdev *dev, int flags, int fmt, struct thread *td) 39161911Scokane{ 39261911Scokane /* 39361911Scokane * The open cdev method handles open(2) calls to /dev/3dfx[n] 39461911Scokane * We can pretty much allow any opening of the device. 39561911Scokane */ 396191056Sed struct tdfx_softc *tdfx_info = dev->si_drv1; 39761911Scokane if(tdfx_info->busy != 0) return EBUSY; 39861931Scokane#ifdef DEBUG 39983366Sjulian printf("3dfx: Opened by #%d\n", td->td_proc->p_pid); 40061911Scokane#endif 40161911Scokane /* Set the driver as busy */ 40261911Scokane tdfx_info->busy++; 40361911Scokane return 0; 40461911Scokane} 40561911Scokane 40661911Scokanestatic int 407130585Sphktdfx_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 40861911Scokane{ 40961911Scokane /* 41061911Scokane * The close cdev method handles close(2) calls to /dev/3dfx[n] 41161911Scokane * We'll always want to close the device when it's called. 41261911Scokane */ 413191056Sed struct tdfx_softc *tdfx_info = dev->si_drv1; 41461911Scokane if(tdfx_info->busy == 0) return EBADF; 41561911Scokane tdfx_info->busy = 0; 41661931Scokane#ifdef DEBUG 41783366Sjulian printf("Closed by #%d\n", td->td_proc->p_pid); 41861911Scokane#endif 41961911Scokane return 0; 42061911Scokane} 42161911Scokane 42261911Scokanestatic int 423201223Srnolandtdfx_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, 424201223Srnoland int nprot, vm_memattr_t *memattr) 42561911Scokane{ 42661911Scokane /* 42761911Scokane * mmap(2) is called by a user process to request that an area of memory 42861911Scokane * associated with this device be mapped for the process to work with. Nprot 42961911Scokane * holds the protections requested, PROT_READ, PROT_WRITE, or both. 43061911Scokane */ 43166910Scokane 43266910Scokane /**** OLD GET CONFIG ****/ 43366910Scokane /* struct tdfx_softc* tdfx_info; */ 43461911Scokane 43561911Scokane /* Get the configuration for our card XXX*/ 436191056Sed /*tdfx_info = dev->si_drv1; */ 43766910Scokane /************************/ 43866910Scokane 43966910Scokane struct tdfx_softc* tdfx_info[2]; 44061911Scokane 44166910Scokane tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0); 44266910Scokane 44361911Scokane /* If, for some reason, its not configured, we bail out */ 44466910Scokane if(tdfx_info[0] == NULL) { 44561931Scokane#ifdef DEBUG 44661911Scokane printf("tdfx: tdfx_info (softc) is NULL\n"); 44761911Scokane#endif 44861911Scokane return -1; 44961911Scokane } 45066910Scokane 45161911Scokane /* We must stay within the bound of our address space */ 45266910Scokane if((offset & 0xff000000) == tdfx_info[0]->addr0) { 45361911Scokane offset &= 0xffffff; 454111462Smux *paddr = rman_get_start(tdfx_info[0]->memrange) + offset; 455111462Smux return 0; 45666910Scokane } 45766910Scokane 45866910Scokane if(tdfx_count > 1) { 45966910Scokane tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1); 46066910Scokane if((offset & 0xff000000) == tdfx_info[1]->addr0) { 46166910Scokane offset &= 0xffffff; 462111462Smux *paddr = rman_get_start(tdfx_info[1]->memrange) + 463111462Smux offset; 464111462Smux return 0; 46566910Scokane } 46666910Scokane } 46763488Scokane 46863488Scokane /* See if the Banshee/V3 LFB is being requested */ 46966910Scokane /*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) == 47064085Scokane tdfx_info->addr1) { 47163488Scokane offset &= 0xffffff; 47266910Scokane return atop(rman_get_start(tdfx_info[1]->memrange2) + offset); 47366910Scokane }*/ /* VoodooNG code */ 47463488Scokane 47566910Scokane /* The ret call */ 47661911Scokane /* atop -> address to page 47761911Scokane * rman_get_start, get the (struct resource*)->r_start member, 47861911Scokane * the mapping base address. 47961911Scokane */ 48066910Scokane return -1; 48161911Scokane} 48261911Scokane 48361911Scokanestatic int 48461911Scokanetdfx_query_boards(void) { 48561911Scokane /* 48661911Scokane * This returns the number of installed tdfx cards, we have been keeping 48761911Scokane * count, look at tdfx_attach 48861911Scokane */ 48961911Scokane return tdfx_count; 49061911Scokane} 49161911Scokane 49261911Scokanestatic int 49361911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod) 49461911Scokane{ 49561911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 49661911Scokane /* Various return values 8bit-32bit */ 49761911Scokane u_int8_t ret_byte; 49861911Scokane u_int16_t ret_word; 49961911Scokane u_int32_t ret_dword; 50061911Scokane struct tdfx_softc* tdfx_info = NULL; 50161911Scokane 50261911Scokane /* This one depend on the tdfx_* structs being properly initialized */ 50361911Scokane 50461911Scokane /*piod->device &= 0xf;*/ 50561911Scokane if((piod == NULL) ||(tdfx_count <= piod->device) || 50661911Scokane (piod->device < 0)) { 50761931Scokane#ifdef DEBUG 50861911Scokane printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n"); 50961911Scokane#endif 51061911Scokane return -EINVAL; 51161911Scokane } 51261911Scokane 51361911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 51461911Scokane piod->device); 51561911Scokane 51661911Scokane if(tdfx_info == NULL) return -ENXIO; 51761911Scokane 51861911Scokane /* We must restrict the size reads from the port, since to high or low of a 51961911Scokane * size witll result in wrong data being passed, and that's bad */ 52061911Scokane /* A few of these were pulled during the attach phase */ 52161911Scokane switch(piod->port) { 52261911Scokane case PCI_VENDOR_ID_FREEBSD: 52361911Scokane if(piod->size != 2) return -EINVAL; 52461911Scokane copyout(&tdfx_info->vendor, piod->value, piod->size); 52561911Scokane return 0; 52661911Scokane case PCI_DEVICE_ID_FREEBSD: 52761911Scokane if(piod->size != 2) return -EINVAL; 52861911Scokane copyout(&tdfx_info->type, piod->value, piod->size); 52961911Scokane return 0; 53061911Scokane case PCI_BASE_ADDRESS_0_FREEBSD: 53161911Scokane if(piod->size != 4) return -EINVAL; 53261911Scokane copyout(&tdfx_info->addr0, piod->value, piod->size); 53361911Scokane return 0; 53465146Scokane case PCI_BASE_ADDRESS_1_FREEBSD: 53565146Scokane if(piod->size != 4) return -EINVAL; 53665146Scokane copyout(&tdfx_info->addr1, piod->value, piod->size); 53765146Scokane return 0; 53865146Scokane case PCI_PRIBUS_FREEBSD: 53965146Scokane if(piod->size != 1) return -EINVAL; 54065146Scokane break; 54165146Scokane case PCI_IOBASE_0_FREEBSD: 54265146Scokane if(piod->size != 2) return -EINVAL; 54365146Scokane break; 54465146Scokane case PCI_IOLIMIT_0_FREEBSD: 54565146Scokane if(piod->size != 2) return -EINVAL; 54665146Scokane break; 54761911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 54861911Scokane if(piod->size != 4) return -EINVAL; 54961911Scokane break; 55061911Scokane case PCI_REVISION_ID_FREEBSD: 55161911Scokane if(piod->size != 1) return -EINVAL; 55261911Scokane break; 55361911Scokane case SST1_PCI_SPECIAL4_FREEBSD: 55461911Scokane if(piod->size != 4) return -EINVAL; 55561911Scokane break; 55661911Scokane default: 55761911Scokane return -EINVAL; 55861911Scokane } 55961911Scokane 56061911Scokane 56161911Scokane /* Read the value and return */ 56261911Scokane switch(piod->size) { 56361911Scokane case 1: 56461911Scokane ret_byte = pci_read_config(tdfx_info[piod->device].dev, 56561911Scokane piod->port, 1); 56661911Scokane copyout(&ret_byte, piod->value, 1); 56761911Scokane break; 56861911Scokane case 2: 56961911Scokane ret_word = pci_read_config(tdfx_info[piod->device].dev, 57061911Scokane piod->port, 2); 57161911Scokane copyout(&ret_word, piod->value, 2); 57261911Scokane break; 57361911Scokane case 4: 57461911Scokane ret_dword = pci_read_config(tdfx_info[piod->device].dev, 57561911Scokane piod->port, 4); 57661911Scokane copyout(&ret_dword, piod->value, 4); 57761911Scokane break; 57861911Scokane default: 57961911Scokane return -EINVAL; 58061911Scokane } 58161911Scokane return 0; 58261911Scokane} 58361911Scokane 58461911Scokanestatic int 58561911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod) 58661911Scokane{ 58761911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 58861911Scokane /* Return vals */ 58961911Scokane u_int8_t ret_byte; 59061911Scokane u_int16_t ret_word; 59161911Scokane u_int32_t ret_dword; 59261911Scokane 59361911Scokane /* Port vals, mask */ 59461911Scokane u_int32_t retval, preval, mask; 59561911Scokane struct tdfx_softc* tdfx_info = NULL; 59661911Scokane 59761911Scokane 59861911Scokane if((piod == NULL) || (piod->device >= (tdfx_count & 59961911Scokane 0xf))) { 60061931Scokane#ifdef DEBUG 60161911Scokane printf("tdfx: Bad struct or device in tdfx_query_update\n"); 60261911Scokane#endif 60361911Scokane return -EINVAL; 60461911Scokane } 60561911Scokane 60661911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 60761911Scokane piod->device); 60861911Scokane if(tdfx_info == NULL) return -ENXIO; 60961911Scokane /* Code below this line in the fuction was taken from the 61061911Scokane * Linux driver and converted for freebsd. */ 61161911Scokane 61261911Scokane /* Check the size for all the ports, to make sure stuff doesn't get messed up 61361911Scokane * by poorly written clients */ 61461911Scokane 61561911Scokane switch(piod->port) { 61661911Scokane case PCI_COMMAND_FREEBSD: 61761911Scokane if(piod->size != 2) return -EINVAL; 61861911Scokane break; 61961911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 62061911Scokane if(piod->size != 4) return -EINVAL; 62161911Scokane break; 62261911Scokane case SST1_PCI_SPECIAL2_FREEBSD: 62361911Scokane if(piod->size != 4) return -EINVAL; 62461911Scokane break; 62561911Scokane case SST1_PCI_SPECIAL3_FREEBSD: 62661911Scokane if(piod->size != 4) return -EINVAL; 62761911Scokane break; 62861911Scokane case SST1_PCI_SPECIAL4_FREEBSD: 62961911Scokane if(piod->size != 4) return -EINVAL; 63061911Scokane break; 63161911Scokane default: 63261911Scokane return -EINVAL; 63361911Scokane } 63461911Scokane /* Read the current value */ 63561911Scokane retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4); 63661911Scokane 63761911Scokane /* These set up a mask to use, since apparently they wanted to write 4 bytes 63861911Scokane * at once to the ports */ 63961911Scokane switch (piod->size) { 64061911Scokane case 1: 64161911Scokane copyin(piod->value, &ret_byte, 1); 64261911Scokane preval = ret_byte << (8 * (piod->port & 0x3)); 64361911Scokane mask = 0xff << (8 * (piod->port & 0x3)); 64461911Scokane break; 64561911Scokane case 2: 64661911Scokane copyin(piod->value, &ret_word, 2); 64761911Scokane preval = ret_word << (8 * (piod->port & 0x3)); 64861911Scokane mask = 0xffff << (8 * (piod->port & 0x3)); 64961911Scokane break; 65061911Scokane case 4: 65161911Scokane copyin(piod->value, &ret_dword, 4); 65261911Scokane preval = ret_dword; 65361911Scokane mask = ~0; 65461911Scokane break; 65561911Scokane default: 65661911Scokane return -EINVAL; 65761911Scokane } 65861911Scokane /* Finally, combine the values and write it to the port */ 65961911Scokane retval = (retval & ~mask) | preval; 66061911Scokane pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4); 66161911Scokane 66261911Scokane return 0; 66361911Scokane} 66461911Scokane 66563488Scokane/* For both of these, I added a variable named workport of type u_int so 66663488Scokane * that I could eliminate the warning about my data type size. The 66763488Scokane * applications expect the port to be of type short, so I needed to change 66863488Scokane * this within the function */ 66961911Scokanestatic int 67061911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod) 67161911Scokane{ 67261911Scokane /* Return val */ 67361911Scokane u_int8_t ret_byte; 67463488Scokane u_int workport; 67565146Scokane struct tdfx_softc *tdfx_info = 67665146Scokane (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 67765146Scokane 67861911Scokane /* Restricts the access of ports other than those we use */ 67965146Scokane if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) || 68065146Scokane (piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) && 68165146Scokane (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 68261911Scokane return -EPERM; 68361911Scokane 68461911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 68561911Scokane if(piod->size != 1) { 68661911Scokane return -EINVAL; 68761911Scokane } 68861911Scokane 68961911Scokane /* Write the data to the intended port */ 69063488Scokane workport = piod->port; 69163488Scokane ret_byte = inb(workport); 69261911Scokane copyout(&ret_byte, piod->value, sizeof(u_int8_t)); 69361911Scokane return 0; 69461911Scokane} 69561911Scokane 69661911Scokanestatic int 69761911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod) 69861911Scokane{ 69961911Scokane /* return val */ 70061911Scokane u_int8_t ret_byte; 70163488Scokane u_int workport; 70265146Scokane struct tdfx_softc *tdfx_info = (struct 70365146Scokane tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 70461911Scokane /* Replace old switch w/ massive if(...) */ 70561911Scokane /* Restricts the access of ports other than those we use */ 70665146Scokane if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) && 70765146Scokane (piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ && 70865146Scokane (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 70961911Scokane return -EPERM; 71061911Scokane 71161911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 71261911Scokane if(piod->size != 1) { 71361911Scokane return -EINVAL; 71461911Scokane } 71561911Scokane 71661911Scokane /* Write the data to the intended port */ 71761911Scokane copyin(piod->value, &ret_byte, sizeof(u_int8_t)); 71863488Scokane workport = piod->port; 71963488Scokane outb(workport, ret_byte); 72061911Scokane return 0; 72161911Scokane} 72261911Scokane 72361911Scokanestatic int 72461911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod) 72561911Scokane{ 72661911Scokane /* There are three sub-commands to the query 0x33 */ 72761911Scokane switch(_IOC_NR(cmd)) { 72861911Scokane case 2: 72961911Scokane return tdfx_query_boards(); 73061911Scokane break; 73161911Scokane case 3: 73261911Scokane return tdfx_query_fetch(cmd, piod); 73361911Scokane break; 73461911Scokane case 4: 73561911Scokane return tdfx_query_update(cmd, piod); 73661911Scokane break; 73761911Scokane default: 73861911Scokane /* In case we are thrown a bogus sub-command! */ 73961931Scokane#ifdef DEBUG 74061911Scokane printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd)); 74161911Scokane#endif 74261911Scokane return -EINVAL; 743115494Sphk } 74461911Scokane} 74561911Scokane 74661911Scokanestatic int 74761911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod) 74861911Scokane{ 74961911Scokane /* Two types of PIO, INPUT and OUTPUT, as the name suggests */ 75061911Scokane switch(_IOC_DIR(cmd)) { 75161911Scokane case IOCV_OUT: 75261911Scokane return tdfx_do_pio_rd(piod); 75361911Scokane break; 75461911Scokane case IOCV_IN: 75561911Scokane return tdfx_do_pio_wt(piod); 75661911Scokane break; 75761911Scokane default: 75861911Scokane return -EINVAL; 759115494Sphk } 76061911Scokane} 76161911Scokane 76261911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO, 76361911Scokane * normally, you would read in the data pointed to by data, then write your 76461911Scokane * output to it. The ioctl *should* normally return zero if everything is 76561911Scokane * alright, but 3dfx didn't make it that way... 76661911Scokane * 76761911Scokane * For all of the ioctl code, in the event of a real error, 76861911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this 76961911Scokane * is that the ioctls actually RET information back to the program 77061911Scokane * sometimes, rather than filling it in the passed structure. We 77161911Scokane * want to distinguish errors from useful data, and maintain compatibility. 77261911Scokane * 77361911Scokane * There is this portion of the proc struct called p_retval[], we can store a 77483366Sjulian * return value in td->td_retval[0] and place the return value if it is positive 77561911Scokane * in there, then we can return 0 (good). If the return value is negative, we 77661911Scokane * can return -retval and the error should be properly handled. 77761911Scokane */ 77861911Scokanestatic int 779130585Sphktdfx_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 78061911Scokane{ 78161911Scokane int retval = 0; 78261911Scokane struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data; 78361931Scokane#ifdef DEBUG 784106578Sjhb printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd, 785106578Sjhb piod); 78661911Scokane#endif 78761911Scokane switch(_IOC_TYPE(cmd)) { 78861911Scokane /* Return the real error if negative, or simply stick the valid return 78983366Sjulian * in td->td_retval */ 79061911Scokane case 0x33: 79161911Scokane /* The '3'(0x33) type IOCTL is for querying the installed cards */ 79283366Sjulian if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval; 79361911Scokane else return -retval; 79461911Scokane break; 79561911Scokane case 0: 79661911Scokane /* The 0 type IOCTL is for programmed I/O methods */ 79783366Sjulian if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval; 79861911Scokane else return -retval; 79961911Scokane break; 80061911Scokane default: 80161911Scokane /* Technically, we won't reach this from linux emu, but when glide 80261911Scokane * finally gets ported, watch out! */ 80361931Scokane#ifdef DEBUG 80483366Sjulian printf("Bad IOCTL from #%d\n", td->td_proc->p_pid); 80561911Scokane#endif 80661911Scokane return ENXIO; 80761911Scokane } 80861911Scokane 80961911Scokane return 0; 81061911Scokane} 81161911Scokane 81261911Scokane/* This is the device driver struct. This is sent to the driver subsystem to 81361911Scokane * register the method structure and the info strcut space for this particular 81461911Scokane * instance of the driver. 81561911Scokane */ 81661911Scokanestatic driver_t tdfx_driver = { 81761911Scokane "tdfx", 81861911Scokane tdfx_methods, 81961911Scokane sizeof(struct tdfx_softc), 82061911Scokane}; 82161911Scokane 82261911Scokane/* Tell Mr. Kernel about us! */ 82361911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0); 824177147ScokaneMODULE_DEPEND(tdfx, mem, 1, 1, 1); 825156260SyarMODULE_VERSION(tdfx, 1); 826