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: releng/11.0/sys/amd64/pci/pci_cfgreg.c 267992 2014-06-28 03:56:17Z hselasky $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/bus.h>
35#include <sys/lock.h>
36#include <sys/kernel.h>
37#include <sys/mutex.h>
38#include <sys/sysctl.h>
39#include <dev/pci/pcivar.h>
40#include <dev/pci/pcireg.h>
41#include <vm/vm.h>
42#include <vm/pmap.h>
43#include <machine/pci_cfgreg.h>
44
45enum {
46	CFGMECH_NONE = 0,
47	CFGMECH_1,
48	CFGMECH_PCIE,
49};
50
51static uint32_t	pci_docfgregread(int bus, int slot, int func, int reg,
52		    int bytes);
53static int	pciereg_cfgread(int bus, unsigned slot, unsigned func,
54		    unsigned reg, unsigned bytes);
55static void	pciereg_cfgwrite(int bus, unsigned slot, unsigned func,
56		    unsigned reg, int data, unsigned bytes);
57static int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
58static void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
59
60SYSCTL_DECL(_hw_pci);
61
62static int cfgmech;
63static vm_offset_t pcie_base;
64static int pcie_minbus, pcie_maxbus;
65static uint32_t pcie_badslots;
66static struct mtx pcicfg_mtx;
67static int mcfg_enable = 1;
68SYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0,
69    "Enable support for PCI-e memory mapped config access");
70
71/*
72 * Initialise access to PCI configuration space
73 */
74int
75pci_cfgregopen(void)
76{
77	static int once = 0;
78	uint64_t pciebar;
79	uint16_t did, vid;
80
81	if (!once) {
82		mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
83		once = 1;
84	}
85
86	if (cfgmech != CFGMECH_NONE)
87		return (1);
88	cfgmech = CFGMECH_1;
89
90	/*
91	 * Grope around in the PCI config space to see if this is a
92	 * chipset that is capable of doing memory-mapped config cycles.
93	 * This also implies that it can do PCIe extended config cycles.
94	 */
95
96	/* Check for supported chipsets */
97	vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2);
98	did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2);
99	switch (vid) {
100	case 0x8086:
101		switch (did) {
102		case 0x3590:
103		case 0x3592:
104			/* Intel 7520 or 7320 */
105			pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
106			pcie_cfgregopen(pciebar, 0, 255);
107			break;
108		case 0x2580:
109		case 0x2584:
110		case 0x2590:
111			/* Intel 915, 925, or 915GM */
112			pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
113			pcie_cfgregopen(pciebar, 0, 255);
114			break;
115		}
116	}
117
118	return (1);
119}
120
121static uint32_t
122pci_docfgregread(int bus, int slot, int func, int reg, int bytes)
123{
124
125	if (cfgmech == CFGMECH_PCIE &&
126	    (bus >= pcie_minbus && bus <= pcie_maxbus) &&
127	    (bus != 0 || !(1 << slot & pcie_badslots)))
128		return (pciereg_cfgread(bus, slot, func, reg, bytes));
129	else
130		return (pcireg_cfgread(bus, slot, func, reg, bytes));
131}
132
133/*
134 * Read configuration space register
135 */
136u_int32_t
137pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
138{
139	uint32_t line;
140
141	/*
142	 * Some BIOS writers seem to want to ignore the spec and put
143	 * 0 in the intline rather than 255 to indicate none.  Some use
144	 * numbers in the range 128-254 to indicate something strange and
145	 * apparently undocumented anywhere.  Assume these are completely bogus
146	 * and map them to 255, which the rest of the PCI code recognizes as
147	 * as an invalid IRQ.
148	 */
149	if (reg == PCIR_INTLINE && bytes == 1) {
150		line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1);
151		if (line == 0 || line >= 128)
152			line = PCI_INVALID_IRQ;
153		return (line);
154	}
155	return (pci_docfgregread(bus, slot, func, reg, bytes));
156}
157
158/*
159 * Write configuration space register
160 */
161void
162pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
163{
164
165	if (cfgmech == CFGMECH_PCIE &&
166	    (bus >= pcie_minbus && bus <= pcie_maxbus) &&
167	    (bus != 0 || !(1 << slot & pcie_badslots)))
168		pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
169	else
170		pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
171}
172
173/*
174 * Configuration space access using direct register operations
175 */
176
177/* enable configuration space accesses and return data port address */
178static int
179pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
180{
181	int dataport = 0;
182
183	if (bus <= PCI_BUSMAX && slot <= PCI_SLOTMAX && func <= PCI_FUNCMAX &&
184	    (unsigned)reg <= PCI_REGMAX && bytes != 3 &&
185	    (unsigned)bytes <= 4 && (reg & (bytes - 1)) == 0) {
186		outl(CONF1_ADDR_PORT, (1U << 31) | (bus << 16) | (slot << 11)
187		    | (func << 8) | (reg & ~0x03));
188		dataport = CONF1_DATA_PORT + (reg & 0x03);
189	}
190	return (dataport);
191}
192
193/* disable configuration space accesses */
194static void
195pci_cfgdisable(void)
196{
197
198	/*
199	 * Do nothing.  Writing a 0 to the address port can apparently
200	 * confuse some bridges and cause spurious access failures.
201	 */
202}
203
204static int
205pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
206{
207	int data = -1;
208	int port;
209
210	mtx_lock_spin(&pcicfg_mtx);
211	port = pci_cfgenable(bus, slot, func, reg, bytes);
212	if (port != 0) {
213		switch (bytes) {
214		case 1:
215			data = inb(port);
216			break;
217		case 2:
218			data = inw(port);
219			break;
220		case 4:
221			data = inl(port);
222			break;
223		}
224		pci_cfgdisable();
225	}
226	mtx_unlock_spin(&pcicfg_mtx);
227	return (data);
228}
229
230static void
231pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
232{
233	int port;
234
235	mtx_lock_spin(&pcicfg_mtx);
236	port = pci_cfgenable(bus, slot, func, reg, bytes);
237	if (port != 0) {
238		switch (bytes) {
239		case 1:
240			outb(port, data);
241			break;
242		case 2:
243			outw(port, data);
244			break;
245		case 4:
246			outl(port, data);
247			break;
248		}
249		pci_cfgdisable();
250	}
251	mtx_unlock_spin(&pcicfg_mtx);
252}
253
254int
255pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
256{
257	uint32_t val1, val2;
258	int slot;
259
260	if (!mcfg_enable)
261		return (0);
262
263	if (minbus != 0)
264		return (0);
265
266	if (bootverbose)
267		printf("PCIe: Memory Mapped configuration base @ 0x%lx\n",
268		    base);
269
270	/* XXX: We should make sure this really fits into the direct map. */
271	pcie_base = (vm_offset_t)pmap_mapdev(base, (maxbus + 1) << 20);
272	pcie_minbus = minbus;
273	pcie_maxbus = maxbus;
274	cfgmech = CFGMECH_PCIE;
275
276	/*
277	 * On some AMD systems, some of the devices on bus 0 are
278	 * inaccessible using memory-mapped PCI config access.  Walk
279	 * bus 0 looking for such devices.  For these devices, we will
280	 * fall back to using type 1 config access instead.
281	 */
282	if (pci_cfgregopen() != 0) {
283		for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
284			val1 = pcireg_cfgread(0, slot, 0, 0, 4);
285			if (val1 == 0xffffffff)
286				continue;
287
288			val2 = pciereg_cfgread(0, slot, 0, 0, 4);
289			if (val2 != val1)
290				pcie_badslots |= (1 << slot);
291		}
292	}
293
294	return (1);
295}
296
297#define PCIE_VADDR(base, reg, bus, slot, func)	\
298	((base)				+	\
299	((((bus) & 0xff) << 20)		|	\
300	(((slot) & 0x1f) << 15)		|	\
301	(((func) & 0x7) << 12)		|	\
302	((reg) & 0xfff)))
303
304/*
305 * AMD BIOS And Kernel Developer's Guides for CPU families starting with 10h
306 * have a requirement that all accesses to the memory mapped PCI configuration
307 * space are done using AX class of registers.
308 * Since other vendors do not currently have any contradicting requirements
309 * the AMD access pattern is applied universally.
310 */
311
312static int
313pciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg,
314    unsigned bytes)
315{
316	vm_offset_t va;
317	int data = -1;
318
319	if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX ||
320	    func > PCI_FUNCMAX || reg > PCIE_REGMAX)
321		return (-1);
322
323	va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
324
325	switch (bytes) {
326	case 4:
327		__asm("movl %1, %0" : "=a" (data)
328		    : "m" (*(volatile uint32_t *)va));
329		break;
330	case 2:
331		__asm("movzwl %1, %0" : "=a" (data)
332		    : "m" (*(volatile uint16_t *)va));
333		break;
334	case 1:
335		__asm("movzbl %1, %0" : "=a" (data)
336		    : "m" (*(volatile uint8_t *)va));
337		break;
338	}
339
340	return (data);
341}
342
343static void
344pciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data,
345    unsigned bytes)
346{
347	vm_offset_t va;
348
349	if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX ||
350	    func > PCI_FUNCMAX || reg > PCIE_REGMAX)
351		return;
352
353	va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
354
355	switch (bytes) {
356	case 4:
357		__asm("movl %1, %0" : "=m" (*(volatile uint32_t *)va)
358		    : "a" (data));
359		break;
360	case 2:
361		__asm("movw %1, %0" : "=m" (*(volatile uint16_t *)va)
362		    : "a" ((uint16_t)data));
363		break;
364	case 1:
365		__asm("movb %1, %0" : "=m" (*(volatile uint8_t *)va)
366		    : "a" ((uint8_t)data));
367		break;
368	}
369}
370