tdfx_pci.c revision 201223
134461Speter/*- 234461Speter * Copyright (c) 2000-2001 by Coleman Kane <cokane@FreeBSD.org> 334461Speter * All rights reserved. 434461Speter * 534461Speter * Redistribution and use in source and binary forms, with or without 634461Speter * modification, are permitted provided that the following conditions 734461Speter * are met: 834461Speter * 1. Redistributions of source code must retain the above copyright 934461Speter * notice, this list of conditions and the following disclaimer. 1034461Speter * 2. Redistributions in binary form must reproduce the above copyright 1134461Speter * notice, this list of conditions and the following disclaimer in the 1234461Speter * documentation and/or other materials provided with the distribution. 1334461Speter * 3. All advertising materials mentioning features or use of this software 1434461Speter * must display the following acknowledgement: 1534461Speter * This product includes software developed by Gardner Buchanan. 1634461Speter * 4. The name of Gardner Buchanan may not be used to endorse or promote 1734461Speter * products derived from this software without specific prior written 1834461Speter * permission. 1934461Speter * 2034461Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2134461Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2234461Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2334461Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2434461Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2534461Speter * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2634461Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2732785Speter * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2832785Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2932785Speter * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3032785Speter */ 3132785Speter 3232785Speter#include <sys/cdefs.h> 3332785Speter__FBSDID("$FreeBSD: head/sys/dev/tdfx/tdfx_pci.c 201223 2009-12-29 21:51:28Z rnoland $"); 3432785Speter 3532785Speter/* 3dfx driver for FreeBSD 4.x - Finished 11 May 2000, 12:25AM ET 3632785Speter * 3732785Speter * Copyright (C) 2000-2001, by Coleman Kane <cokane@FreeBSD.org>, 3832785Speter * based upon the 3dfx driver written for linux, by Daryll Straus, Jon Taylor, 3932785Speter * and Jens Axboe, located at http://linux.3dfx.com. 4032785Speter */ 4132785Speter 4232785Speter#include <sys/param.h> 4332785Speter 4432785Speter#include <sys/bus.h> 4532785Speter#include <sys/cdefs.h> 4632785Speter#include <sys/conf.h> 4732785Speter#include <sys/fcntl.h> 4832785Speter#include <sys/file.h> 4932785Speter#include <sys/filedesc.h> 5032785Speter#include <sys/filio.h> 5132785Speter#include <sys/ioccom.h> 5232785Speter#include <sys/kernel.h> 5332785Speter#include <sys/module.h> 5432785Speter#include <sys/malloc.h> 5532785Speter#include <sys/mman.h> 5632785Speter#include <sys/signalvar.h> 5732785Speter#include <sys/systm.h> 5832785Speter#include <sys/uio.h> 5932785Speter 6032785Speter#include <dev/pci/pcivar.h> 6132785Speter#include <dev/pci/pcireg.h> 6232785Speter 6332785Speter#include <vm/vm.h> 6432785Speter#include <vm/vm_kern.h> 6532785Speter#include <vm/pmap.h> 6632785Speter#include <vm/vm_extern.h> 6732785Speter 6832785Speter/* rman.h depends on machine/bus.h */ 6932785Speter#include <machine/resource.h> 7032785Speter#include <machine/bus.h> 7132785Speter#include <sys/rman.h> 7232785Speter 7332785Speter#include <dev/tdfx/tdfx_io.h> 7432785Speter#include <dev/tdfx/tdfx_vars.h> 7532785Speter#include <dev/tdfx/tdfx_pci.h> 7632785Speter 7732785Speter 7832785Speterstatic devclass_t tdfx_devclass; 7932785Speter 8032785Speter 8132785Speterstatic int tdfx_count = 0; 8232785Speter 8332785Speter 8432785Speter/* Set up the boot probe/attach routines */ 8532785Speterstatic device_method_t tdfx_methods[] = { 8632785Speter DEVMETHOD(device_probe, tdfx_probe), 8732785Speter DEVMETHOD(device_attach, tdfx_attach), 8832785Speter DEVMETHOD(device_detach, tdfx_detach), 8932785Speter DEVMETHOD(device_shutdown, tdfx_shutdown), 9032785Speter { 0, 0 } 9132785Speter}; 9232785Speter 9332785SpeterMALLOC_DEFINE(M_TDFX,"tdfx_driver","3DFX Graphics[/2D]/3D Accelerator(s)"); 9432785Speter 9532785Speter/* Char. Dev. file operations structure */ 9632785Speterstatic struct cdevsw tdfx_cdev = { 9732785Speter .d_version = D_VERSION, 9832785Speter .d_flags = D_NEEDGIANT, 9932785Speter .d_open = tdfx_open, 10032785Speter .d_close = tdfx_close, 10132785Speter .d_ioctl = tdfx_ioctl, 10232785Speter .d_mmap = tdfx_mmap, 10332785Speter .d_name = "tdfx", 10432785Speter}; 10532785Speter 10632785Speterstatic int 10732785Spetertdfx_probe(device_t dev) 10832785Speter{ 10932785Speter /* 11032785Speter * probe routine called on kernel boot to register supported devices. We get 11132785Speter * a device structure to work with, and we can test the VENDOR/DEVICE IDs to 11232785Speter * see if this PCI device is one that we support. Return BUS_PRROBE_DEFAULT 11332785Speter * if yes, ENXIO if not. 11432785Speter */ 11532785Speter switch(pci_get_devid(dev)) { 11632785Speter case PCI_DEVICE_ALLIANCE_AT3D: 11732785Speter device_set_desc(dev, "ProMotion At3D 3D Accelerator"); 11832785Speter return BUS_PROBE_DEFAULT; 11932785Speter case PCI_DEVICE_3DFX_VOODOO2: 12032785Speter device_set_desc(dev, "3DFX Voodoo II 3D Accelerator"); 12132785Speter return BUS_PROBE_DEFAULT; 12232785Speter /*case PCI_DEVICE_3DFX_BANSHEE: 12332785Speter device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator"); 12432785Speter return BUS_PROBE_DEFAULT; 12532785Speter case PCI_DEVICE_3DFX_VOODOO3: 12632785Speter device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator"); 12732785Speter return BUS_PROBE_DEFAULT;*/ 12832785Speter case PCI_DEVICE_3DFX_VOODOO1: 12932785Speter device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator"); 13032785Speter return BUS_PROBE_DEFAULT; 13132785Speter }; 13232785Speter 13332785Speter return ENXIO; 13432785Speter} 13532785Speter 13632785Speterstatic int 13732785Spetertdfx_attach(device_t dev) { 13832785Speter /* 13932785Speter * The attach routine is called after the probe routine successfully says it 14032785Speter * supports a given card. We now proceed to initialize this card for use with 14132785Speter * the system. I want to map the device memory for userland allocation and 14232785Speter * fill an information structure with information on this card. I'd also like 14332785Speter * to set Write Combining with the MTRR code so that we can hopefully speed 14432785Speter * up memory writes. The last thing is to register the character device 14532785Speter * interface to the card, so we can open it from /dev/3dfxN, where N is a 14632785Speter * small, whole number. 14732785Speter */ 14832785Speter struct tdfx_softc *tdfx_info; 14932785Speter u_long val; 15032785Speter /* rid value tells bus_alloc_resource where to find the addresses of ports or 15132785Speter * of memory ranges in the PCI config space*/ 15232785Speter int rid = PCIR_BAR(0); 15332785Speter 15432785Speter /* Increment the card counter (for the ioctl code) */ 15532785Speter tdfx_count++; 15632785Speter 15732785Speter /* Enable MemMap on Voodoo */ 15832785Speter val = pci_read_config(dev, PCIR_COMMAND, 2); 15932785Speter val |= (PCIM_CMD_MEMEN); 16032785Speter pci_write_config(dev, PCIR_COMMAND, val, 2); 16132785Speter val = pci_read_config(dev, PCIR_COMMAND, 2); 16232785Speter 16332785Speter /* Fill the soft config struct with info about this device*/ 16432785Speter tdfx_info = device_get_softc(dev); 16532785Speter tdfx_info->dev = dev; 16632785Speter tdfx_info->vendor = pci_get_vendor(dev); 16732785Speter tdfx_info->type = pci_get_devid(dev) >> 16; 16832785Speter tdfx_info->bus = pci_get_bus(dev); 16932785Speter tdfx_info->dv = pci_get_slot(dev); 17032785Speter tdfx_info->curFile = NULL; 17132785Speter 17232785Speter /* 17332785Speter * Get the Memory Location from the PCI Config, mask out lower word, since 17432785Speter * the config space register is only one word long (this is nicer than a 17532785Speter * bitshift). 17632785Speter */ 17732785Speter tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000); 17832785Speter#ifdef DEBUG 17932785Speter device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0); 18032785Speter#endif 18132785Speter /* Notify the VM that we will be mapping some memory later */ 18232785Speter tdfx_info->memrange = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 18332785Speter &rid, RF_ACTIVE | RF_SHAREABLE); 18432785Speter if(tdfx_info->memrange == NULL) { 18532785Speter#ifdef DEBUG 18632785Speter device_printf(dev, "Error mapping mem, won't be able to use mmap()\n"); 18732785Speter#endif 18832785Speter tdfx_info->memrid = 0; 18932785Speter } 19032785Speter else { 19132785Speter tdfx_info->memrid = rid; 19232785Speter#ifdef DEBUG 19332785Speter device_printf(dev, "Mapped to: 0x%x\n", 19432785Speter (unsigned int)rman_get_start(tdfx_info->memrange)); 19532785Speter#endif 19632785Speter } 19732785Speter 19832785Speter /* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */ 19932785Speter if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 || 20032785Speter pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) { 20132785Speter rid = 0x14; /* 2nd mem map */ 20232785Speter tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000); 20332785Speter#ifdef DEBUG 20432785Speter device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1); 20532785Speter#endif 20632785Speter tdfx_info->memrange2 = bus_alloc_resource_any(dev, 20732785Speter SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE); 20832785Speter if(tdfx_info->memrange2 == NULL) { 20932785Speter#ifdef DEBUG 21032785Speter device_printf(dev, "Mem1 couldn't be allocated, glide may not work."); 21132785Speter#endif 21232785Speter tdfx_info->memrid2 = 0; 21332785Speter } 21432785Speter else { 21532785Speter tdfx_info->memrid2 = rid; 21632785Speter } 21732785Speter /* Now to map the PIO stuff */ 21832785Speter rid = PCIR_IOBASE0_2; 21932785Speter tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2); 22032785Speter tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0; 22132785Speter tdfx_info->piorange = bus_alloc_resource_any(dev, 22232785Speter SYS_RES_IOPORT, &rid, RF_ACTIVE | RF_SHAREABLE); 22332785Speter if(tdfx_info->piorange == NULL) { 22432785Speter#ifdef DEBUG 22532785Speter device_printf(dev, "Couldn't map PIO range."); 22632785Speter#endif 22732785Speter tdfx_info->piorid = 0; 22832785Speter } 22932785Speter else { 23032785Speter tdfx_info->piorid = rid; 23132785Speter } 23232785Speter } else { 23332785Speter tdfx_info->addr1 = 0; 23432785Speter tdfx_info->memrange2 = NULL; 23532785Speter tdfx_info->piorange = NULL; 23632785Speter } 23732785Speter 23832785Speter /* 23932785Speter * Set Writecombining, or at least Uncacheable for the memory region, if we 24032785Speter * are able to 24132785Speter */ 24232785Speter 24332785Speter if(tdfx_setmtrr(dev) != 0) { 24432785Speter#ifdef DEBUG 24532785Speter device_printf(dev, "Some weird error setting MTRRs"); 24632785Speter#endif 24732785Speter return -1; 24832785Speter } 24932785Speter 25032785Speter /* 25132785Speter * make_dev registers the cdev to access the 3dfx card from /dev 25232785Speter * use hex here for the dev num, simply to provide better support if > 10 25332785Speter * voodoo cards, for the mad. The user must set the link, or use MAKEDEV. 25432785Speter * Why would we want that many voodoo cards anyhow? 25532785Speter */ 25632785Speter tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev), 25732785Speter UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev)); 25832785Speter tdfx_info->devt->si_drv1 = tdfx_info; 25932785Speter 26032785Speter return 0; 26132785Speter} 26232785Speter 26332785Speterstatic int 26432785Spetertdfx_detach(device_t dev) { 26532785Speter struct tdfx_softc* tdfx_info; 26632785Speter int retval; 26732785Speter tdfx_info = device_get_softc(dev); 26832785Speter 26932785Speter /* Delete allocated resource, of course */ 27032785Speter bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid, 27132785Speter tdfx_info->memrange); 27232785Speter 27332785Speter /* Release extended Voodoo3/Banshee resources */ 27432785Speter if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE || 27532785Speter pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) { 27632785Speter if(tdfx_info->memrange2 != NULL) 27732785Speter bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2, 27832785Speter tdfx_info->memrange); 27932785Speter /* if(tdfx_info->piorange != NULL) 28032785Speter bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid, 28132785Speter tdfx_info->piorange);*/ 28232785Speter } 28332785Speter 28432785Speter /* Though it is safe to leave the WRCOMB support since the 28532785Speter mem driver checks for it, we should remove it in order 28632785Speter to free an MTRR for another device */ 28732785Speter retval = tdfx_clrmtrr(dev); 28832785Speter#ifdef DEBUG 28932785Speter if(retval != 0) 29032785Speter printf("tdfx: For some reason, I couldn't clear the mtrr\n"); 29132785Speter#endif 29232785Speter /* Remove device entry when it can no longer be accessed */ 29332785Speter destroy_dev(tdfx_info->devt); 29432785Speter return(0); 29532785Speter} 29632785Speter 29732785Speterstatic int 29832785Spetertdfx_shutdown(device_t dev) { 29932785Speter#ifdef DEBUG 30032785Speter device_printf(dev, "tdfx: Device Shutdown\n"); 30132785Speter#endif 30232785Speter return 0; 30332785Speter} 30432785Speter 30532785Speterstatic int 30632785Spetertdfx_clrmtrr(device_t dev) { 30732785Speter /* This function removes the MTRR set by the attach call, so it can be used 30832785Speter * in the future by other drivers. 30932785Speter */ 31032785Speter int retval, act; 31132785Speter struct tdfx_softc *tdfx_info = device_get_softc(dev); 31232785Speter 31332785Speter act = MEMRANGE_SET_REMOVE; 31432785Speter retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 31532785Speter return retval; 31632785Speter} 31732785Speter 31832785Speterstatic int 31932785Spetertdfx_setmtrr(device_t dev) { 32032785Speter /* 32132785Speter * This is the MTRR setting function for the 3dfx card. It is called from 32232785Speter * tdfx_attach. If we can't set the MTRR properly, it's not the end of the 32332785Speter * world. We can still continue, just with slightly (very slightly) degraded 32432785Speter * performance. 32532785Speter */ 32632785Speter int retval = 0, act; 32732785Speter struct tdfx_softc *tdfx_info = device_get_softc(dev); 32832785Speter 32932785Speter /* The older Voodoo cards have a shorter memrange than the newer ones */ 33032785Speter if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) == 33132785Speter PCI_DEVICE_3DFX_VOODOO2)) { 33232785Speter tdfx_info->mrdesc.mr_len = 0x400000; 33332785Speter 33432785Speter /* The memory descriptor is described as the top 15 bits of the real 33532785Speter address */ 33632785Speter tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000; 33732785Speter } 33832785Speter else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) || 33932785Speter (pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) { 34032785Speter tdfx_info->mrdesc.mr_len = 0x1000000; 34132785Speter /* The Voodoo3 and Banshee LFB is the second memory address */ 34232785Speter /* The memory descriptor is described as the top 15 bits of the real 34332785Speter address */ 34432785Speter tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000; 34532785Speter } 34632785Speter else 34732785Speter return 0; 34832785Speter /* 34932785Speter * The Alliance Pro Motion AT3D was not mentioned in the linux 35032785Speter * driver as far as MTRR support goes, so I just won't put the 35132785Speter * code in here for it. This is where it should go, though. 35232785Speter */ 35332785Speter 35432785Speter /* Firstly, try to set write combining */ 35532785Speter tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE; 35632785Speter bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4); 35732785Speter act = MEMRANGE_SET_UPDATE; 35832785Speter retval = mem_range_attr_set(&tdfx_info->mrdesc, &act); 35932785Speter 36032785Speter if(retval == 0) { 36132785Speter#ifdef DEBUG 36232785Speter device_printf(dev, "MTRR Set Correctly for tdfx\n"); 36332785Speter#endif 36432785Speter } else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) || 36532785Speter (pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) { 36632785Speter /* if, for some reason we can't set the WRCOMB range with the V1/V2, we 36732785Speter * can still possibly use the UNCACHEABLE region for it instead, and help 36832785Speter * out in a small way */ 36932785Speter tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE; 37032785Speter /* This length of 1000h was taken from the linux device driver... */ 37132785Speter tdfx_info->mrdesc.mr_len = 0x1000; 37232785Speter 37332785Speter /* 37432785Speter * If, for some reason, we can't set the MTRR (N/A?) we may still continue 37532785Speter */ 37632785Speter#ifdef DEBUG 37732785Speter device_printf(dev, "MTRR Set Type Uncacheable %x\n", 37832785Speter (u_int32_t)tdfx_info->mrdesc.mr_base); 37932785Speter#endif 38032785Speter } 38132785Speter#ifdef DEBUG 38232785Speter else { 38332785Speter device_printf(dev, "Couldn't Set MTRR\n"); 38432785Speter return 0; 38532785Speter } 38632785Speter#endif 38732785Speter return 0; 38832785Speter} 38932785Speter 39032785Speterstatic int 39132785Spetertdfx_open(struct cdev *dev, int flags, int fmt, struct thread *td) 39232785Speter{ 39332785Speter /* 39432785Speter * The open cdev method handles open(2) calls to /dev/3dfx[n] 39532785Speter * We can pretty much allow any opening of the device. 39632785Speter */ 39732785Speter struct tdfx_softc *tdfx_info = dev->si_drv1; 39832785Speter if(tdfx_info->busy != 0) return EBUSY; 39932785Speter#ifdef DEBUG 40032785Speter printf("3dfx: Opened by #%d\n", td->td_proc->p_pid); 40132785Speter#endif 40232785Speter /* Set the driver as busy */ 40332785Speter tdfx_info->busy++; 40432785Speter return 0; 40532785Speter} 40632785Speter 40732785Speterstatic int 40832785Spetertdfx_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 40932785Speter{ 41032785Speter /* 41132785Speter * The close cdev method handles close(2) calls to /dev/3dfx[n] 41232785Speter * We'll always want to close the device when it's called. 41332785Speter */ 41432785Speter struct tdfx_softc *tdfx_info = dev->si_drv1; 41532785Speter if(tdfx_info->busy == 0) return EBADF; 41632785Speter tdfx_info->busy = 0; 41732785Speter#ifdef DEBUG 41832785Speter printf("Closed by #%d\n", td->td_proc->p_pid); 41932785Speter#endif 42032785Speter return 0; 42132785Speter} 42232785Speter 42332785Speterstatic int 42432785Spetertdfx_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, 42532785Speter int nprot, vm_memattr_t *memattr) 42632785Speter{ 42732785Speter /* 42832785Speter * mmap(2) is called by a user process to request that an area of memory 42932785Speter * associated with this device be mapped for the process to work with. Nprot 43032785Speter * holds the protections requested, PROT_READ, PROT_WRITE, or both. 43132785Speter */ 43232785Speter 43332785Speter /**** OLD GET CONFIG ****/ 43432785Speter /* struct tdfx_softc* tdfx_info; */ 43532785Speter 43632785Speter /* Get the configuration for our card XXX*/ 43732785Speter /*tdfx_info = dev->si_drv1; */ 43832785Speter /************************/ 43932785Speter 44032785Speter struct tdfx_softc* tdfx_info[2]; 44132785Speter 44232785Speter tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0); 44332785Speter 44432785Speter /* If, for some reason, its not configured, we bail out */ 44532785Speter if(tdfx_info[0] == NULL) { 44632785Speter#ifdef DEBUG 44732785Speter printf("tdfx: tdfx_info (softc) is NULL\n"); 44832785Speter#endif 44932785Speter return -1; 45032785Speter } 45132785Speter 45232785Speter /* We must stay within the bound of our address space */ 45332785Speter if((offset & 0xff000000) == tdfx_info[0]->addr0) { 45432785Speter offset &= 0xffffff; 45532785Speter *paddr = rman_get_start(tdfx_info[0]->memrange) + offset; 45632785Speter return 0; 45732785Speter } 45832785Speter 45932785Speter if(tdfx_count > 1) { 46032785Speter tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1); 46132785Speter if((offset & 0xff000000) == tdfx_info[1]->addr0) { 46232785Speter offset &= 0xffffff; 46332785Speter *paddr = rman_get_start(tdfx_info[1]->memrange) + 46432785Speter offset; 46532785Speter return 0; 46632785Speter } 46732785Speter } 46832785Speter 46932785Speter /* See if the Banshee/V3 LFB is being requested */ 47032785Speter /*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) == 47132785Speter tdfx_info->addr1) { 47232785Speter offset &= 0xffffff; 47332785Speter return atop(rman_get_start(tdfx_info[1]->memrange2) + offset); 47432785Speter }*/ /* VoodooNG code */ 47532785Speter 47632785Speter /* The ret call */ 47732785Speter /* atop -> address to page 47832785Speter * rman_get_start, get the (struct resource*)->r_start member, 47932785Speter * the mapping base address. 48032785Speter */ 48132785Speter return -1; 48232785Speter} 48332785Speter 48432785Speterstatic int 48532785Spetertdfx_query_boards(void) { 48632785Speter /* 48732785Speter * This returns the number of installed tdfx cards, we have been keeping 48832785Speter * count, look at tdfx_attach 48932785Speter */ 49032785Speter return tdfx_count; 49132785Speter} 49232785Speter 49332785Speterstatic int 49432785Spetertdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod) 49532785Speter{ 49632785Speter /* XXX Comment this later, after careful inspection and spring cleaning :) */ 49732785Speter /* Various return values 8bit-32bit */ 49832785Speter u_int8_t ret_byte; 49932785Speter u_int16_t ret_word; 50032785Speter u_int32_t ret_dword; 50132785Speter struct tdfx_softc* tdfx_info = NULL; 50232785Speter 50332785Speter /* This one depend on the tdfx_* structs being properly initialized */ 50432785Speter 50532785Speter /*piod->device &= 0xf;*/ 50632785Speter if((piod == NULL) ||(tdfx_count <= piod->device) || 50732785Speter (piod->device < 0)) { 50832785Speter#ifdef DEBUG 50932785Speter printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n"); 51032785Speter#endif 51132785Speter return -EINVAL; 51232785Speter } 51332785Speter 51432785Speter tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 51532785Speter piod->device); 51632785Speter 51732785Speter if(tdfx_info == NULL) return -ENXIO; 51832785Speter 51932785Speter /* We must restrict the size reads from the port, since to high or low of a 52032785Speter * size witll result in wrong data being passed, and that's bad */ 52132785Speter /* A few of these were pulled during the attach phase */ 52232785Speter switch(piod->port) { 52332785Speter case PCI_VENDOR_ID_FREEBSD: 52426801Speter if(piod->size != 2) return -EINVAL; 52526801Speter copyout(&tdfx_info->vendor, piod->value, piod->size); 52626801Speter return 0; 52726801Speter case PCI_DEVICE_ID_FREEBSD: 52826801Speter if(piod->size != 2) return -EINVAL; 52926801Speter copyout(&tdfx_info->type, piod->value, piod->size); 53026801Speter return 0; 53126801Speter case PCI_BASE_ADDRESS_0_FREEBSD: 53226801Speter if(piod->size != 4) return -EINVAL; 53326801Speter copyout(&tdfx_info->addr0, piod->value, piod->size); 53426801Speter return 0; 53526801Speter case PCI_BASE_ADDRESS_1_FREEBSD: 53626801Speter if(piod->size != 4) return -EINVAL; 53726801Speter copyout(&tdfx_info->addr1, piod->value, piod->size); 53826801Speter return 0; 53926801Speter case PCI_PRIBUS_FREEBSD: 54026801Speter if(piod->size != 1) return -EINVAL; 54126801Speter break; 54226801Speter case PCI_IOBASE_0_FREEBSD: 54326801Speter if(piod->size != 2) return -EINVAL; 54426801Speter break; 54526065Speter case PCI_IOLIMIT_0_FREEBSD: 54626065Speter if(piod->size != 2) return -EINVAL; 54726065Speter break; 54826065Speter case SST1_PCI_SPECIAL1_FREEBSD: 54926065Speter if(piod->size != 4) return -EINVAL; 55026065Speter break; 55126065Speter case PCI_REVISION_ID_FREEBSD: 55226065Speter if(piod->size != 1) return -EINVAL; 55325839Speter break; 55425839Speter case SST1_PCI_SPECIAL4_FREEBSD: 55525839Speter if(piod->size != 4) return -EINVAL; 55625839Speter break; 55725839Speter default: 55825839Speter return -EINVAL; 55925839Speter } 56025839Speter 56125839Speter 56225839Speter /* Read the value and return */ 56325839Speter switch(piod->size) { 56425839Speter case 1: 56525839Speter ret_byte = pci_read_config(tdfx_info[piod->device].dev, 56625839Speter piod->port, 1); 56725839Speter copyout(&ret_byte, piod->value, 1); 56825839Speter break; 56925839Speter case 2: 57025839Speter ret_word = pci_read_config(tdfx_info[piod->device].dev, 57125839Speter piod->port, 2); 57225839Speter copyout(&ret_word, piod->value, 2); 57325839Speter break; 57425839Speter case 4: 57525839Speter ret_dword = pci_read_config(tdfx_info[piod->device].dev, 57625839Speter piod->port, 4); 57725839Speter copyout(&ret_dword, piod->value, 4); 57825839Speter break; 57925839Speter default: 58025839Speter return -EINVAL; 58125839Speter } 58225839Speter return 0; 58325839Speter} 58425839Speter 58525839Speterstatic int 58625839Spetertdfx_query_update(u_int cmd, struct tdfx_pio_data *piod) 58725839Speter{ 58825839Speter /* XXX Comment this later, after careful inspection and spring cleaning :) */ 58925839Speter /* Return vals */ 59025839Speter u_int8_t ret_byte; 59125839Speter u_int16_t ret_word; 59225839Speter u_int32_t ret_dword; 59325839Speter 59425839Speter /* Port vals, mask */ 59525839Speter u_int32_t retval, preval, mask; 59625839Speter struct tdfx_softc* tdfx_info = NULL; 59725839Speter 59825839Speter 59925839Speter if((piod == NULL) || (piod->device >= (tdfx_count & 60025839Speter 0xf))) { 60125839Speter#ifdef DEBUG 60225839Speter printf("tdfx: Bad struct or device in tdfx_query_update\n"); 60325839Speter#endif 60425839Speter return -EINVAL; 60525839Speter } 60625839Speter 60725839Speter tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 60825839Speter piod->device); 60925839Speter if(tdfx_info == NULL) return -ENXIO; 61025839Speter /* Code below this line in the fuction was taken from the 61125839Speter * Linux driver and converted for freebsd. */ 61225839Speter 61325839Speter /* Check the size for all the ports, to make sure stuff doesn't get messed up 61425839Speter * by poorly written clients */ 61525839Speter 61625839Speter switch(piod->port) { 61725839Speter case PCI_COMMAND_FREEBSD: 61825839Speter if(piod->size != 2) return -EINVAL; 61925839Speter break; 62025839Speter case SST1_PCI_SPECIAL1_FREEBSD: 62125839Speter if(piod->size != 4) return -EINVAL; 62225839Speter break; 62325839Speter case SST1_PCI_SPECIAL2_FREEBSD: 62425839Speter if(piod->size != 4) return -EINVAL; 62525839Speter break; 62625839Speter case SST1_PCI_SPECIAL3_FREEBSD: 62725839Speter if(piod->size != 4) return -EINVAL; 62825839Speter break; 62925839Speter case SST1_PCI_SPECIAL4_FREEBSD: 63025839Speter if(piod->size != 4) return -EINVAL; 63125839Speter break; 63225839Speter default: 63325839Speter return -EINVAL; 63425839Speter } 63525839Speter /* Read the current value */ 63625839Speter retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4); 63725839Speter 63825839Speter /* These set up a mask to use, since apparently they wanted to write 4 bytes 63925839Speter * at once to the ports */ 64025839Speter switch (piod->size) { 64125839Speter case 1: 64225839Speter copyin(piod->value, &ret_byte, 1); 64325839Speter preval = ret_byte << (8 * (piod->port & 0x3)); 64425839Speter mask = 0xff << (8 * (piod->port & 0x3)); 64525839Speter break; 64625839Speter case 2: 64725839Speter copyin(piod->value, &ret_word, 2); 64825839Speter preval = ret_word << (8 * (piod->port & 0x3)); 64925839Speter mask = 0xffff << (8 * (piod->port & 0x3)); 65025839Speter break; 65125839Speter case 4: 65225839Speter copyin(piod->value, &ret_dword, 4); 65325839Speter preval = ret_dword; 65425839Speter mask = ~0; 65525839Speter break; 65625839Speter default: 65725839Speter return -EINVAL; 65825839Speter } 65925839Speter /* Finally, combine the values and write it to the port */ 66025839Speter retval = (retval & ~mask) | preval; 66125839Speter pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4); 66225839Speter 66325839Speter return 0; 66425839Speter} 66525839Speter 66625839Speter/* For both of these, I added a variable named workport of type u_int so 66725839Speter * that I could eliminate the warning about my data type size. The 66825839Speter * applications expect the port to be of type short, so I needed to change 66925839Speter * this within the function */ 67025839Speterstatic int 67125839Spetertdfx_do_pio_rd(struct tdfx_pio_data *piod) 67225839Speter{ 67325839Speter /* Return val */ 67425839Speter u_int8_t ret_byte; 67525839Speter u_int workport; 67625839Speter struct tdfx_softc *tdfx_info = 67725839Speter (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 67825839Speter 67925839Speter /* Restricts the access of ports other than those we use */ 68025839Speter if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) || 68125839Speter (piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) && 68225839Speter (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 68325839Speter return -EPERM; 68425839Speter 68525839Speter /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 68625839Speter if(piod->size != 1) { 68725839Speter return -EINVAL; 68825839Speter } 68925839Speter 69025839Speter /* Write the data to the intended port */ 69125839Speter workport = piod->port; 69225839Speter ret_byte = inb(workport); 69325839Speter copyout(&ret_byte, piod->value, sizeof(u_int8_t)); 69425839Speter return 0; 69525839Speter} 69625839Speter 69725839Speterstatic int 69825839Spetertdfx_do_pio_wt(struct tdfx_pio_data *piod) 69925839Speter{ 70025839Speter /* return val */ 70125839Speter u_int8_t ret_byte; 70225839Speter u_int workport; 70325839Speter struct tdfx_softc *tdfx_info = (struct 70425839Speter tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device); 70525839Speter /* Replace old switch w/ massive if(...) */ 70625839Speter /* Restricts the access of ports other than those we use */ 70725839Speter if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) && 70825839Speter (piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ && 70925839Speter (piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max)) 71025839Speter return -EPERM; 71125839Speter 71225839Speter /* All VGA STATUS REGS are byte registers, size should never be > 1 */ 71325839Speter if(piod->size != 1) { 71425839Speter return -EINVAL; 71525839Speter } 71625839Speter 71725839Speter /* Write the data to the intended port */ 71825839Speter copyin(piod->value, &ret_byte, sizeof(u_int8_t)); 71925839Speter workport = piod->port; 72025839Speter outb(workport, ret_byte); 72125839Speter return 0; 72225839Speter} 72325839Speter 72425839Speterstatic int 72525839Spetertdfx_do_query(u_int cmd, struct tdfx_pio_data *piod) 72625839Speter{ 72725839Speter /* There are three sub-commands to the query 0x33 */ 72825839Speter switch(_IOC_NR(cmd)) { 72925839Speter case 2: 73025839Speter return tdfx_query_boards(); 73125839Speter break; 73225839Speter case 3: 73325839Speter return tdfx_query_fetch(cmd, piod); 73425839Speter break; 73525839Speter case 4: 73625839Speter return tdfx_query_update(cmd, piod); 73725839Speter break; 73825839Speter default: 73925839Speter /* In case we are thrown a bogus sub-command! */ 74025839Speter#ifdef DEBUG 74125839Speter printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd)); 74225839Speter#endif 74325839Speter return -EINVAL; 74425839Speter } 74525839Speter} 74625839Speter 74725839Speterstatic int 74825839Spetertdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod) 74925839Speter{ 75025839Speter /* Two types of PIO, INPUT and OUTPUT, as the name suggests */ 75125839Speter switch(_IOC_DIR(cmd)) { 75225839Speter case IOCV_OUT: 75325839Speter return tdfx_do_pio_rd(piod); 75425839Speter break; 75525839Speter case IOCV_IN: 75625839Speter return tdfx_do_pio_wt(piod); 75725839Speter break; 75825839Speter default: 75925839Speter return -EINVAL; 76025839Speter } 76125839Speter} 76225839Speter 76325839Speter/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO, 76425839Speter * normally, you would read in the data pointed to by data, then write your 76525839Speter * output to it. The ioctl *should* normally return zero if everything is 76625839Speter * alright, but 3dfx didn't make it that way... 76725839Speter * 76825839Speter * For all of the ioctl code, in the event of a real error, 76925839Speter * we return -Exxxx rather than simply Exxxx. The reason for this 77025839Speter * is that the ioctls actually RET information back to the program 77125839Speter * sometimes, rather than filling it in the passed structure. We 77225839Speter * want to distinguish errors from useful data, and maintain compatibility. 77325839Speter * 77425839Speter * There is this portion of the proc struct called p_retval[], we can store a 77525839Speter * return value in td->td_retval[0] and place the return value if it is positive 77625839Speter * in there, then we can return 0 (good). If the return value is negative, we 77725839Speter * can return -retval and the error should be properly handled. 77825839Speter */ 77925839Speterstatic int 78025839Spetertdfx_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 78125839Speter{ 78225839Speter int retval = 0; 78325839Speter struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data; 78425839Speter#ifdef DEBUG 78525839Speter printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd, 78625839Speter piod); 78725839Speter#endif 78825839Speter switch(_IOC_TYPE(cmd)) { 78925839Speter /* Return the real error if negative, or simply stick the valid return 79025839Speter * in td->td_retval */ 79125839Speter case 0x33: 79225839Speter /* The '3'(0x33) type IOCTL is for querying the installed cards */ 79325839Speter if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval; 79425839Speter else return -retval; 79525839Speter break; 79625839Speter case 0: 79725839Speter /* The 0 type IOCTL is for programmed I/O methods */ 79825839Speter if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval; 79925839Speter else return -retval; 80025839Speter break; 80125839Speter default: 80225839Speter /* Technically, we won't reach this from linux emu, but when glide 80325839Speter * finally gets ported, watch out! */ 80425839Speter#ifdef DEBUG 80525839Speter printf("Bad IOCTL from #%d\n", td->td_proc->p_pid); 80625839Speter#endif 80725839Speter return ENXIO; 80825839Speter } 80925839Speter 81025839Speter return 0; 81125839Speter} 81225839Speter 81325839Speter/* This is the device driver struct. This is sent to the driver subsystem to 81425839Speter * register the method structure and the info strcut space for this particular 81525839Speter * instance of the driver. 81625839Speter */ 81725839Speterstatic driver_t tdfx_driver = { 81825839Speter "tdfx", 81925839Speter tdfx_methods, 82025839Speter sizeof(struct tdfx_softc), 82125839Speter}; 82225839Speter 82325839Speter/* Tell Mr. Kernel about us! */ 82425839SpeterDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0); 82525839SpeterMODULE_DEPEND(tdfx, mem, 1, 1, 1); 82625839SpeterMODULE_VERSION(tdfx, 1); 82725839Speter