Date: Sun, 30 Dec 2012 22:00:20 +0000 (UTC) From: Gavin Atkinson <gavin@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r244873 - in projects/pciehp/sys: conf dev/pci Message-ID: <201212302200.qBUM0KWg089170@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
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@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) */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201212302200.qBUM0KWg089170>