1139731Simp/*-
226159Sse * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
366529Smsmith * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
466529Smsmith * Copyright (c) 2000, BSDi
526159Sse * All rights reserved.
626159Sse *
726159Sse * Redistribution and use in source and binary forms, with or without
826159Sse * modification, are permitted provided that the following conditions
926159Sse * are met:
1026159Sse * 1. Redistributions of source code must retain the above copyright
1126159Sse *    notice unmodified, this list of conditions, and the following
1226159Sse *    disclaimer.
1326159Sse * 2. Redistributions in binary form must reproduce the above copyright
1426159Sse *    notice, this list of conditions and the following disclaimer in the
1526159Sse *    documentation and/or other materials provided with the distribution.
1626159Sse *
1726159Sse * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1826159Sse * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1926159Sse * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2026159Sse * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2126159Sse * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2226159Sse * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2326159Sse * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2426159Sse * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2526159Sse * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2626159Sse * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2726159Sse */
286104Sse
29118031Sobrien#include <sys/cdefs.h>
30118031Sobrien__FBSDID("$FreeBSD$");
31118031Sobrien
32126926Speter#include <sys/param.h>
336734Sbde#include <sys/systm.h>
3447307Speter#include <sys/bus.h>
35111068Speter#include <sys/lock.h>
36182947Sjhb#include <sys/kernel.h>
37111068Speter#include <sys/mutex.h>
38192342Sjhb#include <sys/sysctl.h>
39100435Simp#include <dev/pci/pcivar.h>
40100435Simp#include <dev/pci/pcireg.h>
41181987Sjhb#include <vm/vm.h>
42181987Sjhb#include <vm/pmap.h>
4366529Smsmith#include <machine/pci_cfgreg.h>
4459294Smsmith
45181987Sjhbenum {
46181987Sjhb	CFGMECH_NONE = 0,
47181987Sjhb	CFGMECH_1,
48181987Sjhb	CFGMECH_PCIE,
49181987Sjhb};
50181987Sjhb
51182910Sjhbstatic uint32_t	pci_docfgregread(int bus, int slot, int func, int reg,
52182910Sjhb		    int bytes);
53181987Sjhbstatic int	pciereg_cfgread(int bus, unsigned slot, unsigned func,
54181987Sjhb		    unsigned reg, unsigned bytes);
55181987Sjhbstatic void	pciereg_cfgwrite(int bus, unsigned slot, unsigned func,
56181987Sjhb		    unsigned reg, int data, unsigned bytes);
5766529Smsmithstatic int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
5866529Smsmithstatic void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
5959294Smsmith
60192342SjhbSYSCTL_DECL(_hw_pci);
61192342Sjhb
62181987Sjhbstatic int cfgmech;
63181987Sjhbstatic vm_offset_t pcie_base;
64181987Sjhbstatic int pcie_minbus, pcie_maxbus;
65182910Sjhbstatic uint32_t pcie_badslots;
66111068Speterstatic struct mtx pcicfg_mtx;
67182947Sjhbstatic int mcfg_enable = 1;
68182947SjhbTUNABLE_INT("hw.pci.mcfg", &mcfg_enable);
69192342SjhbSYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0,
70192342Sjhb    "Enable support for PCI-e memory mapped config access");
71111068Speter
7266529Smsmith/*
7366529Smsmith * Initialise access to PCI configuration space
7466529Smsmith */
7566529Smsmithint
7666529Smsmithpci_cfgregopen(void)
7759294Smsmith{
78182910Sjhb	static int once = 0;
79181987Sjhb	uint64_t pciebar;
80181987Sjhb	uint16_t did, vid;
8165176Sdfr
82182910Sjhb	if (!once) {
83182910Sjhb		mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
84182910Sjhb		once = 1;
85182910Sjhb	}
86182910Sjhb
87181987Sjhb	if (cfgmech != CFGMECH_NONE)
88114349Speter		return (1);
89181987Sjhb	cfgmech = CFGMECH_1;
90181987Sjhb
91181987Sjhb	/*
92181987Sjhb	 * Grope around in the PCI config space to see if this is a
93181987Sjhb	 * chipset that is capable of doing memory-mapped config cycles.
94181987Sjhb	 * This also implies that it can do PCIe extended config cycles.
95181987Sjhb	 */
96181987Sjhb
97181987Sjhb	/* Check for supported chipsets */
98181987Sjhb	vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2);
99181987Sjhb	did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2);
100181987Sjhb	switch (vid) {
101181987Sjhb	case 0x8086:
102181987Sjhb		switch (did) {
103181987Sjhb		case 0x3590:
104181987Sjhb		case 0x3592:
105181987Sjhb			/* Intel 7520 or 7320 */
106181987Sjhb			pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
107181987Sjhb			pcie_cfgregopen(pciebar, 0, 255);
108181987Sjhb			break;
109181987Sjhb		case 0x2580:
110181987Sjhb		case 0x2584:
111181987Sjhb		case 0x2590:
112181987Sjhb			/* Intel 915, 925, or 915GM */
113181987Sjhb			pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
114181987Sjhb			pcie_cfgregopen(pciebar, 0, 255);
115181987Sjhb			break;
116181987Sjhb		}
117181987Sjhb	}
118181987Sjhb
119114349Speter	return (1);
12059294Smsmith}
12159294Smsmith
122182910Sjhbstatic uint32_t
123182910Sjhbpci_docfgregread(int bus, int slot, int func, int reg, int bytes)
124182910Sjhb{
125182910Sjhb
126182910Sjhb	if (cfgmech == CFGMECH_PCIE &&
127190386Sjhb	    (bus >= pcie_minbus && bus <= pcie_maxbus) &&
128182910Sjhb	    (bus != 0 || !(1 << slot & pcie_badslots)))
129182910Sjhb		return (pciereg_cfgread(bus, slot, func, reg, bytes));
130182910Sjhb	else
131182910Sjhb		return (pcireg_cfgread(bus, slot, func, reg, bytes));
132182910Sjhb}
133182910Sjhb
13466529Smsmith/*
13569783Smsmith * Read configuration space register
13666529Smsmith */
13769783Smsmithu_int32_t
13869783Smsmithpci_cfgregread(int bus, int slot, int func, int reg, int bytes)
13969783Smsmith{
140100435Simp	uint32_t line;
14169783Smsmith
142100435Simp	/*
143100435Simp	 * Some BIOS writers seem to want to ignore the spec and put
144114349Speter	 * 0 in the intline rather than 255 to indicate none.  Some use
145114349Speter	 * numbers in the range 128-254 to indicate something strange and
146114349Speter	 * apparently undocumented anywhere.  Assume these are completely bogus
147114349Speter	 * and map them to 255, which the rest of the PCI code recognizes as
148114349Speter	 * as an invalid IRQ.
149100435Simp	 */
150100435Simp	if (reg == PCIR_INTLINE && bytes == 1) {
151182910Sjhb		line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1);
152114349Speter		if (line == 0 || line >= 128)
153114349Speter			line = PCI_INVALID_IRQ;
154114349Speter		return (line);
155100435Simp	}
156182910Sjhb	return (pci_docfgregread(bus, slot, func, reg, bytes));
15769783Smsmith}
15869783Smsmith
15966529Smsmith/*
16066529Smsmith * Write configuration space register
16166529Smsmith */
16266529Smsmithvoid
16366529Smsmithpci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
16459294Smsmith{
165111068Speter
166182910Sjhb	if (cfgmech == CFGMECH_PCIE &&
167190386Sjhb	    (bus >= pcie_minbus && bus <= pcie_maxbus) &&
168182910Sjhb	    (bus != 0 || !(1 << slot & pcie_badslots)))
169182910Sjhb		pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
170182910Sjhb	else
171182910Sjhb		pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
17259294Smsmith}
17359294Smsmith
17466529Smsmith/*
17566529Smsmith * Configuration space access using direct register operations
17666529Smsmith */
17759294Smsmith
17826159Sse/* enable configuration space accesses and return data port address */
17910887Ssestatic int
18026159Ssepci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
18126159Sse{
182100435Simp	int dataport = 0;
18310887Sse
184197450Savg	if (bus <= PCI_BUSMAX && slot <= PCI_SLOTMAX && func <= PCI_FUNCMAX &&
185197450Savg	    (unsigned)reg <= PCI_REGMAX && bytes != 3 &&
186197450Savg	    (unsigned)bytes <= 4 && (reg & (bytes - 1)) == 0) {
187174050Sjhb		outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11)
188174050Sjhb		    | (func << 8) | (reg & ~0x03));
189174050Sjhb		dataport = CONF1_DATA_PORT + (reg & 0x03);
19026159Sse	}
191100435Simp	return (dataport);
19226159Sse}
1936104Sse
19426159Sse/* disable configuration space accesses */
1956104Ssestatic void
19626159Ssepci_cfgdisable(void)
19726159Sse{
198174050Sjhb
199174050Sjhb	/*
200174050Sjhb	 * Do nothing.  Writing a 0 to the address port can apparently
201174050Sjhb	 * confuse some bridges and cause spurious access failures.
202174050Sjhb	 */
20326159Sse}
2046104Sse
20559294Smsmithstatic int
20665176Sdfrpcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
20726159Sse{
208100435Simp	int data = -1;
209100435Simp	int port;
2107234Sse
211111068Speter	mtx_lock_spin(&pcicfg_mtx);
212100435Simp	port = pci_cfgenable(bus, slot, func, reg, bytes);
213100435Simp	if (port != 0) {
214100435Simp		switch (bytes) {
215100435Simp		case 1:
216100435Simp			data = inb(port);
217100435Simp			break;
218100435Simp		case 2:
219100435Simp			data = inw(port);
220100435Simp			break;
221100435Simp		case 4:
222100435Simp			data = inl(port);
223100435Simp			break;
224100435Simp		}
225100435Simp		pci_cfgdisable();
22626159Sse	}
227111068Speter	mtx_unlock_spin(&pcicfg_mtx);
228100435Simp	return (data);
22926159Sse}
2307234Sse
23159294Smsmithstatic void
23265176Sdfrpcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
23326159Sse{
234100435Simp	int port;
2356104Sse
236111068Speter	mtx_lock_spin(&pcicfg_mtx);
237100435Simp	port = pci_cfgenable(bus, slot, func, reg, bytes);
238100435Simp	if (port != 0) {
239100435Simp		switch (bytes) {
240100435Simp		case 1:
241100435Simp			outb(port, data);
242100435Simp			break;
243100435Simp		case 2:
244100435Simp			outw(port, data);
245100435Simp			break;
246100435Simp		case 4:
247100435Simp			outl(port, data);
248100435Simp			break;
249100435Simp		}
250100435Simp		pci_cfgdisable();
25126159Sse	}
252111068Speter	mtx_unlock_spin(&pcicfg_mtx);
25326159Sse}
254181987Sjhb
255181987Sjhbint
256181987Sjhbpcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
257181987Sjhb{
258182910Sjhb	uint32_t val1, val2;
259182910Sjhb	int slot;
260181987Sjhb
261182947Sjhb	if (!mcfg_enable)
262182947Sjhb		return (0);
263182947Sjhb
264181987Sjhb	if (minbus != 0)
265181987Sjhb		return (0);
266181987Sjhb
267181987Sjhb	if (bootverbose)
268181987Sjhb		printf("PCIe: Memory Mapped configuration base @ 0x%lx\n",
269181987Sjhb		    base);
270181987Sjhb
271181987Sjhb	/* XXX: We should make sure this really fits into the direct map. */
272181987Sjhb	pcie_base = (vm_offset_t)pmap_mapdev(base, (maxbus + 1) << 20);
273181987Sjhb	pcie_minbus = minbus;
274181987Sjhb	pcie_maxbus = maxbus;
275181987Sjhb	cfgmech = CFGMECH_PCIE;
276182910Sjhb
277182910Sjhb	/*
278182910Sjhb	 * On some AMD systems, some of the devices on bus 0 are
279182910Sjhb	 * inaccessible using memory-mapped PCI config access.  Walk
280182910Sjhb	 * bus 0 looking for such devices.  For these devices, we will
281182910Sjhb	 * fall back to using type 1 config access instead.
282182910Sjhb	 */
283182910Sjhb	if (pci_cfgregopen() != 0) {
284197450Savg		for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
285182910Sjhb			val1 = pcireg_cfgread(0, slot, 0, 0, 4);
286182910Sjhb			if (val1 == 0xffffffff)
287182910Sjhb				continue;
288182910Sjhb
289182910Sjhb			val2 = pciereg_cfgread(0, slot, 0, 0, 4);
290182910Sjhb			if (val2 != val1)
291182910Sjhb				pcie_badslots |= (1 << slot);
292182910Sjhb		}
293182910Sjhb	}
294182910Sjhb
295181987Sjhb	return (1);
296181987Sjhb}
297181987Sjhb
298243737Sjkim#define PCIE_VADDR(base, reg, bus, slot, func)	\
299243737Sjkim	((base)				+	\
300243737Sjkim	((((bus) & 0xff) << 20)		|	\
301243737Sjkim	(((slot) & 0x1f) << 15)		|	\
302243737Sjkim	(((func) & 0x7) << 12)		|	\
303243737Sjkim	((reg) & 0xfff)))
304243737Sjkim
305241540Savg/*
306241540Savg * AMD BIOS And Kernel Developer's Guides for CPU families starting with 10h
307241540Savg * have a requirement that all accesses to the memory mapped PCI configuration
308241540Savg * space are done using AX class of registers.
309241540Savg * Since other vendors do not currently have any contradicting requirements
310241540Savg * the AMD access pattern is applied universally.
311241540Savg */
312181987Sjhb
313181987Sjhbstatic int
314181987Sjhbpciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg,
315181987Sjhb    unsigned bytes)
316181987Sjhb{
317243712Sjkim	vm_offset_t va;
318181987Sjhb	int data = -1;
319181987Sjhb
320197450Savg	if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX ||
321197450Savg	    func > PCI_FUNCMAX || reg > PCIE_REGMAX)
322181987Sjhb		return (-1);
323181987Sjhb
324181987Sjhb	va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
325181987Sjhb
326181987Sjhb	switch (bytes) {
327181987Sjhb	case 4:
328243712Sjkim		__asm("movl %1, %0" : "=a" (data)
329243712Sjkim		    : "m" (*(volatile uint32_t *)va));
330181987Sjhb		break;
331181987Sjhb	case 2:
332243712Sjkim		__asm("movzwl %1, %0" : "=a" (data)
333243712Sjkim		    : "m" (*(volatile uint16_t *)va));
334181987Sjhb		break;
335181987Sjhb	case 1:
336243712Sjkim		__asm("movzbl %1, %0" : "=a" (data)
337243712Sjkim		    : "m" (*(volatile uint8_t *)va));
338181987Sjhb		break;
339181987Sjhb	}
340181987Sjhb
341181987Sjhb	return (data);
342181987Sjhb}
343181987Sjhb
344181987Sjhbstatic void
345181987Sjhbpciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data,
346181987Sjhb    unsigned bytes)
347181987Sjhb{
348243712Sjkim	vm_offset_t va;
349181987Sjhb
350197450Savg	if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX ||
351197450Savg	    func > PCI_FUNCMAX || reg > PCIE_REGMAX)
352181987Sjhb		return;
353181987Sjhb
354181987Sjhb	va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
355181987Sjhb
356181987Sjhb	switch (bytes) {
357181987Sjhb	case 4:
358243712Sjkim		__asm("movl %1, %0" : "=m" (*(volatile uint32_t *)va)
359241540Savg		    : "a" (data));
360181987Sjhb		break;
361181987Sjhb	case 2:
362243712Sjkim		__asm("movw %1, %0" : "=m" (*(volatile uint16_t *)va)
363243685Sjkim		    : "a" ((uint16_t)data));
364181987Sjhb		break;
365181987Sjhb	case 1:
366243712Sjkim		__asm("movb %1, %0" : "=m" (*(volatile uint8_t *)va)
367243685Sjkim		    : "a" ((uint8_t)data));
368181987Sjhb		break;
369181987Sjhb	}
370181987Sjhb}
371