Module Name:    src
Committed By:   msaitoh
Date:           Tue Apr 18 05:21:34 UTC 2017

Modified Files:
        src/sys/dev/pci: ppb.c ppbvar.h

Log Message:
 Enable PCIe's interrupt as much as possilbe in ppb(4) to detect and count
status change event. HotPlug function itself have not implemented yet.

 - Interrupt and each event are counted by evcnt(9). Example:

   ppb0 Interrupt                                                0    0 intr
   ppb0 Attention Button Pressed                                 0    0 misc
   ppb0 Power Fault Detected                                     0    0 misc
   ppb0 MRL Sensor Changed                                       0    0 misc
   ppb0 Presence Detect Changed                                  0    0 misc
   ppb0 Command Completed                                        0    0 misc
   ppb0 Data Link Layer State Changed                            0    0 misc

 - Print message if ppb_printevent is not zero. The default vaule is 0.
   The output messages:

   Attention Button Pressed
   Power Fault Detected
   MRL Sensor Changed
   Presence Detect Changed
   Command Completed
   Data Link Layer State Changed

 - Remove workaround code to disable interrupt (ppb.c rev. 1.35).

 Tested with Dell Latitude 2120 without if_bge.c rev. 1.304's workaround.
dmesg when bge's device timeout occured:

   ppb3: Presence Detect Changed
   ppb3: Data Link Layer State Changed
   ppb3: Presence Detect Changed

vmstat -e |grep ppb

   ppb3 Interrupt                                             2    0 intr
   ppb3 Presence Detect Changed                               2    0 misc
   ppb3 Data Link Layer State Changed                         1    0 misc


To generate a diff of this commit:
cvs rdiff -u -r1.56 -r1.57 src/sys/dev/pci/ppb.c
cvs rdiff -u -r1.1 -r1.2 src/sys/dev/pci/ppbvar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/pci/ppb.c
diff -u src/sys/dev/pci/ppb.c:1.56 src/sys/dev/pci/ppb.c:1.57
--- src/sys/dev/pci/ppb.c:1.56	Wed Apr  5 03:51:36 2017
+++ src/sys/dev/pci/ppb.c	Tue Apr 18 05:21:34 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ppb.c,v 1.56 2017/04/05 03:51:36 msaitoh Exp $	*/
+/*	$NetBSD: ppb.c,v 1.57 2017/04/18 05:21:34 msaitoh Exp $	*/
 
 /*
  * Copyright (c) 1996, 1998 Christopher G. Demetriou.  All rights reserved.
@@ -31,12 +31,13 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ppb.c,v 1.56 2017/04/05 03:51:36 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ppb.c,v 1.57 2017/04/18 05:21:34 msaitoh Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/device.h>
+#include <sys/evcnt.h>
 
 #include <dev/pci/pcireg.h>
 #include <dev/pci/pcivar.h>
@@ -52,8 +53,19 @@ static const char pcie_linkspeed_strings
 	"1.25", "2.5", "5.0", "8.0",
 };
 
-static bool		ppb_resume(device_t, const pmf_qual_t *);
-static bool		ppb_suspend(device_t, const pmf_qual_t *);
+int	ppb_printevent = 0; /* Print event type if the value is not 0 */
+
+static int	ppbmatch(device_t, cfdata_t, void *);
+static void	ppbattach(device_t, device_t, void *);
+static int	ppbdetach(device_t, int);
+static void	ppbchilddet(device_t, device_t);
+static int	ppb_intr(void *);
+static bool	ppb_resume(device_t, const pmf_qual_t *);
+static bool	ppb_suspend(device_t, const pmf_qual_t *);
+
+CFATTACH_DECL3_NEW(ppb, sizeof(struct ppb_softc),
+    ppbmatch, ppbattach, ppbdetach, NULL, NULL, ppbchilddet,
+    DVF_DETACH_SHUTDOWN);
 
 static int
 ppbmatch(device_t parent, cfdata_t match, void *aux)
@@ -91,7 +103,7 @@ ppbmatch(device_t parent, cfdata_t match
 }
 
 static void
-ppb_fix_pcie(device_t self)
+ppb_print_pcie(device_t self)
 {
 	struct ppb_softc *sc = device_private(self);
 	pcireg_t reg;
@@ -181,14 +193,6 @@ ppb_fix_pcie(device_t self)
 		aprint_normal(">\n");
 		break;
 	}
-
-	reg = pci_conf_read(sc->sc_pc, sc->sc_tag, off + PCIE_SLCSR);
-	if (reg & PCIE_SLCSR_NOTIFY_MASK) {
-		aprint_debug_dev(self, "disabling notification events\n");
-		reg &= ~PCIE_SLCSR_NOTIFY_MASK;
-		pci_conf_write(sc->sc_pc, sc->sc_tag,
-		    off + PCIE_SLCSR, reg);
-	}
 }
 
 static void
@@ -198,7 +202,9 @@ ppbattach(device_t parent, device_t self
 	struct pci_attach_args *pa = aux;
 	pci_chipset_tag_t pc = pa->pa_pc;
 	struct pcibus_attach_args pba;
-	pcireg_t busdata;
+	char const *intrstr;
+	char intrbuf[PCI_INTRSTR_LEN];
+	pcireg_t busdata, reg;
 
 	pci_aprint_devinfo(pa, NULL);
 
@@ -213,7 +219,7 @@ ppbattach(device_t parent, device_t self
 		return;
 	}
 
-	ppb_fix_pcie(self);
+	ppb_print_pcie(self);
 
 #if 0
 	/*
@@ -227,6 +233,78 @@ ppbattach(device_t parent, device_t self
 		    pa->pa_bus, PPB_BUSINFO_PRIMARY(busdata));
 #endif
 
+	/* Check for PCI Express capabilities and setup hotplug support. */
+	if (pci_get_capability(pc, pa->pa_tag, PCI_CAP_PCIEXPRESS,
+	    &sc->sc_pciecapoff, &reg) && (reg & PCIE_XCAP_SI)) {
+#if 0
+		/*
+		 * XXX Initialize workqueue or something else for
+		 * HotPlug support.
+		 */
+#endif
+
+		if (pci_intr_alloc(pa, &sc->sc_pihp, NULL, 0) == 0)
+			sc->sc_intrhand = pci_intr_establish_xname(pc,
+			    sc->sc_pihp[0], IPL_BIO, ppb_intr, sc,
+			    device_xname(sc->sc_dev));
+
+		if (sc->sc_intrhand) {
+			pcireg_t slcap, slcsr, val;
+
+			intrstr = pci_intr_string(pc, sc->sc_pihp[0], intrbuf,
+			    sizeof(intrbuf));
+			aprint_normal_dev(self, "%s\n", intrstr);
+
+			/* Clear any pending events */
+			slcsr = pci_conf_read(pc, pa->pa_tag,
+			    sc->sc_pciecapoff + PCIE_SLCSR);
+			pci_conf_write(pc, pa->pa_tag,
+			    sc->sc_pciecapoff + PCIE_SLCSR, slcsr);
+
+			/* Enable interrupt. */
+			slcap = pci_conf_read(pc, pa->pa_tag,
+			    sc->sc_pciecapoff + PCIE_SLCAP);
+			val = 0;
+			if (slcap & PCIE_SLCAP_ABP)
+				val |= PCIE_SLCSR_ABE;
+			if (slcap & PCIE_SLCAP_PCP)
+				val |= PCIE_SLCSR_PFE;
+			if (slcap & PCIE_SLCAP_MSP)
+				val |= PCIE_SLCSR_MSE;
+			if ((slcap & PCIE_SLCAP_NCCS) == 0)
+				val |= PCIE_SLCSR_CCE;
+			/* Attention indicator off by default */
+			if (slcap & PCIE_SLCAP_AIP) {
+				val |= __SHIFTIN(PCIE_SLCSR_IND_OFF,
+				    PCIE_SLCSR_AIC);
+			}
+			/* Power indicator */
+			if (slcap & PCIE_SLCAP_PIP) {
+				/*
+				 * Indicator off:
+				 *  a) card not present
+				 *  b) power fault
+				 *  c) MRL sensor off
+				 */
+				if (((slcsr & PCIE_SLCSR_PDS) == 0)
+				    || ((slcsr & PCIE_SLCSR_PFD) != 0)
+				    || (((slcap & PCIE_SLCAP_MSP) != 0)
+					&& ((slcsr & PCIE_SLCSR_MS) != 0)))
+					val |= __SHIFTIN(PCIE_SLCSR_IND_OFF,
+					    PCIE_SLCSR_PIC);
+				else
+					val |= __SHIFTIN(PCIE_SLCSR_IND_ON,
+					    PCIE_SLCSR_PIC);
+			}
+
+			val |= PCIE_SLCSR_DLLSCE | PCIE_SLCSR_HPE
+			    | PCIE_SLCSR_PDE;
+			slcsr = val;
+			pci_conf_write(pc, pa->pa_tag,
+			    sc->sc_pciecapoff + PCIE_SLCSR, slcsr);
+		}
+	}
+
 	if (!pmf_device_register(self, ppb_suspend, ppb_resume))
 		aprint_error_dev(self, "couldn't establish power handler\n");
 
@@ -248,14 +326,40 @@ ppbattach(device_t parent, device_t self
 	pba.pba_intrswiz = pa->pa_intrswiz;
 	pba.pba_intrtag = pa->pa_intrtag;
 
+	/* Attach event counters */
+	evcnt_attach_dynamic(&sc->sc_ev_intr, EVCNT_TYPE_INTR, NULL,
+	    device_xname(sc->sc_dev), "Interrupt");
+	evcnt_attach_dynamic(&sc->sc_ev_abp, EVCNT_TYPE_MISC, NULL,
+	    device_xname(sc->sc_dev), "Attention Button Pressed");
+	evcnt_attach_dynamic(&sc->sc_ev_pfd, EVCNT_TYPE_MISC, NULL,
+	    device_xname(sc->sc_dev), "Power Fault Detected");
+	evcnt_attach_dynamic(&sc->sc_ev_msc, EVCNT_TYPE_MISC, NULL,
+	    device_xname(sc->sc_dev), "MRL Sensor Changed");
+	evcnt_attach_dynamic(&sc->sc_ev_pdc, EVCNT_TYPE_MISC, NULL,
+	    device_xname(sc->sc_dev), "Presence Detect Changed");
+	evcnt_attach_dynamic(&sc->sc_ev_cc, EVCNT_TYPE_MISC, NULL,
+	    device_xname(sc->sc_dev), "Command Completed");
+	evcnt_attach_dynamic(&sc->sc_ev_lacs, EVCNT_TYPE_MISC, NULL,
+	    device_xname(sc->sc_dev), "Data Link Layer State Changed");
+
 	config_found_ia(self, "pcibus", &pba, pcibusprint);
 }
 
 static int
 ppbdetach(device_t self, int flags)
 {
+	struct ppb_softc *sc = device_private(self);
 	int rc;
 
+	/* Detach event counters */
+	evcnt_detach(&sc->sc_ev_intr);
+	evcnt_detach(&sc->sc_ev_abp);
+	evcnt_detach(&sc->sc_ev_pfd);
+	evcnt_detach(&sc->sc_ev_msc);
+	evcnt_detach(&sc->sc_ev_pdc);
+	evcnt_detach(&sc->sc_ev_cc);
+	evcnt_detach(&sc->sc_ev_lacs);
+
 	if ((rc = config_detach_children(self, flags)) != 0)
 		return rc;
 	pmf_device_deregister(self);
@@ -276,8 +380,6 @@ ppb_resume(device_t dv, const pmf_qual_t
 			    sc->sc_pciconfext[(off - 0x40)/4]);
 	}
 
-	ppb_fix_pcie(dv);
-
 	return true;
 }
 
@@ -300,6 +402,67 @@ ppbchilddet(device_t self, device_t chil
 	/* we keep no references to child devices, so do nothing */
 }
 
-CFATTACH_DECL3_NEW(ppb, sizeof(struct ppb_softc),
-    ppbmatch, ppbattach, ppbdetach, NULL, NULL, ppbchilddet,
-    DVF_DETACH_SHUTDOWN);
+static int
+ppb_intr(void *arg)
+{
+	struct ppb_softc *sc = arg;
+	device_t dev = sc->sc_dev;
+	pcireg_t reg;
+
+	sc->sc_ev_intr.ev_count++;
+	reg = pci_conf_read(sc->sc_pc, sc->sc_tag,
+	    sc->sc_pciecapoff + PCIE_SLCSR);
+
+	/* Clear interrupts. */
+	pci_conf_write(sc->sc_pc, sc->sc_tag,
+	    sc->sc_pciecapoff + PCIE_SLCSR, reg);
+
+	/* Attention Button Pressed */
+	if (reg & PCIE_SLCSR_ABP) {
+		sc->sc_ev_abp.ev_count++;
+		if (ppb_printevent)
+			device_printf(dev, "Attention Button Pressed\n");
+	}
+
+	/* Power Fault Detected */
+	if (reg & PCIE_SLCSR_PFD) {
+		sc->sc_ev_pfd.ev_count++;
+		if (ppb_printevent)
+			device_printf(dev, "Power Fault Detected\n");
+	}
+
+	/* MRL Sensor Changed */
+	if (reg & PCIE_SLCSR_MSC) {
+		sc->sc_ev_msc.ev_count++;
+		if (ppb_printevent)
+			device_printf(dev, "MRL Sensor Changed\n");
+	}
+
+	/* Presence Detect Changed */
+	if (reg & PCIE_SLCSR_PDC) {
+		sc->sc_ev_pdc.ev_count++;
+		if (ppb_printevent)
+			device_printf(dev, "Presence Detect Changed\n");
+		if (reg & PCIE_SLCSR_PDS) {
+			/* XXX Insert */
+		} else {
+			/* XXX Remove */
+		}
+	}
+
+	/* Command Completed */
+	if (reg & PCIE_SLCSR_CC) {
+		sc->sc_ev_cc.ev_count++;
+		if (ppb_printevent)
+			device_printf(dev, "Command Completed\n");
+	}
+
+	/* Data Link Layer State Changed */
+	if (reg & PCIE_SLCSR_LACS) {
+		sc->sc_ev_lacs.ev_count++;
+		if (ppb_printevent)
+			device_printf(dev, "Data Link Layer State Changed\n");
+	}
+
+	return 0;
+}

Index: src/sys/dev/pci/ppbvar.h
diff -u src/sys/dev/pci/ppbvar.h:1.1 src/sys/dev/pci/ppbvar.h:1.2
--- src/sys/dev/pci/ppbvar.h:1.1	Wed Apr  5 03:51:36 2017
+++ src/sys/dev/pci/ppbvar.h	Tue Apr 18 05:21:34 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ppbvar.h,v 1.1 2017/04/05 03:51:36 msaitoh Exp $	*/
+/*	$NetBSD: ppbvar.h,v 1.2 2017/04/18 05:21:34 msaitoh Exp $	*/
 
 /*
  * Copyright (c) 1996, 1998 Christopher G. Demetriou.  All rights reserved.
@@ -31,12 +31,13 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ppbvar.h,v 1.1 2017/04/05 03:51:36 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ppbvar.h,v 1.2 2017/04/18 05:21:34 msaitoh Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/device.h>
+#include <sys/evcnt.h>
 
 #include <dev/pci/pcireg.h>
 #include <dev/pci/pcivar.h>
@@ -47,6 +48,16 @@ struct ppb_softc {
 	device_t sc_dev;		/* generic device glue */
 	pci_chipset_tag_t sc_pc;	/* our PCI chipset... */
 	pcitag_t sc_tag;		/* ...and tag. */
+	pci_intr_handle_t *sc_pihp;
+	void *sc_intrhand;
+	int sc_pciecapoff;
+	struct evcnt sc_ev_intr;	/* interrupts */
+	struct evcnt sc_ev_abp;		/*  Attention Button Pressed */
+	struct evcnt sc_ev_pfd;		/*  Power Fault Detected */
+	struct evcnt sc_ev_msc;		/*  MRL Sensor Changed */
+	struct evcnt sc_ev_pdc;		/*  Presence Detect Changed */
+	struct evcnt sc_ev_cc;		/*  Command Completed */
+	struct evcnt sc_ev_lacs;	/*  Data Link Layer State Changed */
 
 	pcireg_t sc_pciconfext[48];
 };

Reply via email to