pci_cfgreg.c revision 61994
1/*
2 * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice unmodified, this list of conditions, and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/amd64/pci/pci_cfgreg.c 61994 2000-06-23 07:44:33Z msmith $
27 *
28 */
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/bus.h>
33#include <sys/kernel.h>
34#include <sys/module.h>
35
36#include <pci/pcivar.h>
37#include <pci/pcireg.h>
38#include <i386/isa/pcibus.h>
39#include <isa/isavar.h>
40
41#include <machine/segments.h>
42#include <machine/pc/bios.h>
43
44static int cfgmech;
45static int devmax;
46static int usebios;
47
48static int	pcibios_cfgread(pcicfgregs *cfg, int reg, int bytes);
49static void	pcibios_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes);
50static int	pcibios_cfgopen(void);
51static int	pcireg_cfgread(pcicfgregs *cfg, int reg, int bytes);
52static void	pcireg_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes);
53static int	pcireg_cfgopen(void);
54
55/* read configuration space register */
56
57int
58pci_cfgread(pcicfgregs *cfg, int reg, int bytes)
59{
60	return(usebios ?
61	       pcibios_cfgread(cfg, reg, bytes) :
62	       pcireg_cfgread(cfg, reg, bytes));
63}
64
65/* write configuration space register */
66
67void
68pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes)
69{
70	return(usebios ?
71	       pcibios_cfgwrite(cfg, reg, data, bytes) :
72	       pcireg_cfgwrite(cfg, reg, data, bytes));
73}
74
75/* initialise access to PCI configuration space */
76static int
77pci_cfgopen(void)
78{
79	if (pcibios_cfgopen() != 0) {
80		usebios = 1;
81	} else if (pcireg_cfgopen() != 0) {
82		usebios = 0;
83	} else {
84		return(0);
85	}
86	return(1);
87}
88
89/* config space access using BIOS functions */
90
91static int
92pcibios_cfgread(pcicfgregs *cfg, int reg, int bytes)
93{
94	struct bios_regs args;
95	u_int mask;
96
97	switch(bytes) {
98	case 1:
99		args.eax = PCIBIOS_READ_CONFIG_BYTE;
100		mask = 0xff;
101		break;
102	case 2:
103		args.eax = PCIBIOS_READ_CONFIG_WORD;
104		mask = 0xffff;
105		break;
106	case 4:
107		args.eax = PCIBIOS_READ_CONFIG_DWORD;
108		mask = 0xffffffff;
109		break;
110	default:
111		return(-1);
112	}
113	args.ebx = (cfg->bus << 8) | (cfg->slot << 3) | (cfg->func);
114	args.edi = reg;
115	bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
116	/* check call results? */
117	return(args.ecx & mask);
118}
119
120static void
121pcibios_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes)
122{
123	struct bios_regs args;
124
125	switch(bytes) {
126	case 1:
127		args.eax = PCIBIOS_WRITE_CONFIG_BYTE;
128		break;
129	case 2:
130		args.eax = PCIBIOS_WRITE_CONFIG_WORD;
131		break;
132	case 4:
133		args.eax = PCIBIOS_WRITE_CONFIG_DWORD;
134		break;
135	default:
136		return;
137	}
138	args.ebx = (cfg->bus << 8) | (cfg->slot << 3) | (cfg->func);
139	args.ecx = data;
140	args.edi = reg;
141	bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
142}
143
144/* determine whether there is a PCI BIOS present */
145
146static int
147pcibios_cfgopen(void)
148{
149	/* check for a found entrypoint */
150	return(PCIbios.entry != 0);
151}
152
153/* configuration space access using direct register operations */
154
155/* enable configuration space accesses and return data port address */
156
157static int
158pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
159{
160	int dataport = 0;
161
162	if (bus <= PCI_BUSMAX
163	    && slot < devmax
164	    && func <= PCI_FUNCMAX
165	    && reg <= PCI_REGMAX
166	    && bytes != 3
167	    && (unsigned) bytes <= 4
168	    && (reg & (bytes -1)) == 0) {
169		switch (cfgmech) {
170		case 1:
171			outl(CONF1_ADDR_PORT, (1 << 31)
172			     | (bus << 16) | (slot << 11)
173			     | (func << 8) | (reg & ~0x03));
174			dataport = CONF1_DATA_PORT + (reg & 0x03);
175			break;
176		case 2:
177			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
178			outb(CONF2_FORWARD_PORT, bus);
179			dataport = 0xc000 | (slot << 8) | reg;
180			break;
181		}
182	}
183	return (dataport);
184}
185
186/* disable configuration space accesses */
187
188static void
189pci_cfgdisable(void)
190{
191	switch (cfgmech) {
192	case 1:
193		outl(CONF1_ADDR_PORT, 0);
194		break;
195	case 2:
196		outb(CONF2_ENABLE_PORT, 0);
197		outb(CONF2_FORWARD_PORT, 0);
198		break;
199	}
200}
201
202static int
203pcireg_cfgread(pcicfgregs *cfg, int reg, int bytes)
204{
205	int data = -1;
206	int port;
207
208	port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes);
209
210	if (port != 0) {
211		switch (bytes) {
212		case 1:
213			data = inb(port);
214			break;
215		case 2:
216			data = inw(port);
217			break;
218		case 4:
219			data = inl(port);
220			break;
221		}
222		pci_cfgdisable();
223	}
224	return (data);
225}
226
227static void
228pcireg_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes)
229{
230	int port;
231
232	port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes);
233	if (port != 0) {
234		switch (bytes) {
235		case 1:
236			outb(port, data);
237			break;
238		case 2:
239			outw(port, data);
240			break;
241		case 4:
242			outl(port, data);
243			break;
244		}
245		pci_cfgdisable();
246	}
247}
248
249/* check whether the configuration mechanism has been correct identified */
250
251static int
252pci_cfgcheck(int maxdev)
253{
254	u_char device;
255
256	if (bootverbose)
257		printf("pci_cfgcheck:\tdevice ");
258
259	for (device = 0; device < maxdev; device++) {
260		unsigned id, class, header;
261		if (bootverbose)
262			printf("%d ", device);
263
264		id = inl(pci_cfgenable(0, device, 0, 0, 4));
265		if (id == 0 || id == -1)
266			continue;
267
268		class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8;
269		if (bootverbose)
270			printf("[class=%06x] ", class);
271		if (class == 0 || (class & 0xf870ff) != 0)
272			continue;
273
274		header = inb(pci_cfgenable(0, device, 0, 14, 1));
275		if (bootverbose)
276			printf("[hdr=%02x] ", header);
277		if ((header & 0x7e) != 0)
278			continue;
279
280		if (bootverbose)
281			printf("is there (id=%08x)\n", id);
282
283		pci_cfgdisable();
284		return (1);
285	}
286	if (bootverbose)
287		printf("-- nothing found\n");
288
289	pci_cfgdisable();
290	return (0);
291}
292
293static int
294pcireg_cfgopen(void)
295{
296	unsigned long mode1res,oldval1;
297	unsigned char mode2res,oldval2;
298
299	oldval1 = inl(CONF1_ADDR_PORT);
300
301	if (bootverbose) {
302		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n",
303		       oldval1);
304	}
305
306	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
307
308		cfgmech = 1;
309		devmax = 32;
310
311		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
312		outb(CONF1_ADDR_PORT +3, 0);
313		mode1res = inl(CONF1_ADDR_PORT);
314		outl(CONF1_ADDR_PORT, oldval1);
315
316		if (bootverbose)
317			printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n",
318			       mode1res, CONF1_ENABLE_CHK);
319
320		if (mode1res) {
321			if (pci_cfgcheck(32))
322				return (cfgmech);
323		}
324
325		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
326		mode1res = inl(CONF1_ADDR_PORT);
327		outl(CONF1_ADDR_PORT, oldval1);
328
329		if (bootverbose)
330			printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n",
331			       mode1res, CONF1_ENABLE_CHK1);
332
333		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
334			if (pci_cfgcheck(32))
335				return (cfgmech);
336		}
337	}
338
339	oldval2 = inb(CONF2_ENABLE_PORT);
340
341	if (bootverbose) {
342		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
343		       oldval2);
344	}
345
346	if ((oldval2 & 0xf0) == 0) {
347
348		cfgmech = 2;
349		devmax = 16;
350
351		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
352		mode2res = inb(CONF2_ENABLE_PORT);
353		outb(CONF2_ENABLE_PORT, oldval2);
354
355		if (bootverbose)
356			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
357			       mode2res, CONF2_ENABLE_CHK);
358
359		if (mode2res == CONF2_ENABLE_RES) {
360			if (bootverbose)
361				printf("pci_open(2a):\tnow trying mechanism 2\n");
362
363			if (pci_cfgcheck(16))
364				return (cfgmech);
365		}
366	}
367
368	cfgmech = 0;
369	devmax = 0;
370	return (cfgmech);
371}
372
373static devclass_t	pcib_devclass;
374
375static const char *
376nexus_pcib_is_host_bridge(pcicfgregs *cfg,
377			  u_int32_t id, u_int8_t class, u_int8_t subclass,
378			  u_int8_t *busnum)
379{
380	const char *s = NULL;
381	static u_int8_t pxb[4];	/* hack for 450nx */
382
383	*busnum = 0;
384
385	switch (id) {
386	case 0x12258086:
387		s = "Intel 824?? host to PCI bridge";
388		/* XXX This is a guess */
389		/* *busnum = pci_cfgread(cfg, 0x41, 1); */
390		*busnum = cfg->bus;
391		break;
392	case 0x71208086:
393		s = "Intel 82810 (i810 GMCH) Host To Hub bridge";
394		break;
395	case 0x71228086:
396		s = "Intel 82810-DC100 (i810-DC100 GMCH) Host To Hub bridge";
397		break;
398	case 0x71248086:
399		s = "Intel 82810E (i810E GMCH) Host To Hub bridge";
400		break;
401	case 0x71808086:
402		s = "Intel 82443LX (440 LX) host to PCI bridge";
403		break;
404	case 0x71908086:
405		s = "Intel 82443BX (440 BX) host to PCI bridge";
406		break;
407	case 0x71928086:
408		s = "Intel 82443BX host to PCI bridge (AGP disabled)";
409		break;
410	case 0x71948086:
411		s = "Intel 82443MX host to PCI bridge";
412		break;
413	case 0x71a08086:
414		s = "Intel 82443GX host to PCI bridge";
415		break;
416	case 0x71a18086:
417		s = "Intel 82443GX host to AGP bridge";
418		break;
419	case 0x71a28086:
420		s = "Intel 82443GX host to PCI bridge (AGP disabled)";
421		break;
422	case 0x84c48086:
423		s = "Intel 82454KX/GX (Orion) host to PCI bridge";
424		*busnum = pci_cfgread(cfg, 0x4a, 1);
425		break;
426	case 0x84ca8086:
427		/*
428		 * For the 450nx chipset, there is a whole bundle of
429		 * things pretending to be host bridges. The MIOC will
430		 * be seen first and isn't really a pci bridge (the
431		 * actual busses are attached to the PXB's). We need to
432		 * read the registers of the MIOC to figure out the
433		 * bus numbers for the PXB channels.
434		 *
435		 * Since the MIOC doesn't have a pci bus attached, we
436		 * pretend it wasn't there.
437		 */
438		pxb[0] = pci_cfgread(cfg, 0xd0, 1); /* BUSNO[0] */
439		pxb[1] = pci_cfgread(cfg, 0xd1, 1) + 1;	/* SUBA[0]+1 */
440		pxb[2] = pci_cfgread(cfg, 0xd3, 1); /* BUSNO[1] */
441		pxb[3] = pci_cfgread(cfg, 0xd4, 1) + 1;	/* SUBA[1]+1 */
442		return NULL;
443	case 0x84cb8086:
444		switch (cfg->slot) {
445		case 0x12:
446			s = "Intel 82454NX PXB#0, Bus#A";
447			*busnum = pxb[0];
448			break;
449		case 0x13:
450			s = "Intel 82454NX PXB#0, Bus#B";
451			*busnum = pxb[1];
452			break;
453		case 0x14:
454			s = "Intel 82454NX PXB#1, Bus#A";
455			*busnum = pxb[2];
456			break;
457		case 0x15:
458			s = "Intel 82454NX PXB#1, Bus#B";
459			*busnum = pxb[3];
460			break;
461		}
462		break;
463
464		/* AMD -- vendor 0x1022 */
465	case 0x70061022:
466		s = "AMD-751 host to PCI bridge";
467		break;
468
469		/* SiS -- vendor 0x1039 */
470	case 0x04961039:
471		s = "SiS 85c496";
472		break;
473	case 0x04061039:
474		s = "SiS 85c501";
475		break;
476	case 0x06011039:
477		s = "SiS 85c601";
478		break;
479	case 0x55911039:
480		s = "SiS 5591 host to PCI bridge";
481		break;
482	case 0x00011039:
483		s = "SiS 5591 host to AGP bridge";
484		break;
485
486		/* VLSI -- vendor 0x1004 */
487	case 0x00051004:
488		s = "VLSI 82C592 Host to PCI bridge";
489		break;
490
491		/* XXX Here is MVP3, I got the datasheet but NO M/B to test it  */
492		/* totally. Please let me know if anything wrong.            -F */
493		/* XXX need info on the MVP3 -- any takers? */
494	case 0x05981106:
495		s = "VIA 82C598MVP (Apollo MVP3) host bridge";
496		break;
497
498		/* AcerLabs -- vendor 0x10b9 */
499		/* Funny : The datasheet told me vendor id is "10b8",sub-vendor */
500		/* id is '10b9" but the register always shows "10b9". -Foxfair  */
501	case 0x154110b9:
502		s = "AcerLabs M1541 (Aladdin-V) PCI host bridge";
503		break;
504
505		/* OPTi -- vendor 0x1045 */
506	case 0xc7011045:
507		s = "OPTi 82C700 host to PCI bridge";
508		break;
509	case 0xc8221045:
510		s = "OPTi 82C822 host to PCI Bridge";
511		break;
512
513		/* RCC -- vendor 0x1166 */
514	case 0x00051166:
515		s = "RCC HE host to PCI bridge";
516		*busnum = pci_cfgread(cfg, 0x44, 1);
517		break;
518
519	case 0x00061166:
520		/* FALLTHROUGH */
521	case 0x00081166:
522		s = "RCC host to PCI bridge";
523		*busnum = pci_cfgread(cfg, 0x44, 1);
524		break;
525
526	case 0x00091166:
527		s = "RCC LE host to PCI bridge";
528		*busnum = pci_cfgread(cfg, 0x44, 1);
529		break;
530
531		/* Integrated Micro Solutions -- vendor 0x10e0 */
532	case 0x884910e0:
533		s = "Integrated Micro Solutions VL Bridge";
534		break;
535
536	default:
537		if (class == PCIC_BRIDGE && subclass == PCIS_BRIDGE_HOST)
538			s = "Host to PCI bridge";
539		break;
540	}
541
542	return s;
543}
544
545/*
546 * Scan the first pci bus for host-pci bridges and add pcib instances
547 * to the nexus for each bridge.
548 */
549static void
550nexus_pcib_identify(driver_t *driver, device_t parent)
551{
552	pcicfgregs probe;
553	u_int8_t  hdrtype;
554	int found = 0;
555	int pcifunchigh;
556	int found824xx = 0;
557
558	if (pci_cfgopen() == 0)
559		return;
560	probe.hose = 0;
561	probe.bus = 0;
562 retry:
563	for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) {
564		probe.func = 0;
565		hdrtype = pci_cfgread(&probe, PCIR_HEADERTYPE, 1);
566		if (hdrtype & PCIM_MFDEV)
567			pcifunchigh = 7;
568		else
569			pcifunchigh = 0;
570		for (probe.func = 0;
571		     probe.func <= pcifunchigh;
572		     probe.func++) {
573			/*
574			 * Read the IDs and class from the device.
575			 */
576			u_int32_t id;
577			u_int8_t class, subclass, busnum;
578			device_t child;
579			const char *s;
580
581			id = pci_cfgread(&probe, PCIR_DEVVENDOR, 4);
582			if (id == -1)
583				continue;
584			class = pci_cfgread(&probe, PCIR_CLASS, 1);
585			subclass = pci_cfgread(&probe, PCIR_SUBCLASS, 1);
586
587			s = nexus_pcib_is_host_bridge(&probe, id,
588						      class, subclass,
589						      &busnum);
590			if (s) {
591				/*
592				 * Add at priority 100 to make sure we
593				 * go after any motherboard resources
594				 */
595				child = BUS_ADD_CHILD(parent, 100,
596						      "pcib", busnum);
597				device_set_desc(child, s);
598				found = 1;
599				if (id == 0x12258086)
600					found824xx = 1;
601			}
602		}
603	}
604	if (found824xx && probe.bus == 0) {
605		probe.bus++;
606		goto retry;
607	}
608
609	/*
610	 * Make sure we add at least one bridge since some old
611	 * hardware doesn't actually have a host-pci bridge device.
612	 * Note that pci_cfgopen() thinks we have PCI devices..
613	 */
614	if (!found) {
615		if (bootverbose)
616			printf(
617	"nexus_pcib_identify: no bridge found, adding pcib0 anyway\n");
618		BUS_ADD_CHILD(parent, 100, "pcib", 0);
619	}
620}
621
622static int
623nexus_pcib_probe(device_t dev)
624{
625	if (pci_cfgopen() != 0) {
626		device_add_child(dev, "pci", device_get_unit(dev));
627		return 0;
628	}
629	return ENXIO;
630}
631
632static device_method_t nexus_pcib_methods[] = {
633	/* Device interface */
634	DEVMETHOD(device_identify,	nexus_pcib_identify),
635	DEVMETHOD(device_probe,		nexus_pcib_probe),
636	DEVMETHOD(device_attach,	bus_generic_attach),
637	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
638	DEVMETHOD(device_suspend,	bus_generic_suspend),
639	DEVMETHOD(device_resume,	bus_generic_resume),
640
641	/* Bus interface */
642	DEVMETHOD(bus_print_child,	bus_generic_print_child),
643	DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
644	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
645	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
646	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
647	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
648	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
649
650	{ 0, 0 }
651};
652
653static driver_t nexus_pcib_driver = {
654	"pcib",
655	nexus_pcib_methods,
656	1,
657};
658
659DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0);
660
661/*
662 * Install placeholder to claim the resources owned by the
663 * PCI bus interface.  This could be used to extract the
664 * config space registers in the extreme case where the PnP
665 * ID is available and the PCI BIOS isn't, but for now we just
666 * eat the PnP ID and do nothing else.
667 *
668 * XXX we should silence this probe, as it will generally confuse
669 * people.
670 */
671static struct isa_pnp_id pcibus_pnp_ids[] = {
672	{ 0x030ad041 /* PNP030A */, "PCI Bus" },
673	{ 0 }
674};
675
676static int
677pcibus_pnp_probe(device_t dev)
678{
679	int result;
680
681	if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, pcibus_pnp_ids)) <= 0)
682		device_quiet(dev);
683	return(result);
684}
685
686static int
687pcibus_pnp_attach(device_t dev)
688{
689	return(0);
690}
691
692static device_method_t pcibus_pnp_methods[] = {
693	/* Device interface */
694	DEVMETHOD(device_probe,		pcibus_pnp_probe),
695	DEVMETHOD(device_attach,	pcibus_pnp_attach),
696	DEVMETHOD(device_detach,	bus_generic_detach),
697	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
698	DEVMETHOD(device_suspend,	bus_generic_suspend),
699	DEVMETHOD(device_resume,	bus_generic_resume),
700	{ 0, 0 }
701};
702
703static driver_t pcibus_pnp_driver = {
704	"pcibus_pnp",
705	pcibus_pnp_methods,
706	1,		/* no softc */
707};
708
709static devclass_t pcibus_pnp_devclass;
710
711DRIVER_MODULE(pcibus_pnp, isa, pcibus_pnp_driver, pcibus_pnp_devclass, 0, 0);
712