pci_cfgreg.c revision 114349
1189251Ssam/*
2189251Ssam * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3189251Ssam * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
4189251Ssam * Copyright (c) 2000, BSDi
5189251Ssam * All rights reserved.
6189251Ssam *
7189251Ssam * Redistribution and use in source and binary forms, with or without
8189251Ssam * modification, are permitted provided that the following conditions
9189251Ssam * are met:
10189251Ssam * 1. Redistributions of source code must retain the above copyright
11189251Ssam *    notice unmodified, this list of conditions, and the following
12189251Ssam *    disclaimer.
13189251Ssam * 2. Redistributions in binary form must reproduce the above copyright
14189251Ssam *    notice, this list of conditions and the following disclaimer in the
15189251Ssam *    documentation and/or other materials provided with the distribution.
16189251Ssam *
17189251Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18189251Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19189251Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20189251Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21189251Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22189251Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23189251Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24189251Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25189251Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26189251Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27189251Ssam *
28189251Ssam * $FreeBSD: head/sys/amd64/pci/pci_cfgreg.c 114349 2003-05-01 01:05:25Z peter $
29189251Ssam *
30189251Ssam */
31189251Ssam
32189251Ssam#include <sys/param.h>		/* XXX trim includes */
33189251Ssam#include <sys/systm.h>
34189251Ssam#include <sys/bus.h>
35189251Ssam#include <sys/kernel.h>
36189251Ssam#include <sys/module.h>
37189251Ssam#include <sys/malloc.h>
38189251Ssam#include <sys/lock.h>
39189251Ssam#include <sys/mutex.h>
40189251Ssam#include <vm/vm.h>
41189251Ssam#include <vm/pmap.h>
42189251Ssam#include <machine/md_var.h>
43189251Ssam#include <dev/pci/pcivar.h>
44189251Ssam#include <dev/pci/pcireg.h>
45189251Ssam#include <isa/isavar.h>
46189251Ssam#include <machine/pci_cfgreg.h>
47189251Ssam
48189251Ssam#include "pcib_if.h"
49189251Ssam
50189251Ssamstatic int cfgmech;
51189251Ssamstatic int devmax;
52189251Ssam
53189251Ssamstatic int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
54189251Ssamstatic void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
55189251Ssamstatic int	pcireg_cfgopen(void);
56189251Ssam
57189251Ssamstatic struct mtx pcicfg_mtx;
58189251Ssam
59189251Ssam/*
60189251Ssam * Initialise access to PCI configuration space
61189251Ssam */
62189251Ssamint
63189251Ssampci_cfgregopen(void)
64189251Ssam{
65189251Ssam	static int		opened = 0;
66189251Ssam
67189251Ssam	if (opened)
68189251Ssam		return (1);
69189251Ssam	if (pcireg_cfgopen() == 0)
70189251Ssam		return (0);
71189251Ssam	mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
72189251Ssam	opened = 1;
73189251Ssam	return (1);
74189251Ssam}
75189251Ssam
76189251Ssam/*
77189251Ssam * Read configuration space register
78189251Ssam */
79189251Ssamu_int32_t
80189251Ssampci_cfgregread(int bus, int slot, int func, int reg, int bytes)
81189251Ssam{
82189251Ssam	uint32_t line;
83189251Ssam
84189251Ssam	/*
85189251Ssam	 * Some BIOS writers seem to want to ignore the spec and put
86189251Ssam	 * 0 in the intline rather than 255 to indicate none.  Some use
87189251Ssam	 * numbers in the range 128-254 to indicate something strange and
88189251Ssam	 * apparently undocumented anywhere.  Assume these are completely bogus
89189251Ssam	 * and map them to 255, which the rest of the PCI code recognizes as
90189251Ssam	 * as an invalid IRQ.
91189251Ssam	 */
92189251Ssam	if (reg == PCIR_INTLINE && bytes == 1) {
93189251Ssam		line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
94189251Ssam		if (line == 0 || line >= 128)
95189251Ssam			line = PCI_INVALID_IRQ;
96189251Ssam		return (line);
97189251Ssam	}
98189251Ssam	return (pcireg_cfgread(bus, slot, func, reg, bytes));
99189251Ssam}
100189251Ssam
101189251Ssam/*
102189251Ssam * Write configuration space register
103189251Ssam */
104189251Ssamvoid
105189251Ssampci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
106189251Ssam{
107189251Ssam
108189251Ssam	pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
109189251Ssam}
110189251Ssam
111189251Ssam/*
112189251Ssam * Route a PCI interrupt
113189251Ssam */
114189251Ssamint
115189251Ssampci_cfgintr(int bus, int device, int pin, int oldirq)
116189251Ssam{
117189251Ssam
118189251Ssam	printf("pci_cfgintr: can't route an interrupt to %d:%d INT%c without ACPI\n", bus,
119189251Ssam	    device, 'A' + pin - 1);
120189251Ssam	return (PCI_INVALID_IRQ);
121189251Ssam}
122189251Ssam
123189251Ssam/*
124189251Ssam * Configuration space access using direct register operations
125189251Ssam */
126189251Ssam
127189251Ssam/* enable configuration space accesses and return data port address */
128189251Ssamstatic int
129189251Ssampci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
130189251Ssam{
131189251Ssam	int dataport = 0;
132189251Ssam
133189251Ssam	if (bus <= PCI_BUSMAX
134189251Ssam	    && slot < devmax
135189251Ssam	    && func <= PCI_FUNCMAX
136189251Ssam	    && reg <= PCI_REGMAX
137189251Ssam	    && bytes != 3
138189251Ssam	    && (unsigned) bytes <= 4
139189251Ssam	    && (reg & (bytes - 1)) == 0) {
140189251Ssam		switch (cfgmech) {
141189251Ssam		case 1:
142189251Ssam			outl(CONF1_ADDR_PORT, (1 << 31)
143189251Ssam			    | (bus << 16) | (slot << 11)
144189251Ssam			    | (func << 8) | (reg & ~0x03));
145189251Ssam			dataport = CONF1_DATA_PORT + (reg & 0x03);
146189251Ssam			break;
147189251Ssam		case 2:
148189251Ssam			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
149189251Ssam			outb(CONF2_FORWARD_PORT, bus);
150189251Ssam			dataport = 0xc000 | (slot << 8) | reg;
151189251Ssam			break;
152189251Ssam		}
153189251Ssam	}
154189251Ssam	return (dataport);
155189251Ssam}
156189251Ssam
157189251Ssam/* disable configuration space accesses */
158189251Ssamstatic void
159189251Ssampci_cfgdisable(void)
160189251Ssam{
161189251Ssam	switch (cfgmech) {
162189251Ssam	case 1:
163189251Ssam		outl(CONF1_ADDR_PORT, 0);
164189251Ssam		break;
165189251Ssam	case 2:
166189251Ssam		outb(CONF2_ENABLE_PORT, 0);
167189251Ssam		outb(CONF2_FORWARD_PORT, 0);
168189251Ssam		break;
169189251Ssam	}
170189251Ssam}
171189251Ssam
172189251Ssamstatic int
173189251Ssampcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
174189251Ssam{
175189251Ssam	int data = -1;
176189251Ssam	int port;
177189251Ssam
178189251Ssam	mtx_lock_spin(&pcicfg_mtx);
179189251Ssam	port = pci_cfgenable(bus, slot, func, reg, bytes);
180189251Ssam	if (port != 0) {
181189251Ssam		switch (bytes) {
182189251Ssam		case 1:
183189251Ssam			data = inb(port);
184189251Ssam			break;
185189251Ssam		case 2:
186189251Ssam			data = inw(port);
187189251Ssam			break;
188189251Ssam		case 4:
189189251Ssam			data = inl(port);
190189251Ssam			break;
191189251Ssam		}
192189251Ssam		pci_cfgdisable();
193189251Ssam	}
194189251Ssam	mtx_unlock_spin(&pcicfg_mtx);
195189251Ssam	return (data);
196189251Ssam}
197189251Ssam
198189251Ssamstatic void
199189251Ssampcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
200189251Ssam{
201189251Ssam	int port;
202189251Ssam
203189251Ssam	mtx_lock_spin(&pcicfg_mtx);
204189251Ssam	port = pci_cfgenable(bus, slot, func, reg, bytes);
205189251Ssam	if (port != 0) {
206189251Ssam		switch (bytes) {
207189251Ssam		case 1:
208189251Ssam			outb(port, data);
209189251Ssam			break;
210189251Ssam		case 2:
211189251Ssam			outw(port, data);
212189251Ssam			break;
213189251Ssam		case 4:
214189251Ssam			outl(port, data);
215189251Ssam			break;
216189251Ssam		}
217189251Ssam		pci_cfgdisable();
218189251Ssam	}
219189251Ssam	mtx_unlock_spin(&pcicfg_mtx);
220189251Ssam}
221189251Ssam
222189251Ssam/* check whether the configuration mechanism has been correctly identified */
223189251Ssamstatic int
224189251Ssampci_cfgcheck(int maxdev)
225189251Ssam{
226189251Ssam	uint32_t id, class;
227189251Ssam	uint8_t header;
228189251Ssam	uint8_t device;
229189251Ssam	int port;
230189251Ssam
231189251Ssam	if (bootverbose)
232189251Ssam		printf("pci_cfgcheck:\tdevice ");
233189251Ssam
234189251Ssam	for (device = 0; device < maxdev; device++) {
235189251Ssam		if (bootverbose)
236189251Ssam			printf("%d ", device);
237189251Ssam
238189251Ssam		port = pci_cfgenable(0, device, 0, 0, 4);
239189251Ssam		id = inl(port);
240189251Ssam		if (id == 0 || id == 0xffffffff)
241189251Ssam			continue;
242189251Ssam
243189251Ssam		port = pci_cfgenable(0, device, 0, 8, 4);
244189251Ssam		class = inl(port) >> 8;
245189251Ssam		if (bootverbose)
246189251Ssam			printf("[class=%06x] ", class);
247189251Ssam		if (class == 0 || (class & 0xf870ff) != 0)
248189251Ssam			continue;
249189251Ssam
250189251Ssam		port = pci_cfgenable(0, device, 0, 14, 1);
251189251Ssam		header = inb(port);
252189251Ssam		if (bootverbose)
253189251Ssam			printf("[hdr=%02x] ", header);
254189251Ssam		if ((header & 0x7e) != 0)
255189251Ssam			continue;
256189251Ssam
257189251Ssam		if (bootverbose)
258189251Ssam			printf("is there (id=%08x)\n", id);
259189251Ssam
260189251Ssam		pci_cfgdisable();
261189251Ssam		return (1);
262189251Ssam	}
263189251Ssam	if (bootverbose)
264189251Ssam		printf("-- nothing found\n");
265189251Ssam
266189251Ssam	pci_cfgdisable();
267189251Ssam	return (0);
268189251Ssam}
269189251Ssam
270189251Ssamstatic int
271189251Ssampcireg_cfgopen(void)
272189251Ssam{
273189251Ssam	uint32_t mode1res, oldval1;
274189251Ssam	uint8_t mode2res, oldval2;
275189251Ssam
276189251Ssam	oldval1 = inl(CONF1_ADDR_PORT);
277189251Ssam
278189251Ssam	if (bootverbose) {
279189251Ssam		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n",
280189251Ssam		    oldval1);
281189251Ssam	}
282189251Ssam
283189251Ssam	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
284189251Ssam
285189251Ssam		cfgmech = 1;
286189251Ssam		devmax = 32;
287189251Ssam
288189251Ssam		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
289189251Ssam		outb(CONF1_ADDR_PORT + 3, 0);
290189251Ssam		mode1res = inl(CONF1_ADDR_PORT);
291189251Ssam		outl(CONF1_ADDR_PORT, oldval1);
292189251Ssam
293189251Ssam		if (bootverbose)
294189251Ssam			printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n",
295189251Ssam			    mode1res, CONF1_ENABLE_CHK);
296189251Ssam
297189251Ssam		if (mode1res) {
298189251Ssam			if (pci_cfgcheck(32))
299189251Ssam				return (cfgmech);
300189251Ssam		}
301189251Ssam
302189251Ssam		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
303189251Ssam		mode1res = inl(CONF1_ADDR_PORT);
304189251Ssam		outl(CONF1_ADDR_PORT, oldval1);
305189251Ssam
306189251Ssam		if (bootverbose)
307189251Ssam			printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n",
308189251Ssam			    mode1res, CONF1_ENABLE_CHK1);
309189251Ssam
310189251Ssam		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
311189251Ssam			if (pci_cfgcheck(32))
312189251Ssam				return (cfgmech);
313189251Ssam		}
314189251Ssam	}
315189251Ssam
316189251Ssam	oldval2 = inb(CONF2_ENABLE_PORT);
317189251Ssam
318189251Ssam	if (bootverbose) {
319189251Ssam		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
320189251Ssam		    oldval2);
321189251Ssam	}
322189251Ssam
323189251Ssam	if ((oldval2 & 0xf0) == 0) {
324189251Ssam
325189251Ssam		cfgmech = 2;
326189251Ssam		devmax = 16;
327189251Ssam
328189251Ssam		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
329189251Ssam		mode2res = inb(CONF2_ENABLE_PORT);
330189251Ssam		outb(CONF2_ENABLE_PORT, oldval2);
331189251Ssam
332189251Ssam		if (bootverbose)
333189251Ssam			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
334189251Ssam			    mode2res, CONF2_ENABLE_CHK);
335189251Ssam
336189251Ssam		if (mode2res == CONF2_ENABLE_RES) {
337189251Ssam			if (bootverbose)
338189251Ssam				printf("pci_open(2a):\tnow trying mechanism 2\n");
339189251Ssam
340189251Ssam			if (pci_cfgcheck(16))
341189251Ssam				return (cfgmech);
342189251Ssam		}
343189251Ssam	}
344189251Ssam
345189251Ssam	cfgmech = 0;
346189251Ssam	devmax = 0;
347189251Ssam	return (cfgmech);
348189251Ssam}
349189251Ssam