tdfx_pci.c revision 177147
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 177147 2008-03-13 14:08:41Z cokane $"); 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/cdefs.h> 4661911Scokane#include <sys/conf.h> 4761911Scokane#include <sys/fcntl.h> 4861911Scokane#include <sys/file.h> 4961911Scokane#include <sys/filedesc.h> 5061911Scokane#include <sys/filio.h> 5161911Scokane#include <sys/ioccom.h> 5261911Scokane#include <sys/kernel.h> 53129879Sphk#include <sys/module.h> 54129879Sphk#include <sys/malloc.h> 5561911Scokane#include <sys/mman.h> 5661911Scokane#include <sys/signalvar.h> 5761911Scokane#include <sys/systm.h> 5861911Scokane#include <sys/uio.h> 5961911Scokane 60119287Simp#include <dev/pci/pcivar.h> 61119287Simp#include <dev/pci/pcireg.h> 6261911Scokane 6361911Scokane#include <vm/vm.h> 6461911Scokane#include <vm/vm_kern.h> 6561911Scokane#include <vm/pmap.h> 6661911Scokane#include <vm/vm_extern.h> 6761911Scokane 6861911Scokane/* rman.h depends on machine/bus.h */ 6961911Scokane#include <machine/resource.h> 7061911Scokane#include <machine/bus.h> 7161911Scokane#include <sys/rman.h> 7262028Scokane 7361911Scokane#include <dev/tdfx/tdfx_io.h> 7461911Scokane#include <dev/tdfx/tdfx_vars.h> 7561911Scokane#include <dev/tdfx/tdfx_pci.h> 7661911Scokane 7761911Scokane 7861911Scokanestatic devclass_t tdfx_devclass; 7961911Scokane 8061911Scokane 8161911Scokanestatic int tdfx_count = 0; 8261911Scokane 8361911Scokane 8461911Scokane/* Set up the boot probe/attach routines */ 8561911Scokanestatic device_method_t tdfx_methods[] = { 8661911Scokane DEVMETHOD(device_probe, tdfx_probe), 8761911Scokane DEVMETHOD(device_attach, tdfx_attach), 8861911Scokane DEVMETHOD(device_detach, tdfx_detach), 8961911Scokane DEVMETHOD(device_shutdown, tdfx_shutdown), 9061911Scokane { 0, 0 } 9161911Scokane}; 9261911Scokane 93151897SrwatsonMALLOC_DEFINE(M_TDFX,"tdfx_driver","3DFX Graphics[/2D]/3D Accelerator(s)"); 9461911Scokane 9561911Scokane/* Char. Dev. file operations structure */ 9661911Scokanestatic struct cdevsw tdfx_cdev = { 97126080Sphk .d_version = D_VERSION, 98126080Sphk .d_flags = D_NEEDGIANT, 99111815Sphk .d_open = tdfx_open, 100111815Sphk .d_close = tdfx_close, 101111815Sphk .d_ioctl = tdfx_ioctl, 102111815Sphk .d_mmap = tdfx_mmap, 103111815Sphk .d_name = "tdfx", 10461911Scokane}; 10561911Scokane 10661911Scokanestatic int 10761911Scokanetdfx_probe(device_t dev) 10861911Scokane{ 10961911Scokane /* 11061911Scokane * probe routine called on kernel boot to register supported devices. We get 11161911Scokane * a device structure to work with, and we can test the VENDOR/DEVICE IDs to 112142880Simp * see if this PCI device is one that we support. Return BUS_PRROBE_DEFAULT 113142880Simp * if yes, ENXIO if not. 11461911Scokane */ 11561911Scokane switch(pci_get_devid(dev)) { 11661911Scokane case PCI_DEVICE_ALLIANCE_AT3D: 11761911Scokane device_set_desc(dev, "ProMotion At3D 3D Accelerator"); 118142880Simp return BUS_PROBE_DEFAULT; 11961911Scokane case PCI_DEVICE_3DFX_VOODOO2: 12061911Scokane device_set_desc(dev, "3DFX Voodoo II 3D Accelerator"); 121142880Simp return BUS_PROBE_DEFAULT; 12265146Scokane /*case PCI_DEVICE_3DFX_BANSHEE: 12361911Scokane device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator"); 124142880Simp return BUS_PROBE_DEFAULT; 12561911Scokane case PCI_DEVICE_3DFX_VOODOO3: 12661911Scokane device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator"); 127142880Simp return BUS_PROBE_DEFAULT;*/ 12861911Scokane case PCI_DEVICE_3DFX_VOODOO1: 12961911Scokane device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator"); 130142880Simp return BUS_PROBE_DEFAULT; 13161911Scokane }; 13261911Scokane 13361911Scokane return ENXIO; 13461911Scokane} 13561911Scokane 13661911Scokanestatic int 13761911Scokanetdfx_attach(device_t dev) { 13861911Scokane /* 13961911Scokane * The attach routine is called after the probe routine successfully says it 14061911Scokane * supports a given card. We now proceed to initialize this card for use with 14161911Scokane * the system. I want to map the device memory for userland allocation and 14261911Scokane * fill an information structure with information on this card. I'd also like 14361911Scokane * to set Write Combining with the MTRR code so that we can hopefully speed 14461911Scokane * up memory writes. The last thing is to register the character device 14561911Scokane * interface to the card, so we can open it from /dev/3dfxN, where N is a 14661911Scokane * small, whole number. 14761911Scokane */ 14861911Scokane struct tdfx_softc *tdfx_info; 14961911Scokane u_long val; 15061911Scokane /* rid value tells bus_alloc_resource where to find the addresses of ports or 15161911Scokane * of memory ranges in the PCI config space*/ 152119690Sjhb int rid = PCIR_BAR(0); 15361911Scokane 15461911Scokane /* Increment the card counter (for the ioctl code) */ 15561911Scokane tdfx_count++; 15661911Scokane 15761911Scokane /* Enable MemMap on Voodoo */ 15861911Scokane val = pci_read_config(dev, PCIR_COMMAND, 2); 15961911Scokane val |= (PCIM_CMD_MEMEN); 16061911Scokane pci_write_config(dev, PCIR_COMMAND, val, 2); 16161911Scokane val = pci_read_config(dev, PCIR_COMMAND, 2); 16261911Scokane 16361911Scokane /* Fill the soft config struct with info about this device*/ 16461911Scokane tdfx_info = device_get_softc(dev); 16561911Scokane tdfx_info->dev = dev; 16661911Scokane tdfx_info->vendor = pci_get_vendor(dev); 16761911Scokane tdfx_info->type = pci_get_devid(dev) >> 16; 16861911Scokane tdfx_info->bus = pci_get_bus(dev); 16961911Scokane tdfx_info->dv = pci_get_slot(dev); 17061911Scokane tdfx_info->curFile = NULL; 17161911Scokane 17261911Scokane /* 17361911Scokane * Get the Memory Location from the PCI Config, mask out lower word, since 17461911Scokane * the config space register is only one word long (this is nicer than a 17561911Scokane * bitshift). 17661911Scokane */ 17761911Scokane tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000); 17861931Scokane#ifdef DEBUG 17961911Scokane device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0); 18061911Scokane#endif 18161911Scokane /* Notify the VM that we will be mapping some memory later */ 182127135Snjl tdfx_info->memrange = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 183127135Snjl &rid, RF_ACTIVE | RF_SHAREABLE); 18461911Scokane if(tdfx_info->memrange == NULL) { 18561931Scokane#ifdef DEBUG 18661911Scokane device_printf(dev, "Error mapping mem, won't be able to use mmap()\n"); 18761911Scokane#endif 18861911Scokane tdfx_info->memrid = 0; 18961911Scokane } 19061911Scokane else { 19161911Scokane tdfx_info->memrid = rid; 19261931Scokane#ifdef DEBUG 19361911Scokane device_printf(dev, "Mapped to: 0x%x\n", 19461911Scokane (unsigned int)rman_get_start(tdfx_info->memrange)); 19561911Scokane#endif 19661911Scokane } 19761911Scokane 19863488Scokane /* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */ 19963488Scokane if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 || 20063488Scokane pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) { 20164085Scokane rid = 0x14; /* 2nd mem map */ 20263488Scokane tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000); 20363488Scokane#ifdef DEBUG 20463488Scokane device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1); 20563488Scokane#endif 206127135Snjl tdfx_info->memrange2 = bus_alloc_resource_any(dev, 207127135Snjl SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE); 20863488Scokane if(tdfx_info->memrange2 == NULL) { 20963488Scokane#ifdef DEBUG 21063488Scokane device_printf(dev, "Mem1 couldn't be allocated, glide may not work."); 21163488Scokane#endif 21263488Scokane tdfx_info->memrid2 = 0; 21363488Scokane } 21463488Scokane else { 21563488Scokane tdfx_info->memrid2 = rid; 21663488Scokane } 21763488Scokane /* Now to map the PIO stuff */ 21865146Scokane rid = PCIR_IOBASE0_2; 21965146Scokane tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2); 22065146Scokane tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0; 221127135Snjl tdfx_info->piorange = bus_alloc_resource_any(dev, 222127135Snjl SYS_RES_IOPORT, &rid, RF_ACTIVE | RF_SHAREABLE); 22363488Scokane if(tdfx_info->piorange == NULL) { 22463488Scokane#ifdef DEBUG 22563488Scokane device_printf(dev, "Couldn't map PIO range."); 22663488Scokane#endif 22763488Scokane tdfx_info->piorid = 0; 22863488Scokane } 22963488Scokane else { 23063488Scokane tdfx_info->piorid = rid; 23165146Scokane } 23263488Scokane } else { 23363488Scokane tdfx_info->addr1 = 0; 23463488Scokane tdfx_info->memrange2 = NULL; 23563488Scokane tdfx_info->piorange = NULL; 23663488Scokane } 23763488Scokane 23861911Scokane /* 23961911Scokane * Set Writecombining, or at least Uncacheable for the memory region, if we 24061911Scokane * are able to 24161911Scokane */ 24261911Scokane 24361911Scokane if(tdfx_setmtrr(dev) != 0) { 24461931Scokane#ifdef DEBUG 24561911Scokane device_printf(dev, "Some weird error setting MTRRs"); 24661911Scokane#endif 24761911Scokane return -1; 24861911Scokane } 24963488Scokane 25061911Scokane /* 25161911Scokane * make_dev registers the cdev to access the 3dfx card from /dev 25261911Scokane * use hex here for the dev num, simply to provide better support if > 10 25361911Scokane * voodoo cards, for the mad. The user must set the link, or use MAKEDEV. 25461911Scokane * Why would we want that many voodoo cards anyhow? 25561911Scokane */ 256104111Sphk tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev), 257108323Srwatson UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev)); 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 */ 39661911Scokane struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 39761911Scokane UNIT(minor(dev))); 39861911Scokane if(tdfx_info->busy != 0) return EBUSY; 39961931Scokane#ifdef DEBUG 40083366Sjulian printf("3dfx: Opened by #%d\n", td->td_proc->p_pid); 40161911Scokane#endif 40261911Scokane /* Set the driver as busy */ 40361911Scokane tdfx_info->busy++; 40461911Scokane return 0; 40561911Scokane} 40661911Scokane 40761911Scokanestatic int 408130585Sphktdfx_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 40961911Scokane{ 41061911Scokane /* 41161911Scokane * The close cdev method handles close(2) calls to /dev/3dfx[n] 41261911Scokane * We'll always want to close the device when it's called. 41361911Scokane */ 41461911Scokane struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 41561911Scokane UNIT(minor(dev))); 41661911Scokane if(tdfx_info->busy == 0) return EBADF; 41761911Scokane tdfx_info->busy = 0; 41861931Scokane#ifdef DEBUG 41983366Sjulian printf("Closed by #%d\n", td->td_proc->p_pid); 42061911Scokane#endif 42161911Scokane return 0; 42261911Scokane} 42361911Scokane 42461911Scokanestatic int 425130585Sphktdfx_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) 42661911Scokane{ 42761911Scokane /* 42861911Scokane * mmap(2) is called by a user process to request that an area of memory 42961911Scokane * associated with this device be mapped for the process to work with. Nprot 43061911Scokane * holds the protections requested, PROT_READ, PROT_WRITE, or both. 43161911Scokane */ 43266910Scokane 43366910Scokane /**** OLD GET CONFIG ****/ 43466910Scokane /* struct tdfx_softc* tdfx_info; */ 43561911Scokane 43661911Scokane /* Get the configuration for our card XXX*/ 43766910Scokane /*tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 43866910Scokane UNIT(minor(dev)));*/ 43966910Scokane /************************/ 44066910Scokane 44166910Scokane struct tdfx_softc* tdfx_info[2]; 44261911Scokane 44366910Scokane tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0); 44466910Scokane 44561911Scokane /* If, for some reason, its not configured, we bail out */ 44666910Scokane if(tdfx_info[0] == NULL) { 44761931Scokane#ifdef DEBUG 44861911Scokane printf("tdfx: tdfx_info (softc) is NULL\n"); 44961911Scokane#endif 45061911Scokane return -1; 45161911Scokane } 45266910Scokane 45361911Scokane /* We must stay within the bound of our address space */ 45466910Scokane if((offset & 0xff000000) == tdfx_info[0]->addr0) { 45561911Scokane offset &= 0xffffff; 456111462Smux *paddr = rman_get_start(tdfx_info[0]->memrange) + offset; 457111462Smux return 0; 45866910Scokane } 45966910Scokane 46066910Scokane if(tdfx_count > 1) { 46166910Scokane tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1); 46266910Scokane if((offset & 0xff000000) == tdfx_info[1]->addr0) { 46366910Scokane offset &= 0xffffff; 464111462Smux *paddr = rman_get_start(tdfx_info[1]->memrange) + 465111462Smux offset; 466111462Smux return 0; 46766910Scokane } 46866910Scokane } 46963488Scokane 47063488Scokane /* See if the Banshee/V3 LFB is being requested */ 47166910Scokane /*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) == 47264085Scokane tdfx_info->addr1) { 47363488Scokane offset &= 0xffffff; 47466910Scokane return atop(rman_get_start(tdfx_info[1]->memrange2) + offset); 47566910Scokane }*/ /* VoodooNG code */ 47663488Scokane 47766910Scokane /* The ret call */ 47861911Scokane /* atop -> address to page 47961911Scokane * rman_get_start, get the (struct resource*)->r_start member, 48061911Scokane * the mapping base address. 48161911Scokane */ 48266910Scokane return -1; 48361911Scokane} 48461911Scokane 48561911Scokanestatic int 48661911Scokanetdfx_query_boards(void) { 48761911Scokane /* 48861911Scokane * This returns the number of installed tdfx cards, we have been keeping 48961911Scokane * count, look at tdfx_attach 49061911Scokane */ 49161911Scokane return tdfx_count; 49261911Scokane} 49361911Scokane 49461911Scokanestatic int 49561911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod) 49661911Scokane{ 49761911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 49861911Scokane /* Various return values 8bit-32bit */ 49961911Scokane u_int8_t ret_byte; 50061911Scokane u_int16_t ret_word; 50161911Scokane u_int32_t ret_dword; 50261911Scokane struct tdfx_softc* tdfx_info = NULL; 50361911Scokane 50461911Scokane /* This one depend on the tdfx_* structs being properly initialized */ 50561911Scokane 50661911Scokane /*piod->device &= 0xf;*/ 50761911Scokane if((piod == NULL) ||(tdfx_count <= piod->device) || 50861911Scokane (piod->device < 0)) { 50961931Scokane#ifdef DEBUG 51061911Scokane printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n"); 51161911Scokane#endif 51261911Scokane return -EINVAL; 51361911Scokane } 51461911Scokane 51561911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 51661911Scokane piod->device); 51761911Scokane 51861911Scokane if(tdfx_info == NULL) return -ENXIO; 51961911Scokane 52061911Scokane /* We must restrict the size reads from the port, since to high or low of a 52161911Scokane * size witll result in wrong data being passed, and that's bad */ 52261911Scokane /* A few of these were pulled during the attach phase */ 52361911Scokane switch(piod->port) { 52461911Scokane case PCI_VENDOR_ID_FREEBSD: 52561911Scokane if(piod->size != 2) return -EINVAL; 52661911Scokane copyout(&tdfx_info->vendor, piod->value, piod->size); 52761911Scokane return 0; 52861911Scokane case PCI_DEVICE_ID_FREEBSD: 52961911Scokane if(piod->size != 2) return -EINVAL; 53061911Scokane copyout(&tdfx_info->type, piod->value, piod->size); 53161911Scokane return 0; 53261911Scokane case PCI_BASE_ADDRESS_0_FREEBSD: 53361911Scokane if(piod->size != 4) return -EINVAL; 53461911Scokane copyout(&tdfx_info->addr0, piod->value, piod->size); 53561911Scokane return 0; 53665146Scokane case PCI_BASE_ADDRESS_1_FREEBSD: 53765146Scokane if(piod->size != 4) return -EINVAL; 53865146Scokane copyout(&tdfx_info->addr1, piod->value, piod->size); 53965146Scokane return 0; 54065146Scokane case PCI_PRIBUS_FREEBSD: 54165146Scokane if(piod->size != 1) return -EINVAL; 54265146Scokane break; 54365146Scokane case PCI_IOBASE_0_FREEBSD: 54465146Scokane if(piod->size != 2) return -EINVAL; 54565146Scokane break; 54665146Scokane case PCI_IOLIMIT_0_FREEBSD: 54765146Scokane if(piod->size != 2) return -EINVAL; 54865146Scokane break; 54961911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 55061911Scokane if(piod->size != 4) return -EINVAL; 55161911Scokane break; 55261911Scokane case PCI_REVISION_ID_FREEBSD: 55361911Scokane if(piod->size != 1) return -EINVAL; 55461911Scokane break; 55561911Scokane case SST1_PCI_SPECIAL4_FREEBSD: 55661911Scokane if(piod->size != 4) return -EINVAL; 55761911Scokane break; 55861911Scokane default: 55961911Scokane return -EINVAL; 56061911Scokane } 56161911Scokane 56261911Scokane 56361911Scokane /* Read the value and return */ 56461911Scokane switch(piod->size) { 56561911Scokane case 1: 56661911Scokane ret_byte = pci_read_config(tdfx_info[piod->device].dev, 56761911Scokane piod->port, 1); 56861911Scokane copyout(&ret_byte, piod->value, 1); 56961911Scokane break; 57061911Scokane case 2: 57161911Scokane ret_word = pci_read_config(tdfx_info[piod->device].dev, 57261911Scokane piod->port, 2); 57361911Scokane copyout(&ret_word, piod->value, 2); 57461911Scokane break; 57561911Scokane case 4: 57661911Scokane ret_dword = pci_read_config(tdfx_info[piod->device].dev, 57761911Scokane piod->port, 4); 57861911Scokane copyout(&ret_dword, piod->value, 4); 57961911Scokane break; 58061911Scokane default: 58161911Scokane return -EINVAL; 58261911Scokane } 58361911Scokane return 0; 58461911Scokane} 58561911Scokane 58661911Scokanestatic int 58761911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod) 58861911Scokane{ 58961911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 59061911Scokane /* Return vals */ 59161911Scokane u_int8_t ret_byte; 59261911Scokane u_int16_t ret_word; 59361911Scokane u_int32_t ret_dword; 59461911Scokane 59561911Scokane /* Port vals, mask */ 59661911Scokane u_int32_t retval, preval, mask; 59761911Scokane struct tdfx_softc* tdfx_info = NULL; 59861911Scokane 59961911Scokane 60061911Scokane if((piod == NULL) || (piod->device >= (tdfx_count & 60161911Scokane 0xf))) { 60261931Scokane#ifdef DEBUG 60361911Scokane printf("tdfx: Bad struct or device in tdfx_query_update\n"); 60461911Scokane#endif 60561911Scokane return -EINVAL; 60661911Scokane } 60761911Scokane 60861911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 60961911Scokane piod->device); 61061911Scokane if(tdfx_info == NULL) return -ENXIO; 61161911Scokane /* Code below this line in the fuction was taken from the 61261911Scokane * Linux driver and converted for freebsd. */ 61361911Scokane 61461911Scokane /* Check the size for all the ports, to make sure stuff doesn't get messed up 61561911Scokane * by poorly written clients */ 61661911Scokane 61761911Scokane switch(piod->port) { 61861911Scokane case PCI_COMMAND_FREEBSD: 61961911Scokane if(piod->size != 2) return -EINVAL; 62061911Scokane break; 62161911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 62261911Scokane if(piod->size != 4) return -EINVAL; 62361911Scokane break; 62461911Scokane case SST1_PCI_SPECIAL2_FREEBSD: 62561911Scokane if(piod->size != 4) return -EINVAL; 62661911Scokane break; 62761911Scokane case SST1_PCI_SPECIAL3_FREEBSD: 62861911Scokane if(piod->size != 4) return -EINVAL; 62961911Scokane break; 63061911Scokane case SST1_PCI_SPECIAL4_FREEBSD: 63161911Scokane if(piod->size != 4) return -EINVAL; 63261911Scokane break; 63361911Scokane default: 63461911Scokane return -EINVAL; 63561911Scokane } 63661911Scokane /* Read the current value */ 63761911Scokane retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4); 63861911Scokane 63961911Scokane /* These set up a mask to use, since apparently they wanted to write 4 bytes 64061911Scokane * at once to the ports */ 64161911Scokane switch (piod->size) { 64261911Scokane case 1: 64361911Scokane copyin(piod->value, &ret_byte, 1); 64461911Scokane preval = ret_byte << (8 * (piod->port & 0x3)); 64561911Scokane mask = 0xff << (8 * (piod->port & 0x3)); 64661911Scokane break; 64761911Scokane case 2: 64861911Scokane copyin(piod->value, &ret_word, 2); 64961911Scokane preval = ret_word << (8 * (piod->port & 0x3)); 65061911Scokane mask = 0xffff << (8 * (piod->port & 0x3)); 65161911Scokane break; 65261911Scokane case 4: 65361911Scokane copyin(piod->value, &ret_dword, 4); 65461911Scokane preval = ret_dword; 65561911Scokane mask = ~0; 65661911Scokane break; 65761911Scokane default: 65861911Scokane return -EINVAL; 65961911Scokane } 66061911Scokane /* Finally, combine the values and write it to the port */ 66161911Scokane retval = (retval & ~mask) | preval; 66261911Scokane pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4); 66361911Scokane 66461911Scokane return 0; 66561911Scokane} 66661911Scokane 66763488Scokane/* For both of these, I added a variable named workport of type u_int so 66863488Scokane * that I could eliminate the warning about my data type size. The 66963488Scokane * applications expect the port to be of type short, so I needed to change 67063488Scokane * this within the function */ 67161911Scokanestatic int 67261911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod) 67361911Scokane{ 67461911Scokane /* Return val */ 67561911Scokane u_int8_t ret_byte; 67663488Scokane u_int workport; 67765146Scokane struct tdfx_softc *tdfx_info = 67865146Scokane (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 67965146Scokane 68061911Scokane /* Restricts the access of ports other than those we use */ 68165146Scokane if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) || 68265146Scokane (piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) && 68365146Scokane (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 68461911Scokane return -EPERM; 68561911Scokane 68661911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 68761911Scokane if(piod->size != 1) { 68861911Scokane return -EINVAL; 68961911Scokane } 69061911Scokane 69161911Scokane /* Write the data to the intended port */ 69263488Scokane workport = piod->port; 69363488Scokane ret_byte = inb(workport); 69461911Scokane copyout(&ret_byte, piod->value, sizeof(u_int8_t)); 69561911Scokane return 0; 69661911Scokane} 69761911Scokane 69861911Scokanestatic int 69961911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod) 70061911Scokane{ 70161911Scokane /* return val */ 70261911Scokane u_int8_t ret_byte; 70363488Scokane u_int workport; 70465146Scokane struct tdfx_softc *tdfx_info = (struct 70565146Scokane tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 70661911Scokane /* Replace old switch w/ massive if(...) */ 70761911Scokane /* Restricts the access of ports other than those we use */ 70865146Scokane if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) && 70965146Scokane (piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ && 71065146Scokane (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 71161911Scokane return -EPERM; 71261911Scokane 71361911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 71461911Scokane if(piod->size != 1) { 71561911Scokane return -EINVAL; 71661911Scokane } 71761911Scokane 71861911Scokane /* Write the data to the intended port */ 71961911Scokane copyin(piod->value, &ret_byte, sizeof(u_int8_t)); 72063488Scokane workport = piod->port; 72163488Scokane outb(workport, ret_byte); 72261911Scokane return 0; 72361911Scokane} 72461911Scokane 72561911Scokanestatic int 72661911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod) 72761911Scokane{ 72861911Scokane /* There are three sub-commands to the query 0x33 */ 72961911Scokane switch(_IOC_NR(cmd)) { 73061911Scokane case 2: 73161911Scokane return tdfx_query_boards(); 73261911Scokane break; 73361911Scokane case 3: 73461911Scokane return tdfx_query_fetch(cmd, piod); 73561911Scokane break; 73661911Scokane case 4: 73761911Scokane return tdfx_query_update(cmd, piod); 73861911Scokane break; 73961911Scokane default: 74061911Scokane /* In case we are thrown a bogus sub-command! */ 74161931Scokane#ifdef DEBUG 74261911Scokane printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd)); 74361911Scokane#endif 74461911Scokane return -EINVAL; 745115494Sphk } 74661911Scokane} 74761911Scokane 74861911Scokanestatic int 74961911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod) 75061911Scokane{ 75161911Scokane /* Two types of PIO, INPUT and OUTPUT, as the name suggests */ 75261911Scokane switch(_IOC_DIR(cmd)) { 75361911Scokane case IOCV_OUT: 75461911Scokane return tdfx_do_pio_rd(piod); 75561911Scokane break; 75661911Scokane case IOCV_IN: 75761911Scokane return tdfx_do_pio_wt(piod); 75861911Scokane break; 75961911Scokane default: 76061911Scokane return -EINVAL; 761115494Sphk } 76261911Scokane} 76361911Scokane 76461911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO, 76561911Scokane * normally, you would read in the data pointed to by data, then write your 76661911Scokane * output to it. The ioctl *should* normally return zero if everything is 76761911Scokane * alright, but 3dfx didn't make it that way... 76861911Scokane * 76961911Scokane * For all of the ioctl code, in the event of a real error, 77061911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this 77161911Scokane * is that the ioctls actually RET information back to the program 77261911Scokane * sometimes, rather than filling it in the passed structure. We 77361911Scokane * want to distinguish errors from useful data, and maintain compatibility. 77461911Scokane * 77561911Scokane * There is this portion of the proc struct called p_retval[], we can store a 77683366Sjulian * return value in td->td_retval[0] and place the return value if it is positive 77761911Scokane * in there, then we can return 0 (good). If the return value is negative, we 77861911Scokane * can return -retval and the error should be properly handled. 77961911Scokane */ 78061911Scokanestatic int 781130585Sphktdfx_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 78261911Scokane{ 78361911Scokane int retval = 0; 78461911Scokane struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data; 78561931Scokane#ifdef DEBUG 786106578Sjhb printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd, 787106578Sjhb piod); 78861911Scokane#endif 78961911Scokane switch(_IOC_TYPE(cmd)) { 79061911Scokane /* Return the real error if negative, or simply stick the valid return 79183366Sjulian * in td->td_retval */ 79261911Scokane case 0x33: 79361911Scokane /* The '3'(0x33) type IOCTL is for querying the installed cards */ 79483366Sjulian if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval; 79561911Scokane else return -retval; 79661911Scokane break; 79761911Scokane case 0: 79861911Scokane /* The 0 type IOCTL is for programmed I/O methods */ 79983366Sjulian if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval; 80061911Scokane else return -retval; 80161911Scokane break; 80261911Scokane default: 80361911Scokane /* Technically, we won't reach this from linux emu, but when glide 80461911Scokane * finally gets ported, watch out! */ 80561931Scokane#ifdef DEBUG 80683366Sjulian printf("Bad IOCTL from #%d\n", td->td_proc->p_pid); 80761911Scokane#endif 80861911Scokane return ENXIO; 80961911Scokane } 81061911Scokane 81161911Scokane return 0; 81261911Scokane} 81361911Scokane 81461911Scokane/* This is the device driver struct. This is sent to the driver subsystem to 81561911Scokane * register the method structure and the info strcut space for this particular 81661911Scokane * instance of the driver. 81761911Scokane */ 81861911Scokanestatic driver_t tdfx_driver = { 81961911Scokane "tdfx", 82061911Scokane tdfx_methods, 82161911Scokane sizeof(struct tdfx_softc), 82261911Scokane}; 82361911Scokane 82461911Scokane/* Tell Mr. Kernel about us! */ 82561911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0); 826177147ScokaneMODULE_DEPEND(tdfx, mem, 1, 1, 1); 827156260SyarMODULE_VERSION(tdfx, 1); 828