pci_pir.c revision 11378
16104Sse/**************************************************************************
26104Sse**
311378Sse**  $Id: pcibus.c,v 1.15 1995/09/22 19:10:54 se Exp $
46104Sse**
56104Sse**  pci bus subroutines for i386 architecture.
66104Sse**
76104Sse**  FreeBSD
86104Sse**
96104Sse**-------------------------------------------------------------------------
106104Sse**
116104Sse** Copyright (c) 1994 Wolfgang Stanglmeier.  All rights reserved.
126104Sse**
136104Sse** Redistribution and use in source and binary forms, with or without
146104Sse** modification, are permitted provided that the following conditions
156104Sse** are met:
166104Sse** 1. Redistributions of source code must retain the above copyright
176104Sse**    notice, this list of conditions and the following disclaimer.
186104Sse** 2. Redistributions in binary form must reproduce the above copyright
196104Sse**    notice, this list of conditions and the following disclaimer in the
206104Sse**    documentation and/or other materials provided with the distribution.
216104Sse** 3. The name of the author may not be used to endorse or promote products
226104Sse**    derived from this software without specific prior written permission.
236104Sse**
246104Sse** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
256104Sse** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
266104Sse** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
276104Sse** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
286104Sse** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
296104Sse** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
306104Sse** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
316104Sse** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
326104Sse** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
336104Sse** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
346104Sse**
356104Sse***************************************************************************
366104Sse*/
376104Sse
386734Sbde#include <sys/param.h>
396734Sbde#include <sys/systm.h>
406734Sbde#include <sys/kernel.h>
416734Sbde
4210807Sse#include <machine/cpu.h> /* bootverbose */
4310807Sse
446734Sbde#include <i386/isa/icu.h>
456104Sse#include <i386/isa/isa.h>
466104Sse#include <i386/isa/isa_device.h>
476104Sse
486104Sse#include <pci/pcivar.h>
496104Sse#include <pci/pcireg.h>
506104Sse#include <pci/pcibus.h>
516104Sse
526104Sse/*-----------------------------------------------------------------
536104Sse**
546104Sse**	The following functions are provided by the pci bios.
556104Sse**	They are used only by the pci configuration.
566104Sse**
577234Sse**	pcibus_setup():
586104Sse**		Probes for a pci system.
597234Sse**		Sets pci_maxdevice and pci_mechanism.
606104Sse**
616104Sse**	pcibus_tag():
627234Sse**		Creates a handle for pci configuration space access.
637234Sse**		This handle is given to the read/write functions.
646104Sse**
657234Sse**	pcibus_ftag():
667234Sse**		Creates a modified handle.
677234Sse**
686104Sse**	pcibus_read():
696104Sse**		Read a long word from the pci configuration space.
706104Sse**		Requires a tag (from pcitag) and the register
716104Sse**		number (should be a long word alligned one).
726104Sse**
736104Sse**	pcibus_write():
746104Sse**		Writes a long word to the pci configuration space.
756104Sse**		Requires a tag (from pcitag), the register number
766104Sse**		(should be a long word alligned one), and a value.
776104Sse**
786104Sse**	pcibus_regirq():
796104Sse**		Register an interupt handler for a pci device.
806104Sse**		Requires a tag (from pcitag), the register number
816104Sse**		(should be a long word alligned one), and a value.
826104Sse**
836104Sse**-----------------------------------------------------------------
846104Sse*/
856104Sse
8610887Ssestatic int
8710887Ssepcibus_check (void);
8810887Sse
897234Ssestatic void
907234Ssepcibus_setup (void);
916104Sse
926104Ssestatic pcici_t
936104Ssepcibus_tag (u_char bus, u_char device, u_char func);
946104Sse
957234Ssestatic pcici_t
967234Ssepcibus_ftag (pcici_t tag, u_char func);
977234Sse
986104Ssestatic u_long
996104Ssepcibus_read (pcici_t tag, u_long reg);
1006104Sse
1016104Ssestatic void
1026104Ssepcibus_write (pcici_t tag, u_long reg, u_long data);
1036104Sse
1046104Ssestatic int
1057234Ssepcibus_ihandler_attach (int irq, void(*ihandler)(), int arg, unsigned* maskp);
1066104Sse
1077234Ssestatic int
1087234Ssepcibus_ihandler_detach (int irq, void(*handler)());
1097234Sse
1107234Ssestatic int
1117234Ssepcibus_imask_include (int irq, unsigned* maskptr);
1127234Sse
1137234Ssestatic int
1147234Ssepcibus_imask_exclude (int irq, unsigned* maskptr);
1157234Sse
1166104Ssestruct pcibus i386pci = {
1176104Sse	"pci",
1187234Sse	pcibus_setup,
1196104Sse	pcibus_tag,
1207234Sse	pcibus_ftag,
1216104Sse	pcibus_read,
1226104Sse	pcibus_write,
1237234Sse	ICU_LEN,
1247234Sse	pcibus_ihandler_attach,
1257234Sse	pcibus_ihandler_detach,
1267234Sse	pcibus_imask_include,
1277234Sse	pcibus_imask_exclude,
1286104Sse};
1296104Sse
1306104Sse/*
1316104Sse**	Announce structure to generic driver
1326104Sse*/
1336104Sse
1346104SseDATA_SET (pcibus_set, i386pci);
1356104Sse
1366104Sse/*--------------------------------------------------------------------
1376104Sse**
1386104Sse**      Determine configuration mode
1396104Sse**
1406104Sse**--------------------------------------------------------------------
1416104Sse*/
1426104Sse
1436104Sse
1446104Sse#define CONF1_ADDR_PORT    0x0cf8
1456104Sse#define CONF1_DATA_PORT    0x0cfc
1466104Sse
14710807Sse#define CONF1_ENABLE       0x80000000ul
14810960Sse#define CONF1_ENABLE_CHK   0x80000000ul
14910960Sse#define CONF1_ENABLE_CHK1  0xFF000001ul
15010960Sse#define CONF1_ENABLE_MSK1  0x80000000ul
15110887Sse#define CONF1_ENABLE_RES1  0x80000000ul
1526104Sse
1536104Sse#define CONF2_ENABLE_PORT  0x0cf8
1546104Sse#define CONF2_FORWARD_PORT 0x0cfa
1556104Sse
15610807Sse#define CONF2_ENABLE_CHK   0x0e
15710807Sse#define CONF2_ENABLE_RES   0x0e
1586104Sse
15910887Ssestatic int
16010887Ssepcibus_check (void)
16110887Sse{
16210887Sse	u_char device;
16310735Sse
16410960Sse	if (bootverbose) printf ("pcibus_check:\tdevice ");
16510960Sse
16610887Sse	for (device = 0; device < pci_maxdevice; device++) {
16711378Sse		unsigned long id;
16811378Sse		if (bootverbose)
16911378Sse			printf ("%d ", device);
17011378Sse		id = pcibus_read (pcibus_tag (0,device,0), 0);
17111378Sse		if (id != 0xfffffffful) {
17211378Sse			if (bootverbose) printf ("is there (id=%08lx)\n", id);
17310887Sse			return 1;
17410960Sse		}
17510887Sse	}
17611378Sse	if (bootverbose)
17711378Sse		printf ("-- nothing found\n");
17810887Sse	return 0;
17910887Sse}
18010887Sse
1817234Ssestatic void
1827234Ssepcibus_setup (void)
1836104Sse{
18410960Sse	unsigned long mode1res,oldval;
18510960Sse	unsigned char mode2res;
1866104Sse
18710960Sse	oldval = inl (CONF1_ADDR_PORT);
18810960Sse	outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
18910960Sse	outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
19010960Sse	mode1res = inl(CONF1_ADDR_PORT);
19110960Sse	mode2res = inb(CONF2_ENABLE_PORT);
19211378Sse	outb (CONF2_ENABLE_PORT, 0);
19310960Sse	outl (CONF1_ADDR_PORT, oldval);
19410960Sse
19510960Sse	if (bootverbose) {
19610960Sse		printf ("pcibus_setup(1):\tmode1res=0x%08lx (0x%08lx), "
19710960Sse			"mode2res=0x%02x (0x%02x)\n",
19810960Sse			mode1res,CONF1_ENABLE_CHK,
19910960Sse			(int)mode2res,CONF2_ENABLE_CHK);
20010960Sse	}
20110960Sse
2026104Sse	/*---------------------------------------
20310960Sse	**	No PCI, if neither mode1res nor mode2res could be read back
2046104Sse	**---------------------------------------
2056104Sse	*/
2066104Sse
20710960Sse	if ((mode1res != CONF1_ENABLE_CHK) && (mode2res != CONF2_ENABLE_CHK)) {
20810960Sse		return;
20910960Sse	}
21010960Sse
21110960Sse	/*---------------------------------------
21210960Sse	**      Assume configuration mechanism 1 for now ...
21310960Sse	**---------------------------------------
21410960Sse	*/
21510960Sse
21610960Sse	pci_mechanism = 1;
21710960Sse	pci_maxdevice = 32;
21810960Sse
21910960Sse	outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
22010887Sse	outb (CONF1_ADDR_PORT +3, 0);
22110960Sse	mode1res = inl (CONF1_ADDR_PORT);
2226104Sse	outl (CONF1_ADDR_PORT, oldval);
2236104Sse
22410960Sse	if (bootverbose)
22510960Sse		printf ("pcibus_setup(2):\tmode1res=0x%08lx (0x%08lx)\n",
22610960Sse			mode1res, CONF1_ENABLE_CHK);
22710960Sse
22810960Sse	if (mode1res) {
22910887Sse		if (pcibus_check())
23010887Sse			return;
2316104Sse	};
2326104Sse
23310960Sse	outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
23410960Sse	outl (CONF1_DATA_PORT, 0);
23510960Sse	mode1res = inl(CONF1_ADDR_PORT);
23610960Sse	outl (CONF1_ADDR_PORT, oldval);
2379360Sse
23810960Sse	if (bootverbose)
23910960Sse		printf ("pcibus_setup(3):\tmode1res=0x%08lx (0x%08lx)\n",
24010960Sse			mode1res, CONF1_ENABLE_CHK1);
24110807Sse
24211378Sse	if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
24310887Sse		if (pcibus_check())
24410887Sse			return;
2459360Sse	};
2469360Sse
24710960Sse	/*---------------------------------------
24810960Sse	**      Try configuration mechanism 2 ...
24910960Sse	**---------------------------------------
25010887Sse	*/
25110887Sse
25211378Sse	if (bootverbose)
25311378Sse		printf ("pcibus_setup(4):\tnow trying mechanism 2\n");
25411378Sse
25510960Sse	pci_mechanism = 2;
25610960Sse	pci_maxdevice = 16;
25710887Sse
25810960Sse	if (pcibus_check())
25910960Sse	    return;
26010887Sse
2619360Sse	/*---------------------------------------
26210710Sse	**      No PCI bus host bridge found
2636104Sse	**---------------------------------------
2646104Sse	*/
26510710Sse
26610710Sse	pci_mechanism = 0;
26710710Sse	pci_maxdevice = 0;
2686104Sse}
2697234Sse
2706104Sse/*--------------------------------------------------------------------
2716104Sse**
2726104Sse**      Build a pcitag from bus, device and function number
2736104Sse**
2746104Sse**--------------------------------------------------------------------
2756104Sse*/
2766104Sse
2776104Ssestatic pcici_t
2786104Ssepcibus_tag (unsigned char bus, unsigned char device, unsigned char func)
2796104Sse{
2806104Sse	pcici_t tag;
2816104Sse
2826104Sse	tag.cfg1 = 0;
2836104Sse	if (func   >=  8) return tag;
2846104Sse
2857234Sse	switch (pci_mechanism) {
2866104Sse
2876104Sse	case 1:
28810807Sse		if (device < 32) {
28910807Sse			tag.cfg1 = CONF1_ENABLE
29010807Sse				| (((u_long) bus   ) << 16ul)
29110807Sse				| (((u_long) device) << 11ul)
29210807Sse				| (((u_long) func  ) <<  8ul);
29310807Sse		}
2946104Sse		break;
2956104Sse	case 2:
29610807Sse		if (device < 16) {
29710807Sse			tag.cfg2.port    = 0xc000 | (device << 8ul);
29810807Sse			tag.cfg2.enable  = 0xf0 | (func << 1ul);
29910807Sse			tag.cfg2.forward = bus;
30010807Sse		}
3016104Sse		break;
3026104Sse	};
3036104Sse	return tag;
3046104Sse}
3057234Sse
3067234Ssestatic pcici_t
3077234Ssepcibus_ftag (pcici_t tag, u_char func)
3087234Sse{
3097234Sse	switch (pci_mechanism) {
3107234Sse
3117234Sse	case 1:
3127234Sse		tag.cfg1 &= ~0x700ul;
3137234Sse		tag.cfg1 |= (((u_long) func) << 8ul);
3147234Sse		break;
3157234Sse	case 2:
31610807Sse		tag.cfg2.enable  = 0xf0 | (func << 1ul);
3177234Sse		break;
3187234Sse	};
3197234Sse	return tag;
3207234Sse}
3217234Sse
3226104Sse/*--------------------------------------------------------------------
3236104Sse**
3246104Sse**      Read register from configuration space.
3256104Sse**
3266104Sse**--------------------------------------------------------------------
3276104Sse*/
3286104Sse
3296104Ssestatic u_long
3306104Ssepcibus_read (pcici_t tag, u_long reg)
3316104Sse{
3326104Sse	u_long addr, data = 0;
3336104Sse
3346104Sse	if (!tag.cfg1) return (0xfffffffful);
3356104Sse
3367234Sse	switch (pci_mechanism) {
3376104Sse
3386104Sse	case 1:
3396104Sse		addr = tag.cfg1 | (reg & 0xfc);
3406104Sse#ifdef PCI_DEBUG
3416104Sse		printf ("pci_conf_read(1): addr=%x ", addr);
3426104Sse#endif
3436104Sse		outl (CONF1_ADDR_PORT, addr);
3446104Sse		data = inl (CONF1_DATA_PORT);
3456104Sse		outl (CONF1_ADDR_PORT, 0   );
3466104Sse		break;
3476104Sse
3486104Sse	case 2:
3496104Sse		addr = tag.cfg2.port | (reg & 0xfc);
3506104Sse#ifdef PCI_DEBUG
3516104Sse		printf ("pci_conf_read(2): addr=%x ", addr);
3526104Sse#endif
3536104Sse		outb (CONF2_ENABLE_PORT , tag.cfg2.enable );
3546104Sse		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
3556104Sse
3566104Sse		data = inl ((u_short) addr);
3576104Sse
3586104Sse		outb (CONF2_ENABLE_PORT,  0);
3596104Sse		outb (CONF2_FORWARD_PORT, 0);
3606104Sse		break;
3616104Sse	};
3626104Sse
3636104Sse#ifdef PCI_DEBUG
3646104Sse	printf ("data=%x\n", data);
3656104Sse#endif
3666104Sse
3676104Sse	return (data);
3686104Sse}
3697234Sse
3706104Sse/*--------------------------------------------------------------------
3716104Sse**
3726104Sse**      Write register into configuration space.
3736104Sse**
3746104Sse**--------------------------------------------------------------------
3756104Sse*/
3766104Sse
3776104Ssestatic void
3786104Ssepcibus_write (pcici_t tag, u_long reg, u_long data)
3796104Sse{
3806104Sse	u_long addr;
3816104Sse
3826104Sse	if (!tag.cfg1) return;
3836104Sse
3847234Sse	switch (pci_mechanism) {
3856104Sse
3866104Sse	case 1:
3876104Sse		addr = tag.cfg1 | (reg & 0xfc);
3886104Sse#ifdef PCI_DEBUG
3896104Sse		printf ("pci_conf_write(1): addr=%x data=%x\n",
3906104Sse			addr, data);
3916104Sse#endif
3926104Sse		outl (CONF1_ADDR_PORT, addr);
3936104Sse		outl (CONF1_DATA_PORT, data);
3946104Sse		outl (CONF1_ADDR_PORT,   0 );
3956104Sse		break;
3966104Sse
3976104Sse	case 2:
3986104Sse		addr = tag.cfg2.port | (reg & 0xfc);
3996104Sse#ifdef PCI_DEBUG
4006104Sse		printf ("pci_conf_write(2): addr=%x data=%x\n",
4016104Sse			addr, data);
4026104Sse#endif
4036104Sse		outb (CONF2_ENABLE_PORT,  tag.cfg2.enable);
4046104Sse		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
4056104Sse
4066104Sse		outl ((u_short) addr, data);
4076104Sse
4086104Sse		outb (CONF2_ENABLE_PORT,  0);
4096104Sse		outb (CONF2_FORWARD_PORT, 0);
4106104Sse		break;
4116104Sse	};
4126104Sse}
4137234Sse
4146104Sse/*-----------------------------------------------------------------------
4156104Sse**
4166104Sse**	Register an interupt handler for a pci device.
4176104Sse**
4186104Sse**-----------------------------------------------------------------------
4196104Sse*/
4206104Sse
4217234Ssestatic int
4227234Ssepcibus_ihandler_attach (int irq, void(*func)(), int arg, unsigned * maskptr)
4237234Sse{
4247234Sse	int result;
4257234Sse	result = register_intr(
4267234Sse		irq,		    /* isa irq	    */
4277234Sse		0,		    /* deviced??    */
4287234Sse		0,		    /* flags?	    */
4297234Sse		(inthand2_t*) func, /* handler	    */
4307234Sse		maskptr,	    /* mask pointer */
4317234Sse		arg);		    /* handler arg  */
4326104Sse
4337234Sse	if (result) {
4347234Sse		printf ("@@@ pcibus_ihandler_attach: result=%d\n", result);
4357234Sse		return (result);
4367234Sse	};
4377234Sse	update_intr_masks();
4386104Sse
4397234Sse	INTREN ((1ul<<irq));
4407234Sse	return (0);
4417234Sse}
4426104Sse
4436104Ssestatic int
4447234Ssepcibus_ihandler_detach (int irq, void(*func)())
4457234Sse{
4467234Sse	int result;
4476104Sse
4487234Sse	INTRDIS ((1ul<<irq));
4496104Sse
4507234Sse	result = unregister_intr (irq, (inthand2_t*) func);
4517234Sse
4527234Sse	if (result)
4537234Sse		printf ("@@@ pcibus_ihandler_detach: result=%d\n", result);
4547234Sse
4557234Sse	update_intr_masks();
4567234Sse
4577234Sse	return (result);
4587234Sse}
4597234Sse
4607234Ssestatic int
4617234Ssepcibus_imask_include (int irq, unsigned* maskptr)
4626104Sse{
4637234Sse	unsigned mask;
4646104Sse
4657234Sse	if (!maskptr) return (0);
4666104Sse
4676104Sse	mask = 1ul << irq;
4686104Sse
4697234Sse	if (*maskptr & mask)
4707234Sse		return (-1);
4716104Sse
4726104Sse	INTRMASK (*maskptr, mask);
4737234Sse	update_intr_masks();
4746104Sse
4757234Sse	return (0);
4767234Sse}
4776104Sse
4787234Ssestatic int
4797234Ssepcibus_imask_exclude (int irq, unsigned* maskptr)
4807234Sse{
4817234Sse	unsigned mask;
4826104Sse
4837234Sse	if (!maskptr) return (0);
4846104Sse
4857234Sse	mask = 1ul << irq;
4867234Sse
4877234Sse	if (! (*maskptr & mask))
4887234Sse		return (-1);
4897234Sse
4907234Sse	*maskptr &= ~mask;
4917234Sse	update_intr_masks();
4927234Sse
4937234Sse	return (0);
4946104Sse}
495