tdfx_pci.c revision 111462
161931Scokane/* 274534Scokane * Copyright (c) 2000-2001 by Coleman Kane <cokane@FreeBSD.org> 361931Scokane * All rights reserved. 461931Scokane * 561931Scokane * Redistribution and use in source and binary forms, with or without 661931Scokane * modification, are permitted provided that the following conditions 761931Scokane * are met: 861931Scokane * 1. Redistributions of source code must retain the above copyright 961931Scokane * notice, this list of conditions and the following disclaimer. 1061931Scokane * 2. Redistributions in binary form must reproduce the above copyright 1161931Scokane * notice, this list of conditions and the following disclaimer in the 1261931Scokane * documentation and/or other materials provided with the distribution. 1361931Scokane * 3. All advertising materials mentioning features or use of this software 1461931Scokane * must display the following acknowledgement: 1561931Scokane * This product includes software developed by Gardner Buchanan. 1661931Scokane * 4. The name of Gardner Buchanan may not be used to endorse or promote 1761931Scokane * products derived from this software without specific prior written 1861931Scokane * permission. 1961931Scokane * 2061931Scokane * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2161931Scokane * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2261931Scokane * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2361931Scokane * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2461931Scokane * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2561931Scokane * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2661931Scokane * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2761931Scokane * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2861931Scokane * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2961931Scokane * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3061931Scokane * 3161931Scokane * $FreeBSD: head/sys/dev/tdfx/tdfx_pci.c 111462 2003-02-25 03:21:22Z mux $ 3261931Scokane */ 3361931Scokane 3461911Scokane/* 3dfx driver for FreeBSD 4.x - Finished 11 May 2000, 12:25AM ET 3561911Scokane * 3674534Scokane * Copyright (C) 2000-2001, by Coleman Kane <cokane@FreeBSD.org>, 3761911Scokane * based upon the 3dfx driver written for linux, by Daryll Straus, Jon Taylor, 3861911Scokane * and Jens Axboe, located at http://linux.3dfx.com. 3961911Scokane */ 4061911Scokane 4161911Scokane#include <sys/param.h> 4261911Scokane 4361911Scokane#include <sys/bus.h> 4461911Scokane#include <sys/cdefs.h> 4561911Scokane#include <sys/conf.h> 4661911Scokane#include <sys/fcntl.h> 4761911Scokane#include <sys/file.h> 4861911Scokane#include <sys/filedesc.h> 4961911Scokane#include <sys/filio.h> 5061911Scokane#include <sys/ioccom.h> 5161911Scokane#include <sys/kernel.h> 5261911Scokane#include <sys/malloc.h> 5361911Scokane#include <sys/mman.h> 5461911Scokane#include <sys/signalvar.h> 5561911Scokane#include <sys/systm.h> 5661911Scokane#include <sys/uio.h> 5761911Scokane 5861911Scokane#include <pci/pcivar.h> 5961911Scokane#include <pci/pcireg.h> 6061911Scokane 6161911Scokane#include <vm/vm.h> 6261911Scokane#include <vm/vm_kern.h> 6361911Scokane#include <vm/pmap.h> 6461911Scokane#include <vm/vm_extern.h> 6561911Scokane 6661911Scokane/* rman.h depends on machine/bus.h */ 6761911Scokane#include <machine/resource.h> 6861911Scokane#include <machine/bus.h> 6961911Scokane#include <sys/rman.h> 7062028Scokane 7162028Scokane/* This must come first */ 7262028Scokane#include "opt_tdfx.h" 7361931Scokane#ifdef TDFX_LINUX 7461931Scokane#include <dev/tdfx/tdfx_linux.h> 7561931Scokane#endif 7661911Scokane 7761911Scokane#include <dev/tdfx/tdfx_io.h> 7861911Scokane#include <dev/tdfx/tdfx_vars.h> 7961911Scokane#include <dev/tdfx/tdfx_pci.h> 8061911Scokane 8161911Scokane 8261911Scokanestatic devclass_t tdfx_devclass; 8361911Scokane 8461911Scokane 8561911Scokanestatic int tdfx_count = 0; 8661911Scokane 8761911Scokane 8861911Scokane/* Set up the boot probe/attach routines */ 8961911Scokanestatic device_method_t tdfx_methods[] = { 9061911Scokane DEVMETHOD(device_probe, tdfx_probe), 9161911Scokane DEVMETHOD(device_attach, tdfx_attach), 9261911Scokane DEVMETHOD(device_detach, tdfx_detach), 9361911Scokane DEVMETHOD(device_shutdown, tdfx_shutdown), 9461911Scokane { 0, 0 } 9561911Scokane}; 9661911Scokane 9761911ScokaneMALLOC_DEFINE(M_TDFX,"TDFX Driver","3DFX Graphics[/2D]/3D Accelerator(s)"); 9861911Scokane 9961931Scokane#ifdef TDFX_LINUX 10061989ScokaneMODULE_DEPEND(tdfx, linux, 1, 1, 1); 10161931ScokaneLINUX_IOCTL_SET(tdfx, LINUX_IOCTL_TDFX_MIN, LINUX_IOCTL_TDFX_MAX); 10261931Scokane#endif 10361931Scokane 10461911Scokane/* Char. Dev. file operations structure */ 10561911Scokanestatic struct cdevsw tdfx_cdev = { 10661911Scokane tdfx_open, /* open */ 10761911Scokane tdfx_close, /* close */ 10861911Scokane noread, /* read */ 10961911Scokane nowrite, /* write */ 11061911Scokane tdfx_ioctl, /* ioctl */ 11161911Scokane nopoll, /* poll */ 11261911Scokane tdfx_mmap, /* mmap */ 11361911Scokane nostrategy, /* strategy */ 11461911Scokane "tdfx", /* dev name */ 11561911Scokane CDEV_MAJOR, /* char major */ 11661911Scokane nodump, /* dump */ 11761911Scokane nopsize, /* size */ 11861911Scokane 0, /* flags (no set flags) */ 11961911Scokane}; 12061911Scokane 12161911Scokanestatic int 12261911Scokanetdfx_probe(device_t dev) 12361911Scokane{ 12461911Scokane /* 12561911Scokane * probe routine called on kernel boot to register supported devices. We get 12661911Scokane * a device structure to work with, and we can test the VENDOR/DEVICE IDs to 12761911Scokane * see if this PCI device is one that we support. Return 0 if yes, ENXIO if 12861911Scokane * not. 12961911Scokane */ 13061911Scokane switch(pci_get_devid(dev)) { 13161911Scokane case PCI_DEVICE_ALLIANCE_AT3D: 13261911Scokane device_set_desc(dev, "ProMotion At3D 3D Accelerator"); 13361911Scokane return 0; 13461911Scokane case PCI_DEVICE_3DFX_VOODOO2: 13561911Scokane device_set_desc(dev, "3DFX Voodoo II 3D Accelerator"); 13661911Scokane return 0; 13765146Scokane /*case PCI_DEVICE_3DFX_BANSHEE: 13861911Scokane device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator"); 13961911Scokane return 0; 14061911Scokane case PCI_DEVICE_3DFX_VOODOO3: 14161911Scokane device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator"); 14265146Scokane return 0;*/ 14361911Scokane case PCI_DEVICE_3DFX_VOODOO1: 14461911Scokane device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator"); 14561911Scokane return 0;; 14661911Scokane }; 14761911Scokane 14861911Scokane return ENXIO; 14961911Scokane} 15061911Scokane 15161911Scokanestatic int 15261911Scokanetdfx_attach(device_t dev) { 15361911Scokane /* 15461911Scokane * The attach routine is called after the probe routine successfully says it 15561911Scokane * supports a given card. We now proceed to initialize this card for use with 15661911Scokane * the system. I want to map the device memory for userland allocation and 15761911Scokane * fill an information structure with information on this card. I'd also like 15861911Scokane * to set Write Combining with the MTRR code so that we can hopefully speed 15961911Scokane * up memory writes. The last thing is to register the character device 16061911Scokane * interface to the card, so we can open it from /dev/3dfxN, where N is a 16161911Scokane * small, whole number. 16261911Scokane */ 16361911Scokane struct tdfx_softc *tdfx_info; 16461911Scokane u_long val; 16561911Scokane /* rid value tells bus_alloc_resource where to find the addresses of ports or 16661911Scokane * of memory ranges in the PCI config space*/ 16761911Scokane int rid = PCIR_MAPS; 16861911Scokane 16961911Scokane /* Increment the card counter (for the ioctl code) */ 17061911Scokane tdfx_count++; 17161911Scokane 17261911Scokane /* Enable MemMap on Voodoo */ 17361911Scokane val = pci_read_config(dev, PCIR_COMMAND, 2); 17461911Scokane val |= (PCIM_CMD_MEMEN); 17561911Scokane pci_write_config(dev, PCIR_COMMAND, val, 2); 17661911Scokane val = pci_read_config(dev, PCIR_COMMAND, 2); 17761911Scokane 17861911Scokane /* Fill the soft config struct with info about this device*/ 17961911Scokane tdfx_info = device_get_softc(dev); 18061911Scokane tdfx_info->dev = dev; 18161911Scokane tdfx_info->vendor = pci_get_vendor(dev); 18261911Scokane tdfx_info->type = pci_get_devid(dev) >> 16; 18361911Scokane tdfx_info->bus = pci_get_bus(dev); 18461911Scokane tdfx_info->dv = pci_get_slot(dev); 18561911Scokane tdfx_info->curFile = NULL; 18661911Scokane 18761911Scokane /* 18861911Scokane * Get the Memory Location from the PCI Config, mask out lower word, since 18961911Scokane * the config space register is only one word long (this is nicer than a 19061911Scokane * bitshift). 19161911Scokane */ 19261911Scokane tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000); 19361931Scokane#ifdef DEBUG 19461911Scokane device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0); 19561911Scokane#endif 19661911Scokane /* Notify the VM that we will be mapping some memory later */ 19761911Scokane tdfx_info->memrange = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1, 19861967Scokane RF_ACTIVE | RF_SHAREABLE); 19961911Scokane if(tdfx_info->memrange == NULL) { 20061931Scokane#ifdef DEBUG 20161911Scokane device_printf(dev, "Error mapping mem, won't be able to use mmap()\n"); 20261911Scokane#endif 20361911Scokane tdfx_info->memrid = 0; 20461911Scokane } 20561911Scokane else { 20661911Scokane tdfx_info->memrid = rid; 20761931Scokane#ifdef DEBUG 20861911Scokane device_printf(dev, "Mapped to: 0x%x\n", 20961911Scokane (unsigned int)rman_get_start(tdfx_info->memrange)); 21061911Scokane#endif 21161911Scokane } 21261911Scokane 21363488Scokane /* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */ 21463488Scokane if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 || 21563488Scokane pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) { 21664085Scokane rid = 0x14; /* 2nd mem map */ 21763488Scokane tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000); 21863488Scokane#ifdef DEBUG 21963488Scokane device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1); 22063488Scokane#endif 22163488Scokane tdfx_info->memrange2 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 22263488Scokane 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 22363488Scokane if(tdfx_info->memrange2 == NULL) { 22463488Scokane#ifdef DEBUG 22563488Scokane device_printf(dev, "Mem1 couldn't be allocated, glide may not work."); 22663488Scokane#endif 22763488Scokane tdfx_info->memrid2 = 0; 22863488Scokane } 22963488Scokane else { 23063488Scokane tdfx_info->memrid2 = rid; 23163488Scokane } 23263488Scokane /* Now to map the PIO stuff */ 23365146Scokane rid = PCIR_IOBASE0_2; 23465146Scokane tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2); 23565146Scokane tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0; 23663488Scokane tdfx_info->piorange = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 23763488Scokane 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 23863488Scokane if(tdfx_info->piorange == NULL) { 23963488Scokane#ifdef DEBUG 24063488Scokane device_printf(dev, "Couldn't map PIO range."); 24163488Scokane#endif 24263488Scokane tdfx_info->piorid = 0; 24363488Scokane } 24463488Scokane else { 24563488Scokane tdfx_info->piorid = rid; 24665146Scokane } 24763488Scokane } else { 24863488Scokane tdfx_info->addr1 = 0; 24963488Scokane tdfx_info->memrange2 = NULL; 25063488Scokane tdfx_info->piorange = NULL; 25163488Scokane } 25263488Scokane 25361911Scokane /* 25461911Scokane * Set Writecombining, or at least Uncacheable for the memory region, if we 25561911Scokane * are able to 25661911Scokane */ 25761911Scokane 25861911Scokane if(tdfx_setmtrr(dev) != 0) { 25961931Scokane#ifdef DEBUG 26061911Scokane device_printf(dev, "Some weird error setting MTRRs"); 26161911Scokane#endif 26261911Scokane return -1; 26361911Scokane } 26463488Scokane 26561911Scokane /* 26661911Scokane * make_dev registers the cdev to access the 3dfx card from /dev 26761911Scokane * use hex here for the dev num, simply to provide better support if > 10 26861911Scokane * voodoo cards, for the mad. The user must set the link, or use MAKEDEV. 26961911Scokane * Why would we want that many voodoo cards anyhow? 27061911Scokane */ 271104111Sphk tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev), 272108323Srwatson UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev)); 27361911Scokane 27461911Scokane return 0; 27561911Scokane} 27661911Scokane 27761911Scokanestatic int 27861911Scokanetdfx_detach(device_t dev) { 27961911Scokane struct tdfx_softc* tdfx_info; 28061911Scokane int retval; 28161911Scokane tdfx_info = device_get_softc(dev); 28261911Scokane 28361911Scokane /* Delete allocated resource, of course */ 28463488Scokane bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid, 28561911Scokane tdfx_info->memrange); 28663488Scokane 28763488Scokane /* Release extended Voodoo3/Banshee resources */ 28863488Scokane if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE || 28963488Scokane pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) { 29063488Scokane if(tdfx_info->memrange2 != NULL) 29163488Scokane bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2, 29263488Scokane tdfx_info->memrange); 29364085Scokane /* if(tdfx_info->piorange != NULL) 29463488Scokane bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid, 29564085Scokane tdfx_info->piorange);*/ 29663488Scokane } 29763488Scokane 29861911Scokane /* Though it is safe to leave the WRCOMB support since the 29961911Scokane mem driver checks for it, we should remove it in order 30061911Scokane to free an MTRR for another device */ 30161911Scokane retval = tdfx_clrmtrr(dev); 30261931Scokane#ifdef DEBUG 30361911Scokane if(retval != 0) 30461911Scokane printf("tdfx: For some reason, I couldn't clear the mtrr\n"); 30561911Scokane#endif 30661989Scokane /* Remove device entry when it can no longer be accessed */ 30761989Scokane destroy_dev(tdfx_info->devt); 30861911Scokane return(0); 30961911Scokane} 31061911Scokane 31161911Scokanestatic int 31261911Scokanetdfx_shutdown(device_t dev) { 31361931Scokane#ifdef DEBUG 31461911Scokane device_printf(dev, "tdfx: Device Shutdown\n"); 31561911Scokane#endif 31661911Scokane return 0; 31761911Scokane} 31861911Scokane 31961911Scokanestatic int 32061911Scokanetdfx_clrmtrr(device_t dev) { 32161911Scokane /* This function removes the MTRR set by the attach call, so it can be used 32261911Scokane * in the future by other drivers. 32361911Scokane */ 32461911Scokane int retval, act; 32561911Scokane struct tdfx_softc *tdfx_info = device_get_softc(dev); 32661911Scokane 32761911Scokane act = MEMRANGE_SET_REMOVE; 32861911Scokane retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 32961911Scokane return retval; 33061911Scokane} 33161911Scokane 33261911Scokanestatic int 33361911Scokanetdfx_setmtrr(device_t dev) { 33461911Scokane /* 33561911Scokane * This is the MTRR setting function for the 3dfx card. It is called from 33661911Scokane * tdfx_attach. If we can't set the MTRR properly, it's not the end of the 33761911Scokane * world. We can still continue, just with slightly (very slightly) degraded 33861911Scokane * performance. 33961911Scokane */ 34061911Scokane int retval = 0, act; 34161911Scokane struct tdfx_softc *tdfx_info = device_get_softc(dev); 34261911Scokane 34361911Scokane /* The older Voodoo cards have a shorter memrange than the newer ones */ 34461911Scokane if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) == 34563488Scokane PCI_DEVICE_3DFX_VOODOO2)) { 34661911Scokane tdfx_info->mrdesc.mr_len = 0x400000; 34763488Scokane 34863488Scokane /* The memory descriptor is described as the top 15 bits of the real 34963488Scokane address */ 35063488Scokane tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000; 35163488Scokane } 35261911Scokane else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) || 35363488Scokane (pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) { 35461911Scokane tdfx_info->mrdesc.mr_len = 0x1000000; 35563488Scokane /* The Voodoo3 and Banshee LFB is the second memory address */ 35663488Scokane /* The memory descriptor is described as the top 15 bits of the real 35763488Scokane address */ 35863488Scokane tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000; 35963488Scokane } 36063488Scokane else 36163488Scokane return 0; 36261911Scokane /* 36361911Scokane * The Alliance Pro Motion AT3D was not mentioned in the linux 36461911Scokane * driver as far as MTRR support goes, so I just won't put the 36561911Scokane * code in here for it. This is where it should go, though. 36661911Scokane */ 36761911Scokane 36861911Scokane /* Firstly, try to set write combining */ 36961911Scokane tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE; 37061911Scokane bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4); 37161911Scokane act = MEMRANGE_SET_UPDATE; 37261911Scokane retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 37361911Scokane 37461911Scokane if(retval == 0) { 37561931Scokane#ifdef DEBUG 37661911Scokane device_printf(dev, "MTRR Set Correctly for tdfx\n"); 37761911Scokane#endif 37861911Scokane } else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) || 37961911Scokane (pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) { 38061911Scokane /* if, for some reason we can't set the WRCOMB range with the V1/V2, we 38161911Scokane * can still possibly use the UNCACHEABLE region for it instead, and help 38261911Scokane * out in a small way */ 38361911Scokane tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE; 38461911Scokane /* This length of 1000h was taken from the linux device driver... */ 38561911Scokane tdfx_info->mrdesc.mr_len = 0x1000; 38661911Scokane 38761911Scokane /* 38861911Scokane * If, for some reason, we can't set the MTRR (N/A?) we may still continue 38961911Scokane */ 39061931Scokane#ifdef DEBUG 39161911Scokane if(retval == 0) { 39295092Smarcel device_printf(dev, "MTRR Set Type Uncacheable %x\n", 39395092Smarcel (u_int32_t)tdfx_info->mrdesc.mr_base); 39461911Scokane } else { 39561911Scokane device_printf(dev, "Couldn't Set MTRR\n"); 39661911Scokane } 39761911Scokane#endif 39861911Scokane } 39961931Scokane#ifdef DEBUG 40061911Scokane else { 40161911Scokane device_printf(dev, "Couldn't Set MTRR\n"); 40261911Scokane return 0; 40361911Scokane } 40461911Scokane#endif 40561911Scokane return 0; 40661911Scokane} 40761911Scokane 40861911Scokanestatic int 40983366Sjuliantdfx_open(dev_t dev, int flags, int fmt, struct thread *td) 41061911Scokane{ 41161911Scokane /* 41261911Scokane * The open cdev method handles open(2) calls to /dev/3dfx[n] 41361911Scokane * We can pretty much allow any opening of the device. 41461911Scokane */ 41561911Scokane struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 41661911Scokane UNIT(minor(dev))); 41761911Scokane if(tdfx_info->busy != 0) return EBUSY; 41861931Scokane#ifdef DEBUG 41983366Sjulian printf("3dfx: Opened by #%d\n", td->td_proc->p_pid); 42061911Scokane#endif 42161911Scokane /* Set the driver as busy */ 42261911Scokane tdfx_info->busy++; 42361911Scokane return 0; 42461911Scokane} 42561911Scokane 42661911Scokanestatic int 42783366Sjuliantdfx_close(dev_t dev, int fflag, int devtype, struct thread *td) 42861911Scokane{ 42961911Scokane /* 43061911Scokane * The close cdev method handles close(2) calls to /dev/3dfx[n] 43161911Scokane * We'll always want to close the device when it's called. 43261911Scokane */ 43361911Scokane struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 43461911Scokane UNIT(minor(dev))); 43561911Scokane if(tdfx_info->busy == 0) return EBADF; 43661911Scokane tdfx_info->busy = 0; 43761931Scokane#ifdef DEBUG 43883366Sjulian printf("Closed by #%d\n", td->td_proc->p_pid); 43961911Scokane#endif 44061911Scokane return 0; 44161911Scokane} 44261911Scokane 44361911Scokanestatic int 444111462Smuxtdfx_mmap(dev_t dev, vm_offset_t offset, vm_offset_t *paddr, int nprot) 44561911Scokane{ 44661911Scokane /* 44761911Scokane * mmap(2) is called by a user process to request that an area of memory 44861911Scokane * associated with this device be mapped for the process to work with. Nprot 44961911Scokane * holds the protections requested, PROT_READ, PROT_WRITE, or both. 45061911Scokane */ 45166910Scokane 45266910Scokane /**** OLD GET CONFIG ****/ 45366910Scokane /* struct tdfx_softc* tdfx_info; */ 45461911Scokane 45561911Scokane /* Get the configuration for our card XXX*/ 45666910Scokane /*tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 45766910Scokane UNIT(minor(dev)));*/ 45866910Scokane /************************/ 45966910Scokane 46066910Scokane struct tdfx_softc* tdfx_info[2]; 46161911Scokane 46266910Scokane tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0); 46366910Scokane 46461911Scokane /* If, for some reason, its not configured, we bail out */ 46566910Scokane if(tdfx_info[0] == NULL) { 46661931Scokane#ifdef DEBUG 46761911Scokane printf("tdfx: tdfx_info (softc) is NULL\n"); 46861911Scokane#endif 46961911Scokane return -1; 47061911Scokane } 47166910Scokane 47261911Scokane /* We must stay within the bound of our address space */ 47366910Scokane if((offset & 0xff000000) == tdfx_info[0]->addr0) { 47461911Scokane offset &= 0xffffff; 475111462Smux *paddr = rman_get_start(tdfx_info[0]->memrange) + offset; 476111462Smux return 0; 47766910Scokane } 47866910Scokane 47966910Scokane if(tdfx_count > 1) { 48066910Scokane tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1); 48166910Scokane if((offset & 0xff000000) == tdfx_info[1]->addr0) { 48266910Scokane offset &= 0xffffff; 483111462Smux *paddr = rman_get_start(tdfx_info[1]->memrange) + 484111462Smux offset; 485111462Smux return 0; 48666910Scokane } 48766910Scokane } 48863488Scokane 48963488Scokane /* See if the Banshee/V3 LFB is being requested */ 49066910Scokane /*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) == 49164085Scokane tdfx_info->addr1) { 49263488Scokane offset &= 0xffffff; 49366910Scokane return atop(rman_get_start(tdfx_info[1]->memrange2) + offset); 49466910Scokane }*/ /* VoodooNG code */ 49563488Scokane 49666910Scokane /* The ret call */ 49761911Scokane /* atop -> address to page 49861911Scokane * rman_get_start, get the (struct resource*)->r_start member, 49961911Scokane * the mapping base address. 50061911Scokane */ 50166910Scokane return -1; 50261911Scokane} 50361911Scokane 50461911Scokanestatic int 50561911Scokanetdfx_query_boards(void) { 50661911Scokane /* 50761911Scokane * This returns the number of installed tdfx cards, we have been keeping 50861911Scokane * count, look at tdfx_attach 50961911Scokane */ 51061911Scokane return tdfx_count; 51161911Scokane} 51261911Scokane 51361911Scokanestatic int 51461911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod) 51561911Scokane{ 51661911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 51761911Scokane /* Various return values 8bit-32bit */ 51861911Scokane u_int8_t ret_byte; 51961911Scokane u_int16_t ret_word; 52061911Scokane u_int32_t ret_dword; 52161911Scokane struct tdfx_softc* tdfx_info = NULL; 52261911Scokane 52361911Scokane /* This one depend on the tdfx_* structs being properly initialized */ 52461911Scokane 52561911Scokane /*piod->device &= 0xf;*/ 52661911Scokane if((piod == NULL) ||(tdfx_count <= piod->device) || 52761911Scokane (piod->device < 0)) { 52861931Scokane#ifdef DEBUG 52961911Scokane printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n"); 53061911Scokane#endif 53161911Scokane return -EINVAL; 53261911Scokane } 53361911Scokane 53461911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 53561911Scokane piod->device); 53661911Scokane 53761911Scokane if(tdfx_info == NULL) return -ENXIO; 53861911Scokane 53961911Scokane /* We must restrict the size reads from the port, since to high or low of a 54061911Scokane * size witll result in wrong data being passed, and that's bad */ 54161911Scokane /* A few of these were pulled during the attach phase */ 54261911Scokane switch(piod->port) { 54361911Scokane case PCI_VENDOR_ID_FREEBSD: 54461911Scokane if(piod->size != 2) return -EINVAL; 54561911Scokane copyout(&tdfx_info->vendor, piod->value, piod->size); 54661911Scokane return 0; 54761911Scokane case PCI_DEVICE_ID_FREEBSD: 54861911Scokane if(piod->size != 2) return -EINVAL; 54961911Scokane copyout(&tdfx_info->type, piod->value, piod->size); 55061911Scokane return 0; 55161911Scokane case PCI_BASE_ADDRESS_0_FREEBSD: 55261911Scokane if(piod->size != 4) return -EINVAL; 55361911Scokane copyout(&tdfx_info->addr0, piod->value, piod->size); 55461911Scokane return 0; 55565146Scokane case PCI_BASE_ADDRESS_1_FREEBSD: 55665146Scokane if(piod->size != 4) return -EINVAL; 55765146Scokane copyout(&tdfx_info->addr1, piod->value, piod->size); 55865146Scokane return 0; 55965146Scokane case PCI_PRIBUS_FREEBSD: 56065146Scokane if(piod->size != 1) return -EINVAL; 56165146Scokane break; 56265146Scokane case PCI_IOBASE_0_FREEBSD: 56365146Scokane if(piod->size != 2) return -EINVAL; 56465146Scokane break; 56565146Scokane case PCI_IOLIMIT_0_FREEBSD: 56665146Scokane if(piod->size != 2) return -EINVAL; 56765146Scokane break; 56861911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 56961911Scokane if(piod->size != 4) return -EINVAL; 57061911Scokane break; 57161911Scokane case PCI_REVISION_ID_FREEBSD: 57261911Scokane if(piod->size != 1) return -EINVAL; 57361911Scokane break; 57461911Scokane case SST1_PCI_SPECIAL4_FREEBSD: 57561911Scokane if(piod->size != 4) return -EINVAL; 57661911Scokane break; 57761911Scokane default: 57861911Scokane return -EINVAL; 57961911Scokane } 58061911Scokane 58161911Scokane 58261911Scokane /* Read the value and return */ 58361911Scokane switch(piod->size) { 58461911Scokane case 1: 58561911Scokane ret_byte = pci_read_config(tdfx_info[piod->device].dev, 58661911Scokane piod->port, 1); 58761911Scokane copyout(&ret_byte, piod->value, 1); 58861911Scokane break; 58961911Scokane case 2: 59061911Scokane ret_word = pci_read_config(tdfx_info[piod->device].dev, 59161911Scokane piod->port, 2); 59261911Scokane copyout(&ret_word, piod->value, 2); 59361911Scokane break; 59461911Scokane case 4: 59561911Scokane ret_dword = pci_read_config(tdfx_info[piod->device].dev, 59661911Scokane piod->port, 4); 59761911Scokane copyout(&ret_dword, piod->value, 4); 59861911Scokane break; 59961911Scokane default: 60061911Scokane return -EINVAL; 60161911Scokane } 60261911Scokane return 0; 60361911Scokane} 60461911Scokane 60561911Scokanestatic int 60661911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod) 60761911Scokane{ 60861911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 60961911Scokane /* Return vals */ 61061911Scokane u_int8_t ret_byte; 61161911Scokane u_int16_t ret_word; 61261911Scokane u_int32_t ret_dword; 61361911Scokane 61461911Scokane /* Port vals, mask */ 61561911Scokane u_int32_t retval, preval, mask; 61661911Scokane struct tdfx_softc* tdfx_info = NULL; 61761911Scokane 61861911Scokane 61961911Scokane if((piod == NULL) || (piod->device >= (tdfx_count & 62061911Scokane 0xf))) { 62161931Scokane#ifdef DEBUG 62261911Scokane printf("tdfx: Bad struct or device in tdfx_query_update\n"); 62361911Scokane#endif 62461911Scokane return -EINVAL; 62561911Scokane } 62661911Scokane 62761911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 62861911Scokane piod->device); 62961911Scokane if(tdfx_info == NULL) return -ENXIO; 63061911Scokane /* Code below this line in the fuction was taken from the 63161911Scokane * Linux driver and converted for freebsd. */ 63261911Scokane 63361911Scokane /* Check the size for all the ports, to make sure stuff doesn't get messed up 63461911Scokane * by poorly written clients */ 63561911Scokane 63661911Scokane switch(piod->port) { 63761911Scokane case PCI_COMMAND_FREEBSD: 63861911Scokane if(piod->size != 2) return -EINVAL; 63961911Scokane break; 64061911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 64161911Scokane if(piod->size != 4) return -EINVAL; 64261911Scokane break; 64361911Scokane case SST1_PCI_SPECIAL2_FREEBSD: 64461911Scokane if(piod->size != 4) return -EINVAL; 64561911Scokane break; 64661911Scokane case SST1_PCI_SPECIAL3_FREEBSD: 64761911Scokane if(piod->size != 4) return -EINVAL; 64861911Scokane break; 64961911Scokane case SST1_PCI_SPECIAL4_FREEBSD: 65061911Scokane if(piod->size != 4) return -EINVAL; 65161911Scokane break; 65261911Scokane default: 65361911Scokane return -EINVAL; 65461911Scokane } 65561911Scokane /* Read the current value */ 65661911Scokane retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4); 65761911Scokane 65861911Scokane /* These set up a mask to use, since apparently they wanted to write 4 bytes 65961911Scokane * at once to the ports */ 66061911Scokane switch (piod->size) { 66161911Scokane case 1: 66261911Scokane copyin(piod->value, &ret_byte, 1); 66361911Scokane preval = ret_byte << (8 * (piod->port & 0x3)); 66461911Scokane mask = 0xff << (8 * (piod->port & 0x3)); 66561911Scokane break; 66661911Scokane case 2: 66761911Scokane copyin(piod->value, &ret_word, 2); 66861911Scokane preval = ret_word << (8 * (piod->port & 0x3)); 66961911Scokane mask = 0xffff << (8 * (piod->port & 0x3)); 67061911Scokane break; 67161911Scokane case 4: 67261911Scokane copyin(piod->value, &ret_dword, 4); 67361911Scokane preval = ret_dword; 67461911Scokane mask = ~0; 67561911Scokane break; 67661911Scokane default: 67761911Scokane return -EINVAL; 67861911Scokane } 67961911Scokane /* Finally, combine the values and write it to the port */ 68061911Scokane retval = (retval & ~mask) | preval; 68161911Scokane pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4); 68261911Scokane 68361911Scokane return 0; 68461911Scokane} 68561911Scokane 68663488Scokane/* For both of these, I added a variable named workport of type u_int so 68763488Scokane * that I could eliminate the warning about my data type size. The 68863488Scokane * applications expect the port to be of type short, so I needed to change 68963488Scokane * this within the function */ 69061911Scokanestatic int 69161911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod) 69261911Scokane{ 69361911Scokane /* Return val */ 69461911Scokane u_int8_t ret_byte; 69563488Scokane u_int workport; 69665146Scokane struct tdfx_softc *tdfx_info = 69765146Scokane (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 69865146Scokane 69961911Scokane /* Restricts the access of ports other than those we use */ 70065146Scokane if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) || 70165146Scokane (piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) && 70265146Scokane (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 70361911Scokane return -EPERM; 70461911Scokane 70561911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 70661911Scokane if(piod->size != 1) { 70761911Scokane return -EINVAL; 70861911Scokane } 70961911Scokane 71061911Scokane /* Write the data to the intended port */ 71163488Scokane workport = piod->port; 71263488Scokane ret_byte = inb(workport); 71361911Scokane copyout(&ret_byte, piod->value, sizeof(u_int8_t)); 71461911Scokane return 0; 71561911Scokane} 71661911Scokane 71761911Scokanestatic int 71861911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod) 71961911Scokane{ 72061911Scokane /* return val */ 72161911Scokane u_int8_t ret_byte; 72263488Scokane u_int workport; 72365146Scokane struct tdfx_softc *tdfx_info = (struct 72465146Scokane tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 72561911Scokane /* Replace old switch w/ massive if(...) */ 72661911Scokane /* Restricts the access of ports other than those we use */ 72765146Scokane if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) && 72865146Scokane (piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ && 72965146Scokane (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 73061911Scokane return -EPERM; 73161911Scokane 73261911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 73361911Scokane if(piod->size != 1) { 73461911Scokane return -EINVAL; 73561911Scokane } 73661911Scokane 73761911Scokane /* Write the data to the intended port */ 73861911Scokane copyin(piod->value, &ret_byte, sizeof(u_int8_t)); 73963488Scokane workport = piod->port; 74063488Scokane outb(workport, ret_byte); 74161911Scokane return 0; 74261911Scokane} 74361911Scokane 74461911Scokanestatic int 74561911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod) 74661911Scokane{ 74761911Scokane /* There are three sub-commands to the query 0x33 */ 74861911Scokane switch(_IOC_NR(cmd)) { 74961911Scokane case 2: 75061911Scokane return tdfx_query_boards(); 75161911Scokane break; 75261911Scokane case 3: 75361911Scokane return tdfx_query_fetch(cmd, piod); 75461911Scokane break; 75561911Scokane case 4: 75661911Scokane return tdfx_query_update(cmd, piod); 75761911Scokane break; 75861911Scokane default: 75961911Scokane /* In case we are thrown a bogus sub-command! */ 76061931Scokane#ifdef DEBUG 76161911Scokane printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd)); 76261911Scokane#endif 76361911Scokane return -EINVAL; 76461911Scokane }; 76561911Scokane} 76661911Scokane 76761911Scokanestatic int 76861911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod) 76961911Scokane{ 77061911Scokane /* Two types of PIO, INPUT and OUTPUT, as the name suggests */ 77161911Scokane switch(_IOC_DIR(cmd)) { 77261911Scokane case IOCV_OUT: 77361911Scokane return tdfx_do_pio_rd(piod); 77461911Scokane break; 77561911Scokane case IOCV_IN: 77661911Scokane return tdfx_do_pio_wt(piod); 77761911Scokane break; 77861911Scokane default: 77961911Scokane return -EINVAL; 78061911Scokane }; 78161911Scokane} 78261911Scokane 78361911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO, 78461911Scokane * normally, you would read in the data pointed to by data, then write your 78561911Scokane * output to it. The ioctl *should* normally return zero if everything is 78661911Scokane * alright, but 3dfx didn't make it that way... 78761911Scokane * 78861911Scokane * For all of the ioctl code, in the event of a real error, 78961911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this 79061911Scokane * is that the ioctls actually RET information back to the program 79161911Scokane * sometimes, rather than filling it in the passed structure. We 79261911Scokane * want to distinguish errors from useful data, and maintain compatibility. 79361911Scokane * 79461911Scokane * There is this portion of the proc struct called p_retval[], we can store a 79583366Sjulian * return value in td->td_retval[0] and place the return value if it is positive 79661911Scokane * in there, then we can return 0 (good). If the return value is negative, we 79761911Scokane * can return -retval and the error should be properly handled. 79861911Scokane */ 79961911Scokanestatic int 80083366Sjuliantdfx_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) 80161911Scokane{ 80261911Scokane int retval = 0; 80361911Scokane struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data; 80461931Scokane#ifdef DEBUG 805106578Sjhb printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd, 806106578Sjhb piod); 80761911Scokane#endif 80861911Scokane switch(_IOC_TYPE(cmd)) { 80961911Scokane /* Return the real error if negative, or simply stick the valid return 81083366Sjulian * in td->td_retval */ 81161911Scokane case 0x33: 81261911Scokane /* The '3'(0x33) type IOCTL is for querying the installed cards */ 81383366Sjulian if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval; 81461911Scokane else return -retval; 81561911Scokane break; 81661911Scokane case 0: 81761911Scokane /* The 0 type IOCTL is for programmed I/O methods */ 81883366Sjulian if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval; 81961911Scokane else return -retval; 82061911Scokane break; 82161911Scokane default: 82261911Scokane /* Technically, we won't reach this from linux emu, but when glide 82361911Scokane * finally gets ported, watch out! */ 82461931Scokane#ifdef DEBUG 82583366Sjulian printf("Bad IOCTL from #%d\n", td->td_proc->p_pid); 82661911Scokane#endif 82761911Scokane return ENXIO; 82861911Scokane } 82961911Scokane 83061911Scokane return 0; 83161911Scokane} 83261911Scokane 83361931Scokane#ifdef TDFX_LINUX 83461931Scokane/* 83561931Scokane * Linux emulation IOCTL for /dev/tdfx 83661931Scokane */ 83761931Scokanestatic int 83883366Sjulianlinux_ioctl_tdfx(struct thread *td, struct linux_ioctl_args* args) 83961931Scokane{ 84061931Scokane int error = 0; 84161931Scokane u_long cmd = args->cmd & 0xffff; 84261911Scokane 84361931Scokane /* The structure passed to ioctl has two shorts, one int 84461931Scokane and one void*. */ 84561931Scokane char d_pio[2*sizeof(short) + sizeof(int) + sizeof(void*)]; 84661931Scokane 84789306Salfred struct file *fp; 84861931Scokane 84989319Salfred if ((error = fget(td, args->fd, &fp)) != 0) 85089319Salfred return (error); 85161931Scokane /* We simply copy the data and send it right to ioctl */ 85261931Scokane copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio)); 853102003Srwatson error = fo_ioctl(fp, cmd, (caddr_t)&d_pio, td->td_ucred, td); 85489306Salfred fdrop(fp, td); 85561931Scokane return error; 85661931Scokane} 85761931Scokane#endif /* TDFX_LINUX */ 85861931Scokane 85961931Scokane 86061911Scokane/* This is the device driver struct. This is sent to the driver subsystem to 86161911Scokane * register the method structure and the info strcut space for this particular 86261911Scokane * instance of the driver. 86361911Scokane */ 86461911Scokanestatic driver_t tdfx_driver = { 86561911Scokane "tdfx", 86661911Scokane tdfx_methods, 86761911Scokane sizeof(struct tdfx_softc), 86861911Scokane}; 86961911Scokane 87061911Scokane/* Tell Mr. Kernel about us! */ 87161911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0); 872