xref: /freebsd/sys/amd64/pci/pci_cfgreg.c (revision 5f06c5bb282cc40fefeab7f63a701e77b466553e)
1 /*-
2  * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3  * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
4  * Copyright (c) 2000, BSDi
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice unmodified, this list of conditions, and the following
12  *    disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/lock.h>
36 #include <sys/mutex.h>
37 #include <dev/pci/pcivar.h>
38 #include <dev/pci/pcireg.h>
39 #include <machine/pci_cfgreg.h>
40 
41 static int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
42 static void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
43 
44 static struct mtx pcicfg_mtx;
45 
46 /*
47  * Initialise access to PCI configuration space
48  */
49 int
50 pci_cfgregopen(void)
51 {
52 	static int		opened = 0;
53 
54 	if (opened)
55 		return (1);
56 	mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
57 	opened = 1;
58 	return (1);
59 }
60 
61 /*
62  * Read configuration space register
63  */
64 u_int32_t
65 pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
66 {
67 	uint32_t line;
68 
69 	/*
70 	 * Some BIOS writers seem to want to ignore the spec and put
71 	 * 0 in the intline rather than 255 to indicate none.  Some use
72 	 * numbers in the range 128-254 to indicate something strange and
73 	 * apparently undocumented anywhere.  Assume these are completely bogus
74 	 * and map them to 255, which the rest of the PCI code recognizes as
75 	 * as an invalid IRQ.
76 	 */
77 	if (reg == PCIR_INTLINE && bytes == 1) {
78 		line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
79 		if (line == 0 || line >= 128)
80 			line = PCI_INVALID_IRQ;
81 		return (line);
82 	}
83 	return (pcireg_cfgread(bus, slot, func, reg, bytes));
84 }
85 
86 /*
87  * Write configuration space register
88  */
89 void
90 pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
91 {
92 
93 	pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
94 }
95 
96 /*
97  * Configuration space access using direct register operations
98  */
99 
100 /* enable configuration space accesses and return data port address */
101 static int
102 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
103 {
104 	int dataport = 0;
105 
106 	if (bus <= PCI_BUSMAX && slot < 32 && func <= PCI_FUNCMAX &&
107 	    reg <= PCI_REGMAX && bytes != 3 && (unsigned) bytes <= 4 &&
108 	    (reg & (bytes - 1)) == 0) {
109 		outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11)
110 		    | (func << 8) | (reg & ~0x03));
111 		dataport = CONF1_DATA_PORT + (reg & 0x03);
112 	}
113 	return (dataport);
114 }
115 
116 /* disable configuration space accesses */
117 static void
118 pci_cfgdisable(void)
119 {
120 
121 	/*
122 	 * Do nothing.  Writing a 0 to the address port can apparently
123 	 * confuse some bridges and cause spurious access failures.
124 	 */
125 }
126 
127 static int
128 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
129 {
130 	int data = -1;
131 	int port;
132 
133 	mtx_lock_spin(&pcicfg_mtx);
134 	port = pci_cfgenable(bus, slot, func, reg, bytes);
135 	if (port != 0) {
136 		switch (bytes) {
137 		case 1:
138 			data = inb(port);
139 			break;
140 		case 2:
141 			data = inw(port);
142 			break;
143 		case 4:
144 			data = inl(port);
145 			break;
146 		}
147 		pci_cfgdisable();
148 	}
149 	mtx_unlock_spin(&pcicfg_mtx);
150 	return (data);
151 }
152 
153 static void
154 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
155 {
156 	int port;
157 
158 	mtx_lock_spin(&pcicfg_mtx);
159 	port = pci_cfgenable(bus, slot, func, reg, bytes);
160 	if (port != 0) {
161 		switch (bytes) {
162 		case 1:
163 			outb(port, data);
164 			break;
165 		case 2:
166 			outw(port, data);
167 			break;
168 		case 4:
169 			outl(port, data);
170 			break;
171 		}
172 		pci_cfgdisable();
173 	}
174 	mtx_unlock_spin(&pcicfg_mtx);
175 }
176