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: stable/11/sys/amd64/pci/pci_cfgreg.c 351439 2019-08-23 22:03:50Z jhb $");
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;
67318207SsepheMTX_SYSINIT(pcicfg_mtx, &pcicfg_mtx, "pcicfg_mtx", MTX_SPIN);
68182947Sjhbstatic int mcfg_enable = 1;
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{
78181987Sjhb	uint64_t pciebar;
79181987Sjhb	uint16_t did, vid;
8065176Sdfr
81181987Sjhb	if (cfgmech != CFGMECH_NONE)
82114349Speter		return (1);
83181987Sjhb	cfgmech = CFGMECH_1;
84181987Sjhb
85181987Sjhb	/*
86181987Sjhb	 * Grope around in the PCI config space to see if this is a
87181987Sjhb	 * chipset that is capable of doing memory-mapped config cycles.
88181987Sjhb	 * This also implies that it can do PCIe extended config cycles.
89181987Sjhb	 */
90181987Sjhb
91181987Sjhb	/* Check for supported chipsets */
92181987Sjhb	vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2);
93181987Sjhb	did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2);
94181987Sjhb	switch (vid) {
95181987Sjhb	case 0x8086:
96181987Sjhb		switch (did) {
97181987Sjhb		case 0x3590:
98181987Sjhb		case 0x3592:
99181987Sjhb			/* Intel 7520 or 7320 */
100181987Sjhb			pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
101181987Sjhb			pcie_cfgregopen(pciebar, 0, 255);
102181987Sjhb			break;
103181987Sjhb		case 0x2580:
104181987Sjhb		case 0x2584:
105181987Sjhb		case 0x2590:
106181987Sjhb			/* Intel 915, 925, or 915GM */
107181987Sjhb			pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
108181987Sjhb			pcie_cfgregopen(pciebar, 0, 255);
109181987Sjhb			break;
110181987Sjhb		}
111181987Sjhb	}
112181987Sjhb
113114349Speter	return (1);
11459294Smsmith}
11559294Smsmith
116182910Sjhbstatic uint32_t
117182910Sjhbpci_docfgregread(int bus, int slot, int func, int reg, int bytes)
118182910Sjhb{
119182910Sjhb
120182910Sjhb	if (cfgmech == CFGMECH_PCIE &&
121190386Sjhb	    (bus >= pcie_minbus && bus <= pcie_maxbus) &&
122182910Sjhb	    (bus != 0 || !(1 << slot & pcie_badslots)))
123182910Sjhb		return (pciereg_cfgread(bus, slot, func, reg, bytes));
124182910Sjhb	else
125182910Sjhb		return (pcireg_cfgread(bus, slot, func, reg, bytes));
126182910Sjhb}
127182910Sjhb
12866529Smsmith/*
12969783Smsmith * Read configuration space register
13066529Smsmith */
13169783Smsmithu_int32_t
13269783Smsmithpci_cfgregread(int bus, int slot, int func, int reg, int bytes)
13369783Smsmith{
134100435Simp	uint32_t line;
13569783Smsmith
136318207Ssephe	if (cfgmech == CFGMECH_NONE)
137318207Ssephe		return (0xffffffff);
138318207Ssephe
139100435Simp	/*
140100435Simp	 * Some BIOS writers seem to want to ignore the spec and put
141114349Speter	 * 0 in the intline rather than 255 to indicate none.  Some use
142114349Speter	 * numbers in the range 128-254 to indicate something strange and
143114349Speter	 * apparently undocumented anywhere.  Assume these are completely bogus
144114349Speter	 * and map them to 255, which the rest of the PCI code recognizes as
145114349Speter	 * as an invalid IRQ.
146100435Simp	 */
147100435Simp	if (reg == PCIR_INTLINE && bytes == 1) {
148182910Sjhb		line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1);
149114349Speter		if (line == 0 || line >= 128)
150114349Speter			line = PCI_INVALID_IRQ;
151114349Speter		return (line);
152100435Simp	}
153182910Sjhb	return (pci_docfgregread(bus, slot, func, reg, bytes));
15469783Smsmith}
15569783Smsmith
15666529Smsmith/*
15766529Smsmith * Write configuration space register
15866529Smsmith */
15966529Smsmithvoid
16066529Smsmithpci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
16159294Smsmith{
162111068Speter
163318207Ssephe	if (cfgmech == CFGMECH_NONE)
164318207Ssephe		return;
165318207Ssephe
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) {
187258780Seadler		outl(CONF1_ADDR_PORT, (1U << 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. */
272351439Sjhb	pcie_base = (vm_offset_t)pmap_mapdev_pciecfg(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