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