tdfx_pci.c revision 65146
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 65146 2000-08-28 04:28:53Z 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; 14565146Scokane /*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"); 15065146Scokane 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 struct tdfx_softc *tdfx_info; 17261911Scokane u_long val; 17361911Scokane /* rid value tells bus_alloc_resource where to find the addresses of ports or 17461911Scokane * of memory ranges in the PCI config space*/ 17561911Scokane int rid = PCIR_MAPS; 17661911Scokane 17761911Scokane /* Increment the card counter (for the ioctl code) */ 17861911Scokane tdfx_count++; 17961911Scokane 18061911Scokane /* Enable MemMap on Voodoo */ 18161911Scokane val = pci_read_config(dev, PCIR_COMMAND, 2); 18261911Scokane val |= (PCIM_CMD_MEMEN); 18361911Scokane pci_write_config(dev, PCIR_COMMAND, val, 2); 18461911Scokane val = pci_read_config(dev, PCIR_COMMAND, 2); 18561911Scokane 18661911Scokane /* Fill the soft config struct with info about this device*/ 18761911Scokane tdfx_info = device_get_softc(dev); 18861911Scokane tdfx_info->dev = dev; 18961911Scokane tdfx_info->vendor = pci_get_vendor(dev); 19061911Scokane tdfx_info->type = pci_get_devid(dev) >> 16; 19161911Scokane tdfx_info->bus = pci_get_bus(dev); 19261911Scokane tdfx_info->dv = pci_get_slot(dev); 19361911Scokane tdfx_info->curFile = NULL; 19461911Scokane 19561911Scokane /* 19661911Scokane * Get the Memory Location from the PCI Config, mask out lower word, since 19761911Scokane * the config space register is only one word long (this is nicer than a 19861911Scokane * bitshift). 19961911Scokane */ 20061911Scokane tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000); 20161931Scokane#ifdef DEBUG 20261911Scokane device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0); 20361911Scokane#endif 20461911Scokane /* Notify the VM that we will be mapping some memory later */ 20561911Scokane tdfx_info->memrange = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1, 20661967Scokane RF_ACTIVE | RF_SHAREABLE); 20761911Scokane if(tdfx_info->memrange == NULL) { 20861931Scokane#ifdef DEBUG 20961911Scokane device_printf(dev, "Error mapping mem, won't be able to use mmap()\n"); 21061911Scokane#endif 21161911Scokane tdfx_info->memrid = 0; 21261911Scokane } 21361911Scokane else { 21461911Scokane tdfx_info->memrid = rid; 21561931Scokane#ifdef DEBUG 21661911Scokane device_printf(dev, "Mapped to: 0x%x\n", 21761911Scokane (unsigned int)rman_get_start(tdfx_info->memrange)); 21861911Scokane#endif 21961911Scokane } 22061911Scokane 22163488Scokane /* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */ 22263488Scokane if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 || 22363488Scokane pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) { 22464085Scokane rid = 0x14; /* 2nd mem map */ 22563488Scokane tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000); 22663488Scokane#ifdef DEBUG 22763488Scokane device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1); 22863488Scokane#endif 22963488Scokane tdfx_info->memrange2 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 23063488Scokane 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 23163488Scokane if(tdfx_info->memrange2 == NULL) { 23263488Scokane#ifdef DEBUG 23363488Scokane device_printf(dev, "Mem1 couldn't be allocated, glide may not work."); 23463488Scokane#endif 23563488Scokane tdfx_info->memrid2 = 0; 23663488Scokane } 23763488Scokane else { 23863488Scokane tdfx_info->memrid2 = rid; 23963488Scokane } 24063488Scokane /* Now to map the PIO stuff */ 24165146Scokane rid = PCIR_IOBASE0_2; 24265146Scokane tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2); 24365146Scokane tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0; 24463488Scokane tdfx_info->piorange = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 24563488Scokane 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 24663488Scokane if(tdfx_info->piorange == NULL) { 24763488Scokane#ifdef DEBUG 24863488Scokane device_printf(dev, "Couldn't map PIO range."); 24963488Scokane#endif 25063488Scokane tdfx_info->piorid = 0; 25163488Scokane } 25263488Scokane else { 25363488Scokane tdfx_info->piorid = rid; 25465146Scokane } 25563488Scokane } else { 25663488Scokane tdfx_info->addr1 = 0; 25763488Scokane tdfx_info->memrange2 = NULL; 25863488Scokane tdfx_info->piorange = NULL; 25963488Scokane } 26063488Scokane 26161911Scokane /* 26261911Scokane * Set Writecombining, or at least Uncacheable for the memory region, if we 26361911Scokane * are able to 26461911Scokane */ 26561911Scokane 26661911Scokane if(tdfx_setmtrr(dev) != 0) { 26761931Scokane#ifdef DEBUG 26861911Scokane device_printf(dev, "Some weird error setting MTRRs"); 26961911Scokane#endif 27061911Scokane return -1; 27161911Scokane } 27263488Scokane 27361911Scokane /* 27461911Scokane * make_dev registers the cdev to access the 3dfx card from /dev 27561911Scokane * use hex here for the dev num, simply to provide better support if > 10 27661911Scokane * voodoo cards, for the mad. The user must set the link, or use MAKEDEV. 27761911Scokane * Why would we want that many voodoo cards anyhow? 27861911Scokane */ 27961989Scokane tdfx_info->devt = make_dev(&tdfx_cdev, dev->unit, 0, 0, 02660, 28061989Scokane "3dfx%x", dev->unit); 28161911Scokane 28261911Scokane return 0; 28361911Scokane} 28461911Scokane 28561911Scokanestatic int 28661911Scokanetdfx_detach(device_t dev) { 28761911Scokane struct tdfx_softc* tdfx_info; 28861911Scokane int retval; 28961911Scokane tdfx_info = device_get_softc(dev); 29061911Scokane 29161911Scokane /* Delete allocated resource, of course */ 29263488Scokane bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid, 29361911Scokane tdfx_info->memrange); 29463488Scokane 29563488Scokane /* Release extended Voodoo3/Banshee resources */ 29663488Scokane if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE || 29763488Scokane pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) { 29863488Scokane if(tdfx_info->memrange2 != NULL) 29963488Scokane bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2, 30063488Scokane tdfx_info->memrange); 30164085Scokane /* if(tdfx_info->piorange != NULL) 30263488Scokane bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid, 30364085Scokane tdfx_info->piorange);*/ 30463488Scokane } 30563488Scokane 30661911Scokane /* Though it is safe to leave the WRCOMB support since the 30761911Scokane mem driver checks for it, we should remove it in order 30861911Scokane to free an MTRR for another device */ 30961911Scokane retval = tdfx_clrmtrr(dev); 31061931Scokane#ifdef DEBUG 31161911Scokane if(retval != 0) 31261911Scokane printf("tdfx: For some reason, I couldn't clear the mtrr\n"); 31361911Scokane#endif 31461989Scokane /* Remove device entry when it can no longer be accessed */ 31561989Scokane destroy_dev(tdfx_info->devt); 31661911Scokane return(0); 31761911Scokane} 31861911Scokane 31961911Scokanestatic int 32061911Scokanetdfx_shutdown(device_t dev) { 32161931Scokane#ifdef DEBUG 32261911Scokane device_printf(dev, "tdfx: Device Shutdown\n"); 32361911Scokane#endif 32461911Scokane return 0; 32561911Scokane} 32661911Scokane 32761911Scokanestatic int 32861911Scokanetdfx_clrmtrr(device_t dev) { 32961911Scokane /* This function removes the MTRR set by the attach call, so it can be used 33061911Scokane * in the future by other drivers. 33161911Scokane */ 33261911Scokane int retval, act; 33361911Scokane struct tdfx_softc *tdfx_info = device_get_softc(dev); 33461911Scokane 33561911Scokane act = MEMRANGE_SET_REMOVE; 33661911Scokane retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 33761911Scokane return retval; 33861911Scokane} 33961911Scokane 34061911Scokanestatic int 34161911Scokanetdfx_setmtrr(device_t dev) { 34261911Scokane /* 34361911Scokane * This is the MTRR setting function for the 3dfx card. It is called from 34461911Scokane * tdfx_attach. If we can't set the MTRR properly, it's not the end of the 34561911Scokane * world. We can still continue, just with slightly (very slightly) degraded 34661911Scokane * performance. 34761911Scokane */ 34861911Scokane int retval = 0, act; 34961911Scokane struct tdfx_softc *tdfx_info = device_get_softc(dev); 35061911Scokane 35161911Scokane /* The older Voodoo cards have a shorter memrange than the newer ones */ 35261911Scokane if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) == 35363488Scokane PCI_DEVICE_3DFX_VOODOO2)) { 35461911Scokane tdfx_info->mrdesc.mr_len = 0x400000; 35563488Scokane 35663488Scokane /* The memory descriptor is described as the top 15 bits of the real 35763488Scokane address */ 35863488Scokane tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000; 35963488Scokane } 36061911Scokane else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) || 36163488Scokane (pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) { 36261911Scokane tdfx_info->mrdesc.mr_len = 0x1000000; 36363488Scokane /* The Voodoo3 and Banshee LFB is the second memory address */ 36463488Scokane /* The memory descriptor is described as the top 15 bits of the real 36563488Scokane address */ 36663488Scokane tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000; 36763488Scokane } 36863488Scokane else 36963488Scokane return 0; 37061911Scokane /* 37161911Scokane * The Alliance Pro Motion AT3D was not mentioned in the linux 37261911Scokane * driver as far as MTRR support goes, so I just won't put the 37361911Scokane * code in here for it. This is where it should go, though. 37461911Scokane */ 37561911Scokane 37661911Scokane /* Firstly, try to set write combining */ 37761911Scokane tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE; 37861911Scokane bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4); 37961911Scokane act = MEMRANGE_SET_UPDATE; 38061911Scokane retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 38161911Scokane 38261911Scokane if(retval == 0) { 38361931Scokane#ifdef DEBUG 38461911Scokane device_printf(dev, "MTRR Set Correctly for tdfx\n"); 38561911Scokane#endif 38661911Scokane } else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) || 38761911Scokane (pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) { 38861911Scokane /* if, for some reason we can't set the WRCOMB range with the V1/V2, we 38961911Scokane * can still possibly use the UNCACHEABLE region for it instead, and help 39061911Scokane * out in a small way */ 39161911Scokane tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE; 39261911Scokane /* This length of 1000h was taken from the linux device driver... */ 39361911Scokane tdfx_info->mrdesc.mr_len = 0x1000; 39461911Scokane 39561911Scokane /* 39661911Scokane * If, for some reason, we can't set the MTRR (N/A?) we may still continue 39761911Scokane */ 39861931Scokane#ifdef DEBUG 39961911Scokane if(retval == 0) { 40061911Scokane device_printf(dev, "MTRR Set Type Uncacheable 40161911Scokane %x\n", (u_int32_t)tdfx_info->mrdesc.mr_base); 40261911Scokane } else { 40361911Scokane device_printf(dev, "Couldn't Set MTRR\n"); 40461911Scokane } 40561911Scokane#endif 40661911Scokane } 40761931Scokane#ifdef DEBUG 40861911Scokane else { 40961911Scokane device_printf(dev, "Couldn't Set MTRR\n"); 41061911Scokane return 0; 41161911Scokane } 41261911Scokane#endif 41361911Scokane return 0; 41461911Scokane} 41561911Scokane 41661911Scokanestatic int 41761911Scokanetdfx_open(dev_t dev, int flags, int fmt, struct proc *p) 41861911Scokane{ 41961911Scokane /* 42061911Scokane * The open cdev method handles open(2) calls to /dev/3dfx[n] 42161911Scokane * We can pretty much allow any opening of the device. 42261911Scokane */ 42361911Scokane struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 42461911Scokane UNIT(minor(dev))); 42561911Scokane if(tdfx_info->busy != 0) return EBUSY; 42661931Scokane#ifdef DEBUG 42761911Scokane printf("3dfx: Opened by #%d\n", p->p_pid); 42861911Scokane#endif 42961911Scokane /* Set the driver as busy */ 43061911Scokane tdfx_info->busy++; 43161911Scokane return 0; 43261911Scokane} 43361911Scokane 43461911Scokanestatic int 43561911Scokanetdfx_close(dev_t dev, int fflag, int devtype, struct proc* p) 43661911Scokane{ 43761911Scokane /* 43861911Scokane * The close cdev method handles close(2) calls to /dev/3dfx[n] 43961911Scokane * We'll always want to close the device when it's called. 44061911Scokane */ 44161911Scokane struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass, 44261911Scokane UNIT(minor(dev))); 44361911Scokane if(tdfx_info->busy == 0) return EBADF; 44461911Scokane tdfx_info->busy = 0; 44561931Scokane#ifdef DEBUG 44661911Scokane printf("Closed by #%d\n", p->p_pid); 44761911Scokane#endif 44861911Scokane return 0; 44961911Scokane} 45061911Scokane 45161911Scokanestatic int 45261911Scokanetdfx_mmap(dev_t dev, vm_offset_t offset, int nprot) 45361911Scokane{ 45461911Scokane /* 45561911Scokane * mmap(2) is called by a user process to request that an area of memory 45661911Scokane * associated with this device be mapped for the process to work with. Nprot 45761911Scokane * holds the protections requested, PROT_READ, PROT_WRITE, or both. 45861911Scokane */ 45961911Scokane struct tdfx_softc* tdfx_info; 46061911Scokane 46161911Scokane /* Get the configuration for our card XXX*/ 46261911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 46361911Scokane UNIT(minor(dev))); 46461911Scokane 46561911Scokane /* If, for some reason, its not configured, we bail out */ 46661911Scokane if(tdfx_info == NULL) { 46761931Scokane#ifdef DEBUG 46861911Scokane printf("tdfx: tdfx_info (softc) is NULL\n"); 46961911Scokane#endif 47061911Scokane return -1; 47161911Scokane } 47261911Scokane 47361911Scokane /* We must stay within the bound of our address space */ 47461911Scokane if((offset & 0xff000000) == tdfx_info->addr0) 47561911Scokane offset &= 0xffffff; 47663488Scokane 47763488Scokane /* See if the Banshee/V3 LFB is being requested */ 47863488Scokane if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) == 47964085Scokane tdfx_info->addr1) { 48063488Scokane offset &= 0xffffff; 48164085Scokane return atop(rman_get_start(tdfx_info->memrange2) + offset); 48264085Scokane } 48363488Scokane 48461911Scokane if((offset >= 0x1000000) || (offset < 0)) { 48561931Scokane#ifdef DEBUG 48661911Scokane printf("tdfx: offset %x out of range\n", offset); 48761911Scokane#endif 48861911Scokane return -1; 48961911Scokane } 49061911Scokane 49161911Scokane /* atop -> address to page 49261911Scokane * rman_get_start, get the (struct resource*)->r_start member, 49361911Scokane * the mapping base address. 49461911Scokane */ 49561911Scokane return atop(rman_get_start(tdfx_info->memrange) + offset); 49661911Scokane} 49761911Scokane 49861911Scokanestatic int 49961911Scokanetdfx_query_boards(void) { 50061911Scokane /* 50161911Scokane * This returns the number of installed tdfx cards, we have been keeping 50261911Scokane * count, look at tdfx_attach 50361911Scokane */ 50461911Scokane return tdfx_count; 50561911Scokane} 50661911Scokane 50761911Scokanestatic int 50861911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod) 50961911Scokane{ 51061911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 51161911Scokane /* Various return values 8bit-32bit */ 51261911Scokane u_int8_t ret_byte; 51361911Scokane u_int16_t ret_word; 51461911Scokane u_int32_t ret_dword; 51561911Scokane struct tdfx_softc* tdfx_info = NULL; 51661911Scokane 51761911Scokane /* This one depend on the tdfx_* structs being properly initialized */ 51861911Scokane 51961911Scokane /*piod->device &= 0xf;*/ 52061911Scokane if((piod == NULL) ||(tdfx_count <= piod->device) || 52161911Scokane (piod->device < 0)) { 52261931Scokane#ifdef DEBUG 52361911Scokane printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n"); 52461911Scokane#endif 52561911Scokane return -EINVAL; 52661911Scokane } 52761911Scokane 52861911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 52961911Scokane piod->device); 53061911Scokane 53161911Scokane if(tdfx_info == NULL) return -ENXIO; 53261911Scokane 53361911Scokane /* We must restrict the size reads from the port, since to high or low of a 53461911Scokane * size witll result in wrong data being passed, and that's bad */ 53561911Scokane /* A few of these were pulled during the attach phase */ 53661911Scokane switch(piod->port) { 53761911Scokane case PCI_VENDOR_ID_FREEBSD: 53861911Scokane if(piod->size != 2) return -EINVAL; 53961911Scokane copyout(&tdfx_info->vendor, piod->value, piod->size); 54061911Scokane return 0; 54161911Scokane case PCI_DEVICE_ID_FREEBSD: 54261911Scokane if(piod->size != 2) return -EINVAL; 54361911Scokane copyout(&tdfx_info->type, piod->value, piod->size); 54461911Scokane return 0; 54561911Scokane case PCI_BASE_ADDRESS_0_FREEBSD: 54661911Scokane if(piod->size != 4) return -EINVAL; 54761911Scokane copyout(&tdfx_info->addr0, piod->value, piod->size); 54861911Scokane return 0; 54965146Scokane case PCI_BASE_ADDRESS_1_FREEBSD: 55065146Scokane if(piod->size != 4) return -EINVAL; 55165146Scokane copyout(&tdfx_info->addr1, piod->value, piod->size); 55265146Scokane return 0; 55365146Scokane case PCI_PRIBUS_FREEBSD: 55465146Scokane if(piod->size != 1) return -EINVAL; 55565146Scokane break; 55665146Scokane case PCI_IOBASE_0_FREEBSD: 55765146Scokane if(piod->size != 2) return -EINVAL; 55865146Scokane break; 55965146Scokane case PCI_IOLIMIT_0_FREEBSD: 56065146Scokane if(piod->size != 2) return -EINVAL; 56165146Scokane break; 56261911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 56361911Scokane if(piod->size != 4) return -EINVAL; 56461911Scokane break; 56561911Scokane case PCI_REVISION_ID_FREEBSD: 56661911Scokane if(piod->size != 1) return -EINVAL; 56761911Scokane break; 56861911Scokane case SST1_PCI_SPECIAL4_FREEBSD: 56961911Scokane if(piod->size != 4) return -EINVAL; 57061911Scokane break; 57161911Scokane default: 57261911Scokane return -EINVAL; 57361911Scokane } 57461911Scokane 57561911Scokane 57661911Scokane /* Read the value and return */ 57761911Scokane switch(piod->size) { 57861911Scokane case 1: 57961911Scokane ret_byte = pci_read_config(tdfx_info[piod->device].dev, 58061911Scokane piod->port, 1); 58161911Scokane copyout(&ret_byte, piod->value, 1); 58261911Scokane break; 58361911Scokane case 2: 58461911Scokane ret_word = pci_read_config(tdfx_info[piod->device].dev, 58561911Scokane piod->port, 2); 58661911Scokane copyout(&ret_word, piod->value, 2); 58761911Scokane break; 58861911Scokane case 4: 58961911Scokane ret_dword = pci_read_config(tdfx_info[piod->device].dev, 59061911Scokane piod->port, 4); 59161911Scokane copyout(&ret_dword, piod->value, 4); 59261911Scokane break; 59361911Scokane default: 59461911Scokane return -EINVAL; 59561911Scokane } 59661911Scokane return 0; 59761911Scokane} 59861911Scokane 59961911Scokanestatic int 60061911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod) 60161911Scokane{ 60261911Scokane /* XXX Comment this later, after careful inspection and spring cleaning :) */ 60361911Scokane /* Return vals */ 60461911Scokane u_int8_t ret_byte; 60561911Scokane u_int16_t ret_word; 60661911Scokane u_int32_t ret_dword; 60761911Scokane 60861911Scokane /* Port vals, mask */ 60961911Scokane u_int32_t retval, preval, mask; 61061911Scokane struct tdfx_softc* tdfx_info = NULL; 61161911Scokane 61261911Scokane 61361911Scokane if((piod == NULL) || (piod->device >= (tdfx_count & 61461911Scokane 0xf))) { 61561931Scokane#ifdef DEBUG 61661911Scokane printf("tdfx: Bad struct or device in tdfx_query_update\n"); 61761911Scokane#endif 61861911Scokane return -EINVAL; 61961911Scokane } 62061911Scokane 62161911Scokane tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 62261911Scokane piod->device); 62361911Scokane if(tdfx_info == NULL) return -ENXIO; 62461911Scokane /* Code below this line in the fuction was taken from the 62561911Scokane * Linux driver and converted for freebsd. */ 62661911Scokane 62761911Scokane /* Check the size for all the ports, to make sure stuff doesn't get messed up 62861911Scokane * by poorly written clients */ 62961911Scokane 63061911Scokane switch(piod->port) { 63161911Scokane case PCI_COMMAND_FREEBSD: 63261911Scokane if(piod->size != 2) return -EINVAL; 63361911Scokane break; 63461911Scokane case SST1_PCI_SPECIAL1_FREEBSD: 63561911Scokane if(piod->size != 4) return -EINVAL; 63661911Scokane break; 63761911Scokane case SST1_PCI_SPECIAL2_FREEBSD: 63861911Scokane if(piod->size != 4) return -EINVAL; 63961911Scokane break; 64061911Scokane case SST1_PCI_SPECIAL3_FREEBSD: 64161911Scokane if(piod->size != 4) return -EINVAL; 64261911Scokane break; 64361911Scokane case SST1_PCI_SPECIAL4_FREEBSD: 64461911Scokane if(piod->size != 4) return -EINVAL; 64561911Scokane break; 64661911Scokane default: 64761911Scokane return -EINVAL; 64861911Scokane } 64961911Scokane /* Read the current value */ 65061911Scokane retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4); 65161911Scokane 65261911Scokane /* These set up a mask to use, since apparently they wanted to write 4 bytes 65361911Scokane * at once to the ports */ 65461911Scokane switch (piod->size) { 65561911Scokane case 1: 65661911Scokane copyin(piod->value, &ret_byte, 1); 65761911Scokane preval = ret_byte << (8 * (piod->port & 0x3)); 65861911Scokane mask = 0xff << (8 * (piod->port & 0x3)); 65961911Scokane break; 66061911Scokane case 2: 66161911Scokane copyin(piod->value, &ret_word, 2); 66261911Scokane preval = ret_word << (8 * (piod->port & 0x3)); 66361911Scokane mask = 0xffff << (8 * (piod->port & 0x3)); 66461911Scokane break; 66561911Scokane case 4: 66661911Scokane copyin(piod->value, &ret_dword, 4); 66761911Scokane preval = ret_dword; 66861911Scokane mask = ~0; 66961911Scokane break; 67061911Scokane default: 67161911Scokane return -EINVAL; 67261911Scokane } 67361911Scokane /* Finally, combine the values and write it to the port */ 67461911Scokane retval = (retval & ~mask) | preval; 67561911Scokane pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4); 67661911Scokane 67761911Scokane return 0; 67861911Scokane} 67961911Scokane 68063488Scokane/* For both of these, I added a variable named workport of type u_int so 68163488Scokane * that I could eliminate the warning about my data type size. The 68263488Scokane * applications expect the port to be of type short, so I needed to change 68363488Scokane * this within the function */ 68461911Scokanestatic int 68561911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod) 68661911Scokane{ 68761911Scokane /* Return val */ 68861911Scokane u_int8_t ret_byte; 68963488Scokane u_int workport; 69065146Scokane struct tdfx_softc *tdfx_info = 69165146Scokane (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 69265146Scokane 69361911Scokane /* Restricts the access of ports other than those we use */ 69465146Scokane if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) || 69565146Scokane (piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) && 69665146Scokane (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 69761911Scokane return -EPERM; 69861911Scokane 69961911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 70061911Scokane if(piod->size != 1) { 70161911Scokane return -EINVAL; 70261911Scokane } 70361911Scokane 70461911Scokane /* Write the data to the intended port */ 70563488Scokane workport = piod->port; 70663488Scokane ret_byte = inb(workport); 70761911Scokane copyout(&ret_byte, piod->value, sizeof(u_int8_t)); 70861911Scokane return 0; 70961911Scokane} 71061911Scokane 71161911Scokanestatic int 71261911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod) 71361911Scokane{ 71461911Scokane /* return val */ 71561911Scokane u_int8_t ret_byte; 71663488Scokane u_int workport; 71765146Scokane struct tdfx_softc *tdfx_info = (struct 71865146Scokane tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 71961911Scokane /* Replace old switch w/ massive if(...) */ 72061911Scokane /* Restricts the access of ports other than those we use */ 72165146Scokane if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) && 72265146Scokane (piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ && 72365146Scokane (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 72461911Scokane return -EPERM; 72561911Scokane 72661911Scokane /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 72761911Scokane if(piod->size != 1) { 72861911Scokane return -EINVAL; 72961911Scokane } 73061911Scokane 73161911Scokane /* Write the data to the intended port */ 73261911Scokane copyin(piod->value, &ret_byte, sizeof(u_int8_t)); 73363488Scokane workport = piod->port; 73463488Scokane outb(workport, ret_byte); 73561911Scokane return 0; 73661911Scokane} 73761911Scokane 73861911Scokanestatic int 73961911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod) 74061911Scokane{ 74161911Scokane /* There are three sub-commands to the query 0x33 */ 74261911Scokane switch(_IOC_NR(cmd)) { 74361911Scokane case 2: 74461911Scokane return tdfx_query_boards(); 74561911Scokane break; 74661911Scokane case 3: 74761911Scokane return tdfx_query_fetch(cmd, piod); 74861911Scokane break; 74961911Scokane case 4: 75061911Scokane return tdfx_query_update(cmd, piod); 75161911Scokane break; 75261911Scokane default: 75361911Scokane /* In case we are thrown a bogus sub-command! */ 75461931Scokane#ifdef DEBUG 75561911Scokane printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd)); 75661911Scokane#endif 75761911Scokane return -EINVAL; 75861911Scokane }; 75961911Scokane} 76061911Scokane 76161911Scokanestatic int 76261911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod) 76361911Scokane{ 76461911Scokane /* Two types of PIO, INPUT and OUTPUT, as the name suggests */ 76561911Scokane switch(_IOC_DIR(cmd)) { 76661911Scokane case IOCV_OUT: 76761911Scokane return tdfx_do_pio_rd(piod); 76861911Scokane break; 76961911Scokane case IOCV_IN: 77061911Scokane return tdfx_do_pio_wt(piod); 77161911Scokane break; 77261911Scokane default: 77361911Scokane return -EINVAL; 77461911Scokane }; 77561911Scokane} 77661911Scokane 77761911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO, 77861911Scokane * normally, you would read in the data pointed to by data, then write your 77961911Scokane * output to it. The ioctl *should* normally return zero if everything is 78061911Scokane * alright, but 3dfx didn't make it that way... 78161911Scokane * 78261911Scokane * For all of the ioctl code, in the event of a real error, 78361911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this 78461911Scokane * is that the ioctls actually RET information back to the program 78561911Scokane * sometimes, rather than filling it in the passed structure. We 78661911Scokane * want to distinguish errors from useful data, and maintain compatibility. 78761911Scokane * 78861911Scokane * There is this portion of the proc struct called p_retval[], we can store a 78961911Scokane * return value in p->p_retval[0] and place the return value if it is positive 79061911Scokane * in there, then we can return 0 (good). If the return value is negative, we 79161911Scokane * can return -retval and the error should be properly handled. 79261911Scokane */ 79361911Scokanestatic int 79461911Scokanetdfx_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc * p) 79561911Scokane{ 79661911Scokane int retval = 0; 79761911Scokane struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data; 79861931Scokane#ifdef DEBUG 79961911Scokane printf("IOCTL'd by #%d, cmd: 0x%x, data: 0x%x\n", p->p_pid, (u_int32_t)cmd, 80061911Scokane (unsigned int)piod); 80161911Scokane#endif 80261911Scokane switch(_IOC_TYPE(cmd)) { 80361911Scokane /* Return the real error if negative, or simply stick the valid return 80461911Scokane * in p->p_retval */ 80561911Scokane case 0x33: 80661911Scokane /* The '3'(0x33) type IOCTL is for querying the installed cards */ 80761911Scokane if((retval = tdfx_do_query(cmd, piod)) > 0) p->p_retval[0] = retval; 80861911Scokane else return -retval; 80961911Scokane break; 81061911Scokane case 0: 81161911Scokane /* The 0 type IOCTL is for programmed I/O methods */ 81261911Scokane if((tdfx_do_pio(cmd, piod)) > 0) p->p_retval[0] = retval; 81361911Scokane else return -retval; 81461911Scokane break; 81561911Scokane default: 81661911Scokane /* Technically, we won't reach this from linux emu, but when glide 81761911Scokane * finally gets ported, watch out! */ 81861931Scokane#ifdef DEBUG 81961911Scokane printf("Bad IOCTL from #%d\n", p->p_pid); 82061911Scokane#endif 82161911Scokane return ENXIO; 82261911Scokane } 82361911Scokane 82461911Scokane return 0; 82561911Scokane} 82661911Scokane 82761931Scokane#ifdef TDFX_LINUX 82861931Scokane/* 82961931Scokane * Linux emulation IOCTL for /dev/tdfx 83061931Scokane */ 83161931Scokanestatic int 83261931Scokanelinux_ioctl_tdfx(struct proc* p, struct linux_ioctl_args* args) 83361931Scokane{ 83461931Scokane int error = 0; 83561931Scokane u_long cmd = args->cmd & 0xffff; 83661911Scokane 83761931Scokane /* The structure passed to ioctl has two shorts, one int 83861931Scokane and one void*. */ 83961931Scokane char d_pio[2*sizeof(short) + sizeof(int) + sizeof(void*)]; 84061931Scokane 84161931Scokane struct file *fp = p->p_fd->fd_ofiles[args->fd]; 84261931Scokane 84361931Scokane /* We simply copy the data and send it right to ioctl */ 84461931Scokane copyin((caddr_t)args->arg, &d_pio, sizeof(d_pio)); 84561931Scokane error = fo_ioctl(fp, cmd, (caddr_t)&d_pio, p); 84661931Scokane return error; 84761931Scokane} 84861931Scokane#endif /* TDFX_LINUX */ 84961931Scokane 85061931Scokane 85161911Scokane/* This is the device driver struct. This is sent to the driver subsystem to 85261911Scokane * register the method structure and the info strcut space for this particular 85361911Scokane * instance of the driver. 85461911Scokane */ 85561911Scokanestatic driver_t tdfx_driver = { 85661911Scokane "tdfx", 85761911Scokane tdfx_methods, 85861911Scokane sizeof(struct tdfx_softc), 85961911Scokane}; 86061911Scokane 86161911Scokane/* Tell Mr. Kernel about us! */ 86261911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0); 86361911Scokane 86461911Scokane 86561911Scokane#endif /* NPCI */ 866