pci_pir.c revision 16354
11590Srgrimes/**************************************************************************
21590Srgrimes**
31590Srgrimes**  $Id: pcibus.c,v 1.24 1996/04/30 21:37:21 se Exp $
41590Srgrimes**
51590Srgrimes**  pci bus subroutines for i386 architecture.
61590Srgrimes**
71590Srgrimes**  FreeBSD
81590Srgrimes**
91590Srgrimes**-------------------------------------------------------------------------
101590Srgrimes**
111590Srgrimes** Copyright (c) 1994 Wolfgang Stanglmeier.  All rights reserved.
121590Srgrimes**
131590Srgrimes** Redistribution and use in source and binary forms, with or without
141590Srgrimes** modification, are permitted provided that the following conditions
151590Srgrimes** are met:
161590Srgrimes** 1. Redistributions of source code must retain the above copyright
171590Srgrimes**    notice, this list of conditions and the following disclaimer.
181590Srgrimes** 2. Redistributions in binary form must reproduce the above copyright
191590Srgrimes**    notice, this list of conditions and the following disclaimer in the
201590Srgrimes**    documentation and/or other materials provided with the distribution.
211590Srgrimes** 3. The name of the author may not be used to endorse or promote products
221590Srgrimes**    derived from this software without specific prior written permission.
231590Srgrimes**
241590Srgrimes** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
251590Srgrimes** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
261590Srgrimes** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
271590Srgrimes** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
281590Srgrimes** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
291590Srgrimes** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
301590Srgrimes** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
311590Srgrimes** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
321590Srgrimes** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
331590Srgrimes** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
341590Srgrimes**
3528564Scharnier***************************************************************************
361590Srgrimes*/
371590Srgrimes
381590Srgrimes#include "vector.h"
391590Srgrimes
4096438Smike#include <sys/param.h>
411590Srgrimes#include <sys/systm.h>
421590Srgrimes#include <sys/kernel.h>
4396438Smike
4428564Scharnier#include <i386/isa/icu.h>
451590Srgrimes#include <i386/isa/isa.h>
4696438Smike#include <i386/isa/isa_device.h>
4796438Smike
4896438Smike#include <pci/pcivar.h>
491590Srgrimes#include <pci/pcireg.h>
501590Srgrimes#include <pci/pcibus.h>
511590Srgrimes
521590Srgrimes/*-----------------------------------------------------------------
531590Srgrimes**
541590Srgrimes**	The following functions are provided by the pci bios.
551590Srgrimes**	They are used only by the pci configuration.
5691661Sjmallett**
571590Srgrimes**	pcibus_setup():
581590Srgrimes**		Probes for a pci system.
5991661Sjmallett**		Sets pci_maxdevice and pci_mechanism.
6091661Sjmallett**
6128564Scharnier**	pcibus_tag():
621590Srgrimes**		Creates a handle for pci configuration space access.
6391661Sjmallett**		This handle is given to the read/write functions.
641590Srgrimes**
6528564Scharnier**	pcibus_ftag():
661590Srgrimes**		Creates a modified handle.
6728564Scharnier**
681590Srgrimes**	pcibus_read():
6987304Sdwmalone**		Read a long word from the pci configuration space.
7089882Smike**		Requires a tag (from pcitag) and the register
7189882Smike**		number (should be a long word alligned one).
721590Srgrimes**
7392922Simp**	pcibus_write():
7492922Simp**		Writes a long word to the pci configuration space.
7592922Simp**		Requires a tag (from pcitag), the register number
7692922Simp**		(should be a long word alligned one), and a value.
7719078Swosch**
781590Srgrimes**	pcibus_regirq():
7996386Salfred**		Register an interupt handler for a pci device.
801590Srgrimes**		Requires a tag (from pcitag), the register number
8119078Swosch**		(should be a long word alligned one), and a value.
821590Srgrimes**
8389882Smike**-----------------------------------------------------------------
8419078Swosch*/
8519078Swosch
8689882Smikestatic int
8789882Smikepcibus_check (void);
8819078Swosch
8919078Swoschstatic void
9032780Swoschpcibus_setup (void);
9132780Swosch
9232780Swoschstatic pcici_t
9389882Smikepcibus_tag (u_char bus, u_char device, u_char func);
9489882Smike
9589882Smikestatic pcici_t
9689882Smikepcibus_ftag (pcici_t tag, u_char func);
9789882Smike
9889882Smikestatic u_long
9989882Smikepcibus_read (pcici_t tag, u_long reg);
10019078Swosch
10189882Smikestatic void
10289882Smikepcibus_write (pcici_t tag, u_long reg, u_long data);
10319078Swosch
10419078Swoschstatic int
10532780Swoschpcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned* maskptr);
10689882Smike
10789882Smikestatic int
10832780Swoschpcibus_ihandler_detach (int irq, inthand2_t *func);
10932780Swosch
11019078Swoschstatic int
11128564Scharnierpcibus_imask_include (int irq, unsigned* maskptr);
11219078Swosch
11319078Swoschstatic int
11419078Swoschpcibus_imask_exclude (int irq, unsigned* maskptr);
11519078Swosch
11619078Swoschstatic struct pcibus i386pci = {
11719078Swosch	"pci",
1181590Srgrimes	pcibus_setup,
1191590Srgrimes	pcibus_tag,
1201590Srgrimes	pcibus_ftag,
12128564Scharnier	pcibus_read,
1221590Srgrimes	pcibus_write,
1231590Srgrimes	ICU_LEN,
1241590Srgrimes	pcibus_ihandler_attach,
1251590Srgrimes	pcibus_ihandler_detach,
1261590Srgrimes	pcibus_imask_include,
1271590Srgrimes	pcibus_imask_exclude,
1281590Srgrimes};
1291590Srgrimes
1301590Srgrimes/*
1311590Srgrimes**	Announce structure to generic driver
1321590Srgrimes*/
1331590Srgrimes
13419078SwoschDATA_SET (pcibus_set, i386pci);
13596438Smike
1361590Srgrimes/*--------------------------------------------------------------------
13719078Swosch**
13819078Swosch**      Determine configuration mode
13919078Swosch**
140102890Sfanf**--------------------------------------------------------------------
14119078Swosch*/
14219078Swosch
14319078Swosch
14419078Swosch#define CONF1_ADDR_PORT    0x0cf8
14519078Swosch#define CONF1_DATA_PORT    0x0cfc
14619078Swosch
14719078Swosch#define CONF1_ENABLE       0x80000000ul
14819078Swosch#define CONF1_ENABLE_CHK   0x80000000ul
14919078Swosch#define CONF1_ENABLE_MSK   0x7ff00000ul
15019078Swosch#define CONF1_ENABLE_CHK1  0xff000001ul
15119078Swosch#define CONF1_ENABLE_MSK1  0x80000001ul
15219078Swosch#define CONF1_ENABLE_RES1  0x80000000ul
15319078Swosch
15496438Smike#define CONF2_ENABLE_PORT  0x0cf8
15519078Swosch#define CONF2_FORWARD_PORT 0x0cfa
1561590Srgrimes
1571590Srgrimes#define CONF2_ENABLE_CHK   0x0e
15858828Ssheldonh#define CONF2_ENABLE_RES   0x0e
15992623Sjmallett
16092623Sjmallettstatic int
16192623Sjmallettpcibus_check (void)
16292623Sjmallett{
16392623Sjmallett	u_char device;
1641590Srgrimes
16591661Sjmallett	if (bootverbose) printf ("pcibus_check:\tdevice ");
1661590Srgrimes
1671590Srgrimes	for (device = 0; device < pci_maxdevice; device++) {
1681590Srgrimes		unsigned long id;
16919078Swosch		if (bootverbose)
17019078Swosch			printf ("%d ", device);
17119078Swosch		id = pcibus_read (pcibus_tag (0,device,0), 0);
17228564Scharnier		if (id && id != 0xfffffffful) {
1731590Srgrimes			if (bootverbose) printf ("is there (id=%08lx)\n", id);
1741590Srgrimes			return 1;
17591661Sjmallett		}
17622887Swosch	}
17791661Sjmallett	if (bootverbose)
17891661Sjmallett		printf ("-- nothing found\n");
17991661Sjmallett	return 0;
18092623Sjmallett}
18192623Sjmallett
18292623Sjmallettstatic void
18392623Sjmallettpcibus_setup (void)
18492623Sjmallett{
18592623Sjmallett	unsigned long mode1res,oldval1;
18692623Sjmallett	unsigned char mode2res,oldval2;
18792623Sjmallett
18892623Sjmallett	oldval1 = inl (CONF1_ADDR_PORT);
18992623Sjmallett
19092623Sjmallett	if (bootverbose) {
19191661Sjmallett		printf ("pcibus_setup(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1);
19292623Sjmallett	}
19392637Sjmallett
19492623Sjmallett	/*---------------------------------------
19592623Sjmallett	**      Assume configuration mechanism 1 for now ...
19692623Sjmallett	**---------------------------------------
1971590Srgrimes	*/
19892623Sjmallett
19992623Sjmallett	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
20032780Swosch
201102890Sfanf		pci_mechanism = 1;
20232780Swosch		pci_maxdevice = 32;
20332780Swosch
20432780Swosch		outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
20532780Swosch		outb (CONF1_ADDR_PORT +3, 0);
20632780Swosch		mode1res = inl (CONF1_ADDR_PORT);
20732780Swosch		outl (CONF1_ADDR_PORT, oldval1);
20832780Swosch
20992623Sjmallett		if (bootverbose)
21092623Sjmallett		    printf ("pcibus_setup(1a):\tmode1res=0x%08lx (0x%08lx)\n",
21192623Sjmallett			    mode1res, CONF1_ENABLE_CHK);
21292623Sjmallett
21392623Sjmallett		if (mode1res) {
21492623Sjmallett			if (pcibus_check())
21592623Sjmallett				return;
21692623Sjmallett		};
21792623Sjmallett
21892623Sjmallett		outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
21992623Sjmallett		mode1res = inl(CONF1_ADDR_PORT);
22092623Sjmallett		outl (CONF1_ADDR_PORT, oldval1);
22192623Sjmallett
22292623Sjmallett		if (bootverbose)
22392623Sjmallett		    printf ("pcibus_setup(1b):\tmode1res=0x%08lx (0x%08lx)\n",
22492623Sjmallett			    mode1res, CONF1_ENABLE_CHK1);
22592623Sjmallett
22692623Sjmallett		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
22792623Sjmallett			if (pcibus_check())
22892623Sjmallett				return;
2291590Srgrimes		};
2301590Srgrimes	}
2311590Srgrimes
2321590Srgrimes	/*---------------------------------------
23319078Swosch	**      Try configuration mechanism 2 ...
23419078Swosch	**---------------------------------------
23519078Swosch	*/
23632780Swosch
23792623Sjmallett	oldval2 = inb (CONF2_ENABLE_PORT);
23892623Sjmallett
23992623Sjmallett	if (bootverbose) {
24058666Ssheldonh		printf ("pcibus_setup(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2);
24132780Swosch	}
24258666Ssheldonh
24358666Ssheldonh	if ((oldval2 & 0xf0) == 0) {
24492623Sjmallett
24532780Swosch		pci_mechanism = 2;
24632780Swosch		pci_maxdevice = 16;
24732780Swosch
24892623Sjmallett		outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
24992623Sjmallett		mode2res = inb(CONF2_ENABLE_PORT);
2501590Srgrimes		outb (CONF2_ENABLE_PORT, oldval2);
25122887Swosch
2521590Srgrimes		if (bootverbose)
2531590Srgrimes		    printf ("pcibus_setup(2a):\tmode2res=0x%02x (0x%02x)\n",
25491661Sjmallett			    mode2res, CONF2_ENABLE_CHK);
2551590Srgrimes
2561590Srgrimes		if (mode2res == CONF2_ENABLE_RES) {
25728564Scharnier		    if (bootverbose)
2581590Srgrimes			printf ("pcibus_setup(2a):\tnow trying mechanism 2\n");
2591590Srgrimes
26091661Sjmallett			if (pcibus_check())
26191661Sjmallett				return;
26291661Sjmallett		}
26391661Sjmallett	}
26491661Sjmallett
26591661Sjmallett	/*---------------------------------------
2661590Srgrimes	**      No PCI bus host bridge found
26724263Swosch	**---------------------------------------
26822887Swosch	*/
26922887Swosch
27022887Swosch	pci_mechanism = 0;
27122887Swosch	pci_maxdevice = 0;
27228564Scharnier}
27328564Scharnier
27428564Scharnier/*--------------------------------------------------------------------
27522887Swosch**
27622887Swosch**      Build a pcitag from bus, device and function number
27758666Ssheldonh**
27858666Ssheldonh**--------------------------------------------------------------------
27958666Ssheldonh*/
28022887Swosch
28122887Swoschstatic pcici_t
2821590Srgrimespcibus_tag (unsigned char bus, unsigned char device, unsigned char func)
2831590Srgrimes{
2841590Srgrimes	pcici_t tag;
2851590Srgrimes
2861590Srgrimes	tag.cfg1 = 0;
2871590Srgrimes	if (func   >=  8) return tag;
2881590Srgrimes
2891590Srgrimes	switch (pci_mechanism) {
290102890Sfanf
29122887Swosch	case 1:
29222887Swosch		if (device < 32) {
29322887Swosch			tag.cfg1 = CONF1_ENABLE
2941590Srgrimes				| (((u_long) bus   ) << 16ul)
29558666Ssheldonh				| (((u_long) device) << 11ul)
2961590Srgrimes				| (((u_long) func  ) <<  8ul);
29758666Ssheldonh		}
2981590Srgrimes		break;
29958666Ssheldonh	case 2:
3001590Srgrimes		if (device < 16) {
3011590Srgrimes			tag.cfg2.port    = 0xc000 | (device << 8ul);
3021590Srgrimes			tag.cfg2.enable  = 0xf0 | (func << 1ul);
30322887Swosch			tag.cfg2.forward = bus;
30422887Swosch		}
3051590Srgrimes		break;
30658666Ssheldonh	};
3071590Srgrimes	return tag;
3081590Srgrimes}
309102890Sfanf
31022887Swoschstatic pcici_t
31122887Swoschpcibus_ftag (pcici_t tag, u_char func)
31222887Swosch{
3131590Srgrimes	switch (pci_mechanism) {
31458666Ssheldonh
3151590Srgrimes	case 1:
3161590Srgrimes		tag.cfg1 &= ~0x700ul;
317102890Sfanf		tag.cfg1 |= (((u_long) func) << 8ul);
31822887Swosch		break;
31922887Swosch	case 2:
3201590Srgrimes		tag.cfg2.enable  = 0xf0 | (func << 1ul);
32158666Ssheldonh		break;
3221590Srgrimes	};
3231590Srgrimes	return tag;
3241590Srgrimes}
325102890Sfanf
32622894Swosch/*--------------------------------------------------------------------
32722894Swosch**
32828564Scharnier**      Read register from configuration space.
3291590Srgrimes**
3301590Srgrimes**--------------------------------------------------------------------
3311590Srgrimes*/
3321590Srgrimes
3331590Srgrimesstatic u_long
33491661Sjmallettpcibus_read (pcici_t tag, u_long reg)
33596438Smike{
33691661Sjmallett	u_long addr, data = 0;
33791661Sjmallett
33891661Sjmallett	if (!tag.cfg1) return (0xfffffffful);
33991661Sjmallett
34095101Sjmallett	switch (pci_mechanism) {
34195101Sjmallett
34295101Sjmallett	case 1:
34395101Sjmallett		addr = tag.cfg1 | (reg & 0xfc);
34491661Sjmallett#ifdef PCI_DEBUG
34591661Sjmallett		printf ("pci_conf_read(1): addr=%x ", addr);
34691661Sjmallett#endif
34795103Sjmallett		outl (CONF1_ADDR_PORT, addr);
34891661Sjmallett		data = inl (CONF1_DATA_PORT);
34991661Sjmallett		outl (CONF1_ADDR_PORT, 0   );
35028564Scharnier		break;
35196438Smike
3521590Srgrimes	case 2:
35396943Sjmallett		addr = tag.cfg2.port | (reg & 0xfc);
35496943Sjmallett#ifdef PCI_DEBUG
35596943Sjmallett		printf ("pci_conf_read(2): addr=%x ", addr);
35696943Sjmallett#endif
35796943Sjmallett		outb (CONF2_ENABLE_PORT , tag.cfg2.enable );
3581590Srgrimes		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
3591590Srgrimes
360		data = inl ((u_short) addr);
361
362		outb (CONF2_ENABLE_PORT,  0);
363		outb (CONF2_FORWARD_PORT, 0);
364		break;
365	};
366
367#ifdef PCI_DEBUG
368	printf ("data=%x\n", data);
369#endif
370
371	return (data);
372}
373
374/*--------------------------------------------------------------------
375**
376**      Write register into configuration space.
377**
378**--------------------------------------------------------------------
379*/
380
381static void
382pcibus_write (pcici_t tag, u_long reg, u_long data)
383{
384	u_long addr;
385
386	if (!tag.cfg1) return;
387
388	switch (pci_mechanism) {
389
390	case 1:
391		addr = tag.cfg1 | (reg & 0xfc);
392#ifdef PCI_DEBUG
393		printf ("pci_conf_write(1): addr=%x data=%x\n",
394			addr, data);
395#endif
396		outl (CONF1_ADDR_PORT, addr);
397		outl (CONF1_DATA_PORT, data);
398		outl (CONF1_ADDR_PORT,   0 );
399		break;
400
401	case 2:
402		addr = tag.cfg2.port | (reg & 0xfc);
403#ifdef PCI_DEBUG
404		printf ("pci_conf_write(2): addr=%x data=%x\n",
405			addr, data);
406#endif
407		outb (CONF2_ENABLE_PORT,  tag.cfg2.enable);
408		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
409
410		outl ((u_short) addr, data);
411
412		outb (CONF2_ENABLE_PORT,  0);
413		outb (CONF2_FORWARD_PORT, 0);
414		break;
415	};
416}
417
418/*-----------------------------------------------------------------------
419**
420**	Register an interupt handler for a pci device.
421**
422**-----------------------------------------------------------------------
423*/
424
425static int
426pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned * maskptr)
427{
428	char buf[16];
429	char *cp;
430	int free_id, id, result;
431
432	sprintf(buf, "pci irq%d", irq);
433	for (cp = intrnames, free_id = 0, id = 0; id < NR_DEVICES; id++) {
434		if (strcmp(cp, buf) == 0)
435			break;
436		if (free_id <= 0 && strcmp(cp, "pci irqnn") == 0)
437			free_id = id;
438		while (*cp++ != '\0')
439			;
440	}
441	if (id == NR_DEVICES) {
442		id = free_id;
443		if (id == 0) {
444			/*
445			 * All pci irq counters are in use, perhaps because
446			 * config is old so there aren't any.  Abuse the
447			 * clk0 counter.
448			 */
449			printf (
450		"pcibus_ihandler_attach: counting pci irq%d's as clk0 irqs\n",
451				irq);
452		}
453	}
454	result = register_intr(
455		irq,		    /* isa irq	    */
456		id,		    /* device id    */
457		0,		    /* flags?	    */
458		func,		    /* handler	    */
459		maskptr,	    /* mask pointer */
460		arg);		    /* handler arg  */
461
462	if (result) {
463		printf ("@@@ pcibus_ihandler_attach: result=%d\n", result);
464		return (result);
465	};
466	update_intr_masks();
467
468	INTREN ((1ul<<irq));
469	return (0);
470}
471
472static int
473pcibus_ihandler_detach (int irq, inthand2_t *func)
474{
475	int result;
476
477	INTRDIS ((1ul<<irq));
478
479	result = unregister_intr (irq, func);
480
481	if (result)
482		printf ("@@@ pcibus_ihandler_detach: result=%d\n", result);
483
484	update_intr_masks();
485
486	return (result);
487}
488
489static int
490pcibus_imask_include (int irq, unsigned* maskptr)
491{
492	unsigned mask;
493
494	if (!maskptr) return (0);
495
496	mask = 1ul << irq;
497
498	if (*maskptr & mask)
499		return (-1);
500
501	INTRMASK (*maskptr, mask);
502	update_intr_masks();
503
504	return (0);
505}
506
507static int
508pcibus_imask_exclude (int irq, unsigned* maskptr)
509{
510	unsigned mask;
511
512	if (!maskptr) return (0);
513
514	mask = 1ul << irq;
515
516	if (! (*maskptr & mask))
517		return (-1);
518
519	*maskptr &= ~mask;
520	update_intr_masks();
521
522	return (0);
523}
524