Subject: OFW based PCI probe routine.
To: None <tech-kern@netbsd.org>
From: matthew green <mrg@eterna.com.au>
List: tech-kern
Date: 02/13/2001 10:36:28
hi folks.


here's a patch that changes the pci bus probe routine to use the OFW device
tree.  essentially, i copied the inner loop from the old pci_probe_bus()
function into a new pci_probe_function() (which is shared between both), and
then added a new version of pci_probe_bus() that loops over the OFW tree at
this node.  one change i haven't yet made that i am planning is re-exposing
the original pci_probe_bus() in all cases, and calling it if the OFW node is
invalid....  unless the macro `__PCI_OFW_BINDING' is defined, there are no
actual changes.  i have tested this code both with and without the macro
being defined.

the main purpose behind these changes is that on some sparc64 PCI
implementations, accessing "random" parts of PCI space causes all sorts
of lossage, from hangs to evil traps.  by using the OFW tree, one should
only be accessing devices that actually exist, avoiding this problem.


what do people think?  i'd be interested to see what happens on a macppc
with __PCI_OFW_BINDING defined and this patch applied.  the PCI host
controller would also need to set pba_node as well (see the simba.c,
psycho.c or ppb.c changes below).  i checked an imac's prom and i would
hazard a guess it might work.


.mrg.



Index: arch/sparc64/dev/simba.c
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/sparc64/dev/simba.c,v
retrieving revision 1.2
diff -p -p -r1.2 simba.c
*** arch/sparc64/dev/simba.c	2000/05/24 20:27:52	1.2
--- arch/sparc64/dev/simba.c	2001/02/12 09:29:49
*************** simba_attach(parent, self, aux)
*** 135,140 ****
--- 135,143 ----
  	pba.pba_bus = PPB_BUSINFO_SECONDARY(busdata);
  	pba.pba_intrswiz = pa->pa_intrswiz;
  	pba.pba_intrtag = pa->pa_intrtag;
+ #ifdef __PCI_OFW_BINDING
+ 	pba.pba_node = pa->pa_node;
+ #endif
  
  	config_found(self, &pba, simba_print);
  }
Index: arch/sparc64/dev/psycho.c
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/sparc64/dev/psycho.c,v
retrieving revision 1.28
diff -p -p -r1.28 psycho.c
*** arch/sparc64/dev/psycho.c	2000/12/04 20:29:34	1.28
--- arch/sparc64/dev/psycho.c	2001/02/12 09:29:51
*************** psycho_attach(parent, self, aux)
*** 209,214 ****
--- 209,217 ----
  	pba.pba_dmat = sc->sc_psycho_this->pp_dmat;
  	pba.pba_iot = sc->sc_psycho_this->pp_iot;
  	pba.pba_memt = sc->sc_psycho_this->pp_memt;
+ #ifdef __PCI_OFW_BINDING
+ 	pba.pba_node = sc->sc_node;
+ #endif
  
  	config_found(self, &pba, psycho_print);
  }
Index: arch/sparc64/include/pci_machdep.h
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/sparc64/include/pci_machdep.h,v
retrieving revision 1.5
diff -p -p -r1.5 pci_machdep.h
*** arch/sparc64/include/pci_machdep.h	2000/12/28 22:59:10	1.5
--- arch/sparc64/include/pci_machdep.h	2001/02/12 09:29:51
*************** void		*pci_intr_establish(pci_chipset_ta
*** 65,68 ****
--- 65,71 ----
  					 int, int (*)(void *), void *);
  void		pci_intr_disestablish(pci_chipset_tag_t, void *);
  
+ /* want pci_attach_args{}->pa_node */
+ #define __PCI_OFW_BINDING
+ 
  #endif /* _MACHINE_PCI_MACHDEP_H_ */
Index: dev/pci/pci.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/pci/pci.c,v
retrieving revision 1.50
diff -p -p -r1.50 pci.c
*** dev/pci/pci.c	2001/02/12 09:14:53	1.50
--- dev/pci/pci.c	2001/02/12 09:29:53
***************
*** 45,50 ****
--- 45,55 ----
  #include <dev/pci/pcivar.h>
  #include <dev/pci/pcidevs.h>
  
+ #ifdef __PCI_OFW_BINDING
+ #include <dev/ofw/openfirm.h>
+ #include <dev/ofw/ofw_pci.h>
+ #endif
+ 
  #ifdef PCI_CONFIG_DUMP
  int pci_config_dump = 1;
  #else
*************** struct pci_softc {
*** 63,74 ****
--- 68,84 ----
  	u_int sc_intrswiz;
  	pcitag_t sc_intrtag;
  	int sc_flags;
+ #ifdef __PCI_OFW_BINDING
+ 	int sc_node;
+ #endif
  };
  
  struct cfattach pci_ca = {
  	sizeof(struct pci_softc), pcimatch, pciattach
  };
  
+ void	pci_probe_function(struct device *, struct pci_attach_args *,
+ 			   pci_chipset_tag_t, int, int);
  void	pci_probe_bus __P((struct device *));
  int	pciprint __P((void *, const char *));
  int	pcisubmatch __P((struct device *, struct cfdata *, void *));
*************** pcimatch(parent, cf, aux)
*** 126,132 ****
--- 136,263 ----
  	return 1;
  }
  
+ /*
+  * Probe a single function.  Call by both the generic and OFW bus probe
+  * routines.
+  */
  void
+ pci_probe_function(self, pa, pc, device, function)
+ 	struct device *self;
+ 	struct pci_attach_args *pa;
+ 	pci_chipset_tag_t pc;
+ 	int device, function;
+ {
+ 	pcireg_t tag, intr, csr, id, class;
+ 	struct pci_softc *sc = (struct pci_softc *)self;
+ 	int bus = sc->sc_bus;
+ 	int pin;
+ 
+ 	tag = pci_make_tag(pc, bus, device, function);
+ 	id = pci_conf_read(pc, tag, PCI_ID_REG);
+ 	csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
+ 	class = pci_conf_read(pc, tag, PCI_CLASS_REG);
+ 	intr = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
+ 
+ 	/* Invalid vendor ID value? */
+ 	if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
+ 		return;
+ 	/* XXX Not invalid, but we've done this ~forever. */
+ 	if (PCI_VENDOR(id) == 0)
+ 		return;
+ 
+ 	pa->pa_iot = sc->sc_iot;
+ 	pa->pa_memt = sc->sc_memt;
+ 	pa->pa_dmat = sc->sc_dmat;
+ 	pa->pa_pc = pc;
+ 	pa->pa_device = device;
+ 	pa->pa_function = function;
+ 	pa->pa_tag = tag;
+ 	pa->pa_id = id;
+ 	pa->pa_class = class;
+ 
+ 	/* From here on is identical to non-OFW version */
+ 	/*
+ 	 * Set up memory, I/O enable, and PCI command flags
+ 	 * as appropriate.
+ 	 */
+ 	pa->pa_flags = sc->sc_flags;
+ 	if ((csr & PCI_COMMAND_IO_ENABLE) == 0)
+ 		pa->pa_flags &= ~PCI_FLAGS_IO_ENABLED;
+ 	if ((csr & PCI_COMMAND_MEM_ENABLE) == 0)
+ 		pa->pa_flags &= ~PCI_FLAGS_MEM_ENABLED;
+ 
+ 	if (bus == 0) {
+ 		pa->pa_intrswiz = 0;
+ 		pa->pa_intrtag = pa->pa_tag;
+ 	} else {
+ 		pa->pa_intrswiz = sc->sc_intrswiz + device;
+ 		pa->pa_intrtag = sc->sc_intrtag;
+ 	}
+ 	pin = PCI_INTERRUPT_PIN(intr);
+ 	if (pin == PCI_INTERRUPT_PIN_NONE) {
+ 		/* no interrupt */
+ 		pa->pa_intrpin = 0;
+ 	} else {
+ 		/*
+ 		 * swizzle it based on the number of
+ 		 * busses we're behind and our device
+ 		 * number.
+ 		 */
+ 		pa->pa_intrpin =			/* XXX */
+ 		    ((pin + pa->pa_intrswiz - 1) % 4) + 1;
+ 	}
+ 	pa->pa_intrline = PCI_INTERRUPT_LINE(intr);
+ 
+ 	config_found_sm(self, pa, pciprint, pcisubmatch);
+ }
+ 
+ #ifdef __PCI_OFW_BINDING
+ static __inline pcireg_t pci_OF_getpropint(int node, char *prop);
+ 
+ static __inline pcireg_t
+ pci_OF_getpropint(node, prop)
+ 	int node;
+ 	char *prop;
+ {
+ 	int it, len;
+ 
+ 	if ((len = OF_getproplen(node, prop)) != sizeof(it) ||
+ 	    OF_getprop(node, prop, &it, sizeof(it)) == 0)
+ 		return (pcireg_t)~0;
+ 
+ 	return (pcireg_t)it;
+ 
+ }
+ 
+ void
+ pci_probe_bus(self)
+ 	struct device *self;
+ {
+ 	struct pci_softc *sc = (struct pci_softc *)self;
+ 	struct ofw_pci_register reg0;
+ 	struct pci_attach_args pa;
+ 	int node, bus, device, function, len;
+ 	pci_chipset_tag_t pc;
+ 
+ 	bus = sc->sc_bus;
+ 	pc = sc->sc_pc;
+ 	for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) {
+ 		len = OF_getproplen(node, "reg");
+ 		if (len < sizeof(reg0))
+ 			continue;
+ 		if (OF_getprop(node, "reg", (void *)&reg0, sizeof(reg0)) != len)
+ 			panic("pci_probe_bus: OF_getprop len botch");
+ 
+ 		device = OFW_PCI_PHYS_HI_DEVICE(reg0.phys_hi);
+ 		function = OFW_PCI_PHYS_HI_FUNCTION(reg0.phys_hi);
+ 
+ 		pa.pa_node = node;
+ 
+ 		pci_probe_function(self, &pa, pc, device, function);
+ 	}
+ }
+ #else
+ void
  pci_probe_bus(self)
  	struct device *self;
  {
*************** pci_probe_bus(self)
*** 144,152 ****
  
  	for (device = 0; device < maxndevs; device++) {
  		pcitag_t tag;
! 		pcireg_t id, class, intr, bhlcr, csr;
  		struct pci_attach_args pa;
- 		int pin;
  
  		tag = pci_make_tag(pc, bus, device, 0);
  		id = pci_conf_read(pc, tag, PCI_ID_REG);
--- 275,282 ----
  
  	for (device = 0; device < maxndevs; device++) {
  		pcitag_t tag;
! 		pcireg_t id, bhlcr;
  		struct pci_attach_args pa;
  
  		tag = pci_make_tag(pc, bus, device, 0);
  		id = pci_conf_read(pc, tag, PCI_ID_REG);
*************** pci_probe_bus(self)
*** 168,233 ****
  		else
  			nfunctions = 1;
  
! 		for (function = 0; function < nfunctions; function++) {
! 			tag = pci_make_tag(pc, bus, device, function);
! 			id = pci_conf_read(pc, tag, PCI_ID_REG);
! 			csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
! 			class = pci_conf_read(pc, tag, PCI_CLASS_REG);
! 			intr = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
! 
! 			/* Invalid vendor ID value? */
! 			if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
! 				continue;
! 			/* XXX Not invalid, but we've done this ~forever. */
! 			if (PCI_VENDOR(id) == 0)
! 				continue;
! 
! 			pa.pa_iot = iot;
! 			pa.pa_memt = memt;
! 			pa.pa_dmat = sc->sc_dmat;
! 			pa.pa_pc = pc;
! 			pa.pa_device = device;
! 			pa.pa_function = function;
! 			pa.pa_tag = tag;
! 			pa.pa_id = id;
! 			pa.pa_class = class;
! 
! 			/*
! 			 * Set up memory, I/O enable, and PCI command flags
! 			 * as appropriate.
! 			 */
! 			pa.pa_flags = sc->sc_flags;
! 			if ((csr & PCI_COMMAND_IO_ENABLE) == 0)
! 				pa.pa_flags &= ~PCI_FLAGS_IO_ENABLED;
! 			if ((csr & PCI_COMMAND_MEM_ENABLE) == 0)
! 				pa.pa_flags &= ~PCI_FLAGS_MEM_ENABLED;
! 
! 			if (bus == 0) {
! 				pa.pa_intrswiz = 0;
! 				pa.pa_intrtag = tag;
! 			} else {
! 				pa.pa_intrswiz = sc->sc_intrswiz + device;
! 				pa.pa_intrtag = sc->sc_intrtag;
! 			}
! 			pin = PCI_INTERRUPT_PIN(intr);
! 			if (pin == PCI_INTERRUPT_PIN_NONE) {
! 				/* no interrupt */
! 				pa.pa_intrpin = 0;
! 			} else {
! 				/*
! 				 * swizzle it based on the number of
! 				 * busses we're behind and our device
! 				 * number.
! 				 */
! 				pa.pa_intrpin =			/* XXX */
! 				    ((pin + pa.pa_intrswiz - 1) % 4) + 1;
! 			}
! 			pa.pa_intrline = PCI_INTERRUPT_LINE(intr);
! 
! 			config_found_sm(self, &pa, pciprint, pcisubmatch);
! 		}
  	}
  }
  
  void
  pciattach(parent, self, aux)
--- 298,308 ----
  		else
  			nfunctions = 1;
  
! 		for (function = 0; function < nfunctions; function++)
! 			pci_probe_function(self, &pa, pc, device, function);
  	}
  }
+ #endif
  
  void
  pciattach(parent, self, aux)
*************** pciattach(parent, self, aux)
*** 286,291 ****
--- 361,369 ----
  	sc->sc_intrswiz = pba->pba_intrswiz;
  	sc->sc_intrtag = pba->pba_intrtag;
  	sc->sc_flags = pba->pba_flags;
+ #ifdef __PCI_OFW_BINDING
+ 	sc->sc_node = pba->pba_node;
+ #endif
  	pci_probe_bus(self);
  }
  
Index: dev/pci/pcivar.h
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/pci/pcivar.h,v
retrieving revision 1.44
diff -p -p -r1.44 pcivar.h
*** dev/pci/pcivar.h	2001/02/12 09:14:53	1.44
--- dev/pci/pcivar.h	2001/02/12 09:29:53
*************** struct pcibus_attach_args {
*** 68,73 ****
--- 68,77 ----
  
  	int		pba_bus;	/* PCI bus number */
  
+ #ifdef __PCI_OFW_BINDING
+ 	int		pba_node;	/* OFW node */
+ #endif
+ 
  	/*
  	 * Interrupt swizzling information.  These fields
  	 * are only used by secondary busses.
*************** struct pci_attach_args {
*** 90,95 ****
--- 94,103 ----
  	u_int		pa_function;
  	pcitag_t	pa_tag;
  	pcireg_t	pa_id, pa_class;
+ 
+ #ifdef __PCI_OFW_BINDING
+ 	int		pa_node;	/* OFW node */
+ #endif
  
  	/*
  	 * Interrupt information.
Index: dev/pci/ppb.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/pci/ppb.c,v
retrieving revision 1.19
diff -p -p -r1.19 ppb.c
*** dev/pci/ppb.c	1999/11/04 19:04:04	1.19
--- dev/pci/ppb.c	2001/02/12 09:29:53
*************** ppbattach(parent, self, aux)
*** 117,122 ****
--- 117,125 ----
  	pba.pba_bus = PPB_BUSINFO_SECONDARY(busdata);
  	pba.pba_intrswiz = pa->pa_intrswiz;
  	pba.pba_intrtag = pa->pa_intrtag;
+ #ifdef __PCI_OFW_BINDING
+ 	pba.pba_node = pa->pa_node;
+ #endif
  
  	config_found(self, &pba, ppbprint);
  }
Index: dev/ofw/ofw_pci.h
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/ofw/ofw_pci.h,v
retrieving revision 1.2
diff -p -p -r1.2 ofw_pci.h
*** dev/ofw/ofw_pci.h	1999/05/05 08:09:34	1.2
--- dev/ofw/ofw_pci.h	2001/02/12 09:29:54
***************
*** 87,90 ****
--- 87,106 ----
  #define	OFW_PCI_PHYS_HI_SPACE_MEM32	0x02000000
  #define	OFW_PCI_PHYS_HI_SPACE_MEM64	0x03000000
  
+ #define OFW_PCI_PHYS_HI_DEVICE(hi) \
+ 	(((hi) & OFW_PCI_PHYS_HI_DEVICEMASK) >> OFW_PCI_PHYS_HI_DEVICESHIFT)
+ #define OFW_PCI_PHYS_HI_FUNCTION(hi) \
+ 	(((hi) & OFW_PCI_PHYS_HI_FUNCTIONMASK) >> OFW_PCI_PHYS_HI_FUNCTIONSHIFT)
+ 
+ /*
+  * This has the 3 32bit cell values, plus 2 more to make up a 64-bit size.
+  */
+ struct ofw_pci_register {
+ 	u_int32_t	phys_hi;
+ 	u_int32_t	phys_mid;
+ 	u_int32_t	phys_lo;
+ 	u_int32_t	size_hi;
+ 	u_int32_t	size_lo;
+ };
+ 
  #endif /* _DEV_OFW_OFW_PCI_H_ */