xref: /freebsd/sys/i386/pci/pci_cfgreg.c (revision 8dc26439da0d68c4035e407368d5e775b266deb2)
1ac19f918SStefan Eßer /*
25bec6157SStefan Eßer  * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
35bec6157SStefan Eßer  * All rights reserved.
45bec6157SStefan Eßer  *
55bec6157SStefan Eßer  * Redistribution and use in source and binary forms, with or without
65bec6157SStefan Eßer  * modification, are permitted provided that the following conditions
75bec6157SStefan Eßer  * are met:
85bec6157SStefan Eßer  * 1. Redistributions of source code must retain the above copyright
95bec6157SStefan Eßer  *    notice unmodified, this list of conditions, and the following
105bec6157SStefan Eßer  *    disclaimer.
115bec6157SStefan Eßer  * 2. Redistributions in binary form must reproduce the above copyright
125bec6157SStefan Eßer  *    notice, this list of conditions and the following disclaimer in the
135bec6157SStefan Eßer  *    documentation and/or other materials provided with the distribution.
145bec6157SStefan Eßer  *
155bec6157SStefan Eßer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
165bec6157SStefan Eßer  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
175bec6157SStefan Eßer  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
185bec6157SStefan Eßer  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
195bec6157SStefan Eßer  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
205bec6157SStefan Eßer  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
215bec6157SStefan Eßer  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
225bec6157SStefan Eßer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
235bec6157SStefan Eßer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
245bec6157SStefan Eßer  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
255bec6157SStefan Eßer  *
268dc26439SPeter Wemm  * $Id: pcibus.c,v 1.41 1997/12/20 09:04:25 se Exp $
275bec6157SStefan Eßer  *
28ac19f918SStefan Eßer  */
29ac19f918SStefan Eßer 
308dc26439SPeter Wemm #include <sys/param.h>
315bec6157SStefan Eßer #include <sys/systm.h>
328dc26439SPeter Wemm #include <sys/bus.h>
338dc26439SPeter Wemm #include <sys/kernel.h>
34ac19f918SStefan Eßer 
355bec6157SStefan Eßer #include <pci/pcivar.h>
365bec6157SStefan Eßer #include <i386/isa/pcibus.h>
37ac19f918SStefan Eßer 
385bec6157SStefan Eßer #ifdef PCI_COMPAT
395bec6157SStefan Eßer /* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */
405bec6157SStefan Eßer #define cfgmech pci_mechanism
415bec6157SStefan Eßer int cfgmech;
42e30f0011SSatoshi Asami #else
435bec6157SStefan Eßer static int cfgmech;
445bec6157SStefan Eßer #endif /* PCI_COMPAT */
455bec6157SStefan Eßer static int devmax;
46ac19f918SStefan Eßer 
475bec6157SStefan Eßer /* enable configuration space accesses and return data port address */
48ac19f918SStefan Eßer 
49a3adc4f8SStefan Eßer static int
505bec6157SStefan Eßer pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
515bec6157SStefan Eßer {
525bec6157SStefan Eßer 	int dataport = 0;
535bec6157SStefan Eßer 
545bec6157SStefan Eßer 	if (bus <= PCI_BUSMAX
555bec6157SStefan Eßer 	    && slot < devmax
565bec6157SStefan Eßer 	    && func <= PCI_FUNCMAX
575bec6157SStefan Eßer 	    && reg <= PCI_REGMAX
585bec6157SStefan Eßer 	    && bytes != 3
595bec6157SStefan Eßer 	    && (unsigned) bytes <= 4
605bec6157SStefan Eßer 	    && (reg & (bytes -1)) == 0) {
615bec6157SStefan Eßer 		switch (cfgmech) {
625bec6157SStefan Eßer 		case 1:
63b3daa02eSStefan Eßer 			outl(CONF1_ADDR_PORT, (1 << 31)
64b3daa02eSStefan Eßer 			     | (bus << 16) | (slot << 11)
65b3daa02eSStefan Eßer 			     | (func << 8) | (reg & ~0x03));
66b3daa02eSStefan Eßer 			dataport = CONF1_DATA_PORT + (reg & 0x03);
675bec6157SStefan Eßer 			break;
685bec6157SStefan Eßer 		case 2:
695bec6157SStefan Eßer 			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
705bec6157SStefan Eßer 			outb(CONF2_FORWARD_PORT, bus);
715bec6157SStefan Eßer 			dataport = 0xc000 | (slot << 8) | reg;
725bec6157SStefan Eßer 			break;
735bec6157SStefan Eßer 		}
745bec6157SStefan Eßer 	}
755bec6157SStefan Eßer 	return (dataport);
765bec6157SStefan Eßer }
775bec6157SStefan Eßer 
785bec6157SStefan Eßer /* disable configuration space accesses */
795bec6157SStefan Eßer 
805bec6157SStefan Eßer static void
815bec6157SStefan Eßer pci_cfgdisable(void)
825bec6157SStefan Eßer {
835bec6157SStefan Eßer 	switch (cfgmech) {
845bec6157SStefan Eßer 	case 1:
855bec6157SStefan Eßer 		outl(CONF1_ADDR_PORT, 0);
865bec6157SStefan Eßer 		break;
875bec6157SStefan Eßer 	case 2:
885bec6157SStefan Eßer 		outb(CONF2_ENABLE_PORT, 0);
895bec6157SStefan Eßer 		outb(CONF2_FORWARD_PORT, 0);
905bec6157SStefan Eßer 		break;
915bec6157SStefan Eßer 	}
925bec6157SStefan Eßer }
935bec6157SStefan Eßer 
945bec6157SStefan Eßer /* read configuration space register */
955bec6157SStefan Eßer 
965bec6157SStefan Eßer int
975bec6157SStefan Eßer pci_cfgread(pcicfgregs *cfg, int reg, int bytes)
985bec6157SStefan Eßer {
995bec6157SStefan Eßer 	int data = -1;
1005bec6157SStefan Eßer 	int port;
1015bec6157SStefan Eßer 
1025bec6157SStefan Eßer 	port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes);
1035bec6157SStefan Eßer 
1045bec6157SStefan Eßer 	if (port != 0) {
1055bec6157SStefan Eßer 		switch (bytes) {
1065bec6157SStefan Eßer 		case 1:
1075bec6157SStefan Eßer 			data = inb(port);
1085bec6157SStefan Eßer 			break;
1095bec6157SStefan Eßer 		case 2:
1105bec6157SStefan Eßer 			data = inw(port);
1115bec6157SStefan Eßer 			break;
1125bec6157SStefan Eßer 		case 4:
1135bec6157SStefan Eßer 			data = inl(port);
1145bec6157SStefan Eßer 			break;
1155bec6157SStefan Eßer 		}
1165bec6157SStefan Eßer 		pci_cfgdisable();
1175bec6157SStefan Eßer 	}
1185bec6157SStefan Eßer 	return (data);
1195bec6157SStefan Eßer }
1205bec6157SStefan Eßer 
1215bec6157SStefan Eßer /* write configuration space register */
1225bec6157SStefan Eßer 
1235bec6157SStefan Eßer void
1245bec6157SStefan Eßer pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes)
1255bec6157SStefan Eßer {
1265bec6157SStefan Eßer 	int port;
1275bec6157SStefan Eßer 
1285bec6157SStefan Eßer 	port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes);
1295bec6157SStefan Eßer 	if (port != 0) {
1305bec6157SStefan Eßer 		switch (bytes) {
1315bec6157SStefan Eßer 		case 1:
1325bec6157SStefan Eßer 			outb(port, data);
1335bec6157SStefan Eßer 			break;
1345bec6157SStefan Eßer 		case 2:
1355bec6157SStefan Eßer 			outw(port, data);
1365bec6157SStefan Eßer 			break;
1375bec6157SStefan Eßer 		case 4:
1385bec6157SStefan Eßer 			outl(port, data);
1395bec6157SStefan Eßer 			break;
1405bec6157SStefan Eßer 		}
1415bec6157SStefan Eßer 		pci_cfgdisable();
1425bec6157SStefan Eßer 	}
1435bec6157SStefan Eßer }
1445bec6157SStefan Eßer 
1455bec6157SStefan Eßer /* check whether the configuration mechanism has been correct identified */
1465bec6157SStefan Eßer 
1475bec6157SStefan Eßer static int
1485bec6157SStefan Eßer pci_cfgcheck(int maxdev)
149a3adc4f8SStefan Eßer {
150a3adc4f8SStefan Eßer 	u_char device;
151a3adc4f8SStefan Eßer 
1525bec6157SStefan Eßer 	if (bootverbose)
1535bec6157SStefan Eßer 		printf("pci_cfgcheck:\tdevice ");
15477b57314SStefan Eßer 
1555bec6157SStefan Eßer 	for (device = 0; device < maxdev; device++) {
1565bec6157SStefan Eßer 		unsigned id, class, header;
157c7483249SStefan Eßer 		if (bootverbose)
158c7483249SStefan Eßer 			printf("%d ", device);
1595bec6157SStefan Eßer 
1605bec6157SStefan Eßer 		id = inl(pci_cfgenable(0, device, 0, 0, 4));
1615bec6157SStefan Eßer 		if (id == 0 || id == -1)
16281cf5d7aSStefan Eßer 			continue;
16381cf5d7aSStefan Eßer 
1645bec6157SStefan Eßer 		class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8;
16581cf5d7aSStefan Eßer 		if (bootverbose)
1665bec6157SStefan Eßer 			printf("[class=%06x] ", class);
1678277ac25SStefan Eßer 		if (class == 0 || (class & 0xf870ff) != 0)
16881cf5d7aSStefan Eßer 			continue;
16981cf5d7aSStefan Eßer 
1705bec6157SStefan Eßer 		header = inb(pci_cfgenable(0, device, 0, 14, 1));
17181cf5d7aSStefan Eßer 		if (bootverbose)
1725bec6157SStefan Eßer 			printf("[hdr=%02x] ", header);
1735bec6157SStefan Eßer 		if ((header & 0x7e) != 0)
17481cf5d7aSStefan Eßer 			continue;
17581cf5d7aSStefan Eßer 
1765bec6157SStefan Eßer 		if (bootverbose)
1775bec6157SStefan Eßer 			printf("is there (id=%08x)\n", id);
1785bec6157SStefan Eßer 
1795bec6157SStefan Eßer 		pci_cfgdisable();
1805bec6157SStefan Eßer 		return (1);
181a3adc4f8SStefan Eßer 	}
182c7483249SStefan Eßer 	if (bootverbose)
183c7483249SStefan Eßer 		printf("-- nothing found\n");
1845bec6157SStefan Eßer 
1855bec6157SStefan Eßer 	pci_cfgdisable();
1865bec6157SStefan Eßer 	return (0);
187a3adc4f8SStefan Eßer }
188d7ea35fcSStefan Eßer 
1898dc26439SPeter Wemm static int
1905bec6157SStefan Eßer pci_cfgopen(void)
191ac19f918SStefan Eßer {
192287911bdSStefan Eßer 	unsigned long mode1res,oldval1;
193287911bdSStefan Eßer 	unsigned char mode2res,oldval2;
1940847c06dSStefan Eßer 
195287911bdSStefan Eßer 	oldval1 = inl(CONF1_ADDR_PORT);
196a3adc4f8SStefan Eßer 
19777b57314SStefan Eßer 	if (bootverbose) {
1985bec6157SStefan Eßer 		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n",
1995bec6157SStefan Eßer 		       oldval1);
200a3adc4f8SStefan Eßer 	}
201a3adc4f8SStefan Eßer 
2020e2f699bSStefan Eßer 	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
203287911bdSStefan Eßer 
2045bec6157SStefan Eßer 		cfgmech = 1;
2055bec6157SStefan Eßer 		devmax = 32;
20677b57314SStefan Eßer 
20777b57314SStefan Eßer 		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
20877b57314SStefan Eßer 		outb(CONF1_ADDR_PORT +3, 0);
20977b57314SStefan Eßer 		mode1res = inl(CONF1_ADDR_PORT);
210287911bdSStefan Eßer 		outl(CONF1_ADDR_PORT, oldval1);
21177b57314SStefan Eßer 
21277b57314SStefan Eßer 		if (bootverbose)
2135bec6157SStefan Eßer 			printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n",
21477b57314SStefan Eßer 			       mode1res, CONF1_ENABLE_CHK);
21577b57314SStefan Eßer 
21677b57314SStefan Eßer 		if (mode1res) {
2175bec6157SStefan Eßer 			if (pci_cfgcheck(32))
2185bec6157SStefan Eßer 				return (cfgmech);
2195bec6157SStefan Eßer 		}
22077b57314SStefan Eßer 
22177b57314SStefan Eßer 		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
22277b57314SStefan Eßer 		mode1res = inl(CONF1_ADDR_PORT);
223287911bdSStefan Eßer 		outl(CONF1_ADDR_PORT, oldval1);
22477b57314SStefan Eßer 
22577b57314SStefan Eßer 		if (bootverbose)
2265bec6157SStefan Eßer 			printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n",
22777b57314SStefan Eßer 			       mode1res, CONF1_ENABLE_CHK1);
22877b57314SStefan Eßer 
229c7483249SStefan Eßer 		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
2305bec6157SStefan Eßer 			if (pci_cfgcheck(32))
2315bec6157SStefan Eßer 				return (cfgmech);
232287911bdSStefan Eßer 		}
2335bec6157SStefan Eßer 	}
23477b57314SStefan Eßer 
235287911bdSStefan Eßer 	oldval2 = inb(CONF2_ENABLE_PORT);
236287911bdSStefan Eßer 
237287911bdSStefan Eßer 	if (bootverbose) {
2385bec6157SStefan Eßer 		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
2395bec6157SStefan Eßer 		       oldval2);
240287911bdSStefan Eßer 	}
241287911bdSStefan Eßer 
242287911bdSStefan Eßer 	if ((oldval2 & 0xf0) == 0) {
243c7483249SStefan Eßer 
2445bec6157SStefan Eßer 		cfgmech = 2;
2455bec6157SStefan Eßer 		devmax = 16;
24677b57314SStefan Eßer 
247287911bdSStefan Eßer 		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
248287911bdSStefan Eßer 		mode2res = inb(CONF2_ENABLE_PORT);
249287911bdSStefan Eßer 		outb(CONF2_ENABLE_PORT, oldval2);
250287911bdSStefan Eßer 
251287911bdSStefan Eßer 		if (bootverbose)
2525bec6157SStefan Eßer 			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
253287911bdSStefan Eßer 			       mode2res, CONF2_ENABLE_CHK);
254287911bdSStefan Eßer 
255287911bdSStefan Eßer 		if (mode2res == CONF2_ENABLE_RES) {
256287911bdSStefan Eßer 			if (bootverbose)
2575bec6157SStefan Eßer 				printf("pci_open(2a):\tnow trying mechanism 2\n");
258287911bdSStefan Eßer 
2595bec6157SStefan Eßer 			if (pci_cfgcheck(16))
2605bec6157SStefan Eßer 				return (cfgmech);
261287911bdSStefan Eßer 		}
262287911bdSStefan Eßer 	}
26377b57314SStefan Eßer 
2645bec6157SStefan Eßer 	cfgmech = 0;
2655bec6157SStefan Eßer 	devmax = 0;
2665bec6157SStefan Eßer 	return (cfgmech);
267ac19f918SStefan Eßer }
2688dc26439SPeter Wemm 
2698dc26439SPeter Wemm static devclass_t	pcib_devclass;
2708dc26439SPeter Wemm 
2718dc26439SPeter Wemm static int
2728dc26439SPeter Wemm nexus_pcib_probe(device_t dev)
2738dc26439SPeter Wemm {
2748dc26439SPeter Wemm 	if (pci_cfgopen() != 0) {
2758dc26439SPeter Wemm 		device_set_desc(dev, "PCI host bus adapter");
2768dc26439SPeter Wemm 
2778dc26439SPeter Wemm 		device_add_child(dev, "pci", 0, 0);
2788dc26439SPeter Wemm 		return 0;
2798dc26439SPeter Wemm 	}
2808dc26439SPeter Wemm 	return ENXIO;
2818dc26439SPeter Wemm }
2828dc26439SPeter Wemm 
2838dc26439SPeter Wemm static device_method_t nexus_pcib_methods[] = {
2848dc26439SPeter Wemm 	/* Device interface */
2858dc26439SPeter Wemm 	DEVMETHOD(device_probe,		nexus_pcib_probe),
2868dc26439SPeter Wemm 	DEVMETHOD(device_attach,	bus_generic_attach),
2878dc26439SPeter Wemm 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
2888dc26439SPeter Wemm 	DEVMETHOD(device_suspend,	bus_generic_suspend),
2898dc26439SPeter Wemm 	DEVMETHOD(device_resume,	bus_generic_resume),
2908dc26439SPeter Wemm 
2918dc26439SPeter Wemm 	/* Bus interface */
2928dc26439SPeter Wemm 	DEVMETHOD(bus_print_child,	bus_generic_print_child),
2938dc26439SPeter Wemm 	DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
2948dc26439SPeter Wemm 	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
2958dc26439SPeter Wemm 	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
2968dc26439SPeter Wemm 	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
2978dc26439SPeter Wemm 	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
2988dc26439SPeter Wemm 	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
2998dc26439SPeter Wemm 
3008dc26439SPeter Wemm 	{ 0, 0 }
3018dc26439SPeter Wemm };
3028dc26439SPeter Wemm 
3038dc26439SPeter Wemm static driver_t nexus_pcib_driver = {
3048dc26439SPeter Wemm 	"pcib",
3058dc26439SPeter Wemm 	nexus_pcib_methods,
3068dc26439SPeter Wemm 	1,
3078dc26439SPeter Wemm };
3088dc26439SPeter Wemm 
3098dc26439SPeter Wemm DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0);
310