xref: /freebsd/sys/i386/pci/pci_cfgreg.c (revision b3daa02e72e719c029f97fbc1359a082d6918219)
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  *
26b3daa02eSStefan Eßer  * $Id: pcibus.c,v 1.38 1997/05/26 21:25:24 se Exp $
275bec6157SStefan Eßer  *
28ac19f918SStefan Eßer  */
29ac19f918SStefan Eßer 
305bec6157SStefan Eßer #include <sys/types.h>
315bec6157SStefan Eßer #include <sys/systm.h>
32ac19f918SStefan Eßer 
335bec6157SStefan Eßer #include <pci/pcireg.h>
345bec6157SStefan Eßer #include <pci/pcivar.h>
355bec6157SStefan Eßer #include <i386/isa/pcibus.h>
36ac19f918SStefan Eßer 
375bec6157SStefan Eßer #ifdef PCI_COMPAT
385bec6157SStefan Eßer /* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */
395bec6157SStefan Eßer #define cfgmech pci_mechanism
405bec6157SStefan Eßer int cfgmech;
41e30f0011SSatoshi Asami #else
425bec6157SStefan Eßer static int cfgmech;
435bec6157SStefan Eßer #endif /* PCI_COMPAT */
445bec6157SStefan Eßer static int devmax;
45ac19f918SStefan Eßer 
465bec6157SStefan Eßer /* enable configuration space accesses and return data port address */
47ac19f918SStefan Eßer 
48a3adc4f8SStefan Eßer static int
495bec6157SStefan Eßer pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
505bec6157SStefan Eßer {
515bec6157SStefan Eßer 	int dataport = 0;
525bec6157SStefan Eßer 
535bec6157SStefan Eßer 	if (bus <= PCI_BUSMAX
545bec6157SStefan Eßer 	    && slot < devmax
555bec6157SStefan Eßer 	    && func <= PCI_FUNCMAX
565bec6157SStefan Eßer 	    && reg <= PCI_REGMAX
575bec6157SStefan Eßer 	    && bytes != 3
585bec6157SStefan Eßer 	    && (unsigned) bytes <= 4
595bec6157SStefan Eßer 	    && (reg & (bytes -1)) == 0) {
605bec6157SStefan Eßer 		switch (cfgmech) {
615bec6157SStefan Eßer 		case 1:
62b3daa02eSStefan Eßer 			outl(CONF1_ADDR_PORT, (1 << 31)
63b3daa02eSStefan Eßer 			     | (bus << 16) | (slot << 11)
64b3daa02eSStefan Eßer 			     | (func << 8) | (reg & ~0x03));
65b3daa02eSStefan Eßer 			dataport = CONF1_DATA_PORT + (reg & 0x03);
665bec6157SStefan Eßer 			break;
675bec6157SStefan Eßer 		case 2:
685bec6157SStefan Eßer 			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
695bec6157SStefan Eßer 			outb(CONF2_FORWARD_PORT, bus);
705bec6157SStefan Eßer 			dataport = 0xc000 | (slot << 8) | reg;
715bec6157SStefan Eßer 			break;
725bec6157SStefan Eßer 		}
735bec6157SStefan Eßer 	}
745bec6157SStefan Eßer 	return (dataport);
755bec6157SStefan Eßer }
765bec6157SStefan Eßer 
775bec6157SStefan Eßer /* disable configuration space accesses */
785bec6157SStefan Eßer 
795bec6157SStefan Eßer static void
805bec6157SStefan Eßer pci_cfgdisable(void)
815bec6157SStefan Eßer {
825bec6157SStefan Eßer 	switch (cfgmech) {
835bec6157SStefan Eßer 	case 1:
845bec6157SStefan Eßer 		outl(CONF1_ADDR_PORT, 0);
855bec6157SStefan Eßer 		break;
865bec6157SStefan Eßer 	case 2:
875bec6157SStefan Eßer 		outb(CONF2_ENABLE_PORT, 0);
885bec6157SStefan Eßer 		outb(CONF2_FORWARD_PORT, 0);
895bec6157SStefan Eßer 		break;
905bec6157SStefan Eßer 	}
915bec6157SStefan Eßer }
925bec6157SStefan Eßer 
935bec6157SStefan Eßer /* read configuration space register */
945bec6157SStefan Eßer 
955bec6157SStefan Eßer int
965bec6157SStefan Eßer pci_cfgread(pcicfgregs *cfg, int reg, int bytes)
975bec6157SStefan Eßer {
985bec6157SStefan Eßer 	int data = -1;
995bec6157SStefan Eßer 	int port;
1005bec6157SStefan Eßer 
1015bec6157SStefan Eßer 	port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes);
1025bec6157SStefan Eßer 
1035bec6157SStefan Eßer 	if (port != 0) {
1045bec6157SStefan Eßer 		switch (bytes) {
1055bec6157SStefan Eßer 		case 1:
1065bec6157SStefan Eßer 			data = inb(port);
1075bec6157SStefan Eßer 			break;
1085bec6157SStefan Eßer 		case 2:
1095bec6157SStefan Eßer 			data = inw(port);
1105bec6157SStefan Eßer 			break;
1115bec6157SStefan Eßer 		case 4:
1125bec6157SStefan Eßer 			data = inl(port);
1135bec6157SStefan Eßer 			break;
1145bec6157SStefan Eßer 		}
1155bec6157SStefan Eßer 		pci_cfgdisable();
1165bec6157SStefan Eßer 	}
1175bec6157SStefan Eßer 	return (data);
1185bec6157SStefan Eßer }
1195bec6157SStefan Eßer 
1205bec6157SStefan Eßer /* write configuration space register */
1215bec6157SStefan Eßer 
1225bec6157SStefan Eßer void
1235bec6157SStefan Eßer pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes)
1245bec6157SStefan Eßer {
1255bec6157SStefan Eßer 	int port;
1265bec6157SStefan Eßer 
1275bec6157SStefan Eßer 	port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes);
1285bec6157SStefan Eßer 	if (port != 0) {
1295bec6157SStefan Eßer 		switch (bytes) {
1305bec6157SStefan Eßer 		case 1:
1315bec6157SStefan Eßer 			outb(port, data);
1325bec6157SStefan Eßer 			break;
1335bec6157SStefan Eßer 		case 2:
1345bec6157SStefan Eßer 			outw(port, data);
1355bec6157SStefan Eßer 			break;
1365bec6157SStefan Eßer 		case 4:
1375bec6157SStefan Eßer 			outl(port, data);
1385bec6157SStefan Eßer 			break;
1395bec6157SStefan Eßer 		}
1405bec6157SStefan Eßer 		pci_cfgdisable();
1415bec6157SStefan Eßer 	}
1425bec6157SStefan Eßer }
1435bec6157SStefan Eßer 
1445bec6157SStefan Eßer /* check whether the configuration mechanism has been correct identified */
1455bec6157SStefan Eßer 
1465bec6157SStefan Eßer static int
1475bec6157SStefan Eßer pci_cfgcheck(int maxdev)
148a3adc4f8SStefan Eßer {
149a3adc4f8SStefan Eßer 	u_char device;
150a3adc4f8SStefan Eßer 
1515bec6157SStefan Eßer 	if (bootverbose)
1525bec6157SStefan Eßer 		printf("pci_cfgcheck:\tdevice ");
15377b57314SStefan Eßer 
1545bec6157SStefan Eßer 	for (device = 0; device < maxdev; device++) {
1555bec6157SStefan Eßer 		unsigned id, class, header;
156c7483249SStefan Eßer 		if (bootverbose)
157c7483249SStefan Eßer 			printf("%d ", device);
1585bec6157SStefan Eßer 
1595bec6157SStefan Eßer 		id = inl(pci_cfgenable(0, device, 0, 0, 4));
1605bec6157SStefan Eßer 		if (id == 0 || id == -1)
16181cf5d7aSStefan Eßer 			continue;
16281cf5d7aSStefan Eßer 
1635bec6157SStefan Eßer 		class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8;
16481cf5d7aSStefan Eßer 		if (bootverbose)
1655bec6157SStefan Eßer 			printf("[class=%06x] ", class);
1665bec6157SStefan Eßer 		if (class == 0 || (class & 0xf8f0ff) != 0)
16781cf5d7aSStefan Eßer 			continue;
16881cf5d7aSStefan Eßer 
1695bec6157SStefan Eßer 		header = inb(pci_cfgenable(0, device, 0, 14, 1));
17081cf5d7aSStefan Eßer 		if (bootverbose)
1715bec6157SStefan Eßer 			printf("[hdr=%02x] ", header);
1725bec6157SStefan Eßer 		if ((header & 0x7e) != 0)
17381cf5d7aSStefan Eßer 			continue;
17481cf5d7aSStefan Eßer 
1755bec6157SStefan Eßer 		if (bootverbose)
1765bec6157SStefan Eßer 			printf("is there (id=%08x)\n", id);
1775bec6157SStefan Eßer 
1785bec6157SStefan Eßer 		pci_cfgdisable();
1795bec6157SStefan Eßer 		return (1);
180a3adc4f8SStefan Eßer 	}
181c7483249SStefan Eßer 	if (bootverbose)
182c7483249SStefan Eßer 		printf("-- nothing found\n");
1835bec6157SStefan Eßer 
1845bec6157SStefan Eßer 	pci_cfgdisable();
1855bec6157SStefan Eßer 	return (0);
186a3adc4f8SStefan Eßer }
187d7ea35fcSStefan Eßer 
1885bec6157SStefan Eßer int
1895bec6157SStefan Eßer pci_cfgopen(void)
190ac19f918SStefan Eßer {
191287911bdSStefan Eßer 	unsigned long mode1res,oldval1;
192287911bdSStefan Eßer 	unsigned char mode2res,oldval2;
1930847c06dSStefan Eßer 
194287911bdSStefan Eßer 	oldval1 = inl(CONF1_ADDR_PORT);
195a3adc4f8SStefan Eßer 
19677b57314SStefan Eßer 	if (bootverbose) {
1975bec6157SStefan Eßer 		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n",
1985bec6157SStefan Eßer 		       oldval1);
199a3adc4f8SStefan Eßer 	}
200a3adc4f8SStefan Eßer 
2010e2f699bSStefan Eßer 	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
202287911bdSStefan Eßer 
2035bec6157SStefan Eßer 		cfgmech = 1;
2045bec6157SStefan Eßer 		devmax = 32;
20577b57314SStefan Eßer 
20677b57314SStefan Eßer 		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
20777b57314SStefan Eßer 		outb(CONF1_ADDR_PORT +3, 0);
20877b57314SStefan Eßer 		mode1res = inl(CONF1_ADDR_PORT);
209287911bdSStefan Eßer 		outl(CONF1_ADDR_PORT, oldval1);
21077b57314SStefan Eßer 
21177b57314SStefan Eßer 		if (bootverbose)
2125bec6157SStefan Eßer 			printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n",
21377b57314SStefan Eßer 			       mode1res, CONF1_ENABLE_CHK);
21477b57314SStefan Eßer 
21577b57314SStefan Eßer 		if (mode1res) {
2165bec6157SStefan Eßer 			if (pci_cfgcheck(32))
2175bec6157SStefan Eßer 				return (cfgmech);
2185bec6157SStefan Eßer 		}
21977b57314SStefan Eßer 
22077b57314SStefan Eßer 		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
22177b57314SStefan Eßer 		mode1res = inl(CONF1_ADDR_PORT);
222287911bdSStefan Eßer 		outl(CONF1_ADDR_PORT, oldval1);
22377b57314SStefan Eßer 
22477b57314SStefan Eßer 		if (bootverbose)
2255bec6157SStefan Eßer 			printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n",
22677b57314SStefan Eßer 			       mode1res, CONF1_ENABLE_CHK1);
22777b57314SStefan Eßer 
228c7483249SStefan Eßer 		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
2295bec6157SStefan Eßer 			if (pci_cfgcheck(32))
2305bec6157SStefan Eßer 				return (cfgmech);
231287911bdSStefan Eßer 		}
2325bec6157SStefan Eßer 	}
23377b57314SStefan Eßer 
234287911bdSStefan Eßer 	oldval2 = inb(CONF2_ENABLE_PORT);
235287911bdSStefan Eßer 
236287911bdSStefan Eßer 	if (bootverbose) {
2375bec6157SStefan Eßer 		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
2385bec6157SStefan Eßer 		       oldval2);
239287911bdSStefan Eßer 	}
240287911bdSStefan Eßer 
241287911bdSStefan Eßer 	if ((oldval2 & 0xf0) == 0) {
242c7483249SStefan Eßer 
2435bec6157SStefan Eßer 		cfgmech = 2;
2445bec6157SStefan Eßer 		devmax = 16;
24577b57314SStefan Eßer 
246287911bdSStefan Eßer 		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
247287911bdSStefan Eßer 		mode2res = inb(CONF2_ENABLE_PORT);
248287911bdSStefan Eßer 		outb(CONF2_ENABLE_PORT, oldval2);
249287911bdSStefan Eßer 
250287911bdSStefan Eßer 		if (bootverbose)
2515bec6157SStefan Eßer 			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
252287911bdSStefan Eßer 			       mode2res, CONF2_ENABLE_CHK);
253287911bdSStefan Eßer 
254287911bdSStefan Eßer 		if (mode2res == CONF2_ENABLE_RES) {
255287911bdSStefan Eßer 			if (bootverbose)
2565bec6157SStefan Eßer 				printf("pci_open(2a):\tnow trying mechanism 2\n");
257287911bdSStefan Eßer 
2585bec6157SStefan Eßer 			if (pci_cfgcheck(16))
2595bec6157SStefan Eßer 				return (cfgmech);
260287911bdSStefan Eßer 		}
261287911bdSStefan Eßer 	}
26277b57314SStefan Eßer 
2635bec6157SStefan Eßer 	cfgmech = 0;
2645bec6157SStefan Eßer 	devmax = 0;
2655bec6157SStefan Eßer 	return (cfgmech);
266ac19f918SStefan Eßer }
267