xref: /freebsd/sys/i386/pci/pci_cfgreg.c (revision 5908d366fb365362004d549066a6225f0c46d3e6)
1ac19f918SStefan Eßer /*
25bec6157SStefan Eßer  * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
312a02d6eSMike Smith  * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
412a02d6eSMike Smith  * Copyright (c) 2000, BSDi
55bec6157SStefan Eßer  * All rights reserved.
65bec6157SStefan Eßer  *
75bec6157SStefan Eßer  * Redistribution and use in source and binary forms, with or without
85bec6157SStefan Eßer  * modification, are permitted provided that the following conditions
95bec6157SStefan Eßer  * are met:
105bec6157SStefan Eßer  * 1. Redistributions of source code must retain the above copyright
115bec6157SStefan Eßer  *    notice unmodified, this list of conditions, and the following
125bec6157SStefan Eßer  *    disclaimer.
135bec6157SStefan Eßer  * 2. Redistributions in binary form must reproduce the above copyright
145bec6157SStefan Eßer  *    notice, this list of conditions and the following disclaimer in the
155bec6157SStefan Eßer  *    documentation and/or other materials provided with the distribution.
165bec6157SStefan Eßer  *
175bec6157SStefan Eßer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
185bec6157SStefan Eßer  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
195bec6157SStefan Eßer  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
205bec6157SStefan Eßer  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
215bec6157SStefan Eßer  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
225bec6157SStefan Eßer  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
235bec6157SStefan Eßer  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
245bec6157SStefan Eßer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
255bec6157SStefan Eßer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
265bec6157SStefan Eßer  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27ac19f918SStefan Eßer  */
28ac19f918SStefan Eßer 
2971c5a901SDavid E. O'Brien #include <sys/cdefs.h>
3071c5a901SDavid E. O'Brien __FBSDID("$FreeBSD$");
3171c5a901SDavid E. O'Brien 
3277fa00faSJohn Baldwin #include <sys/param.h>
335bec6157SStefan Eßer #include <sys/systm.h>
348dc26439SPeter Wemm #include <sys/bus.h>
35af3d516fSPeter Wemm #include <sys/lock.h>
36af3d516fSPeter Wemm #include <sys/mutex.h>
37e300f53cSWarner Losh #include <dev/pci/pcivar.h>
38e300f53cSWarner Losh #include <dev/pci/pcireg.h>
3912a02d6eSMike Smith #include <machine/pci_cfgreg.h>
40300451c4SMike Smith #include <machine/pc/bios.h>
41300451c4SMike Smith 
428ff25e97SJohn Baldwin #define PRVERB(a) do {							\
438ff25e97SJohn Baldwin 	if (bootverbose)						\
448ff25e97SJohn Baldwin 		printf a ;						\
458ff25e97SJohn Baldwin } while(0)
46d626906bSWarner Losh 
475bec6157SStefan Eßer static int cfgmech;
485bec6157SStefan Eßer static int devmax;
49300451c4SMike Smith 
5012a02d6eSMike Smith static int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
5112a02d6eSMike Smith static void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
52300451c4SMike Smith static int	pcireg_cfgopen(void);
53300451c4SMike Smith 
54af3d516fSPeter Wemm static struct mtx pcicfg_mtx;
55af3d516fSPeter Wemm 
568ce1ab3aSWarner Losh /*
578ce1ab3aSWarner Losh  * Some BIOS writers seem to want to ignore the spec and put
588ce1ab3aSWarner Losh  * 0 in the intline rather than 255 to indicate none.  Some use
598ce1ab3aSWarner Losh  * numbers in the range 128-254 to indicate something strange and
608ce1ab3aSWarner Losh  * apparently undocumented anywhere.  Assume these are completely bogus
618ce1ab3aSWarner Losh  * and map them to 255, which means "none".
628ce1ab3aSWarner Losh  */
635908d366SStefan Farfeleder static __inline int
648ce1ab3aSWarner Losh pci_i386_map_intline(int line)
658ce1ab3aSWarner Losh {
668ce1ab3aSWarner Losh 	if (line == 0 || line >= 128)
67e300f53cSWarner Losh 		return (PCI_INVALID_IRQ);
688ce1ab3aSWarner Losh 	return (line);
698ce1ab3aSWarner Losh }
708ce1ab3aSWarner Losh 
71d626906bSWarner Losh static u_int16_t
72d626906bSWarner Losh pcibios_get_version(void)
73d626906bSWarner Losh {
74d626906bSWarner Losh 	struct bios_regs args;
75d626906bSWarner Losh 
765264a94fSJohn Baldwin 	if (PCIbios.ventry == 0) {
77d626906bSWarner Losh 		PRVERB(("pcibios: No call entry point\n"));
78d626906bSWarner Losh 		return (0);
79d626906bSWarner Losh 	}
80d626906bSWarner Losh 	args.eax = PCIBIOS_BIOS_PRESENT;
81d626906bSWarner Losh 	if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) {
82d626906bSWarner Losh 		PRVERB(("pcibios: BIOS_PRESENT call failed\n"));
83d626906bSWarner Losh 		return (0);
84d626906bSWarner Losh 	}
85d626906bSWarner Losh 	if (args.edx != 0x20494350) {
86d626906bSWarner Losh 		PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n"));
87d626906bSWarner Losh 		return (0);
88d626906bSWarner Losh 	}
89d626906bSWarner Losh 	return (args.ebx & 0xffff);
90d626906bSWarner Losh }
91d626906bSWarner Losh 
9212a02d6eSMike Smith /*
9312a02d6eSMike Smith  * Initialise access to PCI configuration space
9412a02d6eSMike Smith  */
9512a02d6eSMike Smith int
9612a02d6eSMike Smith pci_cfgregopen(void)
9721c3015aSDoug Rabson {
9812a02d6eSMike Smith 	static int		opened = 0;
99af3d516fSPeter Wemm 	u_int16_t		v;
10021c3015aSDoug Rabson 
10112a02d6eSMike Smith 	if (opened)
10212a02d6eSMike Smith 		return(1);
103300451c4SMike Smith 
104af3d516fSPeter Wemm 	if (pcireg_cfgopen() == 0)
105300451c4SMike Smith 		return(0);
10654c9005fSWarner Losh 
107af3d516fSPeter Wemm 	v = pcibios_get_version();
108af3d516fSPeter Wemm 	if (v > 0)
10939981fedSJohn Baldwin 		PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8,
11039981fedSJohn Baldwin 		    v & 0xff));
111af3d516fSPeter Wemm 	mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
11212a02d6eSMike Smith 	opened = 1;
11377fa00faSJohn Baldwin 
11477fa00faSJohn Baldwin 	/* $PIR requires PCI BIOS 2.10 or greater. */
11577fa00faSJohn Baldwin 	if (v >= 0x0210)
11677fa00faSJohn Baldwin 		pci_pir_open();
117300451c4SMike Smith 	return(1);
118300451c4SMike Smith }
119300451c4SMike Smith 
12012a02d6eSMike Smith /*
12112a02d6eSMike Smith  * Read configuration space register
12212a02d6eSMike Smith  */
123bb0d0a8eSMike Smith u_int32_t
124bb0d0a8eSMike Smith pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
125bb0d0a8eSMike Smith {
126e300f53cSWarner Losh 	uint32_t line;
127e300f53cSWarner Losh 
128bb0d0a8eSMike Smith 	/*
129d5ccecfaSWarner Losh 	 * Some BIOS writers seem to want to ignore the spec and put
130d5ccecfaSWarner Losh 	 * 0 in the intline rather than 255 to indicate none.  The rest of
131d5ccecfaSWarner Losh 	 * the code uses 255 as an invalid IRQ.
132d5ccecfaSWarner Losh 	 */
133d5ccecfaSWarner Losh 	if (reg == PCIR_INTLINE && bytes == 1) {
134af3d516fSPeter Wemm 		line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
1356f92bdd0SJohn Baldwin 		return (pci_i386_map_intline(line));
136d5ccecfaSWarner Losh 	}
137af3d516fSPeter Wemm 	return (pcireg_cfgread(bus, slot, func, reg, bytes));
138bb0d0a8eSMike Smith }
139bb0d0a8eSMike Smith 
14012a02d6eSMike Smith /*
14112a02d6eSMike Smith  * Write configuration space register
14212a02d6eSMike Smith  */
14312a02d6eSMike Smith void
14412a02d6eSMike Smith pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
14512a02d6eSMike Smith {
146af3d516fSPeter Wemm 
147cb8e4332SPoul-Henning Kamp 	pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
14812a02d6eSMike Smith }
14912a02d6eSMike Smith 
15012a02d6eSMike Smith /*
15112a02d6eSMike Smith  * Configuration space access using direct register operations
15212a02d6eSMike Smith  */
153ac19f918SStefan Eßer 
1545bec6157SStefan Eßer /* enable configuration space accesses and return data port address */
155a3adc4f8SStefan Eßer static int
1565bec6157SStefan Eßer pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
1575bec6157SStefan Eßer {
1585bec6157SStefan Eßer 	int dataport = 0;
1595bec6157SStefan Eßer 
1605bec6157SStefan Eßer 	if (bus <= PCI_BUSMAX
1615bec6157SStefan Eßer 	    && slot < devmax
1625bec6157SStefan Eßer 	    && func <= PCI_FUNCMAX
1635bec6157SStefan Eßer 	    && reg <= PCI_REGMAX
1645bec6157SStefan Eßer 	    && bytes != 3
1655bec6157SStefan Eßer 	    && (unsigned) bytes <= 4
1665bec6157SStefan Eßer 	    && (reg & (bytes - 1)) == 0) {
1675bec6157SStefan Eßer 		switch (cfgmech) {
1685bec6157SStefan Eßer 		case 1:
169b3daa02eSStefan Eßer 			outl(CONF1_ADDR_PORT, (1 << 31)
170b3daa02eSStefan Eßer 			    | (bus << 16) | (slot << 11)
171b3daa02eSStefan Eßer 			    | (func << 8) | (reg & ~0x03));
172b3daa02eSStefan Eßer 			dataport = CONF1_DATA_PORT + (reg & 0x03);
1735bec6157SStefan Eßer 			break;
1745bec6157SStefan Eßer 		case 2:
1755bec6157SStefan Eßer 			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
1765bec6157SStefan Eßer 			outb(CONF2_FORWARD_PORT, bus);
1775bec6157SStefan Eßer 			dataport = 0xc000 | (slot << 8) | reg;
1785bec6157SStefan Eßer 			break;
1795bec6157SStefan Eßer 		}
1805bec6157SStefan Eßer 	}
1815bec6157SStefan Eßer 	return (dataport);
1825bec6157SStefan Eßer }
1835bec6157SStefan Eßer 
1845bec6157SStefan Eßer /* disable configuration space accesses */
1855bec6157SStefan Eßer static void
1865bec6157SStefan Eßer pci_cfgdisable(void)
1875bec6157SStefan Eßer {
1885bec6157SStefan Eßer 	switch (cfgmech) {
1895bec6157SStefan Eßer 	case 1:
1905bec6157SStefan Eßer 		outl(CONF1_ADDR_PORT, 0);
1915bec6157SStefan Eßer 		break;
1925bec6157SStefan Eßer 	case 2:
1935bec6157SStefan Eßer 		outb(CONF2_ENABLE_PORT, 0);
1945bec6157SStefan Eßer 		outb(CONF2_FORWARD_PORT, 0);
1955bec6157SStefan Eßer 		break;
1965bec6157SStefan Eßer 	}
1975bec6157SStefan Eßer }
1985bec6157SStefan Eßer 
199300451c4SMike Smith static int
20021c3015aSDoug Rabson pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
2015bec6157SStefan Eßer {
2025bec6157SStefan Eßer 	int data = -1;
2035bec6157SStefan Eßer 	int port;
2045bec6157SStefan Eßer 
205af3d516fSPeter Wemm 	mtx_lock_spin(&pcicfg_mtx);
20621c3015aSDoug Rabson 	port = pci_cfgenable(bus, slot, func, reg, bytes);
2075bec6157SStefan Eßer 	if (port != 0) {
2085bec6157SStefan Eßer 		switch (bytes) {
2095bec6157SStefan Eßer 		case 1:
2105bec6157SStefan Eßer 			data = inb(port);
2115bec6157SStefan Eßer 			break;
2125bec6157SStefan Eßer 		case 2:
2135bec6157SStefan Eßer 			data = inw(port);
2145bec6157SStefan Eßer 			break;
2155bec6157SStefan Eßer 		case 4:
2165bec6157SStefan Eßer 			data = inl(port);
2175bec6157SStefan Eßer 			break;
2185bec6157SStefan Eßer 		}
2195bec6157SStefan Eßer 		pci_cfgdisable();
2205bec6157SStefan Eßer 	}
221af3d516fSPeter Wemm 	mtx_unlock_spin(&pcicfg_mtx);
2225bec6157SStefan Eßer 	return (data);
2235bec6157SStefan Eßer }
2245bec6157SStefan Eßer 
225300451c4SMike Smith static void
22621c3015aSDoug Rabson pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
2275bec6157SStefan Eßer {
2285bec6157SStefan Eßer 	int port;
2295bec6157SStefan Eßer 
230af3d516fSPeter Wemm 	mtx_lock_spin(&pcicfg_mtx);
23121c3015aSDoug Rabson 	port = pci_cfgenable(bus, slot, func, reg, bytes);
2325bec6157SStefan Eßer 	if (port != 0) {
2335bec6157SStefan Eßer 		switch (bytes) {
2345bec6157SStefan Eßer 		case 1:
2355bec6157SStefan Eßer 			outb(port, data);
2365bec6157SStefan Eßer 			break;
2375bec6157SStefan Eßer 		case 2:
2385bec6157SStefan Eßer 			outw(port, data);
2395bec6157SStefan Eßer 			break;
2405bec6157SStefan Eßer 		case 4:
2415bec6157SStefan Eßer 			outl(port, data);
2425bec6157SStefan Eßer 			break;
2435bec6157SStefan Eßer 		}
2445bec6157SStefan Eßer 		pci_cfgdisable();
2455bec6157SStefan Eßer 	}
246af3d516fSPeter Wemm 	mtx_unlock_spin(&pcicfg_mtx);
2475bec6157SStefan Eßer }
2485bec6157SStefan Eßer 
24912a02d6eSMike Smith /* check whether the configuration mechanism has been correctly identified */
2505bec6157SStefan Eßer static int
2515bec6157SStefan Eßer pci_cfgcheck(int maxdev)
252a3adc4f8SStefan Eßer {
253984de797SWarner Losh 	uint32_t id, class;
254984de797SWarner Losh 	uint8_t header;
255984de797SWarner Losh 	uint8_t device;
256af3d516fSPeter Wemm 	int port;
257a3adc4f8SStefan Eßer 
2585bec6157SStefan Eßer 	if (bootverbose)
2595bec6157SStefan Eßer 		printf("pci_cfgcheck:\tdevice ");
26077b57314SStefan Eßer 
2615bec6157SStefan Eßer 	for (device = 0; device < maxdev; device++) {
262c7483249SStefan Eßer 		if (bootverbose)
263c7483249SStefan Eßer 			printf("%d ", device);
2645bec6157SStefan Eßer 
265af3d516fSPeter Wemm 		port = pci_cfgenable(0, device, 0, 0, 4);
266af3d516fSPeter Wemm 		id = inl(port);
267984de797SWarner Losh 		if (id == 0 || id == 0xffffffff)
26881cf5d7aSStefan Eßer 			continue;
26981cf5d7aSStefan Eßer 
270af3d516fSPeter Wemm 		port = pci_cfgenable(0, device, 0, 8, 4);
271af3d516fSPeter Wemm 		class = inl(port) >> 8;
27281cf5d7aSStefan Eßer 		if (bootverbose)
2735bec6157SStefan Eßer 			printf("[class=%06x] ", class);
2748277ac25SStefan Eßer 		if (class == 0 || (class & 0xf870ff) != 0)
27581cf5d7aSStefan Eßer 			continue;
27681cf5d7aSStefan Eßer 
277af3d516fSPeter Wemm 		port = pci_cfgenable(0, device, 0, 14, 1);
278af3d516fSPeter Wemm 		header = inb(port);
27981cf5d7aSStefan Eßer 		if (bootverbose)
2805bec6157SStefan Eßer 			printf("[hdr=%02x] ", header);
2815bec6157SStefan Eßer 		if ((header & 0x7e) != 0)
28281cf5d7aSStefan Eßer 			continue;
28381cf5d7aSStefan Eßer 
2845bec6157SStefan Eßer 		if (bootverbose)
2855bec6157SStefan Eßer 			printf("is there (id=%08x)\n", id);
2865bec6157SStefan Eßer 
2875bec6157SStefan Eßer 		pci_cfgdisable();
2885bec6157SStefan Eßer 		return (1);
289a3adc4f8SStefan Eßer 	}
290c7483249SStefan Eßer 	if (bootverbose)
291c7483249SStefan Eßer 		printf("-- nothing found\n");
2925bec6157SStefan Eßer 
2935bec6157SStefan Eßer 	pci_cfgdisable();
2945bec6157SStefan Eßer 	return (0);
295a3adc4f8SStefan Eßer }
296d7ea35fcSStefan Eßer 
2978dc26439SPeter Wemm static int
298300451c4SMike Smith pcireg_cfgopen(void)
299ac19f918SStefan Eßer {
300984de797SWarner Losh 	uint32_t mode1res, oldval1;
301984de797SWarner Losh 	uint8_t mode2res, oldval2;
3020847c06dSStefan Eßer 
303287911bdSStefan Eßer 	oldval1 = inl(CONF1_ADDR_PORT);
304a3adc4f8SStefan Eßer 
30577b57314SStefan Eßer 	if (bootverbose) {
306984de797SWarner Losh 		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n",
3075bec6157SStefan Eßer 		    oldval1);
308a3adc4f8SStefan Eßer 	}
309a3adc4f8SStefan Eßer 
3100e2f699bSStefan Eßer 	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
311287911bdSStefan Eßer 
3125bec6157SStefan Eßer 		cfgmech = 1;
3135bec6157SStefan Eßer 		devmax = 32;
31477b57314SStefan Eßer 
31577b57314SStefan Eßer 		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
31621e25fa6SJohn Baldwin 		DELAY(1);
31777b57314SStefan Eßer 		mode1res = inl(CONF1_ADDR_PORT);
318287911bdSStefan Eßer 		outl(CONF1_ADDR_PORT, oldval1);
31977b57314SStefan Eßer 
32077b57314SStefan Eßer 		if (bootverbose)
321984de797SWarner Losh 			printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n",
32277b57314SStefan Eßer 			    mode1res, CONF1_ENABLE_CHK);
32377b57314SStefan Eßer 
32477b57314SStefan Eßer 		if (mode1res) {
3255bec6157SStefan Eßer 			if (pci_cfgcheck(32))
3265bec6157SStefan Eßer 				return (cfgmech);
3275bec6157SStefan Eßer 		}
32877b57314SStefan Eßer 
32977b57314SStefan Eßer 		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
33077b57314SStefan Eßer 		mode1res = inl(CONF1_ADDR_PORT);
331287911bdSStefan Eßer 		outl(CONF1_ADDR_PORT, oldval1);
33277b57314SStefan Eßer 
33377b57314SStefan Eßer 		if (bootverbose)
334984de797SWarner Losh 			printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n",
33577b57314SStefan Eßer 			    mode1res, CONF1_ENABLE_CHK1);
33677b57314SStefan Eßer 
337c7483249SStefan Eßer 		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
3385bec6157SStefan Eßer 			if (pci_cfgcheck(32))
3395bec6157SStefan Eßer 				return (cfgmech);
340287911bdSStefan Eßer 		}
3415bec6157SStefan Eßer 	}
34277b57314SStefan Eßer 
343287911bdSStefan Eßer 	oldval2 = inb(CONF2_ENABLE_PORT);
344287911bdSStefan Eßer 
345287911bdSStefan Eßer 	if (bootverbose) {
3465bec6157SStefan Eßer 		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
3475bec6157SStefan Eßer 		    oldval2);
348287911bdSStefan Eßer 	}
349287911bdSStefan Eßer 
350287911bdSStefan Eßer 	if ((oldval2 & 0xf0) == 0) {
351c7483249SStefan Eßer 
3525bec6157SStefan Eßer 		cfgmech = 2;
3535bec6157SStefan Eßer 		devmax = 16;
35477b57314SStefan Eßer 
355287911bdSStefan Eßer 		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
356287911bdSStefan Eßer 		mode2res = inb(CONF2_ENABLE_PORT);
357287911bdSStefan Eßer 		outb(CONF2_ENABLE_PORT, oldval2);
358287911bdSStefan Eßer 
359287911bdSStefan Eßer 		if (bootverbose)
3605bec6157SStefan Eßer 			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
361287911bdSStefan Eßer 			    mode2res, CONF2_ENABLE_CHK);
362287911bdSStefan Eßer 
363287911bdSStefan Eßer 		if (mode2res == CONF2_ENABLE_RES) {
364287911bdSStefan Eßer 			if (bootverbose)
3655bec6157SStefan Eßer 				printf("pci_open(2a):\tnow trying mechanism 2\n");
366287911bdSStefan Eßer 
3675bec6157SStefan Eßer 			if (pci_cfgcheck(16))
3685bec6157SStefan Eßer 				return (cfgmech);
369287911bdSStefan Eßer 		}
370287911bdSStefan Eßer 	}
37177b57314SStefan Eßer 
3725bec6157SStefan Eßer 	cfgmech = 0;
3735bec6157SStefan Eßer 	devmax = 0;
3745bec6157SStefan Eßer 	return (cfgmech);
375ac19f918SStefan Eßer }
3768dc26439SPeter Wemm 
377