svn commit: r244873 - in projects/pciehp/sys: conf dev/pci

Gavin Atkinson gavin at FreeBSD.org
Sun Dec 30 22:00:21 UTC 2012


Author: gavin
Date: Sun Dec 30 22:00:19 2012
New Revision: 244873
URL: http://svnweb.freebsd.org/changeset/base/244873

Log:
  Commit proof-of-concept first cut at PCIe HotPlug support.
  
  This code is nowhere near ready for prime time.  Almost every line of it
  is a hack, and almost all of it will need to be rewritten.  Regardless,
  this is sufficient to reliably allow hot insertion and removal of an
  ASMedia ASM1042 xhci(4) USB 3.0 ExpressCard into a Toshiba Tecra M5
  laptop.
  
  This is being committed in this very early state due to requests from
  other driver developers.

Added:
  projects/pciehp/sys/dev/pci/pcie_hp.c
Modified:
  projects/pciehp/sys/conf/files
  projects/pciehp/sys/dev/pci/pci.c
  projects/pciehp/sys/dev/pci/pci_private.h
  projects/pciehp/sys/dev/pci/pcivar.h

Modified: projects/pciehp/sys/conf/files
==============================================================================
--- projects/pciehp/sys/conf/files	Sun Dec 30 21:47:11 2012	(r244872)
+++ projects/pciehp/sys/conf/files	Sun Dec 30 22:00:19 2012	(r244873)
@@ -1810,6 +1810,7 @@ dev/pci/pci_pci.c		optional pci
 dev/pci/pci_subr.c		optional pci
 dev/pci/pci_user.c		optional pci
 dev/pci/pcib_if.m		standard
+dev/pci/pcie_hp.c		optional pci
 dev/pci/vga_pci.c		optional pci
 dev/pcn/if_pcn.c		optional pcn pci
 dev/pdq/if_fea.c		optional fea eisa

Modified: projects/pciehp/sys/dev/pci/pci.c
==============================================================================
--- projects/pciehp/sys/dev/pci/pci.c	Sun Dec 30 21:47:11 2012	(r244872)
+++ projects/pciehp/sys/dev/pci/pci.c	Sun Dec 30 22:00:19 2012	(r244873)
@@ -3202,6 +3202,7 @@ pci_add_children(device_t dev, int domai
 	int s, f, pcifunchigh;
 	uint8_t hdrtype;
 
+	printf("pci_add_children\n");
 	KASSERT(dinfo_size >= sizeof(struct pci_devinfo),
 	    ("dinfo_size too small"));
 	maxslots = PCIB_MAXSLOTS(pcib);
@@ -3228,6 +3229,7 @@ pci_add_children(device_t dev, int domai
 void
 pci_add_child(device_t bus, struct pci_devinfo *dinfo)
 {
+	printf("pci_add_child\n");
 	dinfo->cfg.dev = device_add_child(bus, NULL, -1);
 	device_set_ivars(dinfo->cfg.dev, dinfo);
 	resource_list_init(&dinfo->resources);
@@ -3279,6 +3281,9 @@ pci_attach_common(device_t dev)
 	if (!tag_valid)
 #endif
 		sc->sc_dma_tag = bus_get_dma_tag(dev);
+
+	pci_hotplug_init(dev);
+
 	return (0);
 }
 

Modified: projects/pciehp/sys/dev/pci/pci_private.h
==============================================================================
--- projects/pciehp/sys/dev/pci/pci_private.h	Sun Dec 30 21:47:11 2012	(r244872)
+++ projects/pciehp/sys/dev/pci/pci_private.h	Sun Dec 30 22:00:19 2012	(r244873)
@@ -126,4 +126,6 @@ void		pci_cfg_restore(device_t, struct p
  */
 void		pci_cfg_save(device_t, struct pci_devinfo *, int);
 
+void		pci_hotplug_init(device_t dev);
+
 #endif /* _PCI_PRIVATE_H_ */

Added: projects/pciehp/sys/dev/pci/pcie_hp.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/pciehp/sys/dev/pci/pcie_hp.c	Sun Dec 30 22:00:19 2012	(r244873)
@@ -0,0 +1,270 @@
+/*-
+ * Copyright (c) 2012, Gavin Atkinson <gavin at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_bus.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+#include <sys/endian.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_extern.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+#include <machine/stdarg.h>
+
+#if defined(__i386__) || defined(__amd64__) || defined(__powerpc__)
+#include <machine/intr_machdep.h>
+#endif
+
+#include <sys/pciio.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pci_private.h>
+
+#include "pcib_if.h"
+#include "pci_if.h"
+
+
+static struct resource_spec hotplug_res_spec_msi[] = {
+	{ SYS_RES_IRQ,		1,		RF_ACTIVE },
+	{ -1,			0,		0 }
+};
+
+
+static void
+pci_slot_status_print(device_t pcib)
+{
+	struct pci_devinfo *dinfo;
+	int pos;
+
+	dinfo = device_get_ivars(pcib);
+	pos = dinfo->cfg.pcie.pcie_location;
+	device_printf(pcib, "... LINK_STA=0x%b\n",
+	    pci_read_config(pcib, pos + PCIER_LINK_STA, 2),
+	    "\020"
+	    "\001<b0>"
+	    "\002<b1>"
+	    "\003<b3>"
+	    "\004<b3>"
+	    "\005<b4>"
+	    "\006<b5>"
+	    "\007<b6>"
+	    "\010<b7>"
+	    "\011<b8>"
+	    "\012<b9>"
+	    "\013Undef"
+	    "\014LinkTrain"
+	    "\015SlotClkConfig"
+	    "\016DLLLinkActive"
+	    "\017LinkBWManStat"
+	    "\020LinkAutonBwStat"
+	    );
+//	device_printf(pcib, "... SLOT_CAP=0x%x\n",
+//	    pci_read_config(pcib, pos+PCIER_SLOT_CAP, 4));
+	device_printf(pcib, "... SLOT_CTL=0x%b\n",
+	    pci_read_config(pcib, pos + PCIER_SLOT_CTL, 2),
+	    "\020"
+	    "\001AttnButtPressEn"
+	    "\002PowerFaultDetEn"
+	    "\003MRLSensChgEn"
+	    "\004PresDetChgEn"
+	    "\005CmdCompIntEn"
+	    "\006HotPlugIntEn"
+	    "\007AttnIndCtl1"
+	    "\010AttnIndCtl2"
+	    "\011PwrIndCtl1"
+	    "\012PwrIndCtl2"
+	    "\013PwrCtrlrCtl"
+	    "\014ElecMechIntCtl"
+	    "\015DLLStatChEn"
+	    "\016<b13>"
+	    "\017<b14>"
+	    "\020<b15>"
+	    );
+	device_printf(pcib, "... SLOT_STA=0x%b\n",
+	    pci_read_config(pcib, pos + PCIER_SLOT_STA, 2),
+	    "\020"
+	    "\001AttnButtPress"
+	    "\002PowerFaultDet"
+	    "\003MRLSensChg"
+	    "\004PresDetChg"
+	    "\005CmdComplete"
+	    "\006MRLSensState"
+	    "\007PresDetState"
+	    "\010ElecMechIntState"
+	    "\011DLLState"
+	    "\012<b9>"
+	    "\013<b10>"
+	    "\014<b11>"
+	    "\015<b12>"
+	    "\016<b13>"
+	    "\017<b14>"
+	    "\020<b15>"
+	    );
+}
+
+static void
+pci_hotplug_intr_task(void *arg, int npending)
+{
+	device_t dev = arg;
+	device_t pcib = device_get_parent(dev);
+	device_t *devlistp;
+	struct pci_devinfo *dinfo;
+	int busno, devcnt, domain, i, pos;
+	int linksta, slotsta;
+
+	dinfo = device_get_ivars(pcib);
+	pos = dinfo->cfg.pcie.pcie_location;
+
+//	mtx_lock(&dinfo->cfg.hp.hp_mtx);
+
+	linksta = pci_read_config(pcib, pos + PCIER_LINK_STA, 2);
+	slotsta = pci_read_config(pcib, pos + PCIER_SLOT_STA, 2);
+	pci_slot_status_print(pcib);
+/* XXXGA: HACK AHEAD */
+	if (slotsta & PCIEM_SLOT_STA_DLLSC) {
+		if ((linksta & PCIEM_LINK_STA_DL_ACTIVE) && dinfo->cfg.hp.hp_cnt == 0) {
+			dinfo->cfg.hp.hp_cnt=1;
+			/* delay really for DLLSC */
+			DELAY(100000);	/* section 6.7.3.3 */
+			printf("Hotplug: Attaching children\n");
+			mtx_lock(&Giant);
+			domain = pcib_get_domain(dev);
+			busno = pcib_get_bus(dev);
+			pci_add_children(dev, domain, busno,
+			    sizeof(struct pci_devinfo));
+			(void)bus_generic_attach(dev);
+			mtx_unlock(&Giant);
+		} else if (((linksta & PCIEM_LINK_STA_DL_ACTIVE) == 0) && dinfo->cfg.hp.hp_cnt == 1) {
+			printf("Hotplug: Detaching children\n");
+			mtx_lock(&Giant);
+			/* XXXGA error checking */
+			(void)bus_generic_detach(dev);
+			device_get_children(dev, &devlistp, &devcnt);
+			for (i = 0; i < devcnt; i++)
+				device_delete_child(dev, devlistp[i]);
+			free(devlistp, M_TEMP);
+			mtx_unlock(&Giant);
+			dinfo->cfg.hp.hp_cnt=0;
+		} else
+			printf("Hotplug: Ignoring\n");
+	}
+//	mtx_unlock(&dinfo->cfg.hp.hp_mtx);
+}
+
+static int
+pci_hotplug_intr(void *arg)
+{
+	device_t dev = arg;
+	device_t pcib = device_get_parent(dev);
+	struct pci_devinfo *dinfo;
+
+	device_printf(dev, "Received interrupt!\n");
+//	pci_slot_status_print(pcib);
+	dinfo = device_get_ivars(pcib);
+	taskqueue_enqueue_fast(taskqueue_fast, &dinfo->cfg.hp.hp_inttask);
+
+	return (FILTER_HANDLED);
+}
+
+void
+pci_hotplug_init(device_t dev)
+{
+	device_t pcib = device_get_parent(dev);
+	struct pci_devinfo *dinfo;
+	int error, flags, irq, msic, pos;
+
+	dinfo = device_get_ivars(pcib);
+	pos = dinfo->cfg.pcie.pcie_location;
+	device_printf(dev, "dinfo=%p, pos=0x%x\n", dinfo, pos);
+	if (pos != 0) {
+		device_printf(dev, "Hotplug?\n");
+		flags = pci_read_config(pcib, pos + PCIER_FLAGS, 2);
+		device_printf(dev, "... FLAGS = 0x%x\n", flags);
+		if (flags & PCIEM_FLAGS_SLOT) {
+			mtx_init(&dinfo->cfg.hp.hp_mtx,
+			    device_get_nameunit(dev), "pciehp", MTX_DEF);
+			device_printf(dev, "... is slot!\n");
+/* XXX GAV: Check for SLOT_CAP_HPC here */
+			pci_slot_status_print(pcib);
+			irq = (flags & PCIEM_FLAGS_IRQ) >> 9;
+			device_printf(dev, "IRQ = %d\n", irq);
+
+			device_printf(dev, "MSI count self %d parent %d\n", pci_msi_count(dev), pci_msi_count(pcib));
+			device_printf(dev, "MSI-X count self %d parent %d\n", pci_msix_count(dev), pci_msix_count(pcib));
+
+			msic = pci_msi_count(pcib);
+			if (msic == 1) {
+				if (pci_alloc_msi(pcib, &msic) == 0) {
+					if (msic == 1) {
+						device_printf(dev, "Using %d MSI messages\n",
+						    msic);
+						dinfo->cfg.pcie.pcie_irq_spec = hotplug_res_spec_msi;
+					} else {
+						device_printf(dev, "Error: %d MSI messages\n",
+						    msic);
+						pci_release_msi(dev);
+					}
+				}
+			}
+/* XXX GAV: Am currently ignoring "irq" */
+			error = bus_alloc_resources(pcib, dinfo->cfg.pcie.pcie_irq_spec, dinfo->cfg.pcie.pcie_res_irq);
+			if (error) {
+				device_printf(dev, "couldn't allocate IRQ resources, %d\n", error);
+			} else {
+				error = bus_setup_intr(pcib, dinfo->cfg.pcie.pcie_res_irq[0],
+				    INTR_TYPE_AV | INTR_MPSAFE, pci_hotplug_intr, NULL, dev,
+				    &dinfo->cfg.pcie.pcie_intrhand[0]);
+				if (error) {
+					device_printf(dev, "couldn't set up IRQ resources, %d\n", error);
+				}
+			}
+			TASK_INIT(&dinfo->cfg.hp.hp_inttask, 0, pci_hotplug_intr_task, dev);
+			/* XXXGA 6.7.3.1 don't enable things the slot doesn't support */
+			flags = pci_read_config(pcib, pos + PCIER_SLOT_CTL, 2);
+			flags |= PCIEM_SLOT_CTL_PDCE | PCIEM_SLOT_CTL_MRLSCE | PCIEM_SLOT_CTL_HPIE | PCIEM_SLOT_CTL_DLLSCE;
+			pci_write_config(pcib, pos + PCIER_SLOT_CTL, flags, 2);
+			device_printf(dev, "Enabled interrupts\n");
+			pci_slot_status_print(pcib);
+		}
+	}
+}
+

Modified: projects/pciehp/sys/dev/pci/pcivar.h
==============================================================================
--- projects/pciehp/sys/dev/pci/pcivar.h	Sun Dec 30 21:47:11 2012	(r244872)
+++ projects/pciehp/sys/dev/pci/pcivar.h	Sun Dec 30 22:00:19 2012	(r244873)
@@ -30,7 +30,10 @@
 #ifndef _PCIVAR_H_
 #define	_PCIVAR_H_
 
+#include <sys/lock.h>
+#include <sys/mutex.h>
 #include <sys/queue.h>
+#include <sys/taskqueue.h>
 
 /* some PCI bus constants */
 #define	PCI_MAXMAPS_0	6	/* max. no. of memory/port maps */
@@ -123,6 +126,8 @@ struct pcicfg_ht {
     uint64_t	ht_msiaddr;	/* MSI mapping base address */
 };
 
+#define PCIE_MSI_MESSAGES	2
+
 /* Interesting values for PCI-express */
 struct pcicfg_pcie {
     uint8_t	pcie_location;	/* Offset of PCI-e capability registers. */
@@ -135,6 +140,9 @@ struct pcicfg_pcie {
     uint16_t	pcie_device_ctl2; /* Second device control register. */
     uint16_t	pcie_link_ctl2;	/* Second link control register. */
     uint16_t	pcie_slot_ctl2;	/* Second slot control register. */
+    struct resource_spec *pcie_irq_spec;
+    struct resource *pcie_res_irq[PCIE_MSI_MESSAGES];
+    void	*pcie_intrhand[PCIE_MSI_MESSAGES];
 };
 
 struct pcicfg_pcix {
@@ -142,6 +150,13 @@ struct pcicfg_pcix {
     uint8_t	pcix_location;	/* Offset of PCI-X capability registers. */
 };
 
+/* Interesting values for PCIe Hotplug */
+struct pcicfg_hp {
+    struct mtx hp_mtx;
+    struct task	hp_inttask;
+    int		hp_cnt;
+};
+
 /* config header information common to all header types */
 typedef struct pcicfg {
     struct device *dev;		/* device which owns this */
@@ -185,6 +200,7 @@ typedef struct pcicfg {
     struct pcicfg_ht ht;	/* HyperTransport */
     struct pcicfg_pcie pcie;	/* PCI Express */
     struct pcicfg_pcix pcix;	/* PCI-X */
+    struct pcicfg_hp hp;	/* Hotplug */
 } pcicfgregs;
 
 /* additional type 1 device config header information (PCI to PCI bridge) */


More information about the svn-src-projects mailing list