tdfx_pci.c revision 177147
1119418Sobrien/*-
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
32119418Sobrien#include <sys/cdefs.h>
33119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/tdfx/tdfx_pci.c 177147 2008-03-13 14:08:41Z cokane $");
34119418Sobrien
3561911Scokane/* 3dfx driver for FreeBSD 4.x - Finished 11 May 2000, 12:25AM ET
3661911Scokane *
3774534Scokane * Copyright (C) 2000-2001, by Coleman Kane <cokane@FreeBSD.org>,
3861911Scokane * based upon the 3dfx driver written for linux, by Daryll Straus, Jon Taylor,
3961911Scokane * and Jens Axboe, located at http://linux.3dfx.com.
4061911Scokane */
4161911Scokane
4261911Scokane#include <sys/param.h>
4361911Scokane
4461911Scokane#include <sys/bus.h>
4561911Scokane#include <sys/cdefs.h>
4661911Scokane#include <sys/conf.h>
4761911Scokane#include <sys/fcntl.h>
4861911Scokane#include <sys/file.h>
4961911Scokane#include <sys/filedesc.h>
5061911Scokane#include <sys/filio.h>
5161911Scokane#include <sys/ioccom.h>
5261911Scokane#include <sys/kernel.h>
53129879Sphk#include <sys/module.h>
54129879Sphk#include <sys/malloc.h>
5561911Scokane#include <sys/mman.h>
5661911Scokane#include <sys/signalvar.h>
5761911Scokane#include <sys/systm.h>
5861911Scokane#include <sys/uio.h>
5961911Scokane
60119287Simp#include <dev/pci/pcivar.h>
61119287Simp#include <dev/pci/pcireg.h>
6261911Scokane
6361911Scokane#include <vm/vm.h>
6461911Scokane#include <vm/vm_kern.h>
6561911Scokane#include <vm/pmap.h>
6661911Scokane#include <vm/vm_extern.h>
6761911Scokane
6861911Scokane/* rman.h depends on machine/bus.h */
6961911Scokane#include <machine/resource.h>
7061911Scokane#include <machine/bus.h>
7161911Scokane#include <sys/rman.h>
7262028Scokane
7361911Scokane#include <dev/tdfx/tdfx_io.h>
7461911Scokane#include <dev/tdfx/tdfx_vars.h>
7561911Scokane#include <dev/tdfx/tdfx_pci.h>
7661911Scokane
7761911Scokane
7861911Scokanestatic devclass_t tdfx_devclass;
7961911Scokane
8061911Scokane
8161911Scokanestatic int tdfx_count = 0;
8261911Scokane
8361911Scokane
8461911Scokane/* Set up the boot probe/attach routines */
8561911Scokanestatic device_method_t tdfx_methods[] = {
8661911Scokane	DEVMETHOD(device_probe,		tdfx_probe),
8761911Scokane	DEVMETHOD(device_attach,	tdfx_attach),
8861911Scokane	DEVMETHOD(device_detach,	tdfx_detach),
8961911Scokane	DEVMETHOD(device_shutdown,	tdfx_shutdown),
9061911Scokane	{ 0, 0 }
9161911Scokane};
9261911Scokane
93151897SrwatsonMALLOC_DEFINE(M_TDFX,"tdfx_driver","3DFX Graphics[/2D]/3D Accelerator(s)");
9461911Scokane
9561911Scokane/* Char. Dev. file operations structure */
9661911Scokanestatic struct cdevsw tdfx_cdev = {
97126080Sphk	.d_version =	D_VERSION,
98126080Sphk	.d_flags =	D_NEEDGIANT,
99111815Sphk	.d_open =	tdfx_open,
100111815Sphk	.d_close =	tdfx_close,
101111815Sphk	.d_ioctl =	tdfx_ioctl,
102111815Sphk	.d_mmap =	tdfx_mmap,
103111815Sphk	.d_name =	"tdfx",
10461911Scokane};
10561911Scokane
10661911Scokanestatic int
10761911Scokanetdfx_probe(device_t dev)
10861911Scokane{
10961911Scokane	/*
11061911Scokane	 * probe routine called on kernel boot to register supported devices. We get
11161911Scokane	 * a device structure to work with, and we can test the VENDOR/DEVICE IDs to
112142880Simp	 * see if this PCI device is one that we support. Return BUS_PRROBE_DEFAULT
113142880Simp	 * if yes, ENXIO if not.
11461911Scokane	 */
11561911Scokane	switch(pci_get_devid(dev)) {
11661911Scokane	case PCI_DEVICE_ALLIANCE_AT3D:
11761911Scokane		device_set_desc(dev, "ProMotion At3D 3D Accelerator");
118142880Simp		return BUS_PROBE_DEFAULT;
11961911Scokane	case PCI_DEVICE_3DFX_VOODOO2:
12061911Scokane		device_set_desc(dev, "3DFX Voodoo II 3D Accelerator");
121142880Simp		return BUS_PROBE_DEFAULT;
12265146Scokane	/*case PCI_DEVICE_3DFX_BANSHEE:
12361911Scokane		device_set_desc(dev, "3DFX Voodoo Banshee 2D/3D Graphics Accelerator");
124142880Simp		return BUS_PROBE_DEFAULT;
12561911Scokane	case PCI_DEVICE_3DFX_VOODOO3:
12661911Scokane		device_set_desc(dev, "3DFX Voodoo3 2D/3D Graphics Accelerator");
127142880Simp		return BUS_PROBE_DEFAULT;*/
12861911Scokane	case PCI_DEVICE_3DFX_VOODOO1:
12961911Scokane		device_set_desc(dev, "3DFX Voodoo Graphics 3D Accelerator");
130142880Simp		return BUS_PROBE_DEFAULT;
13161911Scokane	};
13261911Scokane
13361911Scokane	return ENXIO;
13461911Scokane}
13561911Scokane
13661911Scokanestatic int
13761911Scokanetdfx_attach(device_t dev) {
13861911Scokane	/*
13961911Scokane	 * The attach routine is called after the probe routine successfully says it
14061911Scokane	 * supports a given card. We now proceed to initialize this card for use with
14161911Scokane	 * the system. I want to map the device memory for userland allocation and
14261911Scokane	 * fill an information structure with information on this card. I'd also like
14361911Scokane	 * to set Write Combining with the MTRR code so that we can hopefully speed
14461911Scokane	 * up memory writes. The last thing is to register the character device
14561911Scokane	 * interface to the card, so we can open it from /dev/3dfxN, where N is a
14661911Scokane	 * small, whole number.
14761911Scokane	 */
14861911Scokane	struct tdfx_softc *tdfx_info;
14961911Scokane	u_long	val;
15061911Scokane	/* rid value tells bus_alloc_resource where to find the addresses of ports or
15161911Scokane	 * of memory ranges in the PCI config space*/
152119690Sjhb	int rid = PCIR_BAR(0);
15361911Scokane
15461911Scokane	/* Increment the card counter (for the ioctl code) */
15561911Scokane	tdfx_count++;
15661911Scokane
15761911Scokane 	/* Enable MemMap on Voodoo */
15861911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
15961911Scokane	val |= (PCIM_CMD_MEMEN);
16061911Scokane	pci_write_config(dev, PCIR_COMMAND, val, 2);
16161911Scokane	val = pci_read_config(dev, PCIR_COMMAND, 2);
16261911Scokane
16361911Scokane	/* Fill the soft config struct with info about this device*/
16461911Scokane	tdfx_info = device_get_softc(dev);
16561911Scokane	tdfx_info->dev = dev;
16661911Scokane	tdfx_info->vendor = pci_get_vendor(dev);
16761911Scokane	tdfx_info->type = pci_get_devid(dev) >> 16;
16861911Scokane	tdfx_info->bus = pci_get_bus(dev);
16961911Scokane	tdfx_info->dv = pci_get_slot(dev);
17061911Scokane	tdfx_info->curFile = NULL;
17161911Scokane
17261911Scokane	/*
17361911Scokane	 *	Get the Memory Location from the PCI Config, mask out lower word, since
17461911Scokane	 * the config space register is only one word long (this is nicer than a
17561911Scokane	 * bitshift).
17661911Scokane	 */
17761911Scokane	tdfx_info->addr0 = (pci_read_config(dev, 0x10, 4) & 0xffff0000);
17861931Scokane#ifdef DEBUG
17961911Scokane	device_printf(dev, "Base0 @ 0x%x\n", tdfx_info->addr0);
18061911Scokane#endif
18161911Scokane	/* Notify the VM that we will be mapping some memory later */
182127135Snjl	tdfx_info->memrange = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
183127135Snjl		&rid, RF_ACTIVE | RF_SHAREABLE);
18461911Scokane	if(tdfx_info->memrange == NULL) {
18561931Scokane#ifdef DEBUG
18661911Scokane		device_printf(dev, "Error mapping mem, won't be able to use mmap()\n");
18761911Scokane#endif
18861911Scokane		tdfx_info->memrid = 0;
18961911Scokane	}
19061911Scokane	else {
19161911Scokane		tdfx_info->memrid = rid;
19261931Scokane#ifdef DEBUG
19361911Scokane		device_printf(dev, "Mapped to: 0x%x\n",
19461911Scokane				(unsigned int)rman_get_start(tdfx_info->memrange));
19561911Scokane#endif
19661911Scokane	}
19761911Scokane
19863488Scokane	/* Setup for Voodoo3 and Banshee, PIO and an extram Memrange */
19963488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3 ||
20063488Scokane		pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE) {
20164085Scokane		rid = 0x14;	/* 2nd mem map */
20263488Scokane		tdfx_info->addr1 = (pci_read_config(dev, 0x14, 4) & 0xffff0000);
20363488Scokane#ifdef DEBUG
20463488Scokane		device_printf(dev, "Base1 @ 0x%x\n", tdfx_info->addr1);
20563488Scokane#endif
206127135Snjl		tdfx_info->memrange2 = bus_alloc_resource_any(dev,
207127135Snjl			SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE);
20863488Scokane		if(tdfx_info->memrange2 == NULL) {
20963488Scokane#ifdef DEBUG
21063488Scokane			device_printf(dev, "Mem1 couldn't be allocated, glide may not work.");
21163488Scokane#endif
21263488Scokane			tdfx_info->memrid2 = 0;
21363488Scokane		}
21463488Scokane		else {
21563488Scokane			tdfx_info->memrid2 = rid;
21663488Scokane		}
21763488Scokane		/* Now to map the PIO stuff */
21865146Scokane		rid = PCIR_IOBASE0_2;
21965146Scokane		tdfx_info->pio0 = pci_read_config(dev, 0x2c, 2);
22065146Scokane		tdfx_info->pio0max = pci_read_config(dev, 0x30, 2) + tdfx_info->pio0;
221127135Snjl		tdfx_info->piorange = bus_alloc_resource_any(dev,
222127135Snjl			SYS_RES_IOPORT, &rid, RF_ACTIVE | RF_SHAREABLE);
22363488Scokane		if(tdfx_info->piorange == NULL) {
22463488Scokane#ifdef DEBUG
22563488Scokane			device_printf(dev, "Couldn't map PIO range.");
22663488Scokane#endif
22763488Scokane			tdfx_info->piorid = 0;
22863488Scokane		}
22963488Scokane		else {
23063488Scokane			tdfx_info->piorid = rid;
23165146Scokane		}
23263488Scokane	} else {
23363488Scokane	  tdfx_info->addr1 = 0;
23463488Scokane	  tdfx_info->memrange2 = NULL;
23563488Scokane	  tdfx_info->piorange = NULL;
23663488Scokane	}
23763488Scokane
23861911Scokane	/*
23961911Scokane	 *	Set Writecombining, or at least Uncacheable for the memory region, if we
24061911Scokane	 * are able to
24161911Scokane	 */
24261911Scokane
24361911Scokane	if(tdfx_setmtrr(dev) != 0) {
24461931Scokane#ifdef DEBUG
24561911Scokane		device_printf(dev, "Some weird error setting MTRRs");
24661911Scokane#endif
24761911Scokane		return -1;
24861911Scokane	}
24963488Scokane
25061911Scokane	/*
25161911Scokane	 * make_dev registers the cdev to access the 3dfx card from /dev
25261911Scokane	 *	use hex here for the dev num, simply to provide better support if > 10
25361911Scokane	 * voodoo cards, for the mad. The user must set the link, or use MAKEDEV.
25461911Scokane	 * Why would we want that many voodoo cards anyhow?
25561911Scokane	 */
256104111Sphk	tdfx_info->devt = make_dev(&tdfx_cdev, device_get_unit(dev),
257108323Srwatson		UID_ROOT, GID_WHEEL, 0600, "3dfx%x", device_get_unit(dev));
25861911Scokane
25961911Scokane	return 0;
26061911Scokane}
26161911Scokane
26261911Scokanestatic int
26361911Scokanetdfx_detach(device_t dev) {
26461911Scokane	struct tdfx_softc* tdfx_info;
26561911Scokane	int retval;
26661911Scokane	tdfx_info = device_get_softc(dev);
26761911Scokane
26861911Scokane	/* Delete allocated resource, of course */
26963488Scokane	bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid,
27061911Scokane			tdfx_info->memrange);
27163488Scokane
27263488Scokane	/* Release extended Voodoo3/Banshee resources */
27363488Scokane	if(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE ||
27463488Scokane			pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) {
27563488Scokane		if(tdfx_info->memrange2 != NULL)
27663488Scokane			bus_release_resource(dev, SYS_RES_MEMORY, tdfx_info->memrid2,
27763488Scokane				tdfx_info->memrange);
27864085Scokane	/*	if(tdfx_info->piorange != NULL)
27963488Scokane			bus_release_resource(dev, SYS_RES_IOPORT, tdfx_info->piorid,
28064085Scokane				tdfx_info->piorange);*/
28163488Scokane	}
28263488Scokane
28361911Scokane	/* Though it is safe to leave the WRCOMB support since the
28461911Scokane		mem driver checks for it, we should remove it in order
28561911Scokane		to free an MTRR for another device */
28661911Scokane	retval = tdfx_clrmtrr(dev);
28761931Scokane#ifdef DEBUG
28861911Scokane	if(retval != 0)
28961911Scokane		printf("tdfx: For some reason, I couldn't clear the mtrr\n");
29061911Scokane#endif
29161989Scokane	/* Remove device entry when it can no longer be accessed */
29261989Scokane   destroy_dev(tdfx_info->devt);
29361911Scokane	return(0);
29461911Scokane}
29561911Scokane
29661911Scokanestatic int
29761911Scokanetdfx_shutdown(device_t dev) {
29861931Scokane#ifdef DEBUG
29961911Scokane	device_printf(dev, "tdfx: Device Shutdown\n");
30061911Scokane#endif
30161911Scokane	return 0;
30261911Scokane}
30361911Scokane
30461911Scokanestatic int
30561911Scokanetdfx_clrmtrr(device_t dev) {
30661911Scokane	/* This function removes the MTRR set by the attach call, so it can be used
30761911Scokane	 * in the future by other drivers.
30861911Scokane	 */
30961911Scokane	int retval, act;
31061911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
31161911Scokane
31261911Scokane	act = MEMRANGE_SET_REMOVE;
31361911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
31461911Scokane	return retval;
31561911Scokane}
31661911Scokane
31761911Scokanestatic int
31861911Scokanetdfx_setmtrr(device_t dev) {
31961911Scokane	/*
32061911Scokane	 * This is the MTRR setting function for the 3dfx card. It is called from
32161911Scokane	 * tdfx_attach. If we can't set the MTRR properly, it's not the end of the
32261911Scokane	 * world. We can still continue, just with slightly (very slightly) degraded
32361911Scokane	 * performance.
32461911Scokane	 */
32561911Scokane	int retval = 0, act;
32661911Scokane	struct tdfx_softc *tdfx_info = device_get_softc(dev);
32761911Scokane
32861911Scokane	/* The older Voodoo cards have a shorter memrange than the newer ones */
32961911Scokane	if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1) || (pci_get_devid(dev) ==
33063488Scokane			PCI_DEVICE_3DFX_VOODOO2)) {
33161911Scokane		tdfx_info->mrdesc.mr_len = 0x400000;
33263488Scokane
33363488Scokane		/* The memory descriptor is described as the top 15 bits of the real
33463488Scokane			address */
33563488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr0 & 0xfffe0000;
33663488Scokane	}
33761911Scokane	else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO3) ||
33863488Scokane			(pci_get_devid(dev) == PCI_DEVICE_3DFX_BANSHEE)) {
33961911Scokane		tdfx_info->mrdesc.mr_len = 0x1000000;
34063488Scokane		/* The Voodoo3 and Banshee LFB is the second memory address */
34163488Scokane		/* The memory descriptor is described as the top 15 bits of the real
34263488Scokane			address */
34363488Scokane		tdfx_info->mrdesc.mr_base = tdfx_info->addr1 & 0xfffe0000;
34463488Scokane	}
34563488Scokane	else
34663488Scokane		 return 0;
34761911Scokane	/*
34861911Scokane    *	The Alliance Pro Motion AT3D was not mentioned in the linux
34961911Scokane	 * driver as far as MTRR support goes, so I just won't put the
35061911Scokane	 * code in here for it. This is where it should go, though.
35161911Scokane	 */
35261911Scokane
35361911Scokane	/* Firstly, try to set write combining */
35461911Scokane	tdfx_info->mrdesc.mr_flags = MDF_WRITECOMBINE;
35561911Scokane	bcopy("tdfx", &tdfx_info->mrdesc.mr_owner, 4);
35661911Scokane	act = MEMRANGE_SET_UPDATE;
35761911Scokane	retval = mem_range_attr_set(&tdfx_info->mrdesc, &act);
35861911Scokane
35961911Scokane	if(retval == 0) {
36061931Scokane#ifdef DEBUG
36161911Scokane		device_printf(dev, "MTRR Set Correctly for tdfx\n");
36261911Scokane#endif
36361911Scokane	} else if((pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO2) ||
36461911Scokane		(pci_get_devid(dev) == PCI_DEVICE_3DFX_VOODOO1)) {
36561911Scokane		/* if, for some reason we can't set the WRCOMB range with the V1/V2, we
36661911Scokane		 * can still possibly use the UNCACHEABLE region for it instead, and help
36761911Scokane		 * out in a small way */
36861911Scokane		tdfx_info->mrdesc.mr_flags = MDF_UNCACHEABLE;
36961911Scokane		/* This length of 1000h was taken from the linux device driver... */
37061911Scokane		tdfx_info->mrdesc.mr_len = 0x1000;
37161911Scokane
37261911Scokane		/*
37361911Scokane		 * If, for some reason, we can't set the MTRR (N/A?) we may still continue
37461911Scokane		 */
37561931Scokane#ifdef DEBUG
376142253Ssam		device_printf(dev, "MTRR Set Type Uncacheable %x\n",
377142253Ssam		    (u_int32_t)tdfx_info->mrdesc.mr_base);
37861911Scokane#endif
37961911Scokane	}
38061931Scokane#ifdef DEBUG
38161911Scokane	else {
38261911Scokane		device_printf(dev, "Couldn't Set MTRR\n");
38361911Scokane		return 0;
38461911Scokane	}
38561911Scokane#endif
38661911Scokane	return 0;
38761911Scokane}
38861911Scokane
38961911Scokanestatic int
390130585Sphktdfx_open(struct cdev *dev, int flags, int fmt, struct thread *td)
39161911Scokane{
39261911Scokane	/*
39361911Scokane	 *	The open cdev method handles open(2) calls to /dev/3dfx[n]
39461911Scokane	 * We can pretty much allow any opening of the device.
39561911Scokane	 */
39661911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
39761911Scokane			UNIT(minor(dev)));
39861911Scokane	if(tdfx_info->busy != 0) return EBUSY;
39961931Scokane#ifdef	DEBUG
40083366Sjulian	printf("3dfx: Opened by #%d\n", td->td_proc->p_pid);
40161911Scokane#endif
40261911Scokane	/* Set the driver as busy */
40361911Scokane	tdfx_info->busy++;
40461911Scokane	return 0;
40561911Scokane}
40661911Scokane
40761911Scokanestatic int
408130585Sphktdfx_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
40961911Scokane{
41061911Scokane	/*
41161911Scokane	 *	The close cdev method handles close(2) calls to /dev/3dfx[n]
41261911Scokane	 * We'll always want to close the device when it's called.
41361911Scokane	 */
41461911Scokane	struct tdfx_softc *tdfx_info = devclass_get_softc(tdfx_devclass,
41561911Scokane		UNIT(minor(dev)));
41661911Scokane	if(tdfx_info->busy == 0) return EBADF;
41761911Scokane	tdfx_info->busy = 0;
41861931Scokane#ifdef	DEBUG
41983366Sjulian	printf("Closed by #%d\n", td->td_proc->p_pid);
42061911Scokane#endif
42161911Scokane	return 0;
42261911Scokane}
42361911Scokane
42461911Scokanestatic int
425130585Sphktdfx_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
42661911Scokane{
42761911Scokane	/*
42861911Scokane	 * mmap(2) is called by a user process to request that an area of memory
42961911Scokane	 * associated with this device be mapped for the process to work with. Nprot
43061911Scokane	 * holds the protections requested, PROT_READ, PROT_WRITE, or both.
43161911Scokane	 */
43266910Scokane
43366910Scokane	/**** OLD GET CONFIG ****/
43466910Scokane	/* struct tdfx_softc* tdfx_info; */
43561911Scokane
43661911Scokane	/* Get the configuration for our card XXX*/
43766910Scokane	/*tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
43866910Scokane			UNIT(minor(dev)));*/
43966910Scokane	/************************/
44066910Scokane
44166910Scokane	struct tdfx_softc* tdfx_info[2];
44261911Scokane
44366910Scokane	tdfx_info[0] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 0);
44466910Scokane
44561911Scokane	/* If, for some reason, its not configured, we bail out */
44666910Scokane	if(tdfx_info[0] == NULL) {
44761931Scokane#ifdef	DEBUG
44861911Scokane	   printf("tdfx: tdfx_info (softc) is NULL\n");
44961911Scokane#endif
45061911Scokane	   return -1;
45161911Scokane	}
45266910Scokane
45361911Scokane	/* We must stay within the bound of our address space */
45466910Scokane	if((offset & 0xff000000) == tdfx_info[0]->addr0) {
45561911Scokane		offset &= 0xffffff;
456111462Smux		*paddr = rman_get_start(tdfx_info[0]->memrange) + offset;
457111462Smux		return 0;
45866910Scokane	}
45966910Scokane
46066910Scokane	if(tdfx_count > 1) {
46166910Scokane		tdfx_info[1] = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass, 1);
46266910Scokane		if((offset & 0xff000000) == tdfx_info[1]->addr0) {
46366910Scokane			offset &= 0xffffff;
464111462Smux			*paddr = rman_get_start(tdfx_info[1]->memrange) +
465111462Smux			    offset;
466111462Smux			return 0;
46766910Scokane		}
46866910Scokane	}
46963488Scokane
47063488Scokane	/* See if the Banshee/V3 LFB is being requested */
47166910Scokane	/*if(tdfx_info->memrange2 != NULL && (offset & 0xff000000) ==
47264085Scokane			tdfx_info->addr1) {
47363488Scokane	  	offset &= 0xffffff;
47466910Scokane		return atop(rman_get_start(tdfx_info[1]->memrange2) + offset);
47566910Scokane	}*/ /* VoodooNG code */
47663488Scokane
47766910Scokane	/* The ret call */
47861911Scokane	/* atop -> address to page
47961911Scokane	 * rman_get_start, get the (struct resource*)->r_start member,
48061911Scokane	 * the mapping base address.
48161911Scokane	 */
48266910Scokane	return -1;
48361911Scokane}
48461911Scokane
48561911Scokanestatic int
48661911Scokanetdfx_query_boards(void) {
48761911Scokane	/*
48861911Scokane    *	This returns the number of installed tdfx cards, we have been keeping
48961911Scokane	 * count, look at tdfx_attach
49061911Scokane	 */
49161911Scokane	return tdfx_count;
49261911Scokane}
49361911Scokane
49461911Scokanestatic int
49561911Scokanetdfx_query_fetch(u_int cmd, struct tdfx_pio_data *piod)
49661911Scokane{
49761911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
49861911Scokane	/* Various return values 8bit-32bit */
49961911Scokane	u_int8_t  ret_byte;
50061911Scokane	u_int16_t ret_word;
50161911Scokane	u_int32_t ret_dword;
50261911Scokane	struct tdfx_softc* tdfx_info = NULL;
50361911Scokane
50461911Scokane	/* This one depend on the tdfx_* structs being properly initialized */
50561911Scokane
50661911Scokane	/*piod->device &= 0xf;*/
50761911Scokane	if((piod == NULL) ||(tdfx_count <= piod->device) ||
50861911Scokane			(piod->device < 0)) {
50961931Scokane#ifdef DEBUG
51061911Scokane		printf("tdfx: Bad device or internal struct in tdfx_query_fetch\n");
51161911Scokane#endif
51261911Scokane		return -EINVAL;
51361911Scokane	}
51461911Scokane
51561911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
51661911Scokane			piod->device);
51761911Scokane
51861911Scokane	if(tdfx_info == NULL) return -ENXIO;
51961911Scokane
52061911Scokane	/* We must restrict the size reads from the port, since to high or low of a
52161911Scokane	 * size witll result in wrong data being passed, and that's bad */
52261911Scokane	/* A few of these were pulled during the attach phase */
52361911Scokane	switch(piod->port) {
52461911Scokane		case PCI_VENDOR_ID_FREEBSD:
52561911Scokane			if(piod->size != 2) return -EINVAL;
52661911Scokane			copyout(&tdfx_info->vendor, piod->value, piod->size);
52761911Scokane			return 0;
52861911Scokane		case PCI_DEVICE_ID_FREEBSD:
52961911Scokane			if(piod->size != 2) return -EINVAL;
53061911Scokane			copyout(&tdfx_info->type, piod->value, piod->size);
53161911Scokane			return 0;
53261911Scokane		case PCI_BASE_ADDRESS_0_FREEBSD:
53361911Scokane			if(piod->size != 4) return -EINVAL;
53461911Scokane			copyout(&tdfx_info->addr0, piod->value, piod->size);
53561911Scokane			return 0;
53665146Scokane		case PCI_BASE_ADDRESS_1_FREEBSD:
53765146Scokane			if(piod->size != 4) return -EINVAL;
53865146Scokane			copyout(&tdfx_info->addr1, piod->value, piod->size);
53965146Scokane			return 0;
54065146Scokane		case PCI_PRIBUS_FREEBSD:
54165146Scokane			if(piod->size != 1) return -EINVAL;
54265146Scokane			break;
54365146Scokane		case PCI_IOBASE_0_FREEBSD:
54465146Scokane			if(piod->size != 2) return -EINVAL;
54565146Scokane			break;
54665146Scokane		case PCI_IOLIMIT_0_FREEBSD:
54765146Scokane			if(piod->size != 2) return -EINVAL;
54865146Scokane			break;
54961911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
55061911Scokane			if(piod->size != 4) return -EINVAL;
55161911Scokane			break;
55261911Scokane		case PCI_REVISION_ID_FREEBSD:
55361911Scokane			if(piod->size != 1) return -EINVAL;
55461911Scokane			break;
55561911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
55661911Scokane			if(piod->size != 4) return -EINVAL;
55761911Scokane			break;
55861911Scokane		default:
55961911Scokane			return -EINVAL;
56061911Scokane	}
56161911Scokane
56261911Scokane
56361911Scokane	/* Read the value and return */
56461911Scokane	switch(piod->size) {
56561911Scokane		case 1:
56661911Scokane			ret_byte = pci_read_config(tdfx_info[piod->device].dev,
56761911Scokane					piod->port, 1);
56861911Scokane			copyout(&ret_byte, piod->value, 1);
56961911Scokane			break;
57061911Scokane		case 2:
57161911Scokane			ret_word = pci_read_config(tdfx_info[piod->device].dev,
57261911Scokane					piod->port, 2);
57361911Scokane			copyout(&ret_word, piod->value, 2);
57461911Scokane			break;
57561911Scokane		case 4:
57661911Scokane			ret_dword = pci_read_config(tdfx_info[piod->device].dev,
57761911Scokane					piod->port, 4);
57861911Scokane			copyout(&ret_dword, piod->value, 4);
57961911Scokane			break;
58061911Scokane		default:
58161911Scokane			return -EINVAL;
58261911Scokane	}
58361911Scokane	return 0;
58461911Scokane}
58561911Scokane
58661911Scokanestatic int
58761911Scokanetdfx_query_update(u_int cmd, struct tdfx_pio_data *piod)
58861911Scokane{
58961911Scokane	/* XXX Comment this later, after careful inspection and spring cleaning :) */
59061911Scokane	/* Return vals */
59161911Scokane	u_int8_t  ret_byte;
59261911Scokane	u_int16_t ret_word;
59361911Scokane	u_int32_t ret_dword;
59461911Scokane
59561911Scokane	/* Port vals, mask */
59661911Scokane	u_int32_t retval, preval, mask;
59761911Scokane	struct tdfx_softc* tdfx_info = NULL;
59861911Scokane
59961911Scokane
60061911Scokane	if((piod == NULL) || (piod->device >= (tdfx_count &
60161911Scokane					0xf))) {
60261931Scokane#ifdef DEBUG
60361911Scokane		printf("tdfx: Bad struct or device in tdfx_query_update\n");
60461911Scokane#endif
60561911Scokane		return -EINVAL;
60661911Scokane	}
60761911Scokane
60861911Scokane	tdfx_info = (struct tdfx_softc*)devclass_get_softc(tdfx_devclass,
60961911Scokane			piod->device);
61061911Scokane	if(tdfx_info == NULL) return -ENXIO;
61161911Scokane	/* Code below this line in the fuction was taken from the
61261911Scokane	 * Linux driver and converted for freebsd. */
61361911Scokane
61461911Scokane	/* Check the size for all the ports, to make sure stuff doesn't get messed up
61561911Scokane	 * by poorly written clients */
61661911Scokane
61761911Scokane	switch(piod->port) {
61861911Scokane		case PCI_COMMAND_FREEBSD:
61961911Scokane			if(piod->size != 2) return -EINVAL;
62061911Scokane			break;
62161911Scokane		case SST1_PCI_SPECIAL1_FREEBSD:
62261911Scokane			if(piod->size != 4) return -EINVAL;
62361911Scokane			break;
62461911Scokane		case SST1_PCI_SPECIAL2_FREEBSD:
62561911Scokane			if(piod->size != 4) return -EINVAL;
62661911Scokane			break;
62761911Scokane		case SST1_PCI_SPECIAL3_FREEBSD:
62861911Scokane			if(piod->size != 4) return -EINVAL;
62961911Scokane			break;
63061911Scokane		case SST1_PCI_SPECIAL4_FREEBSD:
63161911Scokane			if(piod->size != 4) return -EINVAL;
63261911Scokane			break;
63361911Scokane		default:
63461911Scokane			return -EINVAL;
63561911Scokane	}
63661911Scokane	/* Read the current value */
63761911Scokane	retval = pci_read_config(tdfx_info->dev, piod->port & ~3, 4);
63861911Scokane
63961911Scokane	/* These set up a mask to use, since apparently they wanted to write 4 bytes
64061911Scokane	 * at once to the ports */
64161911Scokane	switch (piod->size) {
64261911Scokane		case 1:
64361911Scokane			copyin(piod->value, &ret_byte, 1);
64461911Scokane			preval = ret_byte << (8 * (piod->port & 0x3));
64561911Scokane			mask = 0xff << (8 * (piod->port & 0x3));
64661911Scokane			break;
64761911Scokane		case 2:
64861911Scokane			copyin(piod->value, &ret_word, 2);
64961911Scokane			preval = ret_word << (8 * (piod->port & 0x3));
65061911Scokane			mask = 0xffff << (8 * (piod->port & 0x3));
65161911Scokane			break;
65261911Scokane		case 4:
65361911Scokane			copyin(piod->value, &ret_dword, 4);
65461911Scokane			preval = ret_dword;
65561911Scokane			mask = ~0;
65661911Scokane			break;
65761911Scokane		default:
65861911Scokane			return -EINVAL;
65961911Scokane	}
66061911Scokane	/* Finally, combine the values and write it to the port */
66161911Scokane	retval = (retval & ~mask) | preval;
66261911Scokane	pci_write_config(tdfx_info->dev, piod->port & ~3, retval, 4);
66361911Scokane
66461911Scokane	return 0;
66561911Scokane}
66661911Scokane
66763488Scokane/* For both of these, I added a variable named workport of type u_int so
66863488Scokane * that I could eliminate the warning about my data type size. The
66963488Scokane * applications expect the port to be of type short, so I needed to change
67063488Scokane * this within the function */
67161911Scokanestatic int
67261911Scokanetdfx_do_pio_rd(struct tdfx_pio_data *piod)
67361911Scokane{
67461911Scokane	/* Return val */
67561911Scokane	u_int8_t  ret_byte;
67663488Scokane	u_int 	 workport;
67765146Scokane	struct tdfx_softc *tdfx_info =
67865146Scokane		(struct tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
67965146Scokane
68061911Scokane	/* Restricts the access of ports other than those we use */
68165146Scokane	if(((piod->port != VGA_INPUT_STATUS_1C) || (piod->port != SC_INDEX) ||
68265146Scokane		(piod->port != SC_DATA) || (piod->port != VGA_MISC_OUTPUT_READ)) &&
68365146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
68461911Scokane		return -EPERM;
68561911Scokane
68661911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
68761911Scokane	if(piod->size != 1) {
68861911Scokane		return -EINVAL;
68961911Scokane	}
69061911Scokane
69161911Scokane	/* Write the data to the intended port */
69263488Scokane	workport = piod->port;
69363488Scokane	ret_byte = inb(workport);
69461911Scokane	copyout(&ret_byte, piod->value, sizeof(u_int8_t));
69561911Scokane	return 0;
69661911Scokane}
69761911Scokane
69861911Scokanestatic int
69961911Scokanetdfx_do_pio_wt(struct tdfx_pio_data *piod)
70061911Scokane{
70161911Scokane	/* return val */
70261911Scokane	u_int8_t  ret_byte;
70363488Scokane	u_int		 workport;
70465146Scokane	struct tdfx_softc *tdfx_info = (struct
70565146Scokane			tdfx_softc*)devclass_get_softc(tdfx_devclass, piod->device);
70661911Scokane	/* Replace old switch w/ massive if(...) */
70761911Scokane	/* Restricts the access of ports other than those we use */
70865146Scokane	if(((piod->port != SC_INDEX) && (piod->port != SC_DATA) &&
70965146Scokane		(piod->port != VGA_MISC_OUTPUT_READ)) /* Can't write VGA_ST_1C */ &&
71065146Scokane		(piod->port < tdfx_info->pio0) && (piod->port > tdfx_info->pio0max))
71161911Scokane		return -EPERM;
71261911Scokane
71361911Scokane	/* All VGA STATUS REGS are byte registers, size should never be > 1 */
71461911Scokane	if(piod->size != 1) {
71561911Scokane		return -EINVAL;
71661911Scokane	}
71761911Scokane
71861911Scokane	/* Write the data to the intended port */
71961911Scokane	copyin(piod->value, &ret_byte, sizeof(u_int8_t));
72063488Scokane	workport = piod->port;
72163488Scokane	outb(workport, ret_byte);
72261911Scokane	return 0;
72361911Scokane}
72461911Scokane
72561911Scokanestatic int
72661911Scokanetdfx_do_query(u_int cmd, struct tdfx_pio_data *piod)
72761911Scokane{
72861911Scokane	/* There are three sub-commands to the query 0x33 */
72961911Scokane	switch(_IOC_NR(cmd)) {
73061911Scokane		case 2:
73161911Scokane			return tdfx_query_boards();
73261911Scokane			break;
73361911Scokane		case 3:
73461911Scokane			return tdfx_query_fetch(cmd, piod);
73561911Scokane			break;
73661911Scokane		case 4:
73761911Scokane			return tdfx_query_update(cmd, piod);
73861911Scokane			break;
73961911Scokane		default:
74061911Scokane			/* In case we are thrown a bogus sub-command! */
74161931Scokane#ifdef DEBUG
74261911Scokane			printf("Bad Sub-cmd: 0x%x\n", _IOC_NR(cmd));
74361911Scokane#endif
74461911Scokane			return -EINVAL;
745115494Sphk	}
74661911Scokane}
74761911Scokane
74861911Scokanestatic int
74961911Scokanetdfx_do_pio(u_int cmd, struct tdfx_pio_data *piod)
75061911Scokane{
75161911Scokane	/* Two types of PIO, INPUT and OUTPUT, as the name suggests */
75261911Scokane	switch(_IOC_DIR(cmd)) {
75361911Scokane		case IOCV_OUT:
75461911Scokane			return tdfx_do_pio_rd(piod);
75561911Scokane			break;
75661911Scokane		case IOCV_IN:
75761911Scokane			return tdfx_do_pio_wt(piod);
75861911Scokane			break;
75961911Scokane		default:
76061911Scokane			return -EINVAL;
761115494Sphk	}
76261911Scokane}
76361911Scokane
76461911Scokane/* Calls to ioctl(2) eventually end up here. Unhandled ioctls return an ENXIO,
76561911Scokane * normally, you would read in the data pointed to by data, then write your
76661911Scokane * output to it. The ioctl *should* normally return zero if everything is
76761911Scokane * alright, but 3dfx didn't make it that way...
76861911Scokane *
76961911Scokane * For all of the ioctl code, in the event of a real error,
77061911Scokane * we return -Exxxx rather than simply Exxxx. The reason for this
77161911Scokane * is that the ioctls actually RET information back to the program
77261911Scokane * sometimes, rather than filling it in the passed structure. We
77361911Scokane * want to distinguish errors from useful data, and maintain compatibility.
77461911Scokane *
77561911Scokane * There is this portion of the proc struct called p_retval[], we can store a
77683366Sjulian * return value in td->td_retval[0] and place the return value if it is positive
77761911Scokane * in there, then we can return 0 (good). If the return value is negative, we
77861911Scokane * can return -retval and the error should be properly handled.
77961911Scokane */
78061911Scokanestatic int
781130585Sphktdfx_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
78261911Scokane{
78361911Scokane	int retval = 0;
78461911Scokane	struct tdfx_pio_data *piod = (struct tdfx_pio_data*)data;
78561931Scokane#ifdef	DEBUG
786106578Sjhb	printf("IOCTL'd by #%d, cmd: 0x%x, data: %p\n", td->td_proc->p_pid, (u_int32_t)cmd,
787106578Sjhb			piod);
78861911Scokane#endif
78961911Scokane	switch(_IOC_TYPE(cmd)) {
79061911Scokane		/* Return the real error if negative, or simply stick the valid return
79183366Sjulian		 * in td->td_retval */
79261911Scokane	case 0x33:
79361911Scokane			/* The '3'(0x33) type IOCTL is for querying the installed cards */
79483366Sjulian			if((retval = tdfx_do_query(cmd, piod)) > 0) td->td_retval[0] = retval;
79561911Scokane			else return -retval;
79661911Scokane			break;
79761911Scokane		case 0:
79861911Scokane			/* The 0 type IOCTL is for programmed I/O methods */
79983366Sjulian			if((tdfx_do_pio(cmd, piod)) > 0) td->td_retval[0] = retval;
80061911Scokane			else return -retval;
80161911Scokane			break;
80261911Scokane		default:
80361911Scokane			/* Technically, we won't reach this from linux emu, but when glide
80461911Scokane			 * finally gets ported, watch out! */
80561931Scokane#ifdef DEBUG
80683366Sjulian			printf("Bad IOCTL from #%d\n", td->td_proc->p_pid);
80761911Scokane#endif
80861911Scokane			return ENXIO;
80961911Scokane	}
81061911Scokane
81161911Scokane	return 0;
81261911Scokane}
81361911Scokane
81461911Scokane/* This is the device driver struct. This is sent to the driver subsystem to
81561911Scokane * register the method structure and the info strcut space for this particular
81661911Scokane * instance of the driver.
81761911Scokane */
81861911Scokanestatic driver_t tdfx_driver = {
81961911Scokane	"tdfx",
82061911Scokane	tdfx_methods,
82161911Scokane	sizeof(struct tdfx_softc),
82261911Scokane};
82361911Scokane
82461911Scokane/* Tell Mr. Kernel about us! */
82561911ScokaneDRIVER_MODULE(tdfx, pci, tdfx_driver, tdfx_devclass, 0, 0);
826177147ScokaneMODULE_DEPEND(tdfx, mem, 1, 1, 1);
827156260SyarMODULE_VERSION(tdfx, 1);
828