pci_pir.c revision 47307
126159Sse/*
226159Sse * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
326159Sse * All rights reserved.
426159Sse *
526159Sse * Redistribution and use in source and binary forms, with or without
626159Sse * modification, are permitted provided that the following conditions
726159Sse * are met:
826159Sse * 1. Redistributions of source code must retain the above copyright
926159Sse *    notice unmodified, this list of conditions, and the following
1026159Sse *    disclaimer.
1126159Sse * 2. Redistributions in binary form must reproduce the above copyright
1226159Sse *    notice, this list of conditions and the following disclaimer in the
1326159Sse *    documentation and/or other materials provided with the distribution.
1426159Sse *
1526159Sse * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1626159Sse * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1726159Sse * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1826159Sse * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1926159Sse * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2026159Sse * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2126159Sse * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2226159Sse * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2326159Sse * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2426159Sse * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2526159Sse *
2647307Speter * $Id: pcibus.c,v 1.41 1997/12/20 09:04:25 se Exp $
2726159Sse *
2826159Sse */
296104Sse
3047307Speter#include <sys/param.h>
316734Sbde#include <sys/systm.h>
3247307Speter#include <sys/bus.h>
3347307Speter#include <sys/kernel.h>
346734Sbde
356104Sse#include <pci/pcivar.h>
3626159Sse#include <i386/isa/pcibus.h>
376104Sse
3826159Sse#ifdef PCI_COMPAT
3926159Sse/* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */
4026159Sse#define cfgmech pci_mechanism
4126159Sseint cfgmech;
4226159Sse#else
4326159Ssestatic int cfgmech;
4426159Sse#endif /* PCI_COMPAT */
4526159Ssestatic int devmax;
466104Sse
4726159Sse/* enable configuration space accesses and return data port address */
4826159Sse
4910887Ssestatic int
5026159Ssepci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
5126159Sse{
5226159Sse	int dataport = 0;
5310887Sse
5426159Sse	if (bus <= PCI_BUSMAX
5526159Sse	    && slot < devmax
5626159Sse	    && func <= PCI_FUNCMAX
5726159Sse	    && reg <= PCI_REGMAX
5826159Sse	    && bytes != 3
5926159Sse	    && (unsigned) bytes <= 4
6026159Sse	    && (reg & (bytes -1)) == 0) {
6126159Sse		switch (cfgmech) {
6226159Sse		case 1:
6326174Sse			outl(CONF1_ADDR_PORT, (1 << 31)
6426174Sse			     | (bus << 16) | (slot << 11)
6526174Sse			     | (func << 8) | (reg & ~0x03));
6626174Sse			dataport = CONF1_DATA_PORT + (reg & 0x03);
6726159Sse			break;
6826159Sse		case 2:
6926159Sse			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
7026159Sse			outb(CONF2_FORWARD_PORT, bus);
7126159Sse			dataport = 0xc000 | (slot << 8) | reg;
7226159Sse			break;
7326159Sse		}
7426159Sse	}
7526159Sse	return (dataport);
7626159Sse}
776104Sse
7826159Sse/* disable configuration space accesses */
796104Sse
806104Ssestatic void
8126159Ssepci_cfgdisable(void)
8226159Sse{
8326159Sse	switch (cfgmech) {
8426159Sse	case 1:
8526159Sse		outl(CONF1_ADDR_PORT, 0);
8626159Sse		break;
8726159Sse	case 2:
8826159Sse		outb(CONF2_ENABLE_PORT, 0);
8926159Sse		outb(CONF2_FORWARD_PORT, 0);
9026159Sse		break;
9126159Sse	}
9226159Sse}
936104Sse
9426159Sse/* read configuration space register */
956104Sse
9626159Sseint
9726159Ssepci_cfgread(pcicfgregs *cfg, int reg, int bytes)
9826159Sse{
9926159Sse	int data = -1;
10026159Sse	int port;
1017234Sse
10226159Sse	port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes);
1037234Sse
10426159Sse	if (port != 0) {
10526159Sse		switch (bytes) {
10626159Sse		case 1:
10726159Sse			data = inb(port);
10826159Sse			break;
10926159Sse		case 2:
11026159Sse			data = inw(port);
11126159Sse			break;
11226159Sse		case 4:
11326159Sse			data = inl(port);
11426159Sse			break;
11526159Sse		}
11626159Sse		pci_cfgdisable();
11726159Sse	}
11826159Sse	return (data);
11926159Sse}
1207234Sse
12126159Sse/* write configuration space register */
1226104Sse
12326159Ssevoid
12426159Ssepci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes)
12526159Sse{
12626159Sse	int port;
1276104Sse
12826159Sse	port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes);
12926159Sse	if (port != 0) {
13026159Sse		switch (bytes) {
13126159Sse		case 1:
13226159Sse			outb(port, data);
13326159Sse			break;
13426159Sse		case 2:
13526159Sse			outw(port, data);
13626159Sse			break;
13726159Sse		case 4:
13826159Sse			outl(port, data);
13926159Sse			break;
14026159Sse		}
14126159Sse		pci_cfgdisable();
14226159Sse	}
14326159Sse}
1446104Sse
14526159Sse/* check whether the configuration mechanism has been correct identified */
1466104Sse
14710887Ssestatic int
14826159Ssepci_cfgcheck(int maxdev)
14910887Sse{
15010887Sse	u_char device;
15110735Sse
15226159Sse	if (bootverbose)
15326159Sse		printf("pci_cfgcheck:\tdevice ");
15410960Sse
15526159Sse	for (device = 0; device < maxdev; device++) {
15626159Sse		unsigned id, class, header;
15711378Sse		if (bootverbose)
15826159Sse			printf("%d ", device);
15926159Sse
16026159Sse		id = inl(pci_cfgenable(0, device, 0, 0, 4));
16126159Sse		if (id == 0 || id == -1)
16223415Sse			continue;
16323415Sse
16426159Sse		class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8;
16523415Sse		if (bootverbose)
16626159Sse			printf("[class=%06x] ", class);
16731893Sse		if (class == 0 || (class & 0xf870ff) != 0)
16823415Sse			continue;
16923415Sse
17026159Sse		header = inb(pci_cfgenable(0, device, 0, 14, 1));
17123415Sse		if (bootverbose)
17226159Sse			printf("[hdr=%02x] ", header);
17326159Sse		if ((header & 0x7e) != 0)
17423415Sse			continue;
17523415Sse
17626159Sse		if (bootverbose)
17726159Sse			printf("is there (id=%08x)\n", id);
17826159Sse
17926159Sse		pci_cfgdisable();
18026159Sse		return (1);
18110887Sse	}
18211378Sse	if (bootverbose)
18326159Sse		printf("-- nothing found\n");
18426159Sse
18526159Sse	pci_cfgdisable();
18626159Sse	return (0);
18710887Sse}
18810887Sse
18947307Speterstatic int
19026159Ssepci_cfgopen(void)
1916104Sse{
19211524Sse	unsigned long mode1res,oldval1;
19311524Sse	unsigned char mode2res,oldval2;
1946104Sse
19526159Sse	oldval1 = inl(CONF1_ADDR_PORT);
19610960Sse
19710960Sse	if (bootverbose) {
19826159Sse		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n",
19926159Sse		       oldval1);
20010960Sse	}
20110960Sse
20211544Sse	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
20310960Sse
20426159Sse		cfgmech = 1;
20526159Sse		devmax = 32;
20610960Sse
20726159Sse		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
20826159Sse		outb(CONF1_ADDR_PORT +3, 0);
20926159Sse		mode1res = inl(CONF1_ADDR_PORT);
21026159Sse		outl(CONF1_ADDR_PORT, oldval1);
21110960Sse
21211524Sse		if (bootverbose)
21326159Sse			printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n",
21426159Sse			       mode1res, CONF1_ENABLE_CHK);
2156104Sse
21611524Sse		if (mode1res) {
21726159Sse			if (pci_cfgcheck(32))
21826159Sse				return (cfgmech);
21926159Sse		}
22010960Sse
22126159Sse		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
22211524Sse		mode1res = inl(CONF1_ADDR_PORT);
22326159Sse		outl(CONF1_ADDR_PORT, oldval1);
2246104Sse
22511524Sse		if (bootverbose)
22626159Sse			printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n",
22726159Sse			       mode1res, CONF1_ENABLE_CHK1);
2289360Sse
22911524Sse		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
23026159Sse			if (pci_cfgcheck(32))
23126159Sse				return (cfgmech);
23226159Sse		}
23311524Sse	}
23410807Sse
23526159Sse	oldval2 = inb(CONF2_ENABLE_PORT);
23610887Sse
23711524Sse	if (bootverbose) {
23826159Sse		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
23926159Sse		       oldval2);
24011524Sse	}
24110887Sse
24211524Sse	if ((oldval2 & 0xf0) == 0) {
24310887Sse
24426159Sse		cfgmech = 2;
24526159Sse		devmax = 16;
24626159Sse
24726159Sse		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
24811524Sse		mode2res = inb(CONF2_ENABLE_PORT);
24926159Sse		outb(CONF2_ENABLE_PORT, oldval2);
25011524Sse
25111524Sse		if (bootverbose)
25226159Sse			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
25326159Sse			       mode2res, CONF2_ENABLE_CHK);
25411524Sse
25511524Sse		if (mode2res == CONF2_ENABLE_RES) {
25626159Sse			if (bootverbose)
25726159Sse				printf("pci_open(2a):\tnow trying mechanism 2\n");
25811524Sse
25926159Sse			if (pci_cfgcheck(16))
26026159Sse				return (cfgmech);
26111524Sse		}
26211524Sse	}
26311524Sse
26426159Sse	cfgmech = 0;
26526159Sse	devmax = 0;
26626159Sse	return (cfgmech);
2676104Sse}
26847307Speter
26947307Speterstatic devclass_t	pcib_devclass;
27047307Speter
27147307Speterstatic int
27247307Speternexus_pcib_probe(device_t dev)
27347307Speter{
27447307Speter	if (pci_cfgopen() != 0) {
27547307Speter		device_set_desc(dev, "PCI host bus adapter");
27647307Speter
27747307Speter		device_add_child(dev, "pci", 0, 0);
27847307Speter		return 0;
27947307Speter	}
28047307Speter	return ENXIO;
28147307Speter}
28247307Speter
28347307Speterstatic device_method_t nexus_pcib_methods[] = {
28447307Speter	/* Device interface */
28547307Speter	DEVMETHOD(device_probe,		nexus_pcib_probe),
28647307Speter	DEVMETHOD(device_attach,	bus_generic_attach),
28747307Speter	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
28847307Speter	DEVMETHOD(device_suspend,	bus_generic_suspend),
28947307Speter	DEVMETHOD(device_resume,	bus_generic_resume),
29047307Speter
29147307Speter	/* Bus interface */
29247307Speter	DEVMETHOD(bus_print_child,	bus_generic_print_child),
29347307Speter	DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
29447307Speter	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
29547307Speter	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
29647307Speter	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
29747307Speter	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
29847307Speter	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
29947307Speter
30047307Speter	{ 0, 0 }
30147307Speter};
30247307Speter
30347307Speterstatic driver_t nexus_pcib_driver = {
30447307Speter	"pcib",
30547307Speter	nexus_pcib_methods,
30647307Speter	1,
30747307Speter};
30847307Speter
30947307SpeterDRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0);
310