tdfx_pci.c revision 62028
161931Scokane/* 261931Scokane * Copyright (c) 2000 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 62028 2000-06-24 06:20:55Z cokane $ 3261931Scokane */ 3361931Scokane 3461911Scokane/* 3dfx driver for FreeBSD 4.x - Finished 11 May 2000, 12:25AM ET 3561911Scokane * 3661931Scokane * Copyright (C) 2000, 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/* 4261911Scokane * put this here, so as to bail out immediately if we have no PCI BUS installed 4361911Scokane */ 4461911Scokane#include "pci.h" 4561911Scokane#if NPCI > 0 4661911Scokane 4761911Scokane#include <sys/param.h> 4861911Scokane 4961911Scokane#include <sys/bus_private.h> 5061911Scokane#include <sys/bus.h> 5161911Scokane#include <sys/cdefs.h> 5261911Scokane#include <sys/conf.h> 5361911Scokane#include <sys/fcntl.h> 5461911Scokane#include <sys/file.h> 5561911Scokane#include <sys/filedesc.h> 5661911Scokane#include <sys/filio.h> 5761911Scokane#include <sys/ioccom.h> 5861911Scokane#include <sys/kernel.h> 5961911Scokane#include <sys/malloc.h> 6061911Scokane#include <sys/mman.h> 6161911Scokane#include <sys/signalvar.h> 6261911Scokane#include <sys/systm.h> 6361911Scokane#include <sys/uio.h> 6461911Scokane 6561911Scokane#include <pci/pcivar.h> 6661911Scokane#include <pci/pcireg.h> 6761911Scokane 6861911Scokane#include <vm/vm.h> 6961911Scokane#include <vm/vm_kern.h> 7061911Scokane#include <vm/pmap.h> 7161911Scokane#include <vm/vm_extern.h> 7261911Scokane 7361911Scokane/* rman.h depends on machine/bus.h */ 7461911Scokane#include <machine/resource.h> 7561911Scokane#include <machine/bus.h> 7661911Scokane#include <sys/rman.h> 7762028Scokane 7862028Scokane/* This must come first */ 7962028Scokane#include "opt_tdfx.h" 8061931Scokane#ifdef TDFX_LINUX 8161931Scokane#include <dev/tdfx/tdfx_linux.h> 8261931Scokane#endif 8361911Scokane 8461911Scokane#include <dev/tdfx/tdfx_io.h> 8561911Scokane#include <dev/tdfx/tdfx_vars.h> 8661911Scokane#include <dev/tdfx/tdfx_pci.h> 8761911Scokane 8861911Scokane 8961911Scokanestatic devclass_t tdfx_devclass; 9061911Scokane 9161911Scokane 9261911Scokanestatic int tdfx_count = 0; 9361911Scokane 9461911Scokane 9561911Scokane/* Set up the boot probe/attach routines */ 9661911Scokanestatic device_method_t tdfx_methods[] = { 9761911Scokane DEVMETHOD(device_probe, tdfx_probe), 9861911Scokane DEVMETHOD(device_attach, tdfx_attach), 9961911Scokane DEVMETHOD(device_detach, tdfx_detach), 10061911Scokane DEVMETHOD(device_shutdown, tdfx_shutdown), 10161911Scokane { 0, 0 } 10261911Scokane}; 10361911Scokane 10461911ScokaneMALLOC_DEFINE(M_TDFX,"TDFX Driver","3DFX Graphics[/2D]/3D Accelerator(s)"); 10561911Scokane 10661931Scokane#ifdef TDFX_LINUX 10761989ScokaneMODULE_DEPEND(tdfx, linux, 1, 1, 1); 10861931ScokaneLINUX_IOCTL_SET(tdfx, LINUX_IOCTL_TDFX_MIN, LINUX_IOCTL_TDFX_MAX); 10961931Scokane#endif 11061931Scokane 11161911Scokane/* Char. Dev. file operations structure */ 11261911Scokanestatic struct cdevsw tdfx_cdev = { 11361911Scokane tdfx_open, /* open */ 11461911Scokane tdfx_close, /* close */ 11561911Scokane noread, /* read */ 11661911Scokane nowrite, /* write */ 11761911Scokane tdfx_ioctl, /* ioctl */ 11861911Scokane nopoll, /* poll */ 11961911Scokane tdfx_mmap, /* mmap */ 12061911Scokane nostrategy, /* strategy */ 12161911Scokane "tdfx", /* dev name */ 12261911Scokane CDEV_MAJOR, /* char major */ 12361911Scokane nodump, /* dump */ 12461911Scokane nopsize, /* size */ 12561911Scokane 0, /* flags (no set flags) */ 12661911Scokane -1 /* bmaj (no block dev) */ 12761911Scokane}; 12861911Scokane 12961911Scokanestatic int 13061911Scokanetdfx_probe(device_t dev) 13161911Scokane{ 13261911Scokane /* 13361911Scokane * probe routine called on kernel boot to register supported devices. We get 13461911Scokane * a device structure to work with, and we can test the VENDOR/DEVICE IDs to 13561911Scokane * see if this PCI device is one that we support. Return 0 if yes, ENXIO if 13661911Scokane * not. 13761911Scokane */ 13861911Scokane switch(pci_get_devid(dev)) { 13961911Scokane case PCI_DEVICE_ALLIANCE_AT3D: 14061911Scokane device_set_desc(dev, "ProMotion At3D 3D Accelerator"); 14161911Scokane return 0; 14261911Scokane case PCI_DEVICE_3DFX_VOODOO2: 14361911Scokane device_set_desc(dev, "3DFX Voodoo II 3D Accelerator"); 14461911Scokane return 0; 14561911Scokane case PCI_DEVICE_3DFX_BANSHEE: 14661911Scokane device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator"); 14761911Scokane return 0; 14861911Scokane case PCI_DEVICE_3DFX_VOODOO3: 14961911Scokane device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator"); 15061911Scokane return 0; 15161911Scokane case PCI_DEVICE_3DFX_VOODOO1: 15261911Scokane device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator"); 15361911Scokane return 0;; 15461911Scokane }; 15561911Scokane 15661911Scokane return ENXIO; 15761911Scokane} 15861911Scokane 15961911Scokanestatic int 16061911Scokanetdfx_attach(device_t dev) { 16161911Scokane /* 16261911Scokane * The attach routine is called after the probe routine successfully says it 16361911Scokane * supports a given card. We now proceed to initialize this card for use with 16461911Scokane * the system. I want to map the device memory for userland allocation and 16561911Scokane * fill an information structure with information on this card. I'd also like 16661911Scokane * to set Write Combining with the MTRR code so that we can hopefully speed 16761911Scokane * up memory writes. The last thing is to register the character device 16861911Scokane * interface to the card, so we can open it from /dev/3dfxN, where N is a 16961911Scokane * small, whole number. 17061911Scokane */ 17161911Scokane 17261911Scokane struct tdfx_softc *tdfx_info; 17361911Scokane u_long val; 17461911Scokane /* rid value tells bus_alloc_resource where to find the addresses of ports or 17561911Scokane * of memory ranges in the PCI config space*/ 17661911Scokane int rid = PCIR_MAPS; 17761911Scokane 17861911Scokane /* Increment the card counter (for the ioctl code) */ 17961911Scokane tdfx_count++; 18061911Scokane 18161911Scokane /* Enable MemMap on Voodoo */ 18261911Scokane val = pci_read_config(dev, PCIR_COMMAND, 2); 18361911Scokane val |= (PCIM_CMD_MEMEN); 18461911Scokane pci_write_config(dev, PCIR_COMMAND, val, 2); 18561911Scokane val = pci_read_config(dev, PCIR_COMMAND, 2); 18661911Scokane 18761911Scokane /* Fill the soft config struct with info about this device*/ 18861911Scokane tdfx_info = device_get_softc(dev); 18961911Scokane tdfx_info->dev = dev; 19061911Scokane tdfx_info->vendor = pci_get_vendor(dev); 19161911Scokane tdfx_info->type = pci_get_devid(dev) >> 16; 19261911Scokane tdfx_info->bus = pci_get_bus(dev); 19361911Scokane tdfx_info->dv = pci_get_slot(dev); 19461911Scokane tdfx_info->curFile = NULL; 19561911Scokane 19661911Scokane /* 19761911Scokane * Get the Memory Location from the PCI Config, mask out lower word, since 19861911Scokane * the config space register is only one word long (this is nicer than a 19961911Scokane * bitshift). 20061911Scokane */ 20161911Scokane tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000); 20261931Scokane#ifdef DEBUG 20361911Scokane device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0); 20461911Scokane#endif 20561911Scokane 20661911Scokane /* Notify the VM that we will be mapping some memory later */ 20761911Scokane tdfx_info->memrange = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1, 20861967Scokane RF_ACTIVE | RF_SHAREABLE); 20961911Scokane if(tdfx_info->memrange == NULL) { 21061931Scokane#ifdef DEBUG 21161911Scokane device_printf(dev, "Error mapping mem, won't be able to use mmap()\n"); 21261911Scokane#endif 21361911Scokane tdfx_info->memrid = 0; 21461911Scokane } 21561911Scokane else { 21661911Scokane tdfx_info->memrid = rid; 21761931Scokane#ifdef DEBUG 21861911Scokane device_printf(dev, "Mapped to: 0x%x\n", 21961911Scokane (unsigned int)rman_get_start(tdfx_info->memrange)); 22061911Scokane#endif 22161911Scokane } 22261911Scokane 22361911Scokane /* 22461911Scokane * Set Writecombining, or at least Uncacheable for the memory region, if we 22561911Scokane * are able to 22661911Scokane */ 22761911Scokane 22861911Scokane if(tdfx_setmtrr(dev) != 0) { 22961931Scokane#ifdef DEBUG 23061911Scokane device_printf(dev, "Some weird error setting MTRRs"); 23161911Scokane#endif 23261911Scokane return -1; 23361911Scokane } 23461911Scokane 23561911Scokane /* 23661911Scokane * make_dev registers the cdev to access the 3dfx card from /dev 23761911Scokane * use hex here for the dev num, simply to provide better support if > 10 23861911Scokane * voodoo cards, for the mad. The user must set the link, or use MAKEDEV. 23961911Scokane * Why would we want that many voodoo cards anyhow? 24061911Scokane */ 24161989Scokane tdfx_info->devt = make_dev(&tdfx_cdev, dev->unit, 0, 0, 02660, 24261989Scokane "3dfx%x", dev->unit); 24361911Scokane 24461911Scokane return 0; 24561911Scokane} 24661911Scokane 24761911Scokanestatic int 24861911Scokanetdfx_detach(device_t dev) { 24961911Scokane struct tdfx_softc* tdfx_info; 25061911Scokane int retval; 25161911Scokane tdfx_info = device_get_softc(dev); 25261911Scokane 25361911Scokane /* Delete allocated resource, of course */ 25461911Scokane bus_release_resource(dev, SYS_RES_MEMORY, PCI_MAP_REG_START, 25561911Scokane tdfx_info->memrange); 25661911Scokane 25761911Scokane /* Though it is safe to leave the WRCOMB support since the 25861911Scokane mem driver checks for it, we should remove it in order 25961911Scokane to free an MTRR for another device */ 26061911Scokane retval = tdfx_clrmtrr(dev); 26161931Scokane#ifdef DEBUG 26261911Scokane if(retval != 0) 26361911Scokane printf("tdfx: For some reason, I couldn't clear the mtrr\n"); 26461911Scokane#endif 26561989Scokane /* Remove device entry when it can no longer be accessed */ 26661989Scokane destroy_dev(tdfx_info->devt); 26761911Scokane return(0); 26861911Scokane} 26961911Scokane 27061911Scokanestatic int 27161911Scokanetdfx_shutdown(device_t dev) { 27261931Scokane#ifdef DEBUG 27361911Scokane device_printf(dev, "tdfx: Device Shutdown\n"); 27461911Scokane#endif 27561911Scokane return 0; 27661911Scokane} 27761911Scokane 27861911Scokanestatic int 27961911Scokanetdfx_clrmtrr(device_t dev) { 28061911Scokane /* This function removes the MTRR set by the attach call, so it can be used 28161911Scokane * in the future by other drivers. 28261911Scokane */ 28361911Scokane int retval, act; 28461911Scokane struct tdfx_softc *tdfx_info = device_get_softc(dev); 28561911Scokane 28661911Scokane act = MEMRANGE_SET_REMOVE; 28761911Scokane retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 28861911Scokane return retval; 28961911Scokane} 29061911Scokane 29161911Scokanestatic int 29261911Scokanetdfx_setmtrr(device_t dev) { 29361911Scokane /* 29461911Scokane * This is the MTRR setting function for the 3dfx card. It is called from 29561911Scokane * tdfx_attach. If we can't set the MTRR properly, it's not the end of the 29661911Scokane * world. We can still continue, just with slightly (very slightly) degraded 29761911Scokane * performance. 29861911Scokane */ 29961911Scokane int retval = 0, act; 30061911Scokane struct tdfx_softc *tdfx_info = device_get_softc(dev); 30161911Scokane /* The memory descriptor is described as the top 15 bits of the real 30261911Scokane address */ 30361911Scokane tdfx_info->mrdesc.mr_base = pci_read_config(dev, 0x10, 4) & 0xfffe0000; 30461911Scokane 30561911Scokane /* The older Voodoo cards have a shorter memrange than the newer ones */ 30661911Scokane if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) == 30761911Scokane PCI_DEVICE_3DFX_VOODOO2)) 30861911Scokane tdfx_info->mrdesc.mr_len = 0x400000; 30961911Scokane else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) || 31061911Scokane (pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) 31161911Scokane tdfx_info->mrdesc.mr_len = 0x1000000; 31261911Scokane 31361911Scokane else return 0; 31461911Scokane /* 31561911Scokane * The Alliance Pro Motion AT3D was not mentioned in the linux 31661911Scokane * driver as far as MTRR support goes, so I just won't put the 31761911Scokane * code in here for it. This is where it should go, though. 31861911Scokane */ 31961911Scokane 32061911Scokane /* Firstly, try to set write combining */ 32161911Scokane tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE; 32261911Scokane bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4); 32361911Scokane act = MEMRANGE_SET_UPDATE; 32461911Scokane retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 32561911Scokane 32661911Scokane if(retval == 0) { 32761931Scokane#ifdef DEBUG 32861911Scokane device_printf(dev, "MTRR Set Correctly for tdfx\n"); 32961911Scokane#endif 33061911Scokane } else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) || 33161911Scokane (pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) { 33261911Scokane /* if, for some reason we can't set the WRCOMB range with the V1/V2, we 33361911Scokane * can still possibly use the UNCACHEABLE region for it instead, and help 33461911Scokane * out in a small way */ 33561911Scokane tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE; 33661911Scokane /* This length of 1000h was taken from the linux device driver... */ 33761911Scokane tdfx_info->mrdesc.mr_len = 0x1000; 33861911Scokane 33961911Scokane /* 34061911Scokane * If, for some reason, we can't set the MTRR (N/A?) we may still continue 34161911Scokane */ 34261931Scokane#ifdef DEBUG 34361911Scokane if(retval == 0) { 34461911Scokane device_printf(dev, "MTRR Set Type Uncacheable 34561911Scokane %x\n", (u_int32_t)tdfx_info->mrdesc.mr_base); 34661911Scokane } else { 34761911Scokane device_printf(dev, "Couldn't Set MTRR\n"); 34861911Scokane } 34961911Scokane#endif 35061911Scokane } 35161931Scokane#ifdef DEBUG 35261911Scokane else { 35361911Scokane device_printf(dev, "Couldn't Set MTRR\n"); 35461911Scokane return 0; 35561911Scokane } 35661911Scokane#endif 35761911Scokane return 0; 35861911Scokane} 35961911Scokane 36061911Scokanestatic int 36161911Scokanetdfx_open(dev_t dev, int flags, int fmt, struct proc *p) 36261911Scokane{ 36361911Scokane /* 36461911Scokane * The open cdev method handles open(2) calls to /dev/3dfx[n] 36561911Scokane * We can pretty much allow any opening of the device. 36661911Scokane */ 36761911Scokane struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 36861911Scokane UNIT(minor(dev))); 36961911Scokane if(tdfx_info->busy != 0) return EBUSY; 37061931Scokane#ifdef DEBUG 37161911Scokane printf("3dfx: Opened by #%d\n", p->p_pid); 37261911Scokane#endif 37361911Scokane /* Set the driver as busy */ 37461911Scokane tdfx_info->busy++; 37561911Scokane return 0; 37661911Scokane} 37761911Scokane 37861911Scokanestatic int 37961911Scokanetdfx_close(dev_t dev, int fflag, int devtype, struct proc* p) 38061911Scokane{ 38161911Scokane /* 38261911Scokane * The close cdev method handles close(2) calls to /dev/3dfx[n] 38361911Scokane * We'll always want to close the device when it's called. 38461911Scokane */ 38561911Scokane struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 38661911Scokane UNIT(minor(dev))); 38761911Scokane if(tdfx_info->busy == 0) return EBADF; 38861911Scokane tdfx_info->busy = 0; 38961931Scokane#ifdef DEBUG 39061911Scokane printf("Closed by #%d\n", p->p_pid); 39161911Scokane#endif 39261911Scokane return 0; 39361911Scokane} 39461911Scokane 39561911Scokanestatic int 39661911Scokanetdfx_mmap(dev_t dev, vm_offset_t offset, int nprot) 39761911Scokane{ 39861911Scokane /* 39961911Scokane * mmap(2) is called by a user process to request that an area of memory 40061911Scokane * associated with this device be mapped for the process to work with. Nprot 40161911Scokane * holds the protections requested, PROT_READ, PROT_WRITE, or both. 40261911Scokane */ 40361911Scokane struct tdfx_softc* tdfx_info; 40461911Scokane 40561911Scokane /* Get the configuration for our card XXX*/ 40661911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 40761911Scokane UNIT(minor(dev))); 40861911Scokane 40961911Scokane /* If, for some reason, its not configured, we bail out */ 41061911Scokane if(tdfx_info == NULL) { 41161931Scokane#ifdef DEBUG 41261911Scokane printf("tdfx: tdfx_info (softc) is NULL\n"); 41361911Scokane#endif 41461911Scokane return -1; 41561911Scokane } 41661911Scokane 41761911Scokane /* We must stay within the bound of our address space */ 41861911Scokane if((offset & 0xff000000) == tdfx_info->addr0) 41961911Scokane offset &= 0xffffff; 42061911Scokane if((offset >= 0x1000000) || (offset < 0)) { 42161931Scokane#ifdef DEBUG 42261911Scokane printf("tdfx: offset %x out of range\n", offset); 42361911Scokane#endif 42461911Scokane return -1; 42561911Scokane } 42661911Scokane 42761911Scokane /* atop -> address to page 42861911Scokane * rman_get_start, get the (struct resource*)->r_start member, 42961911Scokane * the mapping base address. 43061911Scokane */ 43161911Scokane return atop(rman_get_start(tdfx_info->memrange) + offset); 43261911Scokane} 43361911Scokane 43461911Scokanestatic int 43561911Scokanetdfx_query_boards(void) { 43661911Scokane /* 43761911Scokane * This returns the number of installed tdfx cards, we have been keeping 43861911Scokane * count, look at tdfx_attach 43961911Scokane */ 44061911Scokane return tdfx_count; 44161911Scokane} 44261911Scokane 44361911Scokanestatic int 44461911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod) 44561911Scokane{ 44661911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 44761911Scokane /* Various return values 8bit-32bit */ 44861911Scokane u_int8_t ret_byte; 44961911Scokane u_int16_t ret_word; 45061911Scokane u_int32_t ret_dword; 45161911Scokane struct tdfx_softc* tdfx_info = NULL; 45261911Scokane 45361911Scokane /* This one depend on the tdfx_* structs being properly initialized */ 45461911Scokane 45561911Scokane /*piod->device &= 0xf;*/ 45661911Scokane if((piod == NULL) ||(tdfx_count <= piod->device) || 45761911Scokane (piod->device < 0)) { 45861931Scokane#ifdef DEBUG 45961911Scokane printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n"); 46061911Scokane#endif 46161911Scokane return -EINVAL; 46261911Scokane } 46361911Scokane 46461911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 46561911Scokane piod->device); 46661911Scokane 46761911Scokane if(tdfx_info == NULL) return -ENXIO; 46861911Scokane 46961911Scokane /* We must restrict the size reads from the port, since to high or low of a 47061911Scokane * size witll result in wrong data being passed, and that's bad */ 47161911Scokane /* A few of these were pulled during the attach phase */ 47261911Scokane switch(piod->port) { 47361911Scokane case PCI_VENDOR_ID_FREEBSD: 47461911Scokane if(piod->size != 2) return -EINVAL; 47561911Scokane copyout(&tdfx_info->vendor, piod->value, piod->size); 47661911Scokane return 0; 47761911Scokane case PCI_DEVICE_ID_FREEBSD: 47861911Scokane if(piod->size != 2) return -EINVAL; 47961911Scokane copyout(&tdfx_info->type, piod->value, piod->size); 48061911Scokane return 0; 48161911Scokane case PCI_BASE_ADDRESS_0_FREEBSD: 48261911Scokane if(piod->size != 4) return -EINVAL; 48361911Scokane copyout(&tdfx_info->addr0, piod->value, piod->size); 48461911Scokane return 0; 48561911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 48661911Scokane if(piod->size != 4) return -EINVAL; 48761911Scokane break; 48861911Scokane case PCI_REVISION_ID_FREEBSD: 48961911Scokane if(piod->size != 1) return -EINVAL; 49061911Scokane break; 49161911Scokane case SST1_PCI_SPECIAL4_FREEBSD: 49261911Scokane if(piod->size != 4) return -EINVAL; 49361911Scokane break; 49461911Scokane default: 49561911Scokane return -EINVAL; 49661911Scokane } 49761911Scokane 49861911Scokane 49961911Scokane /* Read the value and return */ 50061911Scokane switch(piod->size) { 50161911Scokane case 1: 50261911Scokane ret_byte = pci_read_config(tdfx_info[piod->device].dev, 50361911Scokane piod->port, 1); 50461911Scokane copyout(&ret_byte, piod->value, 1); 50561911Scokane break; 50661911Scokane case 2: 50761911Scokane ret_word = pci_read_config(tdfx_info[piod->device].dev, 50861911Scokane piod->port, 2); 50961911Scokane copyout(&ret_word, piod->value, 2); 51061911Scokane break; 51161911Scokane case 4: 51261911Scokane ret_dword = pci_read_config(tdfx_info[piod->device].dev, 51361911Scokane piod->port, 4); 51461911Scokane copyout(&ret_dword, piod->value, 4); 51561911Scokane break; 51661911Scokane default: 51761911Scokane return -EINVAL; 51861911Scokane } 51961911Scokane return 0; 52061911Scokane} 52161911Scokane 52261911Scokanestatic int 52361911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod) 52461911Scokane{ 52561911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 52661911Scokane /* Return vals */ 52761911Scokane u_int8_t ret_byte; 52861911Scokane u_int16_t ret_word; 52961911Scokane u_int32_t ret_dword; 53061911Scokane 53161911Scokane /* Port vals, mask */ 53261911Scokane u_int32_t retval, preval, mask; 53361911Scokane struct tdfx_softc* tdfx_info = NULL; 53461911Scokane 53561911Scokane 53661911Scokane if((piod == NULL) || (piod->device >= (tdfx_count & 53761911Scokane 0xf))) { 53861931Scokane#ifdef DEBUG 53961911Scokane printf("tdfx: Bad struct or device in tdfx_query_update\n"); 54061911Scokane#endif 54161911Scokane return -EINVAL; 54261911Scokane } 54361911Scokane 54461911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 54561911Scokane piod->device); 54661911Scokane if(tdfx_info == NULL) return -ENXIO; 54761911Scokane /* Code below this line in the fuction was taken from the 54861911Scokane * Linux driver and converted for freebsd. */ 54961911Scokane 55061911Scokane /* Check the size for all the ports, to make sure stuff doesn't get messed up 55161911Scokane * by poorly written clients */ 55261911Scokane 55361911Scokane switch(piod->port) { 55461911Scokane case PCI_COMMAND_FREEBSD: 55561911Scokane if(piod->size != 2) return -EINVAL; 55661911Scokane break; 55761911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 55861911Scokane if(piod->size != 4) return -EINVAL; 55961911Scokane break; 56061911Scokane case SST1_PCI_SPECIAL2_FREEBSD: 56161911Scokane if(piod->size != 4) return -EINVAL; 56261911Scokane break; 56361911Scokane case SST1_PCI_SPECIAL3_FREEBSD: 56461911Scokane if(piod->size != 4) 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 /* Read the current value */ 57361911Scokane retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4); 57461911Scokane 57561911Scokane /* These set up a mask to use, since apparently they wanted to write 4 bytes 57661911Scokane * at once to the ports */ 57761911Scokane switch (piod->size) { 57861911Scokane case 1: 57961911Scokane copyin(piod->value, &ret_byte, 1); 58061911Scokane preval = ret_byte << (8 * (piod->port & 0x3)); 58161911Scokane mask = 0xff << (8 * (piod->port & 0x3)); 58261911Scokane break; 58361911Scokane case 2: 58461911Scokane copyin(piod->value, &ret_word, 2); 58561911Scokane preval = ret_word << (8 * (piod->port & 0x3)); 58661911Scokane mask = 0xffff << (8 * (piod->port & 0x3)); 58761911Scokane break; 58861911Scokane case 4: 58961911Scokane copyin(piod->value, &ret_dword, 4); 59061911Scokane preval = ret_dword; 59161911Scokane mask = ~0; 59261911Scokane break; 59361911Scokane default: 59461911Scokane return -EINVAL; 59561911Scokane } 59661911Scokane /* Finally, combine the values and write it to the port */ 59761911Scokane retval = (retval & ~mask) | preval; 59861911Scokane pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4); 59961911Scokane 60061911Scokane return 0; 60161911Scokane} 60261911Scokane 60361911Scokanestatic int 60461911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod) 60561911Scokane{ 60661911Scokane /* Return val */ 60761911Scokane u_int8_t ret_byte; 60861911Scokane 60961911Scokane /* Restricts the access of ports other than those we use */ 61061911Scokane if((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) || 61161911Scokane (piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) 61261911Scokane return -EPERM; 61361911Scokane 61461911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 61561911Scokane if(piod->size != 1) { 61661911Scokane return -EINVAL; 61761911Scokane } 61861911Scokane 61961911Scokane /* Write the data to the intended port */ 62061911Scokane ret_byte = inb(piod->port); 62161911Scokane copyout(&ret_byte, piod->value, sizeof(u_int8_t)); 62261911Scokane return 0; 62361911Scokane} 62461911Scokane 62561911Scokanestatic int 62661911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod) 62761911Scokane{ 62861911Scokane /* return val */ 62961911Scokane u_int8_t ret_byte; 63061911Scokane 63161911Scokane /* Replace old switch w/ massive if(...) */ 63261911Scokane /* Restricts the access of ports other than those we use */ 63361911Scokane if((piod->port != SC_INDEX) && (piod->port != SC_DATA) && 63461911Scokane (piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ 63561911Scokane return -EPERM; 63661911Scokane 63761911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 63861911Scokane if(piod->size != 1) { 63961911Scokane return -EINVAL; 64061911Scokane } 64161911Scokane 64261911Scokane /* Write the data to the intended port */ 64361911Scokane copyin(piod->value, &ret_byte, sizeof(u_int8_t)); 64461911Scokane outb(piod->port, ret_byte); 64561911Scokane return 0; 64661911Scokane} 64761911Scokane 64861911Scokanestatic int 64961911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod) 65061911Scokane{ 65161911Scokane /* There are three sub-commands to the query 0x33 */ 65261911Scokane switch(_IOC_NR(cmd)) { 65361911Scokane case 2: 65461911Scokane return tdfx_query_boards(); 65561911Scokane break; 65661911Scokane case 3: 65761911Scokane return tdfx_query_fetch(cmd, piod); 65861911Scokane break; 65961911Scokane case 4: 66061911Scokane return tdfx_query_update(cmd, piod); 66161911Scokane break; 66261911Scokane default: 66361911Scokane /* In case we are thrown a bogus sub-command! */ 66461931Scokane#ifdef DEBUG 66561911Scokane printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd)); 66661911Scokane#endif 66761911Scokane return -EINVAL; 66861911Scokane }; 66961911Scokane} 67061911Scokane 67161911Scokanestatic int 67261911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod) 67361911Scokane{ 67461911Scokane /* Two types of PIO, INPUT and OUTPUT, as the name suggests */ 67561911Scokane switch(_IOC_DIR(cmd)) { 67661911Scokane case IOCV_OUT: 67761911Scokane return tdfx_do_pio_rd(piod); 67861911Scokane break; 67961911Scokane case IOCV_IN: 68061911Scokane return tdfx_do_pio_wt(piod); 68161911Scokane break; 68261911Scokane default: 68361911Scokane return -EINVAL; 68461911Scokane }; 68561911Scokane} 68661911Scokane 68761911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO, 68861911Scokane * normally, you would read in the data pointed to by data, then write your 68961911Scokane * output to it. The ioctl *should* normally return zero if everything is 69061911Scokane * alright, but 3dfx didn't make it that way... 69161911Scokane * 69261911Scokane * For all of the ioctl code, in the event of a real error, 69361911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this 69461911Scokane * is that the ioctls actually RET information back to the program 69561911Scokane * sometimes, rather than filling it in the passed structure. We 69661911Scokane * want to distinguish errors from useful data, and maintain compatibility. 69761911Scokane * 69861911Scokane * There is this portion of the proc struct called p_retval[], we can store a 69961911Scokane * return value in p->p_retval[0] and place the return value if it is positive 70061911Scokane * in there, then we can return 0 (good). If the return value is negative, we 70161911Scokane * can return -retval and the error should be properly handled. 70261911Scokane */ 70361911Scokanestatic int 70461911Scokanetdfx_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc * p) 70561911Scokane{ 70661911Scokane int retval = 0; 70761911Scokane struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data; 70861931Scokane#ifdef DEBUG 70961911Scokane printf("IOCTL'd by #%d, cmd: 0x%x, data: 0x%x\n", p->p_pid, (u_int32_t)cmd, 71061911Scokane (unsigned int)piod); 71161911Scokane#endif 71261911Scokane switch(_IOC_TYPE(cmd)) { 71361911Scokane /* Return the real error if negative, or simply stick the valid return 71461911Scokane * in p->p_retval */ 71561911Scokane case 0x33: 71661911Scokane /* The '3'(0x33) type IOCTL is for querying the installed cards */ 71761911Scokane if((retval = tdfx_do_query(cmd, piod)) > 0) p->p_retval[0] = retval; 71861911Scokane else return -retval; 71961911Scokane break; 72061911Scokane case 0: 72161911Scokane /* The 0 type IOCTL is for programmed I/O methods */ 72261911Scokane if((tdfx_do_pio(cmd, piod)) > 0) p->p_retval[0] = retval; 72361911Scokane else return -retval; 72461911Scokane break; 72561911Scokane default: 72661911Scokane /* Technically, we won't reach this from linux emu, but when glide 72761911Scokane * finally gets ported, watch out! */ 72861931Scokane#ifdef DEBUG 72961911Scokane printf("Bad IOCTL from #%d\n", p->p_pid); 73061911Scokane#endif 73161911Scokane return ENXIO; 73261911Scokane } 73361911Scokane 73461911Scokane return 0; 73561911Scokane} 73661911Scokane 73761931Scokane#ifdef TDFX_LINUX 73861931Scokane/* 73961931Scokane * Linux emulation IOCTL for /dev/tdfx 74061931Scokane */ 74161931Scokanestatic int 74261931Scokanelinux_ioctl_tdfx(struct proc* p, struct linux_ioctl_args* args) 74361931Scokane{ 74461931Scokane int error = 0; 74561931Scokane u_long cmd = args->cmd & 0xffff; 74661911Scokane 74761931Scokane /* The structure passed to ioctl has two shorts, one int 74861931Scokane and one void*. */ 74961931Scokane char d_pio[2*sizeof(short) + sizeof(int) + sizeof(void*)]; 75061931Scokane 75161931Scokane struct file *fp = p->p_fd->fd_ofiles[args->fd]; 75261931Scokane 75361931Scokane /* We simply copy the data and send it right to ioctl */ 75461931Scokane copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio)); 75561931Scokane error = fo_ioctl(fp, cmd, (caddr_t)&d_pio, p); 75661931Scokane return error; 75761931Scokane} 75861931Scokane#endif /* TDFX_LINUX */ 75961931Scokane 76061931Scokane 76161911Scokane/* This is the device driver struct. This is sent to the driver subsystem to 76261911Scokane * register the method structure and the info strcut space for this particular 76361911Scokane * instance of the driver. 76461911Scokane */ 76561911Scokanestatic driver_t tdfx_driver = { 76661911Scokane "tdfx", 76761911Scokane tdfx_methods, 76861911Scokane sizeof(struct tdfx_softc), 76961911Scokane}; 77061911Scokane 77161911Scokane/* Tell Mr. Kernel about us! */ 77261911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0); 77361911Scokane 77461911Scokane 77561911Scokane#endif /* NPCI */ 776