tdfx_pci.c revision 151897
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 151897 2005-10-31 15:41:29Z rwatson $"); 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 7362028Scokane/* This must come first */ 7462028Scokane#include "opt_tdfx.h" 7561931Scokane#ifdef TDFX_LINUX 7661931Scokane#include <dev/tdfx/tdfx_linux.h> 7761931Scokane#endif 7861911Scokane 7961911Scokane#include <dev/tdfx/tdfx_io.h> 8061911Scokane#include <dev/tdfx/tdfx_vars.h> 8161911Scokane#include <dev/tdfx/tdfx_pci.h> 8261911Scokane 8361911Scokane 8461911Scokanestatic devclass_t tdfx_devclass; 8561911Scokane 8661911Scokane 8761911Scokanestatic int tdfx_count = 0; 8861911Scokane 8961911Scokane 9061911Scokane/* Set up the boot probe/attach routines */ 9161911Scokanestatic device_method_t tdfx_methods[] = { 9261911Scokane DEVMETHOD(device_probe, tdfx_probe), 9361911Scokane DEVMETHOD(device_attach, tdfx_attach), 9461911Scokane DEVMETHOD(device_detach, tdfx_detach), 9561911Scokane DEVMETHOD(device_shutdown, tdfx_shutdown), 9661911Scokane { 0, 0 } 9761911Scokane}; 9861911Scokane 99151897SrwatsonMALLOC_DEFINE(M_TDFX,"tdfx_driver","3DFX Graphics[/2D]/3D Accelerator(s)"); 10061911Scokane 10161931Scokane#ifdef TDFX_LINUX 10261989ScokaneMODULE_DEPEND(tdfx, linux, 1, 1, 1); 10361931ScokaneLINUX_IOCTL_SET(tdfx, LINUX_IOCTL_TDFX_MIN, LINUX_IOCTL_TDFX_MAX); 10461931Scokane#endif 10561931Scokane 10661911Scokane/* Char. Dev. file operations structure */ 10761911Scokanestatic struct cdevsw tdfx_cdev = { 108126080Sphk .d_version = D_VERSION, 109126080Sphk .d_flags = D_NEEDGIANT, 110111815Sphk .d_open = tdfx_open, 111111815Sphk .d_close = tdfx_close, 112111815Sphk .d_ioctl = tdfx_ioctl, 113111815Sphk .d_mmap = tdfx_mmap, 114111815Sphk .d_name = "tdfx", 11561911Scokane}; 11661911Scokane 11761911Scokanestatic int 11861911Scokanetdfx_probe(device_t dev) 11961911Scokane{ 12061911Scokane /* 12161911Scokane * probe routine called on kernel boot to register supported devices. We get 12261911Scokane * a device structure to work with, and we can test the VENDOR/DEVICE IDs to 123142880Simp * see if this PCI device is one that we support. Return BUS_PRROBE_DEFAULT 124142880Simp * if yes, ENXIO if not. 12561911Scokane */ 12661911Scokane switch(pci_get_devid(dev)) { 12761911Scokane case PCI_DEVICE_ALLIANCE_AT3D: 12861911Scokane device_set_desc(dev, "ProMotion At3D 3D Accelerator"); 129142880Simp return BUS_PROBE_DEFAULT; 13061911Scokane case PCI_DEVICE_3DFX_VOODOO2: 13161911Scokane device_set_desc(dev, "3DFX Voodoo II 3D Accelerator"); 132142880Simp return BUS_PROBE_DEFAULT; 13365146Scokane /*case PCI_DEVICE_3DFX_BANSHEE: 13461911Scokane device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator"); 135142880Simp return BUS_PROBE_DEFAULT; 13661911Scokane case PCI_DEVICE_3DFX_VOODOO3: 13761911Scokane device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator"); 138142880Simp return BUS_PROBE_DEFAULT;*/ 13961911Scokane case PCI_DEVICE_3DFX_VOODOO1: 14061911Scokane device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator"); 141142880Simp return BUS_PROBE_DEFAULT; 14261911Scokane }; 14361911Scokane 14461911Scokane return ENXIO; 14561911Scokane} 14661911Scokane 14761911Scokanestatic int 14861911Scokanetdfx_attach(device_t dev) { 14961911Scokane /* 15061911Scokane * The attach routine is called after the probe routine successfully says it 15161911Scokane * supports a given card. We now proceed to initialize this card for use with 15261911Scokane * the system. I want to map the device memory for userland allocation and 15361911Scokane * fill an information structure with information on this card. I'd also like 15461911Scokane * to set Write Combining with the MTRR code so that we can hopefully speed 15561911Scokane * up memory writes. The last thing is to register the character device 15661911Scokane * interface to the card, so we can open it from /dev/3dfxN, where N is a 15761911Scokane * small, whole number. 15861911Scokane */ 15961911Scokane struct tdfx_softc *tdfx_info; 16061911Scokane u_long val; 16161911Scokane /* rid value tells bus_alloc_resource where to find the addresses of ports or 16261911Scokane * of memory ranges in the PCI config space*/ 163119690Sjhb int rid = PCIR_BAR(0); 16461911Scokane 16561911Scokane /* Increment the card counter (for the ioctl code) */ 16661911Scokane tdfx_count++; 16761911Scokane 16861911Scokane /* Enable MemMap on Voodoo */ 16961911Scokane val = pci_read_config(dev, PCIR_COMMAND, 2); 17061911Scokane val |= (PCIM_CMD_MEMEN); 17161911Scokane pci_write_config(dev, PCIR_COMMAND, val, 2); 17261911Scokane val = pci_read_config(dev, PCIR_COMMAND, 2); 17361911Scokane 17461911Scokane /* Fill the soft config struct with info about this device*/ 17561911Scokane tdfx_info = device_get_softc(dev); 17661911Scokane tdfx_info->dev = dev; 17761911Scokane tdfx_info->vendor = pci_get_vendor(dev); 17861911Scokane tdfx_info->type = pci_get_devid(dev) >> 16; 17961911Scokane tdfx_info->bus = pci_get_bus(dev); 18061911Scokane tdfx_info->dv = pci_get_slot(dev); 18161911Scokane tdfx_info->curFile = NULL; 18261911Scokane 18361911Scokane /* 18461911Scokane * Get the Memory Location from the PCI Config, mask out lower word, since 18561911Scokane * the config space register is only one word long (this is nicer than a 18661911Scokane * bitshift). 18761911Scokane */ 18861911Scokane tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000); 18961931Scokane#ifdef DEBUG 19061911Scokane device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0); 19161911Scokane#endif 19261911Scokane /* Notify the VM that we will be mapping some memory later */ 193127135Snjl tdfx_info->memrange = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 194127135Snjl &rid, RF_ACTIVE | RF_SHAREABLE); 19561911Scokane if(tdfx_info->memrange == NULL) { 19661931Scokane#ifdef DEBUG 19761911Scokane device_printf(dev, "Error mapping mem, won't be able to use mmap()\n"); 19861911Scokane#endif 19961911Scokane tdfx_info->memrid = 0; 20061911Scokane } 20161911Scokane else { 20261911Scokane tdfx_info->memrid = rid; 20361931Scokane#ifdef DEBUG 20461911Scokane device_printf(dev, "Mapped to: 0x%x\n", 20561911Scokane (unsigned int)rman_get_start(tdfx_info->memrange)); 20661911Scokane#endif 20761911Scokane } 20861911Scokane 20963488Scokane /* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */ 21063488Scokane if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 || 21163488Scokane pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) { 21264085Scokane rid = 0x14; /* 2nd mem map */ 21363488Scokane tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000); 21463488Scokane#ifdef DEBUG 21563488Scokane device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1); 21663488Scokane#endif 217127135Snjl tdfx_info->memrange2 = bus_alloc_resource_any(dev, 218127135Snjl SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE); 21963488Scokane if(tdfx_info->memrange2 == NULL) { 22063488Scokane#ifdef DEBUG 22163488Scokane device_printf(dev, "Mem1 couldn't be allocated, glide may not work."); 22263488Scokane#endif 22363488Scokane tdfx_info->memrid2 = 0; 22463488Scokane } 22563488Scokane else { 22663488Scokane tdfx_info->memrid2 = rid; 22763488Scokane } 22863488Scokane /* Now to map the PIO stuff */ 22965146Scokane rid = PCIR_IOBASE0_2; 23065146Scokane tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2); 23165146Scokane tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0; 232127135Snjl tdfx_info->piorange = bus_alloc_resource_any(dev, 233127135Snjl SYS_RES_IOPORT, &rid, RF_ACTIVE | RF_SHAREABLE); 23463488Scokane if(tdfx_info->piorange == NULL) { 23563488Scokane#ifdef DEBUG 23663488Scokane device_printf(dev, "Couldn't map PIO range."); 23763488Scokane#endif 23863488Scokane tdfx_info->piorid = 0; 23963488Scokane } 24063488Scokane else { 24163488Scokane tdfx_info->piorid = rid; 24265146Scokane } 24363488Scokane } else { 24463488Scokane tdfx_info->addr1 = 0; 24563488Scokane tdfx_info->memrange2 = NULL; 24663488Scokane tdfx_info->piorange = NULL; 24763488Scokane } 24863488Scokane 24961911Scokane /* 25061911Scokane * Set Writecombining, or at least Uncacheable for the memory region, if we 25161911Scokane * are able to 25261911Scokane */ 25361911Scokane 25461911Scokane if(tdfx_setmtrr(dev) != 0) { 25561931Scokane#ifdef DEBUG 25661911Scokane device_printf(dev, "Some weird error setting MTRRs"); 25761911Scokane#endif 25861911Scokane return -1; 25961911Scokane } 26063488Scokane 26161911Scokane /* 26261911Scokane * make_dev registers the cdev to access the 3dfx card from /dev 26361911Scokane * use hex here for the dev num, simply to provide better support if > 10 26461911Scokane * voodoo cards, for the mad. The user must set the link, or use MAKEDEV. 26561911Scokane * Why would we want that many voodoo cards anyhow? 26661911Scokane */ 267104111Sphk tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev), 268108323Srwatson UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev)); 26961911Scokane 27061911Scokane return 0; 27161911Scokane} 27261911Scokane 27361911Scokanestatic int 27461911Scokanetdfx_detach(device_t dev) { 27561911Scokane struct tdfx_softc* tdfx_info; 27661911Scokane int retval; 27761911Scokane tdfx_info = device_get_softc(dev); 27861911Scokane 27961911Scokane /* Delete allocated resource, of course */ 28063488Scokane bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid, 28161911Scokane tdfx_info->memrange); 28263488Scokane 28363488Scokane /* Release extended Voodoo3/Banshee resources */ 28463488Scokane if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE || 28563488Scokane pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) { 28663488Scokane if(tdfx_info->memrange2 != NULL) 28763488Scokane bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2, 28863488Scokane tdfx_info->memrange); 28964085Scokane /* if(tdfx_info->piorange != NULL) 29063488Scokane bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid, 29164085Scokane tdfx_info->piorange);*/ 29263488Scokane } 29363488Scokane 29461911Scokane /* Though it is safe to leave the WRCOMB support since the 29561911Scokane mem driver checks for it, we should remove it in order 29661911Scokane to free an MTRR for another device */ 29761911Scokane retval = tdfx_clrmtrr(dev); 29861931Scokane#ifdef DEBUG 29961911Scokane if(retval != 0) 30061911Scokane printf("tdfx: For some reason, I couldn't clear the mtrr\n"); 30161911Scokane#endif 30261989Scokane /* Remove device entry when it can no longer be accessed */ 30361989Scokane destroy_dev(tdfx_info->devt); 30461911Scokane return(0); 30561911Scokane} 30661911Scokane 30761911Scokanestatic int 30861911Scokanetdfx_shutdown(device_t dev) { 30961931Scokane#ifdef DEBUG 31061911Scokane device_printf(dev, "tdfx: Device Shutdown\n"); 31161911Scokane#endif 31261911Scokane return 0; 31361911Scokane} 31461911Scokane 31561911Scokanestatic int 31661911Scokanetdfx_clrmtrr(device_t dev) { 31761911Scokane /* This function removes the MTRR set by the attach call, so it can be used 31861911Scokane * in the future by other drivers. 31961911Scokane */ 32061911Scokane int retval, act; 32161911Scokane struct tdfx_softc *tdfx_info = device_get_softc(dev); 32261911Scokane 32361911Scokane act = MEMRANGE_SET_REMOVE; 32461911Scokane retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 32561911Scokane return retval; 32661911Scokane} 32761911Scokane 32861911Scokanestatic int 32961911Scokanetdfx_setmtrr(device_t dev) { 33061911Scokane /* 33161911Scokane * This is the MTRR setting function for the 3dfx card. It is called from 33261911Scokane * tdfx_attach. If we can't set the MTRR properly, it's not the end of the 33361911Scokane * world. We can still continue, just with slightly (very slightly) degraded 33461911Scokane * performance. 33561911Scokane */ 33661911Scokane int retval = 0, act; 33761911Scokane struct tdfx_softc *tdfx_info = device_get_softc(dev); 33861911Scokane 33961911Scokane /* The older Voodoo cards have a shorter memrange than the newer ones */ 34061911Scokane if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) == 34163488Scokane PCI_DEVICE_3DFX_VOODOO2)) { 34261911Scokane tdfx_info->mrdesc.mr_len = 0x400000; 34363488Scokane 34463488Scokane /* The memory descriptor is described as the top 15 bits of the real 34563488Scokane address */ 34663488Scokane tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000; 34763488Scokane } 34861911Scokane else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) || 34963488Scokane (pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) { 35061911Scokane tdfx_info->mrdesc.mr_len = 0x1000000; 35163488Scokane /* The Voodoo3 and Banshee LFB is the second memory address */ 35263488Scokane /* The memory descriptor is described as the top 15 bits of the real 35363488Scokane address */ 35463488Scokane tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000; 35563488Scokane } 35663488Scokane else 35763488Scokane return 0; 35861911Scokane /* 35961911Scokane * The Alliance Pro Motion AT3D was not mentioned in the linux 36061911Scokane * driver as far as MTRR support goes, so I just won't put the 36161911Scokane * code in here for it. This is where it should go, though. 36261911Scokane */ 36361911Scokane 36461911Scokane /* Firstly, try to set write combining */ 36561911Scokane tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE; 36661911Scokane bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4); 36761911Scokane act = MEMRANGE_SET_UPDATE; 36861911Scokane retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 36961911Scokane 37061911Scokane if(retval == 0) { 37161931Scokane#ifdef DEBUG 37261911Scokane device_printf(dev, "MTRR Set Correctly for tdfx\n"); 37361911Scokane#endif 37461911Scokane } else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) || 37561911Scokane (pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) { 37661911Scokane /* if, for some reason we can't set the WRCOMB range with the V1/V2, we 37761911Scokane * can still possibly use the UNCACHEABLE region for it instead, and help 37861911Scokane * out in a small way */ 37961911Scokane tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE; 38061911Scokane /* This length of 1000h was taken from the linux device driver... */ 38161911Scokane tdfx_info->mrdesc.mr_len = 0x1000; 38261911Scokane 38361911Scokane /* 38461911Scokane * If, for some reason, we can't set the MTRR (N/A?) we may still continue 38561911Scokane */ 38661931Scokane#ifdef DEBUG 387142253Ssam device_printf(dev, "MTRR Set Type Uncacheable %x\n", 388142253Ssam (u_int32_t)tdfx_info->mrdesc.mr_base); 38961911Scokane#endif 39061911Scokane } 39161931Scokane#ifdef DEBUG 39261911Scokane else { 39361911Scokane device_printf(dev, "Couldn't Set MTRR\n"); 39461911Scokane return 0; 39561911Scokane } 39661911Scokane#endif 39761911Scokane return 0; 39861911Scokane} 39961911Scokane 40061911Scokanestatic int 401130585Sphktdfx_open(struct cdev *dev, int flags, int fmt, struct thread *td) 40261911Scokane{ 40361911Scokane /* 40461911Scokane * The open cdev method handles open(2) calls to /dev/3dfx[n] 40561911Scokane * We can pretty much allow any opening of the device. 40661911Scokane */ 40761911Scokane struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 40861911Scokane UNIT(minor(dev))); 40961911Scokane if(tdfx_info->busy != 0) return EBUSY; 41061931Scokane#ifdef DEBUG 41183366Sjulian printf("3dfx: Opened by #%d\n", td->td_proc->p_pid); 41261911Scokane#endif 41361911Scokane /* Set the driver as busy */ 41461911Scokane tdfx_info->busy++; 41561911Scokane return 0; 41661911Scokane} 41761911Scokane 41861911Scokanestatic int 419130585Sphktdfx_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 42061911Scokane{ 42161911Scokane /* 42261911Scokane * The close cdev method handles close(2) calls to /dev/3dfx[n] 42361911Scokane * We'll always want to close the device when it's called. 42461911Scokane */ 42561911Scokane struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 42661911Scokane UNIT(minor(dev))); 42761911Scokane if(tdfx_info->busy == 0) return EBADF; 42861911Scokane tdfx_info->busy = 0; 42961931Scokane#ifdef DEBUG 43083366Sjulian printf("Closed by #%d\n", td->td_proc->p_pid); 43161911Scokane#endif 43261911Scokane return 0; 43361911Scokane} 43461911Scokane 43561911Scokanestatic int 436130585Sphktdfx_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) 43761911Scokane{ 43861911Scokane /* 43961911Scokane * mmap(2) is called by a user process to request that an area of memory 44061911Scokane * associated with this device be mapped for the process to work with. Nprot 44161911Scokane * holds the protections requested, PROT_READ, PROT_WRITE, or both. 44261911Scokane */ 44366910Scokane 44466910Scokane /**** OLD GET CONFIG ****/ 44566910Scokane /* struct tdfx_softc* tdfx_info; */ 44661911Scokane 44761911Scokane /* Get the configuration for our card XXX*/ 44866910Scokane /*tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 44966910Scokane UNIT(minor(dev)));*/ 45066910Scokane /************************/ 45166910Scokane 45266910Scokane struct tdfx_softc* tdfx_info[2]; 45361911Scokane 45466910Scokane tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0); 45566910Scokane 45661911Scokane /* If, for some reason, its not configured, we bail out */ 45766910Scokane if(tdfx_info[0] == NULL) { 45861931Scokane#ifdef DEBUG 45961911Scokane printf("tdfx: tdfx_info (softc) is NULL\n"); 46061911Scokane#endif 46161911Scokane return -1; 46261911Scokane } 46366910Scokane 46461911Scokane /* We must stay within the bound of our address space */ 46566910Scokane if((offset & 0xff000000) == tdfx_info[0]->addr0) { 46661911Scokane offset &= 0xffffff; 467111462Smux *paddr = rman_get_start(tdfx_info[0]->memrange) + offset; 468111462Smux return 0; 46966910Scokane } 47066910Scokane 47166910Scokane if(tdfx_count > 1) { 47266910Scokane tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1); 47366910Scokane if((offset & 0xff000000) == tdfx_info[1]->addr0) { 47466910Scokane offset &= 0xffffff; 475111462Smux *paddr = rman_get_start(tdfx_info[1]->memrange) + 476111462Smux offset; 477111462Smux return 0; 47866910Scokane } 47966910Scokane } 48063488Scokane 48163488Scokane /* See if the Banshee/V3 LFB is being requested */ 48266910Scokane /*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) == 48364085Scokane tdfx_info->addr1) { 48463488Scokane offset &= 0xffffff; 48566910Scokane return atop(rman_get_start(tdfx_info[1]->memrange2) + offset); 48666910Scokane }*/ /* VoodooNG code */ 48763488Scokane 48866910Scokane /* The ret call */ 48961911Scokane /* atop -> address to page 49061911Scokane * rman_get_start, get the (struct resource*)->r_start member, 49161911Scokane * the mapping base address. 49261911Scokane */ 49366910Scokane return -1; 49461911Scokane} 49561911Scokane 49661911Scokanestatic int 49761911Scokanetdfx_query_boards(void) { 49861911Scokane /* 49961911Scokane * This returns the number of installed tdfx cards, we have been keeping 50061911Scokane * count, look at tdfx_attach 50161911Scokane */ 50261911Scokane return tdfx_count; 50361911Scokane} 50461911Scokane 50561911Scokanestatic int 50661911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod) 50761911Scokane{ 50861911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 50961911Scokane /* Various return values 8bit-32bit */ 51061911Scokane u_int8_t ret_byte; 51161911Scokane u_int16_t ret_word; 51261911Scokane u_int32_t ret_dword; 51361911Scokane struct tdfx_softc* tdfx_info = NULL; 51461911Scokane 51561911Scokane /* This one depend on the tdfx_* structs being properly initialized */ 51661911Scokane 51761911Scokane /*piod->device &= 0xf;*/ 51861911Scokane if((piod == NULL) ||(tdfx_count <= piod->device) || 51961911Scokane (piod->device < 0)) { 52061931Scokane#ifdef DEBUG 52161911Scokane printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n"); 52261911Scokane#endif 52361911Scokane return -EINVAL; 52461911Scokane } 52561911Scokane 52661911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 52761911Scokane piod->device); 52861911Scokane 52961911Scokane if(tdfx_info == NULL) return -ENXIO; 53061911Scokane 53161911Scokane /* We must restrict the size reads from the port, since to high or low of a 53261911Scokane * size witll result in wrong data being passed, and that's bad */ 53361911Scokane /* A few of these were pulled during the attach phase */ 53461911Scokane switch(piod->port) { 53561911Scokane case PCI_VENDOR_ID_FREEBSD: 53661911Scokane if(piod->size != 2) return -EINVAL; 53761911Scokane copyout(&tdfx_info->vendor, piod->value, piod->size); 53861911Scokane return 0; 53961911Scokane case PCI_DEVICE_ID_FREEBSD: 54061911Scokane if(piod->size != 2) return -EINVAL; 54161911Scokane copyout(&tdfx_info->type, piod->value, piod->size); 54261911Scokane return 0; 54361911Scokane case PCI_BASE_ADDRESS_0_FREEBSD: 54461911Scokane if(piod->size != 4) return -EINVAL; 54561911Scokane copyout(&tdfx_info->addr0, piod->value, piod->size); 54661911Scokane return 0; 54765146Scokane case PCI_BASE_ADDRESS_1_FREEBSD: 54865146Scokane if(piod->size != 4) return -EINVAL; 54965146Scokane copyout(&tdfx_info->addr1, piod->value, piod->size); 55065146Scokane return 0; 55165146Scokane case PCI_PRIBUS_FREEBSD: 55265146Scokane if(piod->size != 1) return -EINVAL; 55365146Scokane break; 55465146Scokane case PCI_IOBASE_0_FREEBSD: 55565146Scokane if(piod->size != 2) return -EINVAL; 55665146Scokane break; 55765146Scokane case PCI_IOLIMIT_0_FREEBSD: 55865146Scokane if(piod->size != 2) return -EINVAL; 55965146Scokane break; 56061911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 56161911Scokane if(piod->size != 4) return -EINVAL; 56261911Scokane break; 56361911Scokane case PCI_REVISION_ID_FREEBSD: 56461911Scokane if(piod->size != 1) return -EINVAL; 56561911Scokane break; 56661911Scokane case SST1_PCI_SPECIAL4_FREEBSD: 56761911Scokane if(piod->size != 4) return -EINVAL; 56861911Scokane break; 56961911Scokane default: 57061911Scokane return -EINVAL; 57161911Scokane } 57261911Scokane 57361911Scokane 57461911Scokane /* Read the value and return */ 57561911Scokane switch(piod->size) { 57661911Scokane case 1: 57761911Scokane ret_byte = pci_read_config(tdfx_info[piod->device].dev, 57861911Scokane piod->port, 1); 57961911Scokane copyout(&ret_byte, piod->value, 1); 58061911Scokane break; 58161911Scokane case 2: 58261911Scokane ret_word = pci_read_config(tdfx_info[piod->device].dev, 58361911Scokane piod->port, 2); 58461911Scokane copyout(&ret_word, piod->value, 2); 58561911Scokane break; 58661911Scokane case 4: 58761911Scokane ret_dword = pci_read_config(tdfx_info[piod->device].dev, 58861911Scokane piod->port, 4); 58961911Scokane copyout(&ret_dword, piod->value, 4); 59061911Scokane break; 59161911Scokane default: 59261911Scokane return -EINVAL; 59361911Scokane } 59461911Scokane return 0; 59561911Scokane} 59661911Scokane 59761911Scokanestatic int 59861911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod) 59961911Scokane{ 60061911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 60161911Scokane /* Return vals */ 60261911Scokane u_int8_t ret_byte; 60361911Scokane u_int16_t ret_word; 60461911Scokane u_int32_t ret_dword; 60561911Scokane 60661911Scokane /* Port vals, mask */ 60761911Scokane u_int32_t retval, preval, mask; 60861911Scokane struct tdfx_softc* tdfx_info = NULL; 60961911Scokane 61061911Scokane 61161911Scokane if((piod == NULL) || (piod->device >= (tdfx_count & 61261911Scokane 0xf))) { 61361931Scokane#ifdef DEBUG 61461911Scokane printf("tdfx: Bad struct or device in tdfx_query_update\n"); 61561911Scokane#endif 61661911Scokane return -EINVAL; 61761911Scokane } 61861911Scokane 61961911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 62061911Scokane piod->device); 62161911Scokane if(tdfx_info == NULL) return -ENXIO; 62261911Scokane /* Code below this line in the fuction was taken from the 62361911Scokane * Linux driver and converted for freebsd. */ 62461911Scokane 62561911Scokane /* Check the size for all the ports, to make sure stuff doesn't get messed up 62661911Scokane * by poorly written clients */ 62761911Scokane 62861911Scokane switch(piod->port) { 62961911Scokane case PCI_COMMAND_FREEBSD: 63061911Scokane if(piod->size != 2) return -EINVAL; 63161911Scokane break; 63261911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 63361911Scokane if(piod->size != 4) return -EINVAL; 63461911Scokane break; 63561911Scokane case SST1_PCI_SPECIAL2_FREEBSD: 63661911Scokane if(piod->size != 4) return -EINVAL; 63761911Scokane break; 63861911Scokane case SST1_PCI_SPECIAL3_FREEBSD: 63961911Scokane if(piod->size != 4) return -EINVAL; 64061911Scokane break; 64161911Scokane case SST1_PCI_SPECIAL4_FREEBSD: 64261911Scokane if(piod->size != 4) return -EINVAL; 64361911Scokane break; 64461911Scokane default: 64561911Scokane return -EINVAL; 64661911Scokane } 64761911Scokane /* Read the current value */ 64861911Scokane retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4); 64961911Scokane 65061911Scokane /* These set up a mask to use, since apparently they wanted to write 4 bytes 65161911Scokane * at once to the ports */ 65261911Scokane switch (piod->size) { 65361911Scokane case 1: 65461911Scokane copyin(piod->value, &ret_byte, 1); 65561911Scokane preval = ret_byte << (8 * (piod->port & 0x3)); 65661911Scokane mask = 0xff << (8 * (piod->port & 0x3)); 65761911Scokane break; 65861911Scokane case 2: 65961911Scokane copyin(piod->value, &ret_word, 2); 66061911Scokane preval = ret_word << (8 * (piod->port & 0x3)); 66161911Scokane mask = 0xffff << (8 * (piod->port & 0x3)); 66261911Scokane break; 66361911Scokane case 4: 66461911Scokane copyin(piod->value, &ret_dword, 4); 66561911Scokane preval = ret_dword; 66661911Scokane mask = ~0; 66761911Scokane break; 66861911Scokane default: 66961911Scokane return -EINVAL; 67061911Scokane } 67161911Scokane /* Finally, combine the values and write it to the port */ 67261911Scokane retval = (retval & ~mask) | preval; 67361911Scokane pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4); 67461911Scokane 67561911Scokane return 0; 67661911Scokane} 67761911Scokane 67863488Scokane/* For both of these, I added a variable named workport of type u_int so 67963488Scokane * that I could eliminate the warning about my data type size. The 68063488Scokane * applications expect the port to be of type short, so I needed to change 68163488Scokane * this within the function */ 68261911Scokanestatic int 68361911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod) 68461911Scokane{ 68561911Scokane /* Return val */ 68661911Scokane u_int8_t ret_byte; 68763488Scokane u_int workport; 68865146Scokane struct tdfx_softc *tdfx_info = 68965146Scokane (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 69065146Scokane 69161911Scokane /* Restricts the access of ports other than those we use */ 69265146Scokane if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) || 69365146Scokane (piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) && 69465146Scokane (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 69561911Scokane return -EPERM; 69661911Scokane 69761911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 69861911Scokane if(piod->size != 1) { 69961911Scokane return -EINVAL; 70061911Scokane } 70161911Scokane 70261911Scokane /* Write the data to the intended port */ 70363488Scokane workport = piod->port; 70463488Scokane ret_byte = inb(workport); 70561911Scokane copyout(&ret_byte, piod->value, sizeof(u_int8_t)); 70661911Scokane return 0; 70761911Scokane} 70861911Scokane 70961911Scokanestatic int 71061911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod) 71161911Scokane{ 71261911Scokane /* return val */ 71361911Scokane u_int8_t ret_byte; 71463488Scokane u_int workport; 71565146Scokane struct tdfx_softc *tdfx_info = (struct 71665146Scokane tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 71761911Scokane /* Replace old switch w/ massive if(...) */ 71861911Scokane /* Restricts the access of ports other than those we use */ 71965146Scokane if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) && 72065146Scokane (piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ && 72165146Scokane (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 72261911Scokane return -EPERM; 72361911Scokane 72461911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 72561911Scokane if(piod->size != 1) { 72661911Scokane return -EINVAL; 72761911Scokane } 72861911Scokane 72961911Scokane /* Write the data to the intended port */ 73061911Scokane copyin(piod->value, &ret_byte, sizeof(u_int8_t)); 73163488Scokane workport = piod->port; 73263488Scokane outb(workport, ret_byte); 73361911Scokane return 0; 73461911Scokane} 73561911Scokane 73661911Scokanestatic int 73761911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod) 73861911Scokane{ 73961911Scokane /* There are three sub-commands to the query 0x33 */ 74061911Scokane switch(_IOC_NR(cmd)) { 74161911Scokane case 2: 74261911Scokane return tdfx_query_boards(); 74361911Scokane break; 74461911Scokane case 3: 74561911Scokane return tdfx_query_fetch(cmd, piod); 74661911Scokane break; 74761911Scokane case 4: 74861911Scokane return tdfx_query_update(cmd, piod); 74961911Scokane break; 75061911Scokane default: 75161911Scokane /* In case we are thrown a bogus sub-command! */ 75261931Scokane#ifdef DEBUG 75361911Scokane printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd)); 75461911Scokane#endif 75561911Scokane return -EINVAL; 756115494Sphk } 75761911Scokane} 75861911Scokane 75961911Scokanestatic int 76061911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod) 76161911Scokane{ 76261911Scokane /* Two types of PIO, INPUT and OUTPUT, as the name suggests */ 76361911Scokane switch(_IOC_DIR(cmd)) { 76461911Scokane case IOCV_OUT: 76561911Scokane return tdfx_do_pio_rd(piod); 76661911Scokane break; 76761911Scokane case IOCV_IN: 76861911Scokane return tdfx_do_pio_wt(piod); 76961911Scokane break; 77061911Scokane default: 77161911Scokane return -EINVAL; 772115494Sphk } 77361911Scokane} 77461911Scokane 77561911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO, 77661911Scokane * normally, you would read in the data pointed to by data, then write your 77761911Scokane * output to it. The ioctl *should* normally return zero if everything is 77861911Scokane * alright, but 3dfx didn't make it that way... 77961911Scokane * 78061911Scokane * For all of the ioctl code, in the event of a real error, 78161911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this 78261911Scokane * is that the ioctls actually RET information back to the program 78361911Scokane * sometimes, rather than filling it in the passed structure. We 78461911Scokane * want to distinguish errors from useful data, and maintain compatibility. 78561911Scokane * 78661911Scokane * There is this portion of the proc struct called p_retval[], we can store a 78783366Sjulian * return value in td->td_retval[0] and place the return value if it is positive 78861911Scokane * in there, then we can return 0 (good). If the return value is negative, we 78961911Scokane * can return -retval and the error should be properly handled. 79061911Scokane */ 79161911Scokanestatic int 792130585Sphktdfx_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 79361911Scokane{ 79461911Scokane int retval = 0; 79561911Scokane struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data; 79661931Scokane#ifdef DEBUG 797106578Sjhb printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd, 798106578Sjhb piod); 79961911Scokane#endif 80061911Scokane switch(_IOC_TYPE(cmd)) { 80161911Scokane /* Return the real error if negative, or simply stick the valid return 80283366Sjulian * in td->td_retval */ 80361911Scokane case 0x33: 80461911Scokane /* The '3'(0x33) type IOCTL is for querying the installed cards */ 80583366Sjulian if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval; 80661911Scokane else return -retval; 80761911Scokane break; 80861911Scokane case 0: 80961911Scokane /* The 0 type IOCTL is for programmed I/O methods */ 81083366Sjulian if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval; 81161911Scokane else return -retval; 81261911Scokane break; 81361911Scokane default: 81461911Scokane /* Technically, we won't reach this from linux emu, but when glide 81561911Scokane * finally gets ported, watch out! */ 81661931Scokane#ifdef DEBUG 81783366Sjulian printf("Bad IOCTL from #%d\n", td->td_proc->p_pid); 81861911Scokane#endif 81961911Scokane return ENXIO; 82061911Scokane } 82161911Scokane 82261911Scokane return 0; 82361911Scokane} 82461911Scokane 82561931Scokane#ifdef TDFX_LINUX 82661931Scokane/* 82761931Scokane * Linux emulation IOCTL for /dev/tdfx 82861931Scokane */ 82961931Scokanestatic int 83083366Sjulianlinux_ioctl_tdfx(struct thread *td, struct linux_ioctl_args* args) 83161931Scokane{ 83261931Scokane int error = 0; 83361931Scokane u_long cmd = args->cmd & 0xffff; 83461911Scokane 83561931Scokane /* The structure passed to ioctl has two shorts, one int 83661931Scokane and one void*. */ 83761931Scokane char d_pio[2*sizeof(short) + sizeof(int) + sizeof(void*)]; 83861931Scokane 83989306Salfred struct file *fp; 84061931Scokane 84189319Salfred if ((error = fget(td, args->fd, &fp)) != 0) 84289319Salfred return (error); 84361931Scokane /* We simply copy the data and send it right to ioctl */ 84461931Scokane copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio)); 845102003Srwatson error = fo_ioctl(fp, cmd, (caddr_t)&d_pio, td->td_ucred, td); 84689306Salfred fdrop(fp, td); 84761931Scokane return error; 84861931Scokane} 84961931Scokane#endif /* TDFX_LINUX */ 85061931Scokane 85161931Scokane 85261911Scokane/* This is the device driver struct. This is sent to the driver subsystem to 85361911Scokane * register the method structure and the info strcut space for this particular 85461911Scokane * instance of the driver. 85561911Scokane */ 85661911Scokanestatic driver_t tdfx_driver = { 85761911Scokane "tdfx", 85861911Scokane tdfx_methods, 85961911Scokane sizeof(struct tdfx_softc), 86061911Scokane}; 86161911Scokane 86261911Scokane/* Tell Mr. Kernel about us! */ 86361911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0); 864