tdfx_pci.c revision 112569
161931Scokane/* 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 * $FreeBSD: head/sys/dev/tdfx/tdfx_pci.c 112569 2003-03-25 00:07:06Z jake $ 3261931Scokane */ 3361931Scokane 3461911Scokane/* 3dfx driver for FreeBSD 4.x - Finished 11 May 2000, 12:25AM ET 3561911Scokane * 3674534Scokane * Copyright (C) 2000-2001, by Coleman Kane <cokane@FreeBSD.org>, 3761911Scokane * based upon the 3dfx driver written for linux, by Daryll Straus, Jon Taylor, 3861911Scokane * and Jens Axboe, located at http://linux.3dfx.com. 3961911Scokane */ 4061911Scokane 4161911Scokane#include <sys/param.h> 4261911Scokane 4361911Scokane#include <sys/bus.h> 4461911Scokane#include <sys/cdefs.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> 5261911Scokane#include <sys/malloc.h> 5361911Scokane#include <sys/mman.h> 5461911Scokane#include <sys/signalvar.h> 5561911Scokane#include <sys/systm.h> 5661911Scokane#include <sys/uio.h> 5761911Scokane 5861911Scokane#include <pci/pcivar.h> 5961911Scokane#include <pci/pcireg.h> 6061911Scokane 6161911Scokane#include <vm/vm.h> 6261911Scokane#include <vm/vm_kern.h> 6361911Scokane#include <vm/pmap.h> 6461911Scokane#include <vm/vm_extern.h> 6561911Scokane 6661911Scokane/* rman.h depends on machine/bus.h */ 6761911Scokane#include <machine/resource.h> 6861911Scokane#include <machine/bus.h> 6961911Scokane#include <sys/rman.h> 7062028Scokane 7162028Scokane/* This must come first */ 7262028Scokane#include "opt_tdfx.h" 7361931Scokane#ifdef TDFX_LINUX 7461931Scokane#include <dev/tdfx/tdfx_linux.h> 7561931Scokane#endif 7661911Scokane 7761911Scokane#include <dev/tdfx/tdfx_io.h> 7861911Scokane#include <dev/tdfx/tdfx_vars.h> 7961911Scokane#include <dev/tdfx/tdfx_pci.h> 8061911Scokane 8161911Scokane 8261911Scokanestatic devclass_t tdfx_devclass; 8361911Scokane 8461911Scokane 8561911Scokanestatic int tdfx_count = 0; 8661911Scokane 8761911Scokane 8861911Scokane/* Set up the boot probe/attach routines */ 8961911Scokanestatic device_method_t tdfx_methods[] = { 9061911Scokane DEVMETHOD(device_probe, tdfx_probe), 9161911Scokane DEVMETHOD(device_attach, tdfx_attach), 9261911Scokane DEVMETHOD(device_detach, tdfx_detach), 9361911Scokane DEVMETHOD(device_shutdown, tdfx_shutdown), 9461911Scokane { 0, 0 } 9561911Scokane}; 9661911Scokane 9761911ScokaneMALLOC_DEFINE(M_TDFX,"TDFX Driver","3DFX Graphics[/2D]/3D Accelerator(s)"); 9861911Scokane 9961931Scokane#ifdef TDFX_LINUX 10061989ScokaneMODULE_DEPEND(tdfx, linux, 1, 1, 1); 10161931ScokaneLINUX_IOCTL_SET(tdfx, LINUX_IOCTL_TDFX_MIN, LINUX_IOCTL_TDFX_MAX); 10261931Scokane#endif 10361931Scokane 10461911Scokane/* Char. Dev. file operations structure */ 10561911Scokanestatic struct cdevsw tdfx_cdev = { 106111815Sphk .d_open = tdfx_open, 107111815Sphk .d_close = tdfx_close, 108111815Sphk .d_ioctl = tdfx_ioctl, 109111815Sphk .d_mmap = tdfx_mmap, 110111815Sphk .d_name = "tdfx", 111111815Sphk .d_maj = CDEV_MAJOR, 11261911Scokane}; 11361911Scokane 11461911Scokanestatic int 11561911Scokanetdfx_probe(device_t dev) 11661911Scokane{ 11761911Scokane /* 11861911Scokane * probe routine called on kernel boot to register supported devices. We get 11961911Scokane * a device structure to work with, and we can test the VENDOR/DEVICE IDs to 12061911Scokane * see if this PCI device is one that we support. Return 0 if yes, ENXIO if 12161911Scokane * not. 12261911Scokane */ 12361911Scokane switch(pci_get_devid(dev)) { 12461911Scokane case PCI_DEVICE_ALLIANCE_AT3D: 12561911Scokane device_set_desc(dev, "ProMotion At3D 3D Accelerator"); 12661911Scokane return 0; 12761911Scokane case PCI_DEVICE_3DFX_VOODOO2: 12861911Scokane device_set_desc(dev, "3DFX Voodoo II 3D Accelerator"); 12961911Scokane return 0; 13065146Scokane /*case PCI_DEVICE_3DFX_BANSHEE: 13161911Scokane device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator"); 13261911Scokane return 0; 13361911Scokane case PCI_DEVICE_3DFX_VOODOO3: 13461911Scokane device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator"); 13565146Scokane return 0;*/ 13661911Scokane case PCI_DEVICE_3DFX_VOODOO1: 13761911Scokane device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator"); 13861911Scokane return 0;; 13961911Scokane }; 14061911Scokane 14161911Scokane return ENXIO; 14261911Scokane} 14361911Scokane 14461911Scokanestatic int 14561911Scokanetdfx_attach(device_t dev) { 14661911Scokane /* 14761911Scokane * The attach routine is called after the probe routine successfully says it 14861911Scokane * supports a given card. We now proceed to initialize this card for use with 14961911Scokane * the system. I want to map the device memory for userland allocation and 15061911Scokane * fill an information structure with information on this card. I'd also like 15161911Scokane * to set Write Combining with the MTRR code so that we can hopefully speed 15261911Scokane * up memory writes. The last thing is to register the character device 15361911Scokane * interface to the card, so we can open it from /dev/3dfxN, where N is a 15461911Scokane * small, whole number. 15561911Scokane */ 15661911Scokane struct tdfx_softc *tdfx_info; 15761911Scokane u_long val; 15861911Scokane /* rid value tells bus_alloc_resource where to find the addresses of ports or 15961911Scokane * of memory ranges in the PCI config space*/ 16061911Scokane int rid = PCIR_MAPS; 16161911Scokane 16261911Scokane /* Increment the card counter (for the ioctl code) */ 16361911Scokane tdfx_count++; 16461911Scokane 16561911Scokane /* Enable MemMap on Voodoo */ 16661911Scokane val = pci_read_config(dev, PCIR_COMMAND, 2); 16761911Scokane val |= (PCIM_CMD_MEMEN); 16861911Scokane pci_write_config(dev, PCIR_COMMAND, val, 2); 16961911Scokane val = pci_read_config(dev, PCIR_COMMAND, 2); 17061911Scokane 17161911Scokane /* Fill the soft config struct with info about this device*/ 17261911Scokane tdfx_info = device_get_softc(dev); 17361911Scokane tdfx_info->dev = dev; 17461911Scokane tdfx_info->vendor = pci_get_vendor(dev); 17561911Scokane tdfx_info->type = pci_get_devid(dev) >> 16; 17661911Scokane tdfx_info->bus = pci_get_bus(dev); 17761911Scokane tdfx_info->dv = pci_get_slot(dev); 17861911Scokane tdfx_info->curFile = NULL; 17961911Scokane 18061911Scokane /* 18161911Scokane * Get the Memory Location from the PCI Config, mask out lower word, since 18261911Scokane * the config space register is only one word long (this is nicer than a 18361911Scokane * bitshift). 18461911Scokane */ 18561911Scokane tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000); 18661931Scokane#ifdef DEBUG 18761911Scokane device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0); 18861911Scokane#endif 18961911Scokane /* Notify the VM that we will be mapping some memory later */ 19061911Scokane tdfx_info->memrange = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1, 19161967Scokane RF_ACTIVE | RF_SHAREABLE); 19261911Scokane if(tdfx_info->memrange == NULL) { 19361931Scokane#ifdef DEBUG 19461911Scokane device_printf(dev, "Error mapping mem, won't be able to use mmap()\n"); 19561911Scokane#endif 19661911Scokane tdfx_info->memrid = 0; 19761911Scokane } 19861911Scokane else { 19961911Scokane tdfx_info->memrid = rid; 20061931Scokane#ifdef DEBUG 20161911Scokane device_printf(dev, "Mapped to: 0x%x\n", 20261911Scokane (unsigned int)rman_get_start(tdfx_info->memrange)); 20361911Scokane#endif 20461911Scokane } 20561911Scokane 20663488Scokane /* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */ 20763488Scokane if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 || 20863488Scokane pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) { 20964085Scokane rid = 0x14; /* 2nd mem map */ 21063488Scokane tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000); 21163488Scokane#ifdef DEBUG 21263488Scokane device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1); 21363488Scokane#endif 21463488Scokane tdfx_info->memrange2 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 21563488Scokane 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 21663488Scokane if(tdfx_info->memrange2 == NULL) { 21763488Scokane#ifdef DEBUG 21863488Scokane device_printf(dev, "Mem1 couldn't be allocated, glide may not work."); 21963488Scokane#endif 22063488Scokane tdfx_info->memrid2 = 0; 22163488Scokane } 22263488Scokane else { 22363488Scokane tdfx_info->memrid2 = rid; 22463488Scokane } 22563488Scokane /* Now to map the PIO stuff */ 22665146Scokane rid = PCIR_IOBASE0_2; 22765146Scokane tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2); 22865146Scokane tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0; 22963488Scokane tdfx_info->piorange = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 23063488Scokane 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 23163488Scokane if(tdfx_info->piorange == NULL) { 23263488Scokane#ifdef DEBUG 23363488Scokane device_printf(dev, "Couldn't map PIO range."); 23463488Scokane#endif 23563488Scokane tdfx_info->piorid = 0; 23663488Scokane } 23763488Scokane else { 23863488Scokane tdfx_info->piorid = rid; 23965146Scokane } 24063488Scokane } else { 24163488Scokane tdfx_info->addr1 = 0; 24263488Scokane tdfx_info->memrange2 = NULL; 24363488Scokane tdfx_info->piorange = NULL; 24463488Scokane } 24563488Scokane 24661911Scokane /* 24761911Scokane * Set Writecombining, or at least Uncacheable for the memory region, if we 24861911Scokane * are able to 24961911Scokane */ 25061911Scokane 25161911Scokane if(tdfx_setmtrr(dev) != 0) { 25261931Scokane#ifdef DEBUG 25361911Scokane device_printf(dev, "Some weird error setting MTRRs"); 25461911Scokane#endif 25561911Scokane return -1; 25661911Scokane } 25763488Scokane 25861911Scokane /* 25961911Scokane * make_dev registers the cdev to access the 3dfx card from /dev 26061911Scokane * use hex here for the dev num, simply to provide better support if > 10 26161911Scokane * voodoo cards, for the mad. The user must set the link, or use MAKEDEV. 26261911Scokane * Why would we want that many voodoo cards anyhow? 26361911Scokane */ 264104111Sphk tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev), 265108323Srwatson UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev)); 26661911Scokane 26761911Scokane return 0; 26861911Scokane} 26961911Scokane 27061911Scokanestatic int 27161911Scokanetdfx_detach(device_t dev) { 27261911Scokane struct tdfx_softc* tdfx_info; 27361911Scokane int retval; 27461911Scokane tdfx_info = device_get_softc(dev); 27561911Scokane 27661911Scokane /* Delete allocated resource, of course */ 27763488Scokane bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid, 27861911Scokane tdfx_info->memrange); 27963488Scokane 28063488Scokane /* Release extended Voodoo3/Banshee resources */ 28163488Scokane if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE || 28263488Scokane pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) { 28363488Scokane if(tdfx_info->memrange2 != NULL) 28463488Scokane bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2, 28563488Scokane tdfx_info->memrange); 28664085Scokane /* if(tdfx_info->piorange != NULL) 28763488Scokane bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid, 28864085Scokane tdfx_info->piorange);*/ 28963488Scokane } 29063488Scokane 29161911Scokane /* Though it is safe to leave the WRCOMB support since the 29261911Scokane mem driver checks for it, we should remove it in order 29361911Scokane to free an MTRR for another device */ 29461911Scokane retval = tdfx_clrmtrr(dev); 29561931Scokane#ifdef DEBUG 29661911Scokane if(retval != 0) 29761911Scokane printf("tdfx: For some reason, I couldn't clear the mtrr\n"); 29861911Scokane#endif 29961989Scokane /* Remove device entry when it can no longer be accessed */ 30061989Scokane destroy_dev(tdfx_info->devt); 30161911Scokane return(0); 30261911Scokane} 30361911Scokane 30461911Scokanestatic int 30561911Scokanetdfx_shutdown(device_t dev) { 30661931Scokane#ifdef DEBUG 30761911Scokane device_printf(dev, "tdfx: Device Shutdown\n"); 30861911Scokane#endif 30961911Scokane return 0; 31061911Scokane} 31161911Scokane 31261911Scokanestatic int 31361911Scokanetdfx_clrmtrr(device_t dev) { 31461911Scokane /* This function removes the MTRR set by the attach call, so it can be used 31561911Scokane * in the future by other drivers. 31661911Scokane */ 31761911Scokane int retval, act; 31861911Scokane struct tdfx_softc *tdfx_info = device_get_softc(dev); 31961911Scokane 32061911Scokane act = MEMRANGE_SET_REMOVE; 32161911Scokane retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 32261911Scokane return retval; 32361911Scokane} 32461911Scokane 32561911Scokanestatic int 32661911Scokanetdfx_setmtrr(device_t dev) { 32761911Scokane /* 32861911Scokane * This is the MTRR setting function for the 3dfx card. It is called from 32961911Scokane * tdfx_attach. If we can't set the MTRR properly, it's not the end of the 33061911Scokane * world. We can still continue, just with slightly (very slightly) degraded 33161911Scokane * performance. 33261911Scokane */ 33361911Scokane int retval = 0, act; 33461911Scokane struct tdfx_softc *tdfx_info = device_get_softc(dev); 33561911Scokane 33661911Scokane /* The older Voodoo cards have a shorter memrange than the newer ones */ 33761911Scokane if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) == 33863488Scokane PCI_DEVICE_3DFX_VOODOO2)) { 33961911Scokane tdfx_info->mrdesc.mr_len = 0x400000; 34063488Scokane 34163488Scokane /* The memory descriptor is described as the top 15 bits of the real 34263488Scokane address */ 34363488Scokane tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000; 34463488Scokane } 34561911Scokane else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) || 34663488Scokane (pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) { 34761911Scokane tdfx_info->mrdesc.mr_len = 0x1000000; 34863488Scokane /* The Voodoo3 and Banshee LFB is the second memory address */ 34963488Scokane /* The memory descriptor is described as the top 15 bits of the real 35063488Scokane address */ 35163488Scokane tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000; 35263488Scokane } 35363488Scokane else 35463488Scokane return 0; 35561911Scokane /* 35661911Scokane * The Alliance Pro Motion AT3D was not mentioned in the linux 35761911Scokane * driver as far as MTRR support goes, so I just won't put the 35861911Scokane * code in here for it. This is where it should go, though. 35961911Scokane */ 36061911Scokane 36161911Scokane /* Firstly, try to set write combining */ 36261911Scokane tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE; 36361911Scokane bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4); 36461911Scokane act = MEMRANGE_SET_UPDATE; 36561911Scokane retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 36661911Scokane 36761911Scokane if(retval == 0) { 36861931Scokane#ifdef DEBUG 36961911Scokane device_printf(dev, "MTRR Set Correctly for tdfx\n"); 37061911Scokane#endif 37161911Scokane } else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) || 37261911Scokane (pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) { 37361911Scokane /* if, for some reason we can't set the WRCOMB range with the V1/V2, we 37461911Scokane * can still possibly use the UNCACHEABLE region for it instead, and help 37561911Scokane * out in a small way */ 37661911Scokane tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE; 37761911Scokane /* This length of 1000h was taken from the linux device driver... */ 37861911Scokane tdfx_info->mrdesc.mr_len = 0x1000; 37961911Scokane 38061911Scokane /* 38161911Scokane * If, for some reason, we can't set the MTRR (N/A?) we may still continue 38261911Scokane */ 38361931Scokane#ifdef DEBUG 38461911Scokane if(retval == 0) { 38595092Smarcel device_printf(dev, "MTRR Set Type Uncacheable %x\n", 38695092Smarcel (u_int32_t)tdfx_info->mrdesc.mr_base); 38761911Scokane } else { 38861911Scokane device_printf(dev, "Couldn't Set MTRR\n"); 38961911Scokane } 39061911Scokane#endif 39161911Scokane } 39261931Scokane#ifdef DEBUG 39361911Scokane else { 39461911Scokane device_printf(dev, "Couldn't Set MTRR\n"); 39561911Scokane return 0; 39661911Scokane } 39761911Scokane#endif 39861911Scokane return 0; 39961911Scokane} 40061911Scokane 40161911Scokanestatic int 40283366Sjuliantdfx_open(dev_t dev, int flags, int fmt, struct thread *td) 40361911Scokane{ 40461911Scokane /* 40561911Scokane * The open cdev method handles open(2) calls to /dev/3dfx[n] 40661911Scokane * We can pretty much allow any opening of the device. 40761911Scokane */ 40861911Scokane struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 40961911Scokane UNIT(minor(dev))); 41061911Scokane if(tdfx_info->busy != 0) return EBUSY; 41161931Scokane#ifdef DEBUG 41283366Sjulian printf("3dfx: Opened by #%d\n", td->td_proc->p_pid); 41361911Scokane#endif 41461911Scokane /* Set the driver as busy */ 41561911Scokane tdfx_info->busy++; 41661911Scokane return 0; 41761911Scokane} 41861911Scokane 41961911Scokanestatic int 42083366Sjuliantdfx_close(dev_t dev, int fflag, int devtype, struct thread *td) 42161911Scokane{ 42261911Scokane /* 42361911Scokane * The close cdev method handles close(2) calls to /dev/3dfx[n] 42461911Scokane * We'll always want to close the device when it's called. 42561911Scokane */ 42661911Scokane struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 42761911Scokane UNIT(minor(dev))); 42861911Scokane if(tdfx_info->busy == 0) return EBADF; 42961911Scokane tdfx_info->busy = 0; 43061931Scokane#ifdef DEBUG 43183366Sjulian printf("Closed by #%d\n", td->td_proc->p_pid); 43261911Scokane#endif 43361911Scokane return 0; 43461911Scokane} 43561911Scokane 43661911Scokanestatic int 437112569Sjaketdfx_mmap(dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) 43861911Scokane{ 43961911Scokane /* 44061911Scokane * mmap(2) is called by a user process to request that an area of memory 44161911Scokane * associated with this device be mapped for the process to work with. Nprot 44261911Scokane * holds the protections requested, PROT_READ, PROT_WRITE, or both. 44361911Scokane */ 44466910Scokane 44566910Scokane /**** OLD GET CONFIG ****/ 44666910Scokane /* struct tdfx_softc* tdfx_info; */ 44761911Scokane 44861911Scokane /* Get the configuration for our card XXX*/ 44966910Scokane /*tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 45066910Scokane UNIT(minor(dev)));*/ 45166910Scokane /************************/ 45266910Scokane 45366910Scokane struct tdfx_softc* tdfx_info[2]; 45461911Scokane 45566910Scokane tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0); 45666910Scokane 45761911Scokane /* If, for some reason, its not configured, we bail out */ 45866910Scokane if(tdfx_info[0] == NULL) { 45961931Scokane#ifdef DEBUG 46061911Scokane printf("tdfx: tdfx_info (softc) is NULL\n"); 46161911Scokane#endif 46261911Scokane return -1; 46361911Scokane } 46466910Scokane 46561911Scokane /* We must stay within the bound of our address space */ 46666910Scokane if((offset & 0xff000000) == tdfx_info[0]->addr0) { 46761911Scokane offset &= 0xffffff; 468111462Smux *paddr = rman_get_start(tdfx_info[0]->memrange) + offset; 469111462Smux return 0; 47066910Scokane } 47166910Scokane 47266910Scokane if(tdfx_count > 1) { 47366910Scokane tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1); 47466910Scokane if((offset & 0xff000000) == tdfx_info[1]->addr0) { 47566910Scokane offset &= 0xffffff; 476111462Smux *paddr = rman_get_start(tdfx_info[1]->memrange) + 477111462Smux offset; 478111462Smux return 0; 47966910Scokane } 48066910Scokane } 48163488Scokane 48263488Scokane /* See if the Banshee/V3 LFB is being requested */ 48366910Scokane /*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) == 48464085Scokane tdfx_info->addr1) { 48563488Scokane offset &= 0xffffff; 48666910Scokane return atop(rman_get_start(tdfx_info[1]->memrange2) + offset); 48766910Scokane }*/ /* VoodooNG code */ 48863488Scokane 48966910Scokane /* The ret call */ 49061911Scokane /* atop -> address to page 49161911Scokane * rman_get_start, get the (struct resource*)->r_start member, 49261911Scokane * the mapping base address. 49361911Scokane */ 49466910Scokane return -1; 49561911Scokane} 49661911Scokane 49761911Scokanestatic int 49861911Scokanetdfx_query_boards(void) { 49961911Scokane /* 50061911Scokane * This returns the number of installed tdfx cards, we have been keeping 50161911Scokane * count, look at tdfx_attach 50261911Scokane */ 50361911Scokane return tdfx_count; 50461911Scokane} 50561911Scokane 50661911Scokanestatic int 50761911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod) 50861911Scokane{ 50961911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 51061911Scokane /* Various return values 8bit-32bit */ 51161911Scokane u_int8_t ret_byte; 51261911Scokane u_int16_t ret_word; 51361911Scokane u_int32_t ret_dword; 51461911Scokane struct tdfx_softc* tdfx_info = NULL; 51561911Scokane 51661911Scokane /* This one depend on the tdfx_* structs being properly initialized */ 51761911Scokane 51861911Scokane /*piod->device &= 0xf;*/ 51961911Scokane if((piod == NULL) ||(tdfx_count <= piod->device) || 52061911Scokane (piod->device < 0)) { 52161931Scokane#ifdef DEBUG 52261911Scokane printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n"); 52361911Scokane#endif 52461911Scokane return -EINVAL; 52561911Scokane } 52661911Scokane 52761911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 52861911Scokane piod->device); 52961911Scokane 53061911Scokane if(tdfx_info == NULL) return -ENXIO; 53161911Scokane 53261911Scokane /* We must restrict the size reads from the port, since to high or low of a 53361911Scokane * size witll result in wrong data being passed, and that's bad */ 53461911Scokane /* A few of these were pulled during the attach phase */ 53561911Scokane switch(piod->port) { 53661911Scokane case PCI_VENDOR_ID_FREEBSD: 53761911Scokane if(piod->size != 2) return -EINVAL; 53861911Scokane copyout(&tdfx_info->vendor, piod->value, piod->size); 53961911Scokane return 0; 54061911Scokane case PCI_DEVICE_ID_FREEBSD: 54161911Scokane if(piod->size != 2) return -EINVAL; 54261911Scokane copyout(&tdfx_info->type, piod->value, piod->size); 54361911Scokane return 0; 54461911Scokane case PCI_BASE_ADDRESS_0_FREEBSD: 54561911Scokane if(piod->size != 4) return -EINVAL; 54661911Scokane copyout(&tdfx_info->addr0, piod->value, piod->size); 54761911Scokane return 0; 54865146Scokane case PCI_BASE_ADDRESS_1_FREEBSD: 54965146Scokane if(piod->size != 4) return -EINVAL; 55065146Scokane copyout(&tdfx_info->addr1, piod->value, piod->size); 55165146Scokane return 0; 55265146Scokane case PCI_PRIBUS_FREEBSD: 55365146Scokane if(piod->size != 1) return -EINVAL; 55465146Scokane break; 55565146Scokane case PCI_IOBASE_0_FREEBSD: 55665146Scokane if(piod->size != 2) return -EINVAL; 55765146Scokane break; 55865146Scokane case PCI_IOLIMIT_0_FREEBSD: 55965146Scokane if(piod->size != 2) return -EINVAL; 56065146Scokane break; 56161911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 56261911Scokane if(piod->size != 4) return -EINVAL; 56361911Scokane break; 56461911Scokane case PCI_REVISION_ID_FREEBSD: 56561911Scokane if(piod->size != 1) return -EINVAL; 56661911Scokane break; 56761911Scokane case SST1_PCI_SPECIAL4_FREEBSD: 56861911Scokane if(piod->size != 4) return -EINVAL; 56961911Scokane break; 57061911Scokane default: 57161911Scokane return -EINVAL; 57261911Scokane } 57361911Scokane 57461911Scokane 57561911Scokane /* Read the value and return */ 57661911Scokane switch(piod->size) { 57761911Scokane case 1: 57861911Scokane ret_byte = pci_read_config(tdfx_info[piod->device].dev, 57961911Scokane piod->port, 1); 58061911Scokane copyout(&ret_byte, piod->value, 1); 58161911Scokane break; 58261911Scokane case 2: 58361911Scokane ret_word = pci_read_config(tdfx_info[piod->device].dev, 58461911Scokane piod->port, 2); 58561911Scokane copyout(&ret_word, piod->value, 2); 58661911Scokane break; 58761911Scokane case 4: 58861911Scokane ret_dword = pci_read_config(tdfx_info[piod->device].dev, 58961911Scokane piod->port, 4); 59061911Scokane copyout(&ret_dword, piod->value, 4); 59161911Scokane break; 59261911Scokane default: 59361911Scokane return -EINVAL; 59461911Scokane } 59561911Scokane return 0; 59661911Scokane} 59761911Scokane 59861911Scokanestatic int 59961911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod) 60061911Scokane{ 60161911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 60261911Scokane /* Return vals */ 60361911Scokane u_int8_t ret_byte; 60461911Scokane u_int16_t ret_word; 60561911Scokane u_int32_t ret_dword; 60661911Scokane 60761911Scokane /* Port vals, mask */ 60861911Scokane u_int32_t retval, preval, mask; 60961911Scokane struct tdfx_softc* tdfx_info = NULL; 61061911Scokane 61161911Scokane 61261911Scokane if((piod == NULL) || (piod->device >= (tdfx_count & 61361911Scokane 0xf))) { 61461931Scokane#ifdef DEBUG 61561911Scokane printf("tdfx: Bad struct or device in tdfx_query_update\n"); 61661911Scokane#endif 61761911Scokane return -EINVAL; 61861911Scokane } 61961911Scokane 62061911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 62161911Scokane piod->device); 62261911Scokane if(tdfx_info == NULL) return -ENXIO; 62361911Scokane /* Code below this line in the fuction was taken from the 62461911Scokane * Linux driver and converted for freebsd. */ 62561911Scokane 62661911Scokane /* Check the size for all the ports, to make sure stuff doesn't get messed up 62761911Scokane * by poorly written clients */ 62861911Scokane 62961911Scokane switch(piod->port) { 63061911Scokane case PCI_COMMAND_FREEBSD: 63161911Scokane if(piod->size != 2) return -EINVAL; 63261911Scokane break; 63361911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 63461911Scokane if(piod->size != 4) return -EINVAL; 63561911Scokane break; 63661911Scokane case SST1_PCI_SPECIAL2_FREEBSD: 63761911Scokane if(piod->size != 4) return -EINVAL; 63861911Scokane break; 63961911Scokane case SST1_PCI_SPECIAL3_FREEBSD: 64061911Scokane if(piod->size != 4) return -EINVAL; 64161911Scokane break; 64261911Scokane case SST1_PCI_SPECIAL4_FREEBSD: 64361911Scokane if(piod->size != 4) return -EINVAL; 64461911Scokane break; 64561911Scokane default: 64661911Scokane return -EINVAL; 64761911Scokane } 64861911Scokane /* Read the current value */ 64961911Scokane retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4); 65061911Scokane 65161911Scokane /* These set up a mask to use, since apparently they wanted to write 4 bytes 65261911Scokane * at once to the ports */ 65361911Scokane switch (piod->size) { 65461911Scokane case 1: 65561911Scokane copyin(piod->value, &ret_byte, 1); 65661911Scokane preval = ret_byte << (8 * (piod->port & 0x3)); 65761911Scokane mask = 0xff << (8 * (piod->port & 0x3)); 65861911Scokane break; 65961911Scokane case 2: 66061911Scokane copyin(piod->value, &ret_word, 2); 66161911Scokane preval = ret_word << (8 * (piod->port & 0x3)); 66261911Scokane mask = 0xffff << (8 * (piod->port & 0x3)); 66361911Scokane break; 66461911Scokane case 4: 66561911Scokane copyin(piod->value, &ret_dword, 4); 66661911Scokane preval = ret_dword; 66761911Scokane mask = ~0; 66861911Scokane break; 66961911Scokane default: 67061911Scokane return -EINVAL; 67161911Scokane } 67261911Scokane /* Finally, combine the values and write it to the port */ 67361911Scokane retval = (retval & ~mask) | preval; 67461911Scokane pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4); 67561911Scokane 67661911Scokane return 0; 67761911Scokane} 67861911Scokane 67963488Scokane/* For both of these, I added a variable named workport of type u_int so 68063488Scokane * that I could eliminate the warning about my data type size. The 68163488Scokane * applications expect the port to be of type short, so I needed to change 68263488Scokane * this within the function */ 68361911Scokanestatic int 68461911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod) 68561911Scokane{ 68661911Scokane /* Return val */ 68761911Scokane u_int8_t ret_byte; 68863488Scokane u_int workport; 68965146Scokane struct tdfx_softc *tdfx_info = 69065146Scokane (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 69165146Scokane 69261911Scokane /* Restricts the access of ports other than those we use */ 69365146Scokane if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) || 69465146Scokane (piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) && 69565146Scokane (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 69661911Scokane return -EPERM; 69761911Scokane 69861911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 69961911Scokane if(piod->size != 1) { 70061911Scokane return -EINVAL; 70161911Scokane } 70261911Scokane 70361911Scokane /* Write the data to the intended port */ 70463488Scokane workport = piod->port; 70563488Scokane ret_byte = inb(workport); 70661911Scokane copyout(&ret_byte, piod->value, sizeof(u_int8_t)); 70761911Scokane return 0; 70861911Scokane} 70961911Scokane 71061911Scokanestatic int 71161911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod) 71261911Scokane{ 71361911Scokane /* return val */ 71461911Scokane u_int8_t ret_byte; 71563488Scokane u_int workport; 71665146Scokane struct tdfx_softc *tdfx_info = (struct 71765146Scokane tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 71861911Scokane /* Replace old switch w/ massive if(...) */ 71961911Scokane /* Restricts the access of ports other than those we use */ 72065146Scokane if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) && 72165146Scokane (piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ && 72265146Scokane (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 72361911Scokane return -EPERM; 72461911Scokane 72561911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 72661911Scokane if(piod->size != 1) { 72761911Scokane return -EINVAL; 72861911Scokane } 72961911Scokane 73061911Scokane /* Write the data to the intended port */ 73161911Scokane copyin(piod->value, &ret_byte, sizeof(u_int8_t)); 73263488Scokane workport = piod->port; 73363488Scokane outb(workport, ret_byte); 73461911Scokane return 0; 73561911Scokane} 73661911Scokane 73761911Scokanestatic int 73861911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod) 73961911Scokane{ 74061911Scokane /* There are three sub-commands to the query 0x33 */ 74161911Scokane switch(_IOC_NR(cmd)) { 74261911Scokane case 2: 74361911Scokane return tdfx_query_boards(); 74461911Scokane break; 74561911Scokane case 3: 74661911Scokane return tdfx_query_fetch(cmd, piod); 74761911Scokane break; 74861911Scokane case 4: 74961911Scokane return tdfx_query_update(cmd, piod); 75061911Scokane break; 75161911Scokane default: 75261911Scokane /* In case we are thrown a bogus sub-command! */ 75361931Scokane#ifdef DEBUG 75461911Scokane printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd)); 75561911Scokane#endif 75661911Scokane return -EINVAL; 75761911Scokane }; 75861911Scokane} 75961911Scokane 76061911Scokanestatic int 76161911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod) 76261911Scokane{ 76361911Scokane /* Two types of PIO, INPUT and OUTPUT, as the name suggests */ 76461911Scokane switch(_IOC_DIR(cmd)) { 76561911Scokane case IOCV_OUT: 76661911Scokane return tdfx_do_pio_rd(piod); 76761911Scokane break; 76861911Scokane case IOCV_IN: 76961911Scokane return tdfx_do_pio_wt(piod); 77061911Scokane break; 77161911Scokane default: 77261911Scokane return -EINVAL; 77361911Scokane }; 77461911Scokane} 77561911Scokane 77661911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO, 77761911Scokane * normally, you would read in the data pointed to by data, then write your 77861911Scokane * output to it. The ioctl *should* normally return zero if everything is 77961911Scokane * alright, but 3dfx didn't make it that way... 78061911Scokane * 78161911Scokane * For all of the ioctl code, in the event of a real error, 78261911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this 78361911Scokane * is that the ioctls actually RET information back to the program 78461911Scokane * sometimes, rather than filling it in the passed structure. We 78561911Scokane * want to distinguish errors from useful data, and maintain compatibility. 78661911Scokane * 78761911Scokane * There is this portion of the proc struct called p_retval[], we can store a 78883366Sjulian * return value in td->td_retval[0] and place the return value if it is positive 78961911Scokane * in there, then we can return 0 (good). If the return value is negative, we 79061911Scokane * can return -retval and the error should be properly handled. 79161911Scokane */ 79261911Scokanestatic int 79383366Sjuliantdfx_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) 79461911Scokane{ 79561911Scokane int retval = 0; 79661911Scokane struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data; 79761931Scokane#ifdef DEBUG 798106578Sjhb printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd, 799106578Sjhb piod); 80061911Scokane#endif 80161911Scokane switch(_IOC_TYPE(cmd)) { 80261911Scokane /* Return the real error if negative, or simply stick the valid return 80383366Sjulian * in td->td_retval */ 80461911Scokane case 0x33: 80561911Scokane /* The '3'(0x33) type IOCTL is for querying the installed cards */ 80683366Sjulian if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval; 80761911Scokane else return -retval; 80861911Scokane break; 80961911Scokane case 0: 81061911Scokane /* The 0 type IOCTL is for programmed I/O methods */ 81183366Sjulian if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval; 81261911Scokane else return -retval; 81361911Scokane break; 81461911Scokane default: 81561911Scokane /* Technically, we won't reach this from linux emu, but when glide 81661911Scokane * finally gets ported, watch out! */ 81761931Scokane#ifdef DEBUG 81883366Sjulian printf("Bad IOCTL from #%d\n", td->td_proc->p_pid); 81961911Scokane#endif 82061911Scokane return ENXIO; 82161911Scokane } 82261911Scokane 82361911Scokane return 0; 82461911Scokane} 82561911Scokane 82661931Scokane#ifdef TDFX_LINUX 82761931Scokane/* 82861931Scokane * Linux emulation IOCTL for /dev/tdfx 82961931Scokane */ 83061931Scokanestatic int 83183366Sjulianlinux_ioctl_tdfx(struct thread *td, struct linux_ioctl_args* args) 83261931Scokane{ 83361931Scokane int error = 0; 83461931Scokane u_long cmd = args->cmd & 0xffff; 83561911Scokane 83661931Scokane /* The structure passed to ioctl has two shorts, one int 83761931Scokane and one void*. */ 83861931Scokane char d_pio[2*sizeof(short) + sizeof(int) + sizeof(void*)]; 83961931Scokane 84089306Salfred struct file *fp; 84161931Scokane 84289319Salfred if ((error = fget(td, args->fd, &fp)) != 0) 84389319Salfred return (error); 84461931Scokane /* We simply copy the data and send it right to ioctl */ 84561931Scokane copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio)); 846102003Srwatson error = fo_ioctl(fp, cmd, (caddr_t)&d_pio, td->td_ucred, td); 84789306Salfred fdrop(fp, td); 84861931Scokane return error; 84961931Scokane} 85061931Scokane#endif /* TDFX_LINUX */ 85161931Scokane 85261931Scokane 85361911Scokane/* This is the device driver struct. This is sent to the driver subsystem to 85461911Scokane * register the method structure and the info strcut space for this particular 85561911Scokane * instance of the driver. 85661911Scokane */ 85761911Scokanestatic driver_t tdfx_driver = { 85861911Scokane "tdfx", 85961911Scokane tdfx_methods, 86061911Scokane sizeof(struct tdfx_softc), 86161911Scokane}; 86261911Scokane 86361911Scokane/* Tell Mr. Kernel about us! */ 86461911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0); 865