summaryrefslogtreecommitdiff
path: root/usr/src/uts/common
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common')
-rw-r--r--usr/src/uts/common/Makefile.files10
-rw-r--r--usr/src/uts/common/Makefile.rules11
-rw-r--r--usr/src/uts/common/io/busra.c644
-rw-r--r--usr/src/uts/common/io/devinfo.c144
-rw-r--r--usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c1982
-rw-r--r--usr/src/uts/common/io/hotplug/pcihp/pcihp.c22
-rw-r--r--usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c2656
-rw-r--r--usr/src/uts/common/io/pciex/hotplug/pcie_hp.c1300
-rw-r--r--usr/src/uts/common/io/pciex/hotplug/pciehpc.c2285
-rw-r--r--usr/src/uts/common/io/pciex/hotplug/pcishpc.c2645
-rw-r--r--usr/src/uts/common/io/pciex/pcie.c463
-rw-r--r--usr/src/uts/common/io/pciex/pcieb.c409
-rw-r--r--usr/src/uts/common/io/pciex/pcieb.h28
-rw-r--r--usr/src/uts/common/os/ddi_hp_impl.c1139
-rw-r--r--usr/src/uts/common/os/ddi_hp_ndi.c405
-rw-r--r--usr/src/uts/common/os/devcfg.c25
-rw-r--r--usr/src/uts/common/os/modctl.c80
-rw-r--r--usr/src/uts/common/sys/Makefile2
-rw-r--r--usr/src/uts/common/sys/autoconf.h10
-rw-r--r--usr/src/uts/common/sys/ddi_hp.h126
-rw-r--r--usr/src/uts/common/sys/ddi_hp_impl.h160
-rw-r--r--usr/src/uts/common/sys/ddi_impldefs.h16
-rw-r--r--usr/src/uts/common/sys/devinfo_impl.h25
-rw-r--r--usr/src/uts/common/sys/devops.h15
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pcicfg.h17
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pcie_hp.h332
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pciehpc.h37
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h233
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pcihp.h4
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pcishpc.h24
-rwxr-xr-xusr/src/uts/common/sys/hotplug/pci/pcishpc_regs.h60
-rw-r--r--usr/src/uts/common/sys/modctl.h10
-rw-r--r--usr/src/uts/common/sys/pci.h100
-rw-r--r--usr/src/uts/common/sys/pci_impl.h19
-rw-r--r--usr/src/uts/common/sys/pcie.h75
-rw-r--r--usr/src/uts/common/sys/pcie_impl.h87
-rw-r--r--usr/src/uts/common/sys/sunndi.h35
37 files changed, 10155 insertions, 5480 deletions
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index 8ded90867e..3aa180f583 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -132,6 +132,8 @@ GENUNIX_OBJS += \
cyclic.o \
ddi.o \
ddifm.o \
+ ddi_hp_impl.o \
+ ddi_hp_ndi.o \
ddi_intr.o \
ddi_intr_impl.o \
ddi_intr_irm.o \
@@ -978,11 +980,9 @@ VUIDPS2_OBJS += vuidmice.o vuidps2.o
HPCSVC_OBJS += hpcsvc.o
-PCIHPNEXUS_OBJS += pcihp.o
-
-PCIEHPCNEXUS_OBJS += pciehpc.o
+PCIE_MISC_OBJS += pcie.o pcie_fault.o pcie_hp.o pciehpc.o pcishpc.o pcie_pwr.o
-PCISHPC_OBJS += pcishpc.o
+PCIHPNEXUS_OBJS += pcihp.o
OPENEEPR_OBJS += openprom.o
@@ -1813,8 +1813,6 @@ NATIVE_INC_PATH += $(INC_PATH) $(CCYFLAG)$(UTSBASE)/common
AS_INC_PATH += $(INC_PATH) -I$(UTSBASE)/common
INCLUDE_PATH += $(INC_PATH) $(CCYFLAG)$(UTSBASE)/common
-#
-PCIE_MISC_OBJS += pcie.o pcie_fault.o pcie_pwr.o
PCIEB_OBJS += pcieb.o
# Chelsio N110 10G NIC driver module
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules
index e45169a005..ebb286f385 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -716,11 +716,7 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hotplug/hpcsvc/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
-$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hotplug/pciehpc/%.c
- $(COMPILE.c) -o $@ $<
- $(CTFCONVERT_O)
-
-$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hotplug/pcishpc/%.c
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/pciex/hotplug/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1993,10 +1989,7 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/pciex/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/hpcsvc/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
-$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pciehpc/%.c
- @($(LHEAD) $(LINT.c) $< $(LTAIL))
-
-$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pcishpc/%.c
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/pciex/hotplug/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pcihp/%.c
diff --git a/usr/src/uts/common/io/busra.c b/usr/src/uts/common/io/busra.c
index 4a059560f7..8a72954676 100644
--- a/usr/src/uts/common/io/busra.c
+++ b/usr/src/uts/common/io/busra.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -127,6 +127,15 @@ static int isnot_pow2(uint64_t value);
static int claim_pci_busnum(dev_info_t *dip, void *arg);
static int ra_map_exist(dev_info_t *dip, char *type);
+static int pci_get_available_prop(dev_info_t *dip, uint64_t base,
+ uint64_t len, char *busra_type);
+static int pci_put_available_prop(dev_info_t *dip, uint64_t base,
+ uint64_t len, char *busra_type);
+static uint32_t pci_type_ra2pci(char *type);
+static boolean_t is_pcie_fabric(dev_info_t *dip);
+
+#define PCI_ADDR_TYPE_MASK (PCI_REG_ADDR_M | PCI_REG_PF_M)
+#define PCI_ADDR_TYPE_INVAL 0xffffffff
#define RA_INSERT(prev, el) \
el->ra_next = *prev; \
@@ -151,7 +160,7 @@ _init()
int ret;
mutex_init(&ra_lock, NULL, MUTEX_DRIVER,
- (void *)(intptr_t)__ipltospl(SPL7 - 1));
+ (void *)(intptr_t)__ipltospl(SPL7 - 1));
if ((ret = mod_install(&modlinkage)) != 0) {
mutex_destroy(&ra_lock);
}
@@ -206,9 +215,9 @@ ndi_ra_map_setup(dev_info_t *dip, char *type)
if (dipmap == NULL) {
if (backtype == NULL) {
typemapp = (struct ra_type_map *)
- kmem_zalloc(sizeof (*typemapp), KM_SLEEP);
+ kmem_zalloc(sizeof (*typemapp), KM_SLEEP);
typemapp->type = (char *)kmem_zalloc(strlen(type) + 1,
- KM_SLEEP);
+ KM_SLEEP);
(void) strcpy(typemapp->type, type);
RA_INSERT(&ra_map_list_head, typemapp);
} else {
@@ -217,7 +226,7 @@ ndi_ra_map_setup(dev_info_t *dip, char *type)
if (backdip == NULL) {
/* allocate and insert in list of dips for this type */
dipmap = (struct ra_dip_type *)
- kmem_zalloc(sizeof (*dipmap), KM_SLEEP);
+ kmem_zalloc(sizeof (*dipmap), KM_SLEEP);
dipmap->ra_dip = dip;
RA_INSERT(&typemapp->ra_dip_list, dipmap);
}
@@ -413,7 +422,7 @@ ndi_ra_free(dev_info_t *dip, uint64_t base, uint64_t len, char *type,
} else if (base < mapp->ra_base) {
/* somewhere in between so just an insert */
newmap = (struct ra_resource *)
- kmem_zalloc(sizeof (*newmap), KM_SLEEP);
+ kmem_zalloc(sizeof (*newmap), KM_SLEEP);
newmap->ra_base = base;
newmap->ra_len = len;
RA_INSERT(backp, newmap);
@@ -423,13 +432,20 @@ ndi_ra_free(dev_info_t *dip, uint64_t base, uint64_t len, char *type,
if (mapp == NULL) {
/* stick on end */
newmap = (struct ra_resource *)
- kmem_zalloc(sizeof (*newmap), KM_SLEEP);
+ kmem_zalloc(sizeof (*newmap), KM_SLEEP);
newmap->ra_base = base;
newmap->ra_len = len;
RA_INSERT(backp, newmap);
}
mutex_exit(&ra_lock);
+
+ /*
+ * Update dip's "available" property, adding this piece of
+ * resource to the pool.
+ */
+ (void) pci_put_available_prop(dip, base, len, type);
+done:
return (NDI_SUCCESS);
overlap:
@@ -485,10 +501,9 @@ adjust_link(struct ra_resource **backp, struct ra_resource *mapp,
} else {
/* in the middle */
newmap = (struct ra_resource *)
- kmem_zalloc(sizeof (*newmap), KM_SLEEP);
+ kmem_zalloc(sizeof (*newmap), KM_SLEEP);
newmap->ra_base = base + len;
- newmap->ra_len = mapp->ra_len -
- (len + newlen);
+ newmap->ra_len = mapp->ra_len - (len + newlen);
mapp->ra_len = newlen;
RA_INSERT(&(mapp->ra_next), newmap);
}
@@ -523,7 +538,7 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
if (req->ra_flags & NDI_RA_ALIGN_SIZE) {
if (isnot_pow2(req->ra_len)) {
DEBUGPRT(CE_WARN, "ndi_ra_alloc: bad length(pow2) 0x%"
- PRIx64, req->ra_len);
+ PRIx64, req->ra_len);
*retbasep = 0;
*retlenp = 0;
return (NDI_FAILURE);
@@ -543,7 +558,7 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
}
DEBUGPRT(CE_CONT, "ndi_ra_alloc: mapp = %p len=%" PRIx64 ", mask=%"
- PRIx64 "\n", (void *)mapp, len, mask);
+ PRIx64 "\n", (void *)mapp, len, mask);
backp = &(dipmap->ra_rangeset);
backlargestp = NULL;
@@ -560,11 +575,10 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
if ((upper == 0) || (upper < req->ra_boundlen))
upper = ~(uint64_t)0;
DEBUGPRT(CE_CONT, "ndi_ra_alloc: ra_len = %" PRIx64 ", len = %"
- PRIx64 " ra_base=%" PRIx64 ", mask=%" PRIx64
- "\n", mapp->ra_len, len, mapp->ra_base, mask);
- for (; mapp != NULL &&
- (mapp->ra_base + mapp->ra_len) < lower;
- backp = &(mapp->ra_next), mapp = mapp->ra_next) {
+ PRIx64 " ra_base=%" PRIx64 ", mask=%" PRIx64
+ "\n", mapp->ra_len, len, mapp->ra_base, mask);
+ for (; mapp != NULL && (mapp->ra_base + mapp->ra_len) < lower;
+ backp = &(mapp->ra_next), mapp = mapp->ra_next) {
if (((mapp->ra_len + mapp->ra_base) == 0) ||
((mapp->ra_len + mapp->ra_base) < mapp->ra_len))
/*
@@ -583,9 +597,9 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
if (!(req->ra_flags & NDI_RA_ALLOC_SPECIFIED)) {
/* first fit - not user specified */
DEBUGPRT(CE_CONT, "ndi_ra_alloc(unspecified request)"
- "lower=%" PRIx64 ", upper=%" PRIx64 "\n", lower, upper);
+ "lower=%" PRIx64 ", upper=%" PRIx64 "\n", lower, upper);
for (; mapp != NULL && mapp->ra_base <= upper;
- backp = &(mapp->ra_next), mapp = mapp->ra_next) {
+ backp = &(mapp->ra_next), mapp = mapp->ra_next) {
DEBUGPRT(CE_CONT, "ndi_ra_alloc: ra_len = %" PRIx64
", len = %" PRIx64 "", mapp->ra_len, len);
@@ -606,7 +620,7 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
base = base & ~mask;
base += (mask + 1);
DEBUGPRT(CE_CONT, "\tnew base=%" PRIx64 "\n",
- base);
+ base);
/*
* Check to see if the new base is past
@@ -622,7 +636,7 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
remlen = upper - base;
else
remlen = mapp->ra_len -
- (base - mapp->ra_base);
+ (base - mapp->ra_base);
if ((backlargestp == NULL) ||
(largestlen < remlen)) {
@@ -656,7 +670,7 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
base = req->ra_addr;
len = req->ra_len;
for (; mapp != NULL && mapp->ra_base <= upper;
- backp = &(mapp->ra_next), mapp = mapp->ra_next) {
+ backp = &(mapp->ra_next), mapp = mapp->ra_next) {
if (base >= mapp->ra_base &&
((base - mapp->ra_base) < mapp->ra_len)) {
/*
@@ -696,7 +710,7 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
(req->ra_flags & NDI_RA_ALLOC_PARTIAL_OK) &&
(backlargestp != NULL)) {
adjust_link(backlargestp, *backlargestp, largestbase,
- largestlen);
+ largestlen);
base = largestbase;
len = largestlen;
@@ -712,6 +726,14 @@ ndi_ra_alloc(dev_info_t *dip, ndi_ra_request_t *req, uint64_t *retbasep,
*retbasep = base;
*retlenp = len;
}
+
+ /*
+ * Update dip's "available" property, substract this piece of
+ * resource from the pool.
+ */
+ if ((rval == NDI_SUCCESS) || (rval == NDI_RA_PARTIAL_REQ))
+ (void) pci_get_available_prop(dip, *retbasep, *retlenp, type);
+
return (rval);
}
@@ -748,7 +770,7 @@ isa_resource_setup()
used = ddi_find_devinfo("used-resources", -1, 0);
if (used == NULL) {
DEBUGPRT(CE_CONT,
- "isa_resource_setup: used-resources not found");
+ "isa_resource_setup: used-resources not found");
return (NDI_FAILURE);
}
@@ -869,7 +891,7 @@ ra_dump_all(char *type, dev_info_t *dip)
}
cmn_err(CE_CONT, "type is %s\n", typemap->type);
for (dipmap = typemap->ra_dip_list; dipmap != NULL;
- dipmap = dipmap->ra_next) {
+ dipmap = dipmap->ra_next) {
if (dip != NULL) {
if ((dipmap->ra_dip) != dip)
continue;
@@ -877,7 +899,7 @@ ra_dump_all(char *type, dev_info_t *dip)
cmn_err(CE_CONT, " dip is %p\n",
(void *)dipmap->ra_dip);
for (res = dipmap->ra_rangeset; res != NULL;
- res = res->ra_next) {
+ res = res->ra_next) {
cmn_err(CE_CONT, "\t range is %" PRIx64
" %" PRIx64 "\n", res->ra_base,
res->ra_len);
@@ -942,6 +964,26 @@ pci_resource_setup(dev_info_t *dip)
* code would really become an assert to make sure this
* function is not called for the same dip twice.
*/
+ /*
+ * Another user for the check below is hotplug PCI/PCIe bridges.
+ *
+ * For PCI/PCIE devices under a PCIE hierarchy, ndi_ra_alloc/free
+ * will update the devinfo node's "available" property, to reflect
+ * the fact that a piece of resource has been removed/added to
+ * a devinfo node.
+ * During probe of a new PCI bridge in the hotplug case, PCI
+ * configurator firstly allocates maximum MEM/IO from its parent,
+ * then calls ndi_ra_free() to use these resources to setup busra
+ * pool for the new bridge, as well as adding these resources to
+ * the "available" property of the new devinfo node. Then configu-
+ * rator will attach driver for the bridge before probing its
+ * children, and the bridge driver will then initialize its hotplug
+ * contollers (if it supports hotplug) and HPC driver will call
+ * this function to setup the busra pool, but the resource pool
+ * has already been setup at the first of pcicfg_probe_bridge(),
+ * thus we need the check below to return directly in this case.
+ * Otherwise the ndi_ra_free() below will see overlapping resources.
+ */
{
if (ra_map_exist(dip, NDI_RA_TYPE_MEM) == NDI_SUCCESS) {
return (NDI_FAILURE);
@@ -979,45 +1021,54 @@ pci_resource_setup(dev_info_t *dip)
if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"available", (caddr_t)&regs, &rlen) == DDI_SUCCESS) {
/*
+ * Remove "available" property as the entries will be
+ * re-created in ndi_ra_free() below, note prom based
+ * property will not be removed. But in ndi_ra_free()
+ * we'll be creating non prom based property entries.
+ */
+ (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "available");
+ /*
* create the available resource list for both memory and
* io space
*/
rcount = rlen / sizeof (pci_regspec_t);
for (i = 0; i < rcount; i++) {
- switch (PCI_REG_ADDR_G(regs[i].pci_phys_hi)) {
- case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
- (void) ndi_ra_free(dip,
- (uint64_t)regs[i].pci_phys_low,
- (uint64_t)regs[i].pci_size_low,
- (regs[i].pci_phys_hi & PCI_REG_PF_M) ?
- NDI_RA_TYPE_PCI_PREFETCH_MEM : NDI_RA_TYPE_MEM,
- 0);
- break;
- case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
- (void) ndi_ra_free(dip,
- ((uint64_t)(regs[i].pci_phys_mid) << 32) |
- ((uint64_t)(regs[i].pci_phys_low)),
- ((uint64_t)(regs[i].pci_size_hi) << 32) |
- ((uint64_t)(regs[i].pci_size_low)),
- (regs[i].pci_phys_hi & PCI_REG_PF_M) ?
- NDI_RA_TYPE_PCI_PREFETCH_MEM : NDI_RA_TYPE_MEM,
- 0);
- break;
- case PCI_REG_ADDR_G(PCI_ADDR_IO):
- (void) ndi_ra_free(dip,
- (uint64_t)regs[i].pci_phys_low,
- (uint64_t)regs[i].pci_size_low,
- NDI_RA_TYPE_IO,
- 0);
- break;
- case PCI_REG_ADDR_G(PCI_ADDR_CONFIG):
- break;
- default:
- cmn_err(CE_WARN,
- "pci_resource_setup: bad addr type: %x\n",
- PCI_REG_ADDR_G(regs[i].pci_phys_hi));
- break;
- }
+ switch (PCI_REG_ADDR_G(regs[i].pci_phys_hi)) {
+ case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
+ (void) ndi_ra_free(dip,
+ (uint64_t)regs[i].pci_phys_low,
+ (uint64_t)regs[i].pci_size_low,
+ (regs[i].pci_phys_hi & PCI_REG_PF_M) ?
+ NDI_RA_TYPE_PCI_PREFETCH_MEM :
+ NDI_RA_TYPE_MEM,
+ 0);
+ break;
+ case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
+ (void) ndi_ra_free(dip,
+ ((uint64_t)(regs[i].pci_phys_mid) << 32) |
+ ((uint64_t)(regs[i].pci_phys_low)),
+ ((uint64_t)(regs[i].pci_size_hi) << 32) |
+ ((uint64_t)(regs[i].pci_size_low)),
+ (regs[i].pci_phys_hi & PCI_REG_PF_M) ?
+ NDI_RA_TYPE_PCI_PREFETCH_MEM :
+ NDI_RA_TYPE_MEM,
+ 0);
+ break;
+ case PCI_REG_ADDR_G(PCI_ADDR_IO):
+ (void) ndi_ra_free(dip,
+ (uint64_t)regs[i].pci_phys_low,
+ (uint64_t)regs[i].pci_size_low,
+ NDI_RA_TYPE_IO,
+ 0);
+ break;
+ case PCI_REG_ADDR_G(PCI_ADDR_CONFIG):
+ break;
+ default:
+ cmn_err(CE_WARN,
+ "pci_resource_setup: bad addr type: %x\n",
+ PCI_REG_ADDR_G(regs[i].pci_phys_hi));
+ break;
+ }
}
kmem_free(regs, rlen);
}
@@ -1174,22 +1225,16 @@ pci_resource_setup_avail(dev_info_t *dip, pci_regspec_t *avail_p, int entries)
switch (PCI_REG_ADDR_G(avail_p->pci_phys_hi)) {
case PCI_REG_ADDR_G(PCI_ADDR_MEM32): {
- (void) ndi_ra_free(dip,
- (uint64_t)avail_p->pci_phys_low,
- (uint64_t)avail_p->pci_size_low,
- (avail_p->pci_phys_hi &
- PCI_REG_PF_M) ?
- NDI_RA_TYPE_PCI_PREFETCH_MEM :
- NDI_RA_TYPE_MEM,
- 0);
+ (void) ndi_ra_free(dip, (uint64_t)avail_p->pci_phys_low,
+ (uint64_t)avail_p->pci_size_low,
+ (avail_p->pci_phys_hi & PCI_REG_PF_M) ?
+ NDI_RA_TYPE_PCI_PREFETCH_MEM : NDI_RA_TYPE_MEM,
+ 0);
}
break;
case PCI_REG_ADDR_G(PCI_ADDR_IO):
- (void) ndi_ra_free(dip,
- (uint64_t)avail_p->pci_phys_low,
- (uint64_t)avail_p->pci_size_low,
- NDI_RA_TYPE_IO,
- 0);
+ (void) ndi_ra_free(dip, (uint64_t)avail_p->pci_phys_low,
+ (uint64_t)avail_p->pci_size_low, NDI_RA_TYPE_IO, 0);
break;
default:
goto err;
@@ -1204,6 +1249,457 @@ pci_resource_setup_avail(dev_info_t *dip, pci_regspec_t *avail_p, int entries)
err:
cmn_err(CE_WARN, "pci_resource_setup_avail: bad entry[%d]=%x\n",
- i, avail_p->pci_phys_hi);
+ i, avail_p->pci_phys_hi);
return (NDI_FAILURE);
}
+
+/*
+ * Return true if the devinfo node resides on PCI or PCI Express bus,
+ * sitting in a PCI Express hierarchy.
+ */
+static boolean_t
+is_pcie_fabric(dev_info_t *dip)
+{
+ dev_info_t *root = ddi_root_node();
+ dev_info_t *pdip;
+ boolean_t found = B_FALSE;
+ char *bus;
+
+ /*
+ * Is this pci/pcie ?
+ */
+ if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "device_type", &bus) !=
+ DDI_PROP_SUCCESS) {
+ DEBUGPRT(CE_WARN, "is_pcie_fabric: cannot find "
+ "\"device_type\" property for dip %p\n", (void *)dip);
+ return (B_FALSE);
+ }
+
+ if (strcmp(bus, "pciex") == 0) {
+ /* pcie bus, done */
+ ddi_prop_free(bus);
+ return (B_TRUE);
+ } else if (strcmp(bus, "pci") == 0) {
+ /*
+ * pci bus, fall through to check if it resides in
+ * a pcie hierarchy.
+ */
+ ddi_prop_free(bus);
+ } else {
+ /* other bus, return failure */
+ ddi_prop_free(bus);
+ return (B_FALSE);
+ }
+
+ /*
+ * Does this device reside in a pcie fabric ?
+ */
+ for (pdip = ddi_get_parent(dip); pdip && (pdip != root) &&
+ !found; pdip = ddi_get_parent(pdip)) {
+ if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
+ DDI_PROP_DONTPASS, "device_type", &bus) !=
+ DDI_PROP_SUCCESS)
+ break;
+
+ if (strcmp(bus, "pciex") == 0)
+ found = B_TRUE;
+
+ ddi_prop_free(bus);
+ }
+
+ return (found);
+}
+
+/*
+ * Remove a piece of IO/MEM resource from "available" property of 'dip'.
+ */
+static int
+pci_get_available_prop(dev_info_t *dip, uint64_t base, uint64_t len,
+ char *busra_type)
+{
+ pci_regspec_t *regs, *newregs;
+ uint_t status;
+ int rlen, rcount;
+ int i, j, k;
+ uint64_t dlen;
+ boolean_t found = B_FALSE;
+ uint32_t type;
+
+ /* check if we're manipulating MEM/IO resource */
+ if ((type = pci_type_ra2pci(busra_type)) == PCI_ADDR_TYPE_INVAL)
+ return (DDI_SUCCESS);
+
+ /* check if dip is a pci/pcie device resides in a pcie fabric */
+ if (!is_pcie_fabric(dip))
+ return (DDI_SUCCESS);
+
+ status = ddi_getlongprop(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
+ "available", (caddr_t)&regs, &rlen);
+
+ ASSERT(status == DDI_SUCCESS);
+ if (status != DDI_SUCCESS)
+ return (status);
+
+ /*
+ * The updated "available" property will at most have one more entry
+ * than existing one (when the requested range is in the middle of
+ * the matched property entry)
+ */
+ newregs = kmem_alloc(rlen + sizeof (pci_regspec_t), KM_SLEEP);
+
+ rcount = rlen / sizeof (pci_regspec_t);
+ for (i = 0, j = 0; i < rcount; i++) {
+ if (type == (regs[i].pci_phys_hi & PCI_ADDR_TYPE_MASK)) {
+ uint64_t range_base, range_len;
+
+ range_base = ((uint64_t)(regs[i].pci_phys_mid) << 32) |
+ ((uint64_t)(regs[i].pci_phys_low));
+ range_len = ((uint64_t)(regs[i].pci_size_hi) << 32) |
+ ((uint64_t)(regs[i].pci_size_low));
+
+ if ((base < range_base) ||
+ (base + len > range_base + range_len)) {
+ /*
+ * not a match, copy the entry
+ */
+ goto copy_entry;
+ }
+
+ /*
+ * range_base base base+len range_base
+ * +range_len
+ * +------------+-----------+----------+
+ * | |///////////| |
+ * +------------+-----------+----------+
+ */
+ /*
+ * Found a match, remove the range out of this entry.
+ */
+ found = B_TRUE;
+
+ dlen = base - range_base;
+ if (dlen != 0) {
+ newregs[j].pci_phys_hi = regs[i].pci_phys_hi;
+ newregs[j].pci_phys_mid =
+ (uint32_t)(range_base >> 32);
+ newregs[j].pci_phys_low =
+ (uint32_t)(range_base);
+ newregs[j].pci_size_hi = (uint32_t)(dlen >> 32);
+ newregs[j].pci_size_low = (uint32_t)dlen;
+ j++;
+ }
+
+ dlen = (range_base + range_len) - (base + len);
+ if (dlen != 0) {
+ newregs[j].pci_phys_hi = regs[i].pci_phys_hi;
+ newregs[j].pci_phys_mid =
+ (uint32_t)((base + len)>> 32);
+ newregs[j].pci_phys_low =
+ (uint32_t)(base + len);
+ newregs[j].pci_size_hi = (uint32_t)(dlen >> 32);
+ newregs[j].pci_size_low = (uint32_t)dlen;
+ j++;
+ }
+
+ /*
+ * We've allocated the resource from the matched
+ * entry, almost finished but still need to copy
+ * the rest entries from the original property
+ * array.
+ */
+ for (k = i + 1; k < rcount; k++) {
+ newregs[j] = regs[k];
+ j++;
+ }
+
+ goto done;
+
+ } else {
+copy_entry:
+ newregs[j] = regs[i];
+ j++;
+ }
+ }
+
+done:
+ /*
+ * This should not fail so assert it. For non-debug kernel we don't
+ * want to panic thus only logging a warning message.
+ */
+ ASSERT(found == B_TRUE);
+ if (!found) {
+ cmn_err(CE_WARN, "pci_get_available_prop: failed to remove "
+ "resource from dip %p : base 0x%" PRIx64 ", len 0x%" PRIX64
+ ", type 0x%x\n", (void *)dip, base, len, type);
+ kmem_free(newregs, rlen + sizeof (pci_regspec_t));
+ kmem_free(regs, rlen);
+
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Found the resources from parent, update the "available"
+ * property.
+ */
+ if (j == 0) {
+ /* all the resources are consumed, remove the property */
+ (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "available");
+ } else {
+ /*
+ * There are still resource available in the parent dip,
+ * update with the remaining resources.
+ */
+ (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
+ "available", (int *)newregs,
+ (j * sizeof (pci_regspec_t)) / sizeof (int));
+ }
+
+ kmem_free(newregs, rlen + sizeof (pci_regspec_t));
+ kmem_free(regs, rlen);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Add a piece of IO/MEM resource to "available" property of 'dip'.
+ */
+static int
+pci_put_available_prop(dev_info_t *dip, uint64_t base, uint64_t len,
+ char *busra_type)
+{
+ pci_regspec_t *regs, *newregs;
+ uint_t status;
+ int rlen, rcount;
+ int i, j, k;
+ int matched = 0;
+ uint64_t orig_base = base;
+ uint64_t orig_len = len;
+ uint32_t type;
+
+ /* check if we're manipulating MEM/IO resource */
+ if ((type = pci_type_ra2pci(busra_type)) == PCI_ADDR_TYPE_INVAL)
+ return (DDI_SUCCESS);
+
+ /* check if dip is a pci/pcie device resides in a pcie fabric */
+ if (!is_pcie_fabric(dip))
+ return (DDI_SUCCESS);
+
+ status = ddi_getlongprop(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
+ "available", (caddr_t)&regs, &rlen);
+
+ switch (status) {
+ case DDI_PROP_NOT_FOUND:
+ goto not_found;
+
+ case DDI_PROP_SUCCESS:
+ break;
+
+ default:
+ return (status);
+ }
+
+ /*
+ * The "available" property exist on the node, try to put this
+ * resource back, merge if there are adjacent resources.
+ *
+ * The updated "available" property will at most have one more entry
+ * than existing one (when there is no adjacent entries thus the new
+ * resource is appended at the end)
+ */
+ newregs = kmem_alloc(rlen + sizeof (pci_regspec_t), KM_SLEEP);
+
+ rcount = rlen / sizeof (pci_regspec_t);
+ for (i = 0, j = 0; i < rcount; i++) {
+ if (type == (regs[i].pci_phys_hi & PCI_ADDR_TYPE_MASK)) {
+ uint64_t range_base, range_len;
+
+ range_base = ((uint64_t)(regs[i].pci_phys_mid) << 32) |
+ ((uint64_t)(regs[i].pci_phys_low));
+ range_len = ((uint64_t)(regs[i].pci_size_hi) << 32) |
+ ((uint64_t)(regs[i].pci_size_low));
+
+ if ((base + len < range_base) ||
+ (base > range_base + range_len)) {
+ /*
+ * Not adjacent, copy the entry and contiue
+ */
+ goto copy_entry;
+ }
+
+ /*
+ * Adjacent or overlap?
+ *
+ * Should not have overlapping resources so assert it.
+ * For non-debug kernel we don't want to panic thus
+ * only logging a warning message.
+ */
+#if 0
+ ASSERT((base + len == range_base) ||
+ (base == range_base + range_len));
+#endif
+ if ((base + len != range_base) &&
+ (base != range_base + range_len)) {
+ cmn_err(CE_WARN, "pci_put_available_prop: "
+ "failed to add resource to dip %p : "
+ "base 0x%" PRIx64 ", len 0x%" PRIx64 " "
+ "overlaps with existing resource "
+ "base 0x%" PRIx64 ", len 0x%" PRIx64 "\n",
+ (void *)dip, orig_base, orig_len,
+ range_base, range_len);
+
+ goto failure;
+ }
+
+ /*
+ * On the left:
+ *
+ * base range_base
+ * +-------------+-------------+
+ * |/////////////| |
+ * +-------------+-------------+
+ * len range_len
+ *
+ * On the right:
+ *
+ * range_base base
+ * +-------------+-------------+
+ * | |/////////////|
+ * +-------------+-------------+
+ * range_len len
+ */
+ /*
+ * There are at most two piece of resources adjacent
+ * with this resource, assert it.
+ */
+ ASSERT(matched < 2);
+
+ if (!(matched < 2)) {
+ cmn_err(CE_WARN, "pci_put_available_prop: "
+ "failed to add resource to dip %p : "
+ "base 0x%" PRIx64 ", len 0x%" PRIx64 " "
+ "found overlaps in existing resources\n",
+ (void *)dip, orig_base, orig_len);
+
+ goto failure;
+ }
+
+ /* setup base & len to refer to the merged range */
+ len += range_len;
+ if (base == range_base + range_len)
+ base = range_base;
+
+ if (matched == 0) {
+ /*
+ * One adjacent entry, add this resource in
+ */
+ newregs[j].pci_phys_hi = regs[i].pci_phys_hi;
+ newregs[j].pci_phys_mid =
+ (uint32_t)(base >> 32);
+ newregs[j].pci_phys_low = (uint32_t)(base);
+ newregs[j].pci_size_hi = (uint32_t)(len >> 32);
+ newregs[j].pci_size_low = (uint32_t)len;
+
+ matched = 1;
+ k = j;
+ j++;
+ } else { /* matched == 1 */
+ /*
+ * Two adjacent entries, merge them together
+ */
+ newregs[k].pci_phys_hi = regs[i].pci_phys_hi;
+ newregs[k].pci_phys_mid =
+ (uint32_t)(base >> 32);
+ newregs[k].pci_phys_low = (uint32_t)(base);
+ newregs[k].pci_size_hi = (uint32_t)(len >> 32);
+ newregs[k].pci_size_low = (uint32_t)len;
+
+ matched = 2;
+ }
+ } else {
+copy_entry:
+ newregs[j] = regs[i];
+ j++;
+ }
+ }
+
+ if (matched == 0) {
+ /* No adjacent entries, append at end */
+ ASSERT(j == rcount);
+
+ /*
+ * According to page 15 of 1275 spec, bit "n" of "available"
+ * should be set to 1.
+ */
+ newregs[j].pci_phys_hi = type;
+ newregs[j].pci_phys_hi |= PCI_REG_REL_M;
+
+ newregs[j].pci_phys_mid = (uint32_t)(base >> 32);
+ newregs[j].pci_phys_low = (uint32_t)base;
+ newregs[j].pci_size_hi = (uint32_t)(len >> 32);
+ newregs[j].pci_size_low = (uint32_t)len;
+
+ j++;
+ }
+
+ (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
+ "available", (int *)newregs,
+ (j * sizeof (pci_regspec_t)) / sizeof (int));
+
+ kmem_free(newregs, rlen + sizeof (pci_regspec_t));
+ kmem_free(regs, rlen);
+ return (DDI_SUCCESS);
+
+not_found:
+ /*
+ * There is no "available" property on the parent node, create it.
+ */
+ newregs = kmem_alloc(sizeof (pci_regspec_t), KM_SLEEP);
+
+ /*
+ * According to page 15 of 1275 spec, bit "n" of "available" should
+ * be set to 1.
+ */
+ newregs[0].pci_phys_hi = type;
+ newregs[0].pci_phys_hi |= PCI_REG_REL_M;
+
+ newregs[0].pci_phys_mid = (uint32_t)(base >> 32);
+ newregs[0].pci_phys_low = (uint32_t)base;
+ newregs[0].pci_size_hi = (uint32_t)(len >> 32);
+ newregs[0].pci_size_low = (uint32_t)len;
+
+ (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
+ "available", (int *)newregs,
+ sizeof (pci_regspec_t) / sizeof (int));
+ kmem_free(newregs, sizeof (pci_regspec_t));
+ return (DDI_SUCCESS);
+
+failure:
+ kmem_free(newregs, rlen + sizeof (pci_regspec_t));
+ kmem_free(regs, rlen);
+ return (DDI_FAILURE);
+}
+
+static uint32_t
+pci_type_ra2pci(char *type)
+{
+ uint32_t pci_type = PCI_ADDR_TYPE_INVAL;
+
+ /*
+ * No 64 bit mem support for now
+ */
+ if (strcmp(type, NDI_RA_TYPE_IO) == 0) {
+ pci_type = PCI_ADDR_IO;
+
+ } else if (strcmp(type, NDI_RA_TYPE_MEM) == 0) {
+ pci_type = PCI_ADDR_MEM32;
+
+ } else if (strcmp(type, NDI_RA_TYPE_PCI_PREFETCH_MEM) == 0) {
+ pci_type = PCI_ADDR_MEM32;
+ pci_type |= PCI_REG_PF_M;
+ }
+
+ return (pci_type);
+}
diff --git a/usr/src/uts/common/io/devinfo.c b/usr/src/uts/common/io/devinfo.c
index d1d02ce378..490d7fc28e 100644
--- a/usr/src/uts/common/io/devinfo.c
+++ b/usr/src/uts/common/io/devinfo.c
@@ -54,6 +54,10 @@
#include <sys/disp.h>
#include <sys/kobj.h>
#include <sys/crc32.h>
+#include <sys/ddi_hp.h>
+#include <sys/ddi_hp_impl.h>
+#include <sys/sysmacros.h>
+#include <sys/list.h>
#ifdef DEBUG
@@ -231,6 +235,12 @@ typedef struct i_lnode {
i_link_t *link_out;
} i_lnode_t;
+typedef struct i_hp {
+ di_off_t hp_off; /* Offset of di_hp_t in snapshot */
+ dev_info_t *hp_child; /* Child devinfo node of the di_hp_t */
+ list_node_t hp_link; /* List linkage */
+} i_hp_t;
+
/*
* Soft state associated with each instance of driver open.
*/
@@ -246,6 +256,8 @@ static struct di_state {
mod_hash_t *lnode_hash;
mod_hash_t *link_hash;
+
+ list_t hp_list;
} **di_states;
static kmutex_t di_lock; /* serialize instance assignment */
@@ -298,6 +310,8 @@ static di_off_t di_getmdata(struct ddi_minor_data *, di_off_t *, di_off_t,
struct di_state *);
static di_off_t di_getppdata(struct dev_info *, di_off_t *, struct di_state *);
static di_off_t di_getdpdata(struct dev_info *, di_off_t *, struct di_state *);
+static di_off_t di_gethpdata(ddi_hp_cn_handle_t *, di_off_t *,
+ struct di_state *);
static di_off_t di_getprop(int, struct ddi_prop **, di_off_t *,
struct di_state *, struct dev_info *);
static void di_allocmem(struct di_state *, size_t);
@@ -320,6 +334,7 @@ static int di_cache_update(struct di_state *st);
static void di_cache_print(di_cache_debug_t msglevel, char *fmt, ...);
static int build_vhci_list(dev_info_t *vh_devinfo, void *arg);
static int build_phci_list(dev_info_t *ph_devinfo, void *arg);
+static void di_hotplug_children(struct di_state *st);
extern int modrootloaded;
extern void mdi_walk_vhcis(int (*)(dev_info_t *, void *), void *);
@@ -1341,6 +1356,11 @@ di_snapshot(struct di_state *st)
di_key_dtor, mod_hash_null_valdtor, di_hash_byptr,
NULL, di_key_cmp, KM_SLEEP);
+ if (DINFOHP & st->command) {
+ list_create(&st->hp_list, sizeof (i_hp_t),
+ offsetof(i_hp_t, hp_link));
+ }
+
/*
* copy the device tree
*/
@@ -1350,6 +1370,10 @@ di_snapshot(struct di_state *st)
mdi_walk_vhcis(build_vhci_list, st);
}
+ if (DINFOHP & st->command) {
+ di_hotplug_children(st);
+ }
+
ddi_release_devi(rootnode);
/*
@@ -1696,7 +1720,8 @@ di_copynode(struct dev_info *node, struct di_stack *dsp, struct di_state *st)
{
di_off_t off;
struct di_node *me;
- size_t size; struct dev_info *n;
+ size_t size;
+ struct dev_info *n;
dcmn_err2((CE_CONT, "di_copynode: depth = %x\n", dsp->depth));
ASSERT((node != NULL) && (node == TOP_NODE(dsp)));
@@ -1820,7 +1845,7 @@ di_copynode(struct dev_info *node, struct di_stack *dsp, struct di_state *st)
/*
* An optimization to skip mutex_enter when not needed.
*/
- if (!((DINFOMINOR | DINFOPROP | DINFOPATH) & st->command)) {
+ if (!((DINFOMINOR | DINFOPROP | DINFOPATH | DINFOHP) & st->command)) {
goto priv_data;
}
@@ -1873,7 +1898,7 @@ path:
property:
if (!(DINFOPROP & st->command)) {
- goto priv_data;
+ goto hotplug_data;
}
if (node->devi_drv_prop_ptr) { /* driver property list */
@@ -1913,6 +1938,16 @@ property:
}
}
+hotplug_data:
+ if (!(DINFOHP & st->command)) {
+ goto priv_data;
+ }
+
+ if (node->devi_hp_hdlp) { /* hotplug data */
+ me->hp_data = off;
+ off = di_gethpdata(node->devi_hp_hdlp, &me->hp_data, st);
+ }
+
priv_data:
if (!(DINFOPRIVDATA & st->command)) {
goto pm_info;
@@ -3447,6 +3482,88 @@ di_getdpdata(struct dev_info *node, di_off_t *off_p, struct di_state *st)
}
/*
+ * Copy hotplug data associated with a devinfo node into the snapshot.
+ */
+static di_off_t
+di_gethpdata(ddi_hp_cn_handle_t *hp_hdl, di_off_t *off_p,
+ struct di_state *st)
+{
+ struct i_hp *hp;
+ struct di_hp *me;
+ size_t size;
+ di_off_t off;
+
+ dcmn_err2((CE_CONT, "di_gethpdata:\n"));
+
+ /*
+ * check memory first
+ */
+ off = di_checkmem(st, *off_p, sizeof (struct di_hp));
+ *off_p = off;
+
+ do {
+ me = DI_HP(di_mem_addr(st, off));
+ me->self = off;
+ me->hp_name = 0;
+ me->hp_connection = (int)hp_hdl->cn_info.cn_num;
+ me->hp_depends_on = (int)hp_hdl->cn_info.cn_num_dpd_on;
+ (void) ddihp_cn_getstate(hp_hdl);
+ me->hp_state = (int)hp_hdl->cn_info.cn_state;
+ me->hp_type = (int)hp_hdl->cn_info.cn_type;
+ me->hp_type_str = 0;
+ me->hp_last_change = (uint32_t)hp_hdl->cn_info.cn_last_change;
+ me->hp_child = 0;
+
+ /*
+ * Child links are resolved later by di_hotplug_children().
+ * Store a reference to this di_hp_t in the list used later
+ * by di_hotplug_children().
+ */
+ hp = kmem_zalloc(sizeof (i_hp_t), KM_SLEEP);
+ hp->hp_off = off;
+ hp->hp_child = hp_hdl->cn_info.cn_child;
+ list_insert_tail(&st->hp_list, hp);
+
+ off += sizeof (struct di_hp);
+
+ /* Add name of this di_hp_t to the snapshot */
+ if (hp_hdl->cn_info.cn_name) {
+ size = strlen(hp_hdl->cn_info.cn_name) + 1;
+ me->hp_name = off = di_checkmem(st, off, size);
+ (void) strcpy(di_mem_addr(st, off),
+ hp_hdl->cn_info.cn_name);
+ off += size;
+ }
+
+ /* Add type description of this di_hp_t to the snapshot */
+ if (hp_hdl->cn_info.cn_type_str) {
+ size = strlen(hp_hdl->cn_info.cn_type_str) + 1;
+ me->hp_type_str = off = di_checkmem(st, off, size);
+ (void) strcpy(di_mem_addr(st, off),
+ hp_hdl->cn_info.cn_type_str);
+ off += size;
+ }
+
+ /*
+ * Set link to next in the chain of di_hp_t nodes,
+ * or terminate the chain when processing the last node.
+ */
+ if (hp_hdl->next != NULL) {
+ off = di_checkmem(st, off, sizeof (struct di_hp));
+ me->next = off;
+ } else {
+ me->next = 0;
+ }
+
+ /* Update pointer to next in the chain */
+ hp_hdl = hp_hdl->next;
+
+ } while (hp_hdl);
+
+ return (off);
+}
+
+/*
* The driver is stateful across DINFOCPYALL and DINFOUSRLD.
* This function encapsulates the state machine:
*
@@ -4075,3 +4192,24 @@ di_cache_print(di_cache_debug_t msglevel, char *fmt, ...)
vcmn_err(msglevel, fmt, ap);
va_end(ap);
}
+
+static void
+di_hotplug_children(struct di_state *st)
+{
+ di_off_t off;
+ struct di_hp *hp;
+ struct i_hp *hp_list_node;
+
+ while (hp_list_node = (struct i_hp *)list_remove_head(&st->hp_list)) {
+
+ if ((hp_list_node->hp_child != NULL) &&
+ (di_dip_find(st, hp_list_node->hp_child, &off) == 0)) {
+ hp = DI_HP(di_mem_addr(st, hp_list_node->hp_off));
+ hp->hp_child = off;
+ }
+
+ kmem_free(hp_list_node, sizeof (i_hp_t));
+ }
+
+ list_destroy(&st->hp_list);
+}
diff --git a/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c b/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c
deleted file mode 100644
index 735e600c02..0000000000
--- a/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c
+++ /dev/null
@@ -1,1982 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/*
- * PCIEHPC - The Standard PCI Express HotPlug Controller driver module. This
- * driver can be used with PCI Express HotPlug controllers that
- * are compatible with the PCI Express ver 1.0a specification.
- */
-
-#include <sys/types.h>
-#include <sys/note.h>
-#include <sys/conf.h>
-#include <sys/kmem.h>
-#include <sys/debug.h>
-#include <sys/vtrace.h>
-#include <sys/modctl.h>
-#include <sys/autoconf.h>
-#include <sys/varargs.h>
-#include <sys/ddi_impldefs.h>
-#include <sys/pci.h>
-#include <sys/time.h>
-#include <sys/callb.h>
-#include <sys/ddi.h>
-#include <sys/sunddi.h>
-#include <sys/pcie_impl.h>
-#include <sys/hotplug/pci/pciehpc_impl.h>
-
-/*
- * Local data/functions
- */
-
-/* mutex to protect pciehpc_head list */
-static kmutex_t pciehpc_list_mutex;
-
-/* pointer to linked list of pciehpc structures */
-static pciehpc_t *pciehpc_head = NULL;
-
-/* mutex to protect init/uninit controllers */
-static kmutex_t pciehpc_init_mutex;
-static int pciehpc_init_count = 0; /* count of pciehpc instances in use */
-
-static pciehpc_t *pciehpc_create_soft_state(dev_info_t *dip);
-static pciehpc_t *pciehpc_get_soft_state(dev_info_t *dip);
-static void pciehpc_destroy_soft_state(dev_info_t *dip);
-static char *pciehpc_led_state_text(hpc_led_state_t state);
-static void pciehpc_attn_btn_handler(pciehpc_t *ctrl_p);
-static void pciehpc_dev_info(pciehpc_t *ctrl_p);
-
-static int pciehpc_pcie_dev(dev_info_t *dip, ddi_acc_handle_t handle);
-static void pciehpc_disable_errors(pciehpc_t *ctrl_p);
-static void pciehpc_enable_errors(pciehpc_t *ctrl_p);
-
-#ifdef DEBUG
-int pciehpc_debug = 0;
-static void pciehpc_dump_hpregs(pciehpc_t *ctrl_p);
-#endif
-
-/*
- * Module linkage information for the kernel.
- */
-extern struct mod_ops mod_miscops;
-static struct modlmisc modlmisc =
-{
- &mod_miscops,
- "PCIe hotplug driver",
-};
-
-static struct modlinkage modlinkage =
-{
- MODREV_1,
- &modlmisc,
- NULL
-};
-
-
-int
-_init(void)
-{
- int error;
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc: _init() called\n"));
- mutex_init(&pciehpc_list_mutex, NULL, MUTEX_DRIVER, NULL);
- mutex_init(&pciehpc_init_mutex, NULL, MUTEX_DRIVER, NULL);
- if ((error = mod_install(&modlinkage)) != 0) {
- mutex_destroy(&pciehpc_init_mutex);
- mutex_destroy(&pciehpc_list_mutex);
- }
-
- return (error);
-}
-
-int
-_fini(void)
-{
- int error;
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc: _fini() called\n"));
-
- mutex_enter(&pciehpc_init_mutex);
- if (pciehpc_init_count != 0) {
- mutex_exit(&pciehpc_init_mutex);
- return (EBUSY);
- }
- error = mod_remove(&modlinkage);
- if (error != 0) {
- mutex_exit(&pciehpc_init_mutex);
- return (error);
- }
- mutex_destroy(&pciehpc_list_mutex);
- mutex_destroy(&pciehpc_init_mutex);
- return (0);
-}
-
-int
-_info(struct modinfo *modinfop)
-{
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc: _info() called\n"));
- return (mod_info(&modlinkage, modinfop));
-}
-
-/*
- * pciehpc_init()
- *
- * Initialize Hot Plug Controller if present. The arguments are:
- * dip - Devinfo node pointer to the hot plug bus node
- * regops - register ops to access HPC registers for non-standard
- * HPC hw implementations (e.g: HPC in host PCI-E brdiges)
- * This is NULL for standard HPC in PCIe bridges.
- * Returns:
- * DDI_SUCCESS for successful HPC initialization
- * DDI_FAILURE for errors or if HPC hw not found
- */
-int
-pciehpc_init(dev_info_t *dip, pciehpc_regops_t *regops)
-{
- pciehpc_t *ctrl_p;
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_init() called (dip=%p)",
- (void *)dip));
-
- mutex_enter(&pciehpc_init_mutex);
-
- /* Make sure that it is not already initialized */
- if (pciehpc_get_soft_state(dip) != NULL) {
- PCIEHPC_DEBUG((CE_WARN,
- "%s%d: pciehpc instance already initialized!",
- ddi_driver_name(dip), ddi_get_instance(dip)));
- mutex_exit(&pciehpc_init_mutex);
- return (DDI_SUCCESS);
- }
-
- /* allocate a new soft state structure */
- ctrl_p = pciehpc_create_soft_state(dip);
-
- /* get PCI device info */
- pciehpc_dev_info(ctrl_p);
-
- /* setup access handle for HPC regs */
- if (regops != NULL) {
- /* HPC access is non-standard; use the supplied reg ops */
- ctrl_p->regops = *regops;
- } else {
- /* standard HPC in a PCIe bridge */
- if (pciehpc_regs_setup(dip, 0, 0, &ctrl_p->regs_base,
- &ctrl_p->cfghdl) != DDI_SUCCESS)
- goto cleanup;
- }
-
- pciehpc_disable_errors(ctrl_p);
-
- /*
- * Set the platform specific hot plug mode.
- */
- ctrl_p->hp_mode = PCIEHPC_NATIVE_HP_MODE; /* default is Native mode */
- ctrl_p->ops.init_hpc_hw = pciehpc_hpc_init;
- ctrl_p->ops.init_hpc_slotinfo = pciehpc_slotinfo_init;
- ctrl_p->ops.disable_hpc_intr = pciehpc_disable_intr;
- ctrl_p->ops.enable_hpc_intr = pciehpc_enable_intr;
- ctrl_p->ops.uninit_hpc_hw = pciehpc_hpc_uninit;
- ctrl_p->ops.uninit_hpc_slotinfo = pciehpc_slotinfo_uninit;
- ctrl_p->ops.probe_hpc = pciehpc_probe_hpc;
-
-#if defined(__i386) || defined(__amd64)
- pciehpc_update_ops(ctrl_p);
-#endif
- if (regops == NULL) { /* it is a standard HPC in a PCIe bridge */
- /* make sure we really have a hot plug controller */
- if ((ctrl_p->ops.probe_hpc)(ctrl_p) != DDI_SUCCESS)
- goto cleanup1;
- }
-
- /* initialize hot plug controller hw */
- if ((ctrl_p->ops.init_hpc_hw)(ctrl_p) != DDI_SUCCESS)
- goto cleanup1;
-
- /* initialize slot information soft state structure */
- if ((ctrl_p->ops.init_hpc_slotinfo)(ctrl_p) != DDI_SUCCESS)
- goto cleanup2;
-
- /* register the hot plug slot with HPS framework */
- if (pciehpc_register_slot(ctrl_p) != DDI_SUCCESS)
- goto cleanup3;
-
- /* HPC initialization is complete now */
- ctrl_p->soft_state |= PCIEHPC_SOFT_STATE_INITIALIZED;
- ctrl_p->soft_state &= ~PCIEHPC_SOFT_STATE_UNINITIALIZED;
-
-#ifdef DEBUG
- /* For debug, dump the HPC registers */
- if (pciehpc_debug > 2)
- pciehpc_dump_hpregs(ctrl_p);
-#endif
-
- /* enable hot plug interrupts/event */
- (void) (ctrl_p->ops.enable_hpc_intr)(ctrl_p);
-
- pciehpc_init_count++;
-
- mutex_exit(&pciehpc_init_mutex);
-
- return (DDI_SUCCESS);
-
-cleanup3:
- (void) (ctrl_p->ops.uninit_hpc_slotinfo)(ctrl_p);
-
-cleanup2:
- (void) (ctrl_p->ops.uninit_hpc_hw)(ctrl_p);
-
-cleanup1:
- pciehpc_enable_errors(ctrl_p);
- /* free up the HPC register mapping if applicable */
- if (ctrl_p->cfghdl)
- pciehpc_regs_teardown(&ctrl_p->cfghdl);
-
-cleanup:
- pciehpc_destroy_soft_state(dip);
- mutex_exit(&pciehpc_init_mutex);
- return (DDI_FAILURE);
-}
-
-/*
- * Uninitialize HPC soft state structure and free up any resources
- * used for the HPC instance.
- */
-int
-pciehpc_uninit(dev_info_t *dip)
-{
- pciehpc_t *ctrl_p;
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_uninit() called (dip=%p)\n",
- (void *)dip));
-
- mutex_enter(&pciehpc_init_mutex);
-
- /* get the soft state structure for this dip */
- if ((ctrl_p = pciehpc_get_soft_state(dip)) == NULL) {
- mutex_exit(&pciehpc_init_mutex);
- return (DDI_FAILURE);
- }
-
- /* disable interrupts */
- (void) (ctrl_p->ops.disable_hpc_intr)(ctrl_p);
-
- /* unregister the slot */
- (void) pciehpc_unregister_slot(ctrl_p);
-
- /* uninit any slot info data structures */
- (void) (ctrl_p->ops.uninit_hpc_slotinfo)(ctrl_p);
-
- /* uninitialize hpc, remove interrupt handler, etc. */
- (void) (ctrl_p->ops.uninit_hpc_hw)(ctrl_p);
-
- pciehpc_enable_errors(ctrl_p);
-
- /* free up the HPC register mapping if applicable */
- if (ctrl_p->cfghdl)
- pciehpc_regs_teardown(&ctrl_p->cfghdl);
-
- /* destroy the soft state structure */
- pciehpc_destroy_soft_state(dip);
-
- ASSERT(pciehpc_init_count != 0);
-
- pciehpc_init_count--;
-
- mutex_exit(&pciehpc_init_mutex);
-
- return (DDI_SUCCESS);
-}
-
-/*
- * Probe for the inband PCI-E hot plug controller. Returns DDI_SUCCESS
- * if found. This function works only for the standard PCI-E bridge
- * that has inband hot plug controller.
- *
- * NOTE: This won't work for Host-PCIE bridges.
- */
-int
-pciehpc_probe_hpc(pciehpc_t *ctrl_p)
-{
- uint8_t cap_ptr;
- uint8_t cap_id;
- uint16_t status;
-
- /* Read the PCI configuration status register. */
- status = pciehpc_reg_get16(ctrl_p, PCI_CONF_STAT);
-
- /* check for capabilities list */
- if (!(status & PCI_STAT_CAP)) {
- /* no capabilities list */
- return (DDI_FAILURE);
- }
-
- /* Get a pointer to the PCI capabilities list. */
- cap_ptr = pciehpc_reg_get8(ctrl_p, PCI_BCNF_CAP_PTR);
- cap_ptr &= 0xFC; /* mask off reserved bits */
-
- /*
- * Walk thru the capabilities list looking for PCI Express capability
- * structure.
- */
- while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
- cap_id = pciehpc_reg_get8(ctrl_p, (uint_t)cap_ptr);
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_probe_hpc() capability @"
- " pointer=%02x (id=%02x)\n", cap_ptr, cap_id));
-
- if (cap_id == PCI_CAP_ID_PCI_E) {
- uint32_t slot_cap;
-
- /* Read the PCI Express Slot Capabilities Register */
- slot_cap = pciehpc_reg_get32(ctrl_p,
- (uint_t)cap_ptr + PCIE_SLOTCAP);
-
- /* Does it have PCI Express HotPlug capability? */
- if (slot_cap & PCIE_SLOTCAP_HP_CAPABLE) {
- /* Save the offset to PCI Express Capabilities structure */
- ctrl_p->pcie_caps_reg_offset = cap_ptr;
- return (DDI_SUCCESS);
- }
- }
-
- /* Get the pointer to the next capability */
- cap_ptr = pciehpc_reg_get8(ctrl_p, (uint_t)cap_ptr + 1);
- cap_ptr &= 0xFC;
- }
-
- return (DDI_FAILURE);
-}
-
-/*
- * Setup slot information for use with HPS framework.
- */
-int
-pciehpc_slotinfo_init(pciehpc_t *ctrl_p)
-{
- uint32_t slot_capabilities, link_capabilities;
- pciehpc_slot_t *p = &ctrl_p->slot;
-
- /*
- * setup HPS framework slot ops structure
- */
- p->slot_ops.hpc_version = HPC_SLOT_OPS_VERSION;
- p->slot_ops.hpc_op_connect = pciehpc_slot_connect;
- p->slot_ops.hpc_op_disconnect = pciehpc_slot_disconnect;
- p->slot_ops.hpc_op_insert = NULL;
- p->slot_ops.hpc_op_remove = NULL;
- p->slot_ops.hpc_op_control = pciehpc_slot_control;
-
- /*
- * setup HPS framework slot information structure
- */
- p->slot_info.version = HPC_SLOT_OPS_VERSION;
- p->slot_info.slot_type = HPC_SLOT_TYPE_PCIE;
- p->slot_info.slot_flags =
- HPC_SLOT_CREATE_DEVLINK | HPC_SLOT_NO_AUTO_ENABLE;
- p->slot_info.pci_slot_capabilities = HPC_SLOT_64BITS;
- /* the device number is fixed as 0 as per the spec */
- p->slot_info.pci_dev_num = 0;
-
- /* read Slot Capabilities Register */
- slot_capabilities = pciehpc_reg_get32(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCAP);
-
- /* set slot-name/slot-number info */
- pciehpc_set_slot_name(ctrl_p);
-
- /* check if Attn Button present */
- ctrl_p->has_attn = (slot_capabilities & PCIE_SLOTCAP_ATTN_BUTTON) ?
- B_TRUE : B_FALSE;
-
- /* check if Manual Retention Latch sensor present */
- ctrl_p->has_mrl = (slot_capabilities & PCIE_SLOTCAP_MRL_SENSOR) ?
- B_TRUE : B_FALSE;
-
- /*
- * PCI-E version 1.1 defines EMI Lock Present bit
- * in Slot Capabilities register. Check for it.
- */
- ctrl_p->has_emi_lock = (slot_capabilities &
- PCIE_SLOTCAP_EMI_LOCK_PRESENT) ? B_TRUE : B_FALSE;
-
- link_capabilities = pciehpc_reg_get32(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_LINKCAP);
- ctrl_p->dll_active_rep = (link_capabilities &
- PCIE_LINKCAP_DLL_ACTIVE_REP_CAPABLE) ? B_TRUE : B_FALSE;
- if (ctrl_p->dll_active_rep)
- cv_init(&ctrl_p->slot.dll_active_cv, NULL, CV_DRIVER, NULL);
-
- /* initialize synchronization conditional variable */
- cv_init(&ctrl_p->slot.cmd_comp_cv, NULL, CV_DRIVER, NULL);
- ctrl_p->slot.command_pending = B_FALSE;
-
- /* setup thread for handling ATTN button events */
- if (ctrl_p->has_attn) {
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_slotinfo_init: setting up ATTN button event "
- "handler thread for slot %d\n", ctrl_p->slot.slotNum));
- cv_init(&ctrl_p->slot.attn_btn_cv, NULL, CV_DRIVER, NULL);
- ctrl_p->slot.attn_btn_pending = B_FALSE;
- ctrl_p->slot.attn_btn_threadp = thread_create(NULL, 0,
- pciehpc_attn_btn_handler,
- (void *)ctrl_p, 0, &p0, TS_RUN, minclsyspri);
- ctrl_p->slot.attn_btn_thread_exit = B_FALSE;
- }
-
- /* get current slot state from the hw */
- pciehpc_get_slot_state(ctrl_p);
-
- return (DDI_SUCCESS);
-}
-
-/*ARGSUSED*/
-int
-pciehpc_slotinfo_uninit(pciehpc_t *ctrl_p)
-{
- cv_destroy(&ctrl_p->slot.cmd_comp_cv);
-
- if (ctrl_p->slot.attn_btn_threadp != NULL) {
- mutex_enter(&ctrl_p->pciehpc_mutex);
- ctrl_p->slot.attn_btn_thread_exit = B_TRUE;
- cv_signal(&ctrl_p->slot.attn_btn_cv);
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_slotinfo_uninit: waiting for ATTN thread exit\n"));
- cv_wait(&ctrl_p->slot.attn_btn_cv, &ctrl_p->pciehpc_mutex);
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_slotinfo_uninit: ATTN thread exit\n"));
- cv_destroy(&ctrl_p->slot.attn_btn_cv);
- ctrl_p->slot.attn_btn_threadp = NULL;
- mutex_exit(&ctrl_p->pciehpc_mutex);
- }
-
- if (ctrl_p->dll_active_rep)
- cv_destroy(&ctrl_p->slot.dll_active_cv);
-
- return (DDI_SUCCESS);
-}
-
-/*
- * Get the current state of the slot from the hw.
- */
-void
-pciehpc_get_slot_state(pciehpc_t *ctrl_p)
-{
- pciehpc_slot_t *p = &ctrl_p->slot;
- uint16_t control, status;
-
- /* read the Slot Control Register */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- p->fault_led_state = HPC_LED_OFF; /* no fault led */
- p->active_led_state = HPC_LED_OFF; /* no active led */
-
- /* read the current Slot Status Register */
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- /* get POWER led state */
- p->power_led_state =
- pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control));
-
- /* get ATTN led state */
- p->attn_led_state =
- pciehpc_led_state_to_hpc(pcie_slotctl_attn_indicator_get(control));
-
- if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED))
- /* no device present; slot is empty */
- p->slot_state = HPC_SLOT_EMPTY;
-
- else if (!(control & PCIE_SLOTCTL_PWR_CONTROL))
- /* device is present and powered up */
- p->slot_state = HPC_SLOT_CONNECTED;
- else
- /* device is present and powered down */
- p->slot_state = HPC_SLOT_DISCONNECTED;
-}
-
-
-/*
- * pciehpc_regs_setup()
- *
- * Setup PCI-E config registers for DDI access functions.
- *
- * Note: This is same as pci_config_setup() except that this may be
- * used to map specific reg set with an offset in the case of host
- * PCI-E bridges.
- */
-int
-pciehpc_regs_setup(dev_info_t *dip, uint_t rnum, offset_t off,
- caddr_t *addrp, ddi_acc_handle_t *handle)
-{
- ddi_device_acc_attr_t attr;
-
- attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
- attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
- attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
-
- /* Check for fault management capabilities */
- if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(dip)))
- attr.devacc_attr_access = DDI_FLAGERR_ACC;
-
- return (ddi_regs_map_setup(dip, rnum, addrp, off, 0, &attr, handle));
-}
-
-/*
- * pciehpc_regs_teardown()
- *
- * Unmap config register set.
- *
- * Note: This is same as pci_config_teardown() function.
- */
-void
-pciehpc_regs_teardown(ddi_acc_handle_t *handle)
-{
- ddi_regs_map_free(handle);
-}
-
-/*
- * Find the soft state structure for the HPC associated with the dip.
- */
-static pciehpc_t *
-pciehpc_get_soft_state(dev_info_t *dip)
-{
- pciehpc_t *ctrl_p;
-
- mutex_enter(&pciehpc_list_mutex);
-
- ctrl_p = pciehpc_head;
-
- while (ctrl_p) {
- if (ctrl_p->dip == dip) {
- mutex_exit(&pciehpc_list_mutex);
- return (ctrl_p);
- }
- ctrl_p = ctrl_p->nextp;
- }
-
- mutex_exit(&pciehpc_list_mutex);
-
- return (NULL);
-}
-
-/*
- * Allocate a soft state structure for the HPC associated with this dip.
- */
-static pciehpc_t *
-pciehpc_create_soft_state(dev_info_t *dip)
-{
- pciehpc_t *ctrl_p;
-
- ctrl_p = kmem_zalloc(sizeof (pciehpc_t), KM_SLEEP);
-
- ctrl_p->dip = dip;
-
- mutex_enter(&pciehpc_list_mutex);
- ctrl_p->nextp = pciehpc_head;
- pciehpc_head = ctrl_p;
- ctrl_p->soft_state = PCIEHPC_SOFT_STATE_UNINITIALIZED;
- mutex_exit(&pciehpc_list_mutex);
-
- return (ctrl_p);
-}
-
-/*
- * Remove the HPC soft state structure from the linked list.
- */
-static void
-pciehpc_destroy_soft_state(dev_info_t *dip)
-{
- pciehpc_t **pp;
- pciehpc_t *p;
-
- mutex_enter(&pciehpc_list_mutex);
- pp = &pciehpc_head;
- while ((p = *pp) != NULL) {
- if (p->dip == dip) {
- *pp = p->nextp;
- kmem_free(p, sizeof (pciehpc_t));
- break;
- }
- pp = &(p->nextp);
- }
- mutex_exit(&pciehpc_list_mutex);
-}
-
-/*
- * convert LED state from PCIE HPC definition to hpc_led_state_t
- * definition.
- */
-hpc_led_state_t
-pciehpc_led_state_to_hpc(uint16_t state)
-{
- switch (state) {
- case PCIE_SLOTCTL_INDICATOR_STATE_ON:
- return (HPC_LED_ON);
- case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
- return (HPC_LED_BLINK);
- case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
- default:
- return (HPC_LED_OFF);
- }
-}
-
-/*
- * convert LED state from hpc_led_state_t definition to PCIE HPC
- * definition.
- */
-uint16_t
-pciehpc_led_state_to_pciehpc(hpc_led_state_t state)
-{
- switch (state) {
- case HPC_LED_ON:
- return (PCIE_SLOTCTL_INDICATOR_STATE_ON);
- case HPC_LED_BLINK:
- return (PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
- case HPC_LED_OFF:
- default:
- return (PCIE_SLOTCTL_INDICATOR_STATE_OFF);
- }
-}
-
-/*
- * Initialize HPC hardware, install interrupt handler, etc. It doesn't
- * enable hot plug interrupts.
- *
- * (Note: It is called only from pciehpc_init().)
- */
-int
-pciehpc_hpc_init(pciehpc_t *ctrl_p)
-{
- uint16_t reg;
-
- /* read the Slot Control Register */
- reg = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- /* disable all interrupts */
- reg &= ~(SLOTCTL_SUPPORTED_INTRS_MASK);
- pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset +
- PCIE_SLOTCTL, reg);
-
- /* clear any interrupt status bits */
- reg = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS, reg);
-
- /* initialize the interrupt mutex */
- mutex_init(&ctrl_p->pciehpc_mutex, NULL, MUTEX_DRIVER,
- (void *)PCIEHPC_INTR_PRI);
-
- return (DDI_SUCCESS);
-}
-
-/*
- * Uninitialize HPC hardware, uninstall interrupt handler, etc.
- *
- * (Note: It is called only from pciehpc_uninit().)
- */
-int
-pciehpc_hpc_uninit(pciehpc_t *ctrl_p)
-{
- /* disable interrupts */
- (void) pciehpc_disable_intr(ctrl_p);
-
- /* destroy the mutex */
- mutex_destroy(&ctrl_p->pciehpc_mutex);
-
- return (DDI_SUCCESS);
-}
-
-/*
- * Disable hot plug interrupts.
- * Note: this is only for Native hot plug mode.
- */
-int
-pciehpc_disable_intr(pciehpc_t *ctrl_p)
-{
- uint16_t reg;
-
- /* read the Slot Control Register */
- reg = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- /* disable all interrupts */
- reg &= ~(SLOTCTL_SUPPORTED_INTRS_MASK);
- pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset +
- PCIE_SLOTCTL, reg);
-
- /* clear any interrupt status bits */
- reg = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS, reg);
-
- return (DDI_SUCCESS);
-}
-
-/*
- * Enable hot plug interrupts.
- * Note: this is only for Native hot plug mode.
- */
-int
-pciehpc_enable_intr(pciehpc_t *ctrl_p)
-{
- uint16_t reg;
-
- /* clear any interrupt status bits */
- reg = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS, reg);
-
- /* read the Slot Control Register */
- reg = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- /*
- * enable interrupts: power fault detection interrupt is enabled
- * only when the slot is 'connected', i.e. power is ON
- */
- if (ctrl_p->slot.slot_state == HPC_SLOT_CONNECTED)
- pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset +
- PCIE_SLOTCTL, reg | SLOTCTL_SUPPORTED_INTRS_MASK);
- else
- pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset +
- PCIE_SLOTCTL, reg | (SLOTCTL_SUPPORTED_INTRS_MASK &
- ~PCIE_SLOTCTL_PWR_FAULT_EN));
-
- return (DDI_SUCCESS);
-}
-
-/*
- * Register the PCI-E hot plug slot with HPS framework.
- */
-int
-pciehpc_register_slot(pciehpc_t *ctrl_p)
-{
- char nexus_path[MAXNAMELEN];
- pciehpc_slot_t *p = &ctrl_p->slot;
-
- /* get nexus path name */
- (void) ddi_pathname(ctrl_p->dip, nexus_path);
-
- /* register the slot with HPS framework */
- if (hpc_slot_register(ctrl_p->dip, nexus_path,
- &p->slot_info, &p->slot_handle,
- &p->slot_ops, (caddr_t)ctrl_p, 0) != 0) {
- PCIEHPC_DEBUG((CE_WARN,
- "pciehpc_register_slot() failed to register slot %d\n",
- p->slotNum));
- return (DDI_FAILURE);
- }
-
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_register_slot(): registered slot %d\n", p->slotNum));
- return (DDI_SUCCESS);
-}
-
-/*
- * Unregister the PCI-E hot plug slot from the HPS framework.
- */
-int
-pciehpc_unregister_slot(pciehpc_t *ctrl_p)
-{
- pciehpc_slot_t *p = &ctrl_p->slot;
-
- if (hpc_slot_unregister(&p->slot_handle) != 0) {
- PCIEHPC_DEBUG((CE_WARN,
- "pciehpc_unregister_slot() failed to unregister slot %d\n",
- p->slotNum));
- return (DDI_FAILURE);
- }
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_unregister_slot(): unregistered slot %d\n", p->slotNum));
- return (DDI_SUCCESS);
-}
-
-/*
- * pciehpc_intr()
- *
- * Interrupt handler for PCI-E Hot plug controller interrupts.
- *
- * Note: This is only for native mode hot plug. This is called
- * by the nexus driver at interrupt context. Interrupt Service Routine
- * registration is done by the nexus driver for both hot plug and
- * non-hot plug interrupts. This function is called from the ISR
- * of the nexus driver to handle hot-plug interrupts.
- */
-int
-pciehpc_intr(dev_info_t *dip)
-{
- pciehpc_t *ctrl_p;
- uint16_t status, control;
-
- /* get the soft state structure for this dip */
- if ((ctrl_p = pciehpc_get_soft_state(dip)) == NULL)
- return (DDI_INTR_UNCLAIMED);
-
- mutex_enter(&ctrl_p->pciehpc_mutex);
-
- /* make sure the controller soft state is initialized */
- if (ctrl_p->soft_state & PCIEHPC_SOFT_STATE_UNINITIALIZED) {
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (DDI_INTR_UNCLAIMED);
- }
-
- /* if it is not NATIVE hot plug mode then return */
- if (ctrl_p->hp_mode != PCIEHPC_NATIVE_HP_MODE) {
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (DDI_INTR_UNCLAIMED);
- }
-
- /* read the current slot status register */
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- /* check if there are any hot plug interrupts occurred */
- if (!(status & SLOT_STATUS_EVENTS)) {
- /* no hot plug events occurred */
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (DDI_INTR_UNCLAIMED);
- }
-
- /* clear the interrupt status bits */
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS, status);
-
- /* check for CMD COMPLETE interrupt */
- if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_intr(): CMD COMPLETED interrupt received\n"));
- /* wake up any one waiting for Command Completion event */
- cv_signal(&ctrl_p->slot.cmd_comp_cv);
- }
-
- /* check for ATTN button interrupt */
- if (status & PCIE_SLOTSTS_ATTN_BTN_PRESSED) {
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_intr(): ATTN BUTTON interrupt received\n"));
- /* if ATTN button event is still pending then cancel it */
- if (ctrl_p->slot.attn_btn_pending == B_TRUE)
- ctrl_p->slot.attn_btn_pending = B_FALSE;
- else
- ctrl_p->slot.attn_btn_pending = B_TRUE;
- /* wake up the ATTN event handler */
- cv_signal(&ctrl_p->slot.attn_btn_cv);
- }
-
- /* check for power fault interrupt */
- if (status & PCIE_SLOTSTS_PWR_FAULT_DETECTED) {
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_intr(): POWER FAULT interrupt received"
- " on slot %d\n", ctrl_p->slot.slotNum));
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- if (control & PCIE_SLOTCTL_PWR_FAULT_EN) {
- /* disable power fault detction interrupt */
- pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset +
- PCIE_SLOTCTL, control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
-
- /* send the event to HPS framework */
- (void) hpc_slot_event_notify(ctrl_p->slot.slot_handle,
- HPC_EVENT_SLOT_POWER_FAULT, HPC_EVENT_NORMAL);
- }
- }
-
- /* check for MRL SENSOR CHANGED interrupt */
- if (status & PCIE_SLOTSTS_MRL_SENSOR_CHANGED) {
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_intr(): MRL SENSOR CHANGED interrupt received"
- " on slot %d\n", ctrl_p->slot.slotNum));
- /* For now (phase-I), no action is taken on this event */
- }
-
- /* check for PRESENCE CHANGED interrupt */
- if (status & PCIE_SLOTSTS_PRESENCE_CHANGED) {
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_intr(): PRESENCE CHANGED interrupt received"
- " on slot %d\n", ctrl_p->slot.slotNum));
-
- if (status & PCIE_SLOTSTS_PRESENCE_DETECTED) {
- /* card is inserted into the slot */
-
- /* send the event to HPS framework */
- (void) hpc_slot_event_notify(ctrl_p->slot.slot_handle,
- HPC_EVENT_SLOT_INSERTION, HPC_EVENT_NORMAL);
- } else {
- /* card is removed from the slot */
-
- /* make sure to disable power fault detction interrupt */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- if (control & PCIE_SLOTCTL_PWR_FAULT_EN)
- pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset +
- PCIE_SLOTCTL, control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
-
- /* send the event to HPS framework */
- (void) hpc_slot_event_notify(ctrl_p->slot.slot_handle,
- HPC_EVENT_SLOT_REMOVAL, HPC_EVENT_NORMAL);
- }
- }
-
- /* check for DLL state changed interrupt */
- if (ctrl_p->dll_active_rep &&
- (status & PCIE_SLOTSTS_DLL_STATE_CHANGED)) {
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_intr(): DLL STATE CHANGED interrupt received"
- " on slot %d\n", ctrl_p->slot.slotNum));
-
- cv_signal(&ctrl_p->slot.dll_active_cv);
- }
-
- mutex_exit(&ctrl_p->pciehpc_mutex);
-
- return (DDI_INTR_CLAIMED);
-}
-
-#ifdef DEBUG
-/*
- * Dump PCI-E Hot Plug registers.
- */
-static void
-pciehpc_dump_hpregs(pciehpc_t *ctrl_p)
-{
- uint16_t control;
- uint32_t capabilities;
-
- capabilities = pciehpc_reg_get32(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCAP);
-
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- cmn_err(CE_NOTE, "pciehpc_dump_hpregs: Found PCI-E hot plug slot %d\n",
- ctrl_p->slot.slotNum);
- cmn_err(CE_NOTE, "Attention Button Present = %s",
- capabilities & PCIE_SLOTCAP_ATTN_BUTTON ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Power controller Present = %s",
- capabilities & PCIE_SLOTCAP_POWER_CONTROLLER ? "Yes":"No");
-
- cmn_err(CE_NOTE, "MRL Sensor Present = %s",
- capabilities & PCIE_SLOTCAP_MRL_SENSOR ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Attn Indicator Present = %s",
- capabilities & PCIE_SLOTCAP_ATTN_INDICATOR ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Power Indicator Present = %s",
- capabilities & PCIE_SLOTCAP_PWR_INDICATOR ? "Yes":"No");
-
- cmn_err(CE_NOTE, "HotPlug Surprise = %s",
- capabilities & PCIE_SLOTCAP_HP_SURPRISE ? "Yes":"No");
-
- cmn_err(CE_NOTE, "HotPlug Capable = %s",
- capabilities & PCIE_SLOTCAP_HP_CAPABLE ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Physical Slot Number = %d",
- PCIE_SLOTCAP_PHY_SLOT_NUM(capabilities));
-
- cmn_err(CE_NOTE, "Attn Button interrupt Enabled = %s",
- control & PCIE_SLOTCTL_ATTN_BTN_EN ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Power Fault interrupt Enabled = %s",
- control & PCIE_SLOTCTL_PWR_FAULT_EN ? "Yes":"No");
-
- cmn_err(CE_NOTE, "MRL Sensor INTR Enabled = %s",
- control & PCIE_SLOTCTL_MRL_SENSOR_EN ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Presence interrupt Enabled = %s",
- control & PCIE_SLOTCTL_PRESENCE_CHANGE_EN ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Cmd Complete interrupt Enabled = %s",
- control & PCIE_SLOTCTL_CMD_INTR_EN ? "Yes":"No");
-
- cmn_err(CE_NOTE, "HotPlug interrupt Enabled = %s",
- control & PCIE_SLOTCTL_HP_INTR_EN ? "Yes":"No");
-
- cmn_err(CE_NOTE, "Power Indicator LED = %s", pciehpc_led_state_text(
- pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control))));
-
- cmn_err(CE_NOTE, "Attn Indicator LED = %s",
- pciehpc_led_state_text(pciehpc_led_state_to_hpc(
- pcie_slotctl_attn_indicator_get(control))));
-}
-
-static char *
-pciehpc_led_state_text(hpc_led_state_t state)
-{
- switch (state) {
- case HPC_LED_ON:
- return ("on");
- case HPC_LED_OFF:
- return ("off");
- case HPC_LED_BLINK:
- default:
- return ("blink");
- }
-}
-#endif /* DEBUG */
-
-/*
- * pciehpc_slot_connect()
- *
- * Connect power to the PCI-E slot.
- *
- * Returns: HPC_SUCCESS if the slot is powered up and enabled.
- * HPC_ERR_FAILED if the slot can't be enabled.
- *
- * (Note: This function is called by HPS framework at kernel context only.)
- */
-/*ARGSUSED*/
-int
-pciehpc_slot_connect(caddr_t ops_arg, hpc_slot_t slot_hdl,
- void *data, uint_t flags)
-{
- uint16_t status, control;
-
- pciehpc_t *ctrl_p = (pciehpc_t *)ops_arg;
-
- ASSERT(slot_hdl == ctrl_p->slot.slot_handle);
-
- mutex_enter(&ctrl_p->pciehpc_mutex);
-
- /* get the current state of the slot */
- pciehpc_get_slot_state(ctrl_p);
-
- /* check if the slot is already in the 'connected' state */
- if (ctrl_p->slot.slot_state == HPC_SLOT_CONNECTED) {
- /* slot is already in the 'connected' state */
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_slot_connect() slot %d already connected\n",
- ctrl_p->slot.slotNum));
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_SUCCESS);
- }
-
- /* read the Slot Status Register */
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- /* make sure the MRL switch is closed if present */
- if ((ctrl_p->has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) {
- /* MRL switch is open */
- cmn_err(CE_WARN, "MRL switch is open on slot %d\n",
- ctrl_p->slot.slotNum);
- goto cleanup;
- }
-
- /* make sure the slot has a device present */
- if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
- /* slot is empty */
- PCIEHPC_DEBUG((CE_NOTE,
- "slot %d is empty\n", ctrl_p->slot.slotNum));
- goto cleanup;
- }
-
- /* get the current state of Slot Control Register */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- /* check if the slot's power state is ON */
- if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
- /* slot is already powered up */
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_slot_connect() slot %d already connected\n",
- ctrl_p->slot.slotNum));
- ctrl_p->slot.slot_state = HPC_SLOT_CONNECTED;
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_SUCCESS);
- }
-
- /*
- * Enable power to the slot involves:
- * 1. Set power LED to blink and ATTN led to OFF.
- * 2. Set power control ON in Slot Control Reigster and
- * wait for Command Completed Interrupt or 1 sec timeout.
- * 3. If Data Link Layer State Changed events are supported
- * then wait for the event to indicate Data Layer Link
- * is active. The time out value for this event is 1 second.
- * This is specified in PCI-E version 1.1.
- * 4. Set power LED to be ON.
- */
-
- /* 1. set power LED to blink & ATTN led to OFF */
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_BLINK);
- pciehpc_set_led_state(ctrl_p, HPC_ATTN_LED, HPC_LED_OFF);
-
- /* 2. set power control to ON */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- control &= ~PCIE_SLOTCTL_PWR_CONTROL;
- pciehpc_issue_hpc_command(ctrl_p, control);
-
- /* 3. wait for DLL State Change event, if it's supported */
- if (ctrl_p->dll_active_rep) {
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_LINKSTS);
-
- if (!(status & PCIE_LINKSTS_DLL_LINK_ACTIVE)) {
- /* wait 1 sec for the DLL State Changed event */
- (void) cv_timedwait(&ctrl_p->slot.dll_active_cv,
- &ctrl_p->pciehpc_mutex,
- ddi_get_lbolt() +
- SEC_TO_TICK(PCIEHPC_DLL_STATE_CHANGE_TIMEOUT));
-
- /* check Link status */
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset +
- PCIE_LINKSTS);
- if (!(status & PCIE_LINKSTS_DLL_LINK_ACTIVE))
- goto cleanup2;
- }
- }
-
- /* wait 1 sec for link to come up */
- delay(drv_usectohz(1000000));
-
- /* check power is really turned ON */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- if (control & PCIE_SLOTCTL_PWR_CONTROL) {
- PCIEHPC_DEBUG((CE_NOTE,
- "slot %d fails to turn on power on connect\n",
- ctrl_p->slot.slotNum));
-
- goto cleanup1;
- }
-
- /* clear power fault status */
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
- status |= PCIE_SLOTSTS_PWR_FAULT_DETECTED;
- pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS,
- status);
-
- /* enable power fault detection interrupt */
- control |= PCIE_SLOTCTL_PWR_FAULT_EN;
- pciehpc_issue_hpc_command(ctrl_p, control);
-
- /* 4. Set power LED to be ON */
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_ON);
-
- /* if EMI is present, turn it ON */
- if (ctrl_p->has_emi_lock) {
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- if (!(status & PCIE_SLOTSTS_EMI_LOCK_SET)) {
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
- pciehpc_issue_hpc_command(ctrl_p, control);
-
- /* wait 1 sec after toggling the state of EMI lock */
- delay(drv_usectohz(1000000));
- }
- }
-
- ctrl_p->slot.slot_state = HPC_SLOT_CONNECTED;
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_SUCCESS);
-
-cleanup2:
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- /* if power is ON, set power control to OFF */
- if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
- control |= PCIE_SLOTCTL_PWR_CONTROL;
- pciehpc_issue_hpc_command(ctrl_p, control);
- }
-
-cleanup1:
- /* set power led to OFF */
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_OFF);
-
-cleanup:
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_ERR_FAILED);
-}
-
-/*
- * pciehpc_slot_disconnect()
- *
- * Disconnect power to the slot.
- *
- * Returns: HPC_SUCCESS if the slot is powered up and enabled.
- * HPC_ERR_FAILED if the slot can't be enabled.
- *
- * (Note: This function is called by HPS framework at kernel context only.)
- */
-/*ARGSUSED*/
-int
-pciehpc_slot_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl,
- void *data, uint_t flags)
-{
- uint16_t status;
- uint16_t control;
-
- pciehpc_t *ctrl_p = (pciehpc_t *)ops_arg;
-
- ASSERT(slot_hdl == ctrl_p->slot.slot_handle);
-
- mutex_enter(&ctrl_p->pciehpc_mutex);
-
- /* get the current state of the slot */
- pciehpc_get_slot_state(ctrl_p);
-
- /* check if the slot is already in the 'disconnected' state */
- if (ctrl_p->slot.slot_state == HPC_SLOT_DISCONNECTED) {
- /* slot is in the 'disconnected' state */
- PCIEHPC_DEBUG3((CE_NOTE,
- "pciehpc_slot_disconnect(): slot %d already disconnected\n",
- ctrl_p->slot.slotNum));
- ASSERT(ctrl_p->slot.power_led_state == HPC_LED_OFF);
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_SUCCESS);
- }
-
- /* read the Slot Status Register */
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- /* make sure the slot has a device present */
- if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
- /* slot is empty */
- PCIEHPC_DEBUG((CE_NOTE,
- "pciehpc_slot_disconnect(): slot %d is empty\n",
- ctrl_p->slot.slotNum));
- goto cleanup;
- }
-
- /*
- * Disable power to the slot involves:
- * 1. Set power LED to blink.
- * 2. Set power control OFF in Slot Control Reigster and
- * wait for Command Completed Interrupt or 1 sec timeout.
- * 3. Set POWER led and ATTN led to be OFF.
- */
-
- /* 1. set power LED to blink */
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_BLINK);
-
- /* disable power fault detection interrupt */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- control &= ~PCIE_SLOTCTL_PWR_FAULT_EN;
- pciehpc_issue_hpc_command(ctrl_p, control);
-
- /* 2. set power control to OFF */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- control |= PCIE_SLOTCTL_PWR_CONTROL;
- pciehpc_issue_hpc_command(ctrl_p, control);
-
-#ifdef DEBUG
- /* check for power control bit to be OFF */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- ASSERT(control & PCIE_SLOTCTL_PWR_CONTROL);
-#endif
-
- /* 3. Set power LED to be OFF */
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_OFF);
- pciehpc_set_led_state(ctrl_p, HPC_ATTN_LED, HPC_LED_OFF);
-
- /* if EMI is present, turn it OFF */
- if (ctrl_p->has_emi_lock) {
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- if (status & PCIE_SLOTSTS_EMI_LOCK_SET) {
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
- control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
- pciehpc_issue_hpc_command(ctrl_p, control);
-
- /* wait 1 sec after toggling the state of EMI lock */
- delay(drv_usectohz(1000000));
- }
- }
-
- ctrl_p->slot.slot_state = HPC_SLOT_DISCONNECTED;
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_SUCCESS);
-
-cleanup:
- mutex_exit(&ctrl_p->pciehpc_mutex);
- return (HPC_ERR_FAILED);
-}
-
-/*ARGSUSED*/
-int
-pciehpc_slot_control(caddr_t ops_arg, hpc_slot_t slot_hdl,
- int request, caddr_t arg)
-{
- pciehpc_t *ctrl_p;
- hpc_led_info_t *led_info;
- int ret = HPC_SUCCESS;
-
- ctrl_p = (pciehpc_t *)ops_arg;
-
- ASSERT(ctrl_p != NULL);
-
- mutex_enter(&ctrl_p->pciehpc_mutex);
-
- /* get the current slot state */
- pciehpc_get_slot_state(ctrl_p);
-
- switch (request) {
-
- case HPC_CTRL_GET_SLOT_STATE:
- *(hpc_slot_state_t *)arg = ctrl_p->slot.slot_state;
- break;
-
- case HPC_CTRL_GET_BOARD_TYPE:
- if (ctrl_p->slot.slot_state == HPC_SLOT_EMPTY)
- *(hpc_board_type_t *)arg = HPC_BOARD_UNKNOWN;
- else
- *(hpc_board_type_t *)arg = HPC_BOARD_PCI_HOTPLUG;
- break;
-
- case HPC_CTRL_GET_LED_STATE:
- led_info = (hpc_led_info_t *)arg;
- switch (led_info->led) {
- case HPC_ATTN_LED:
- led_info->state = ctrl_p->slot.attn_led_state;
- break;
- case HPC_POWER_LED:
- led_info->state = ctrl_p->slot.power_led_state;
- break;
- case HPC_FAULT_LED:
- case HPC_ACTIVE_LED:
- led_info->state = HPC_LED_OFF;
- break;
- default:
- PCIEHPC_DEBUG((CE_WARN, "pciehpc_slot_control:"
- " unknown led state\n"));
- ret = HPC_ERR_NOTSUPPORTED;
- break;
- }
- break;
- case HPC_CTRL_SET_LED_STATE:
- led_info = (hpc_led_info_t *)arg;
- switch (led_info->led) {
- case HPC_ATTN_LED:
- pciehpc_set_led_state(ctrl_p, led_info->led,
- led_info->state);
- break;
- case HPC_POWER_LED:
- PCIEHPC_DEBUG((CE_WARN, "pciehpc_slot_control: power"
- " LED control is not allowed on slot #%d\n",
- ctrl_p->slot.slotNum));
- ret = HPC_ERR_NOTSUPPORTED;
- break;
- case HPC_FAULT_LED:
- case HPC_ACTIVE_LED:
- break;
- default:
- PCIEHPC_DEBUG((CE_WARN, "pciehpc_slot_control:"
- " unknown led type %d\n", led_info->led));
- ret = HPC_ERR_NOTSUPPORTED;
- break;
- }
- break;
- case HPC_CTRL_DEV_CONFIG_FAILURE:
- /* turn the ATTN led ON for configure failure */
- pciehpc_set_led_state(ctrl_p, HPC_ATTN_LED, HPC_LED_ON);
- /* if power to the slot is still on then set Power led to ON */
- if (ctrl_p->slot.slot_state == HPC_SLOT_CONNECTED)
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_ON);
- break;
- case HPC_CTRL_DEV_UNCONFIG_FAILURE:
- /* if power to the slot is still on then set Power led to ON */
- if (ctrl_p->slot.slot_state == HPC_SLOT_CONNECTED)
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_ON);
- pciehpc_enable_errors(ctrl_p);
- break;
- case HPC_CTRL_ENABLE_AUTOCFG:
- case HPC_CTRL_DISABLE_AUTOCFG:
- /* no action is needed here */
- break;
-
- case HPC_CTRL_DISABLE_SLOT:
- case HPC_CTRL_ENABLE_SLOT:
- /* no action is needed here */
- break;
-
- case HPC_CTRL_DEV_CONFIG_START:
- case HPC_CTRL_DEV_UNCONFIG_START:
- pciehpc_disable_errors(ctrl_p);
- /* no action is needed here */
- break;
- case HPC_CTRL_DEV_CONFIGURED:
- case HPC_CTRL_DEV_UNCONFIGURED:
- /* no action is needed here */
- if (request == HPC_CTRL_DEV_CONFIGURED) {
- pciehpc_enable_errors(ctrl_p);
- }
- break;
- default:
- PCIEHPC_DEBUG((CE_WARN,
- "pciehpc_slot_control: unsupported operation\n"));
- ret = HPC_ERR_NOTSUPPORTED;
- }
-
- mutex_exit(&ctrl_p->pciehpc_mutex);
-
- return (ret);
-}
-
-/*
- * Get the state of an LED.
- */
-hpc_led_state_t
-pciehpc_get_led_state(pciehpc_t *ctrl_p, hpc_led_t led)
-{
- uint16_t control;
- uint16_t state;
-
- /* get the current state of Slot Control register */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- switch (led) {
- case HPC_POWER_LED:
- state = pcie_slotctl_pwr_indicator_get(control);
- break;
- case HPC_ATTN_LED:
- state = pcie_slotctl_attn_indicator_get(control);
- break;
- default:
- PCIEHPC_DEBUG((CE_WARN,
- "pciehpc_get_led_state() invalid LED %d\n", led));
- return (HPC_LED_OFF);
- }
-
- switch (state) {
- case PCIE_SLOTCTL_INDICATOR_STATE_ON:
- return (HPC_LED_ON);
-
- case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
- return (HPC_LED_BLINK);
-
- case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
- default:
- return (HPC_LED_OFF);
- }
-}
-
-/*
- * Set the state of an LED. It updates both hw and sw state.
- */
-void
-pciehpc_set_led_state(pciehpc_t *ctrl_p, hpc_led_t led, hpc_led_state_t state)
-{
- uint16_t control;
-
- /* get the current state of Slot Control register */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- switch (led) {
- case HPC_POWER_LED:
- /* clear led mask */
- control &= ~PCIE_SLOTCTL_PWR_INDICATOR_MASK;
- ctrl_p->slot.power_led_state = state;
- break;
- case HPC_ATTN_LED:
- /* clear led mask */
- control &= ~PCIE_SLOTCTL_ATTN_INDICATOR_MASK;
- ctrl_p->slot.attn_led_state = state;
- break;
- default:
- PCIEHPC_DEBUG((CE_WARN,
- "pciehpc_set_led_state() invalid LED %d\n", led));
- return;
- }
-
- switch (state) {
- case HPC_LED_ON:
- if (led == HPC_POWER_LED)
- control = pcie_slotctl_pwr_indicator_set(control,
- PCIE_SLOTCTL_INDICATOR_STATE_ON);
- else if (led == HPC_ATTN_LED)
- control = pcie_slotctl_attn_indicator_set(control,
- PCIE_SLOTCTL_INDICATOR_STATE_ON);
- break;
- case HPC_LED_OFF:
- if (led == HPC_POWER_LED)
- control = pcie_slotctl_pwr_indicator_set(control,
- PCIE_SLOTCTL_INDICATOR_STATE_OFF);
- else if (led == HPC_ATTN_LED)
- control = pcie_slotctl_attn_indicator_set(control,
- PCIE_SLOTCTL_INDICATOR_STATE_OFF);
- break;
- case HPC_LED_BLINK:
- if (led == HPC_POWER_LED)
- control = pcie_slotctl_pwr_indicator_set(control,
- PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
- else if (led == HPC_ATTN_LED)
- control = pcie_slotctl_attn_indicator_set(control,
- PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
- break;
-
- default:
- PCIEHPC_DEBUG((CE_WARN,
- "pciehpc_set_led_state() invalid LED state %d\n", state));
- return;
- }
-
- /* update the Slot Control Register */
- pciehpc_issue_hpc_command(ctrl_p, control);
-
-#ifdef DEBUG
- /* get the current state of Slot Control register */
- control = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_set_led_state: "
- "slot %d power-led %s attn-led %s\n",
- ctrl_p->slot.slotNum,
- pciehpc_led_state_text(
- pciehpc_led_state_to_hpc(
- pcie_slotctl_pwr_indicator_get(control))),
- pciehpc_led_state_text(
- pciehpc_led_state_to_hpc(
- pcie_slotctl_attn_indicator_get(control)))));
-#endif
-}
-
-/*
- * Send a command to the PCI-E Hot Plug Controller.
- *
- * NOTES: The PCI-E spec defines the following semantics for issuing hot plug
- * commands.
- * 1) If Command Complete events/interrupts are supported then software
- * waits for Command Complete event after issuing a command (i.e writing
- * to the Slot Control register). The command completion could take as
- * long as 1 second so software should be prepared to wait for 1 second
- * before issuing another command.
- *
- * 2) If Command Complete events/interrupts are not supported then
- * software could issue multiple Slot Control writes without any delay
- * between writes.
- */
-void
-pciehpc_issue_hpc_command(pciehpc_t *ctrl_p, uint16_t control)
-{
-
- uint16_t status;
- uint32_t slot_cap;
-
- /*
- * PCI-E version 1.1 spec defines No Command Completed
- * Support bit (bit#18) in Slot Capabilities register. If this
- * bit is set then slot doesn't support notification of command
- * completion events.
- */
- slot_cap = pciehpc_reg_get32(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCAP);
- /*
- * If no Command Completion event is supported or it is ACPI
- * hot plug mode then just issue the command and return.
- */
- if ((slot_cap & PCIE_SLOTCAP_NO_CMD_COMP_SUPP) ||
- (ctrl_p->hp_mode == PCIEHPC_ACPI_HP_MODE)) {
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL, control);
- return;
- }
-
- /*
- * **************************************
- * Command Complete events are supported.
- * **************************************
- */
-
- /*
- * If HPC is not yet initialized then just poll for the Command
- * Completion interrupt.
- */
- if (!(ctrl_p->soft_state & PCIEHPC_SOFT_STATE_INITIALIZED)) {
- int retry = PCIEHPC_CMD_WAIT_RETRY;
-
- /* write the command to the HPC */
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL,
- control);
-
- /* poll for status completion */
- while (retry--) {
- /* wait for 10 msec before checking the status */
- delay(drv_usectohz(PCIEHPC_CMD_WAIT_TIME));
-
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
- /* clear the status bits */
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS,
- status);
- break;
- }
- }
- return;
- }
-
- /* HPC is already initialized */
-
- ASSERT(MUTEX_HELD(&ctrl_p->pciehpc_mutex));
-
- /*
- * If previous command is still pending then wait for its
- * completion. i.e cv_wait()
- */
-
- while (ctrl_p->slot.command_pending == B_TRUE)
- cv_wait(&ctrl_p->slot.cmd_comp_cv, &ctrl_p->pciehpc_mutex);
-
- /*
- * Issue the command and wait for Command Completion or
- * the 1 sec timeout.
- */
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL, control);
-
- ctrl_p->slot.command_pending = B_TRUE;
-
- if (cv_timedwait(&ctrl_p->slot.cmd_comp_cv, &ctrl_p->pciehpc_mutex,
- ddi_get_lbolt() + SEC_TO_TICK(1)) == -1) {
- /* it is a timeout */
- PCIEHPC_DEBUG2((CE_NOTE,
- "pciehpc_issue_hpc_command: Command Complete"
- " interrupt is not received for slot %d\n",
- ctrl_p->slot.slotNum));
-
- /* clear the status info in case interrupts are disabled? */
- status = pciehpc_reg_get16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
-
- if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
- /* clear the status bits */
- pciehpc_reg_put16(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS,
- status);
- }
- }
-
- ctrl_p->slot.command_pending = B_FALSE;
-
- /* wake up any one waiting for issuing another command to HPC */
- cv_signal(&ctrl_p->slot.cmd_comp_cv);
-}
-
-/*
- * pciehcp_attn_btn_handler()
- *
- * This handles ATTN button pressed event as per the PCI-E 1.1 spec.
- */
-static void
-pciehpc_attn_btn_handler(pciehpc_t *ctrl_p)
-{
- hpc_led_state_t power_led_state;
- callb_cpr_t cprinfo;
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_attn_btn_handler: thread started\n"));
-
- CALLB_CPR_INIT(&cprinfo, &ctrl_p->pciehpc_mutex, callb_generic_cpr,
- "pciehpc_attn_btn_handler");
-
- mutex_enter(&ctrl_p->pciehpc_mutex);
-
- /* wait for ATTN button event */
- cv_wait(&ctrl_p->slot.attn_btn_cv, &ctrl_p->pciehpc_mutex);
-
- while (ctrl_p->slot.attn_btn_thread_exit == B_FALSE) {
-
- if (ctrl_p->slot.attn_btn_pending == B_TRUE) {
-
- /* get the current state of power LED */
- power_led_state = pciehpc_get_led_state(ctrl_p, HPC_POWER_LED);
-
- /* Blink the Power LED while we wait for 5 seconds */
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_BLINK);
-
- /* wait for 5 seconds before taking any action */
- if (cv_timedwait(&ctrl_p->slot.attn_btn_cv,
- &ctrl_p->pciehpc_mutex,
- ddi_get_lbolt() + SEC_TO_TICK(5)) == -1) {
- /*
- * It is a time out; make sure the ATTN pending flag is
- * still ON before sending the event to HPS framework.
- */
- if (ctrl_p->slot.attn_btn_pending == B_TRUE) {
- /* send the ATTN button event to HPS framework */
- ctrl_p->slot.attn_btn_pending = B_FALSE;
- (void) hpc_slot_event_notify(
- ctrl_p->slot.slot_handle,
- HPC_EVENT_SLOT_ATTN, HPC_EVENT_NORMAL);
- }
- }
- /* restore the power LED state */
- pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, power_led_state);
- continue;
- }
-
- /* wait for another ATTN button event */
- cv_wait(&ctrl_p->slot.attn_btn_cv, &ctrl_p->pciehpc_mutex);
- }
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_attn_btn_handler: thread exit\n"));
- cv_signal(&ctrl_p->slot.attn_btn_cv);
- CALLB_CPR_EXIT(&cprinfo);
- thread_exit();
-}
-
-/*
- * Read/Write access to HPC registers. If platform nexus has non-standard
- * HPC access mechanism then regops functions are used to do reads/writes.
- */
-uint8_t
-pciehpc_reg_get8(pciehpc_t *ctrl_p, uint_t off)
-{
- PCIEHPC_DEBUG3((CE_NOTE, "read reg8 (offset %x)", off));
-
- if (ctrl_p->regops.get != NULL)
- return ((uint8_t)ctrl_p->regops.get(ctrl_p->regops.cookie,
- (off_t)off));
- else
- return (ddi_get8(ctrl_p->cfghdl,
- (uint8_t *)(ctrl_p->regs_base + off)));
-}
-
-uint16_t
-pciehpc_reg_get16(pciehpc_t *ctrl_p, uint_t off)
-{
- PCIEHPC_DEBUG3((CE_NOTE, "read reg16 (offset %x)", off));
-
- if (ctrl_p->regops.get != NULL)
- return ((uint16_t)ctrl_p->regops.get(ctrl_p->regops.cookie,
- (off_t)off));
- else
- return (ddi_get16(ctrl_p->cfghdl,
- (uint16_t *)(ctrl_p->regs_base + off)));
-}
-
-uint32_t
-pciehpc_reg_get32(pciehpc_t *ctrl_p, uint_t off)
-{
- PCIEHPC_DEBUG3((CE_NOTE, "read reg32 (offset %x)", off));
-
- if (ctrl_p->regops.get != NULL)
- return ((uint32_t)ctrl_p->regops.get(ctrl_p->regops.cookie,
- (off_t)off));
- else
- return (ddi_get32(ctrl_p->cfghdl,
- (uint32_t *)(ctrl_p->regs_base + off)));
-}
-
-void
-pciehpc_reg_put8(pciehpc_t *ctrl_p, uint_t off, uint8_t val)
-{
- PCIEHPC_DEBUG3((CE_NOTE, "write reg8 (offset %x, val %x)",
- off, val));
-
- if (ctrl_p->regops.put != NULL)
- ctrl_p->regops.put(ctrl_p->regops.cookie, (off_t)off, (uint_t)val);
- else
- ddi_put8(ctrl_p->cfghdl,
- (uint8_t *)(ctrl_p->regs_base + off), val);
-}
-
-void
-pciehpc_reg_put16(pciehpc_t *ctrl_p, uint_t off, uint16_t val)
-{
- PCIEHPC_DEBUG3((CE_NOTE, "write reg16 (offset %x, val %x)",
- off, val));
-
- if (ctrl_p->regops.put != NULL)
- ctrl_p->regops.put(ctrl_p->regops.cookie, (off_t)off, (uint_t)val);
- else
- ddi_put16(ctrl_p->cfghdl,
- (uint16_t *)(ctrl_p->regs_base + off), val);
-}
-
-void
-pciehpc_reg_put32(pciehpc_t *ctrl_p, uint_t off, uint32_t val)
-{
- PCIEHPC_DEBUG3((CE_NOTE, "write reg32 (offset %x, val %x)",
- off, val));
-
- if (ctrl_p->regops.put != NULL)
- ctrl_p->regops.put(ctrl_p->regops.cookie, (off_t)off, (uint_t)val);
- else
- ddi_put32(ctrl_p->cfghdl,
- (uint32_t *)(ctrl_p->regs_base + off), val);
-}
-
-static void
-pciehpc_dev_info(pciehpc_t *ctrl_p)
-{
- pci_regspec_t *regspec;
- int reglen;
- dev_info_t *dip = ctrl_p->dip;
-
- /*
- * Check if it is a PCIe fabric hotplug nexus. This is specially
- * not so for Rootcomplex nodes supporting PCIe hotplug.
- * We save this information so as to implement hardening for
- * fabric nodes only via pcie services.
- */
- if (pciehpc_pcie_dev(dip, ctrl_p->cfghdl) == DDI_SUCCESS)
- ctrl_p->soft_state |= PCIEHPC_SOFT_STATE_PCIE_DEV;
-
- /* Get the device number. */
- if (ddi_getlongprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
- "reg", (caddr_t)&regspec, &reglen) != DDI_SUCCESS) {
- return;
- }
-
- ctrl_p->bus = PCI_REG_BUS_G(regspec[0].pci_phys_hi);
- ctrl_p->dev = PCI_REG_DEV_G(regspec[0].pci_phys_hi);
- ctrl_p->func = PCI_REG_FUNC_G(regspec[0].pci_phys_hi);
-
- kmem_free(regspec, reglen);
-
- PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_dev_info: bus=%x, dev=%x, func=%x",
- ctrl_p->bus, ctrl_p->dev, ctrl_p->func));
-}
-
-/*
- * setup slot name/slot-number info.
- */
-void
-pciehpc_set_slot_name(pciehpc_t *ctrl_p)
-{
- pciehpc_slot_t *p = &ctrl_p->slot;
- uchar_t *slotname_data;
- int *slotnum;
- uint_t count;
- int len;
- int invalid_slotnum = 0;
- uint32_t slot_capabilities;
-
- if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->dip,
- DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) ==
- DDI_PROP_SUCCESS) {
- p->slotNum = slotnum[0];
- ddi_prop_free(slotnum);
- } else {
- slot_capabilities = pciehpc_reg_get32(ctrl_p,
- ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCAP);
- p->slotNum = PCIE_SLOTCAP_PHY_SLOT_NUM(slot_capabilities);
- }
-
- if (!p->slotNum) { /* platform may not have initialized it */
- PCIEHPC_DEBUG((CE_WARN, "%s#%d: Invalid slot number! ",
- ddi_driver_name(ctrl_p->dip),
- ddi_get_instance(ctrl_p->dip)));
- p->slotNum = pciehpc_reg_get8(ctrl_p, PCI_BCNF_SECBUS);
- invalid_slotnum = 1;
- }
-
- /*
- * construct the slot_name:
- * if "slot-names" property exists then use that name
- * else if valid slot number exists then it is "pcie<slot-num>".
- * else it will be "pcie<sec-bus-number>dev0"
- */
- if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->dip, DDI_PROP_DONTPASS,
- "slot-names", (caddr_t)&slotname_data,
- &len) == DDI_PROP_SUCCESS) {
- /*
- * Note: for PCI-E slots, the device number is always 0 so the
- * first (and only) string is the slot name for this slot.
- */
- (void) sprintf(p->slot_info.pci_slot_name,
- (char *)slotname_data + 4);
- kmem_free(slotname_data, len);
- } else {
- if (invalid_slotnum) /* use device number ie. 0 */
- (void) snprintf(p->slot_info.pci_slot_name,
- sizeof (p->slot_info.pci_slot_name), "pcie0");
- else
- (void) snprintf(p->slot_info.pci_slot_name,
- sizeof (p->slot_info.pci_slot_name), "pcie%d",
- p->slotNum);
- }
-}
-
-/*ARGSUSED*/
-static int
-pciehpc_pcie_dev(dev_info_t *dip, ddi_acc_handle_t handle)
-{
- /* get parent device's device_type property */
- char *device_type;
- int rc;
- dev_info_t *pdip = ddi_get_parent(dip);
-
- if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
- DDI_PROP_DONTPASS, "device_type", &device_type)
- != DDI_PROP_SUCCESS) {
- PCIEHPC_DEBUG2((CE_NOTE, "device_type property missing for "
- "%s#%d", ddi_get_name(pdip), ddi_get_instance(pdip)));
- return (DDI_FAILURE);
- }
-
- PCIEHPC_DEBUG((CE_NOTE, "device_type=<%s>\n", device_type));
- rc = DDI_FAILURE;
- if (strcmp(device_type, "pciex") == 0)
- rc = DDI_SUCCESS;
- ddi_prop_free(device_type);
- return (rc);
-}
-
-static void
-pciehpc_disable_errors(pciehpc_t *ctrl_p)
-{
- if (ctrl_p->soft_state & PCIEHPC_SOFT_STATE_PCIE_DEV) {
- PCIE_DISABLE_ERRORS(ctrl_p->dip);
- PCIEHPC_DEBUG3((CE_NOTE, "%s%d: pciehpc_disable_errors\n",
- ddi_driver_name(ctrl_p->dip),
- ddi_get_instance(ctrl_p->dip)));
- }
-}
-
-static void
-pciehpc_enable_errors(pciehpc_t *ctrl_p)
-{
- if (ctrl_p->soft_state & PCIEHPC_SOFT_STATE_PCIE_DEV) {
- (void) PCIE_ENABLE_ERRORS(ctrl_p->dip);
- PCIEHPC_DEBUG3((CE_NOTE, "%s%d: pciehpc_enable_errors\n",
- ddi_driver_name(ctrl_p->dip),
- ddi_get_instance(ctrl_p->dip)));
- }
-}
diff --git a/usr/src/uts/common/io/hotplug/pcihp/pcihp.c b/usr/src/uts/common/io/hotplug/pcihp/pcihp.c
index 0faa5b8bdf..d84b72c7cb 100644
--- a/usr/src/uts/common/io/hotplug/pcihp/pcihp.c
+++ b/usr/src/uts/common/io/hotplug/pcihp/pcihp.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -258,9 +258,6 @@ static int pcihp_get_board_type(struct pcihp_slotinfo *);
/* sysevent function */
static void pcihp_gen_sysevent(char *, int, int, dev_info_t *, int);
-extern int pcicfg_configure(dev_info_t *, uint_t);
-extern int pcicfg_unconfigure(dev_info_t *, uint_t);
-
static int pcihp_list_occupants(dev_info_t *, void *);
static int pcihp_indirect_map(dev_info_t *dip);
@@ -1558,7 +1555,8 @@ pcihp_configure_ap(pcihp_t *pcihp_p, int pci_dev)
/*
* Call the configurator to configure the card.
*/
- if (pcicfg_configure(self, pci_dev) != PCICFG_SUCCESS) {
+ if (pcicfg_configure(self, pci_dev, PCICFG_ALL_FUNC, 0)
+ != PCICFG_SUCCESS) {
if (slotinfop->slot_type & HPC_SLOT_TYPE_CPCI) {
if (pcihp_cpci_blue_led)
pcihp_hs_csr_op(pcihp_p, pci_dev,
@@ -1717,8 +1715,8 @@ pcihp_unconfigure_ap(pcihp_t *pcihp_p, int pci_dev)
(void) hpc_nexus_control(slotinfop->slot_hdl,
HPC_CTRL_DEV_UNCONFIG_START, NULL);
- if (pcicfg_unconfigure(self,
- pci_dev) == PCICFG_SUCCESS) {
+ if (pcicfg_unconfigure(self, pci_dev,
+ PCICFG_ALL_FUNC, 0) == PCICFG_SUCCESS) {
/*
* Now that resources are freed,
* clear EXT and Turn LED ON.
@@ -2155,7 +2153,8 @@ pcihp_new_slot_state(dev_info_t *dip, hpc_slot_t hdl,
/*
* Call the configurator to configure the card.
*/
- if (pcicfg_configure(dip, pci_dev) != PCICFG_SUCCESS) {
+ if (pcicfg_configure(dip, pci_dev, PCICFG_ALL_FUNC, 0)
+ != PCICFG_SUCCESS) {
if (slotinfop->slot_type & HPC_SLOT_TYPE_CPCI) {
if (pcihp_cpci_blue_led)
pcihp_hs_csr_op(pcihp_p,
@@ -2465,7 +2464,8 @@ pcihp_event_handler(caddr_t slot_arg, uint_t event_mask)
/*
* Call the configurator to configure the card.
*/
- if (pcicfg_configure(pcihp_p->dip, pci_dev) != PCICFG_SUCCESS) {
+ if (pcicfg_configure(pcihp_p->dip, pci_dev, PCICFG_ALL_FUNC, 0)
+ != PCICFG_SUCCESS) {
if (slotinfop->slot_type & HPC_SLOT_TYPE_CPCI) {
if (pcihp_cpci_blue_led)
pcihp_hs_csr_op(pcihp_p, pci_dev,
@@ -2611,8 +2611,8 @@ pcihp_event_handler(caddr_t slot_arg, uint_t event_mask)
(void) hpc_nexus_control(slotinfop->slot_hdl,
HPC_CTRL_DEV_UNCONFIG_START, NULL);
- if (pcicfg_unconfigure(pcihp_p->dip,
- pci_dev) == PCICFG_SUCCESS) {
+ if (pcicfg_unconfigure(pcihp_p->dip, pci_dev,
+ PCICFG_ALL_FUNC, 0) == PCICFG_SUCCESS) {
/* Resources freed. Turn LED on. Clear EXT. */
if (slotinfop->slot_type & HPC_SLOT_TYPE_CPCI) {
diff --git a/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c b/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c
deleted file mode 100644
index 0ceac54b34..0000000000
--- a/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c
+++ /dev/null
@@ -1,2656 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/*
- * PCISHPC - The Standard PCI HotPlug Controller driver module. This driver
- * can be used with PCI HotPlug controllers that are compatible
- * with the PCI SHPC specification 1.x.
- */
-
-#include <sys/note.h>
-#include <sys/conf.h>
-#include <sys/kmem.h>
-#include <sys/kstat.h>
-#include <sys/debug.h>
-#include <sys/vtrace.h>
-#include <sys/modctl.h>
-#include <sys/autoconf.h>
-#include <sys/varargs.h>
-#include <sys/hwconf.h>
-#include <sys/ddi_impldefs.h>
-#include <sys/pci.h>
-#include <sys/callb.h>
-#include <sys/ddi.h>
-#include <sys/sunddi.h>
-#include <sys/sunndi.h>
-#include <sys/ndi_impldefs.h>
-#include <sys/hotplug/pci/pcishpc.h>
-#include <sys/hotplug/pci/pcishpc_regs.h>
-#include <sys/hotplug/hpcsvc.h>
-
-
-/* General Register bit weights for the 32-bit SHPC registers */
-#define REG_BIT0 0x00000001
-#define REG_BIT1 0x00000002
-#define REG_BIT2 0x00000004
-#define REG_BIT3 0x00000008
-#define REG_BIT4 0x00000010
-#define REG_BIT5 0x00000020
-#define REG_BIT6 0x00000040
-#define REG_BIT7 0x00000080
-#define REG_BIT8 0x00000100
-#define REG_BIT9 0x00000200
-#define REG_BIT10 0x00000400
-#define REG_BIT11 0x00000800
-#define REG_BIT12 0x00001000
-#define REG_BIT13 0x00002000
-#define REG_BIT14 0x00004000
-#define REG_BIT15 0x00008000
-#define REG_BIT16 0x00010000
-#define REG_BIT17 0x00020000
-#define REG_BIT18 0x00040000
-#define REG_BIT19 0x00080000
-#define REG_BIT20 0x00100000
-#define REG_BIT21 0x00200000
-#define REG_BIT22 0x00400000
-#define REG_BIT23 0x00800000
-#define REG_BIT24 0x01000000
-#define REG_BIT25 0x02000000
-#define REG_BIT26 0x04000000
-#define REG_BIT27 0x08000000
-#define REG_BIT28 0x10000000
-#define REG_BIT29 0x20000000
-#define REG_BIT30 0x40000000
-#define REG_BIT31 0x80000000
-
-/* Definitions used with the SHPC SHPC_SLOTS_AVAIL_I_REG register */
-#define SHPC_AVAIL_33MHZ_CONV_SPEED_SHIFT 0
-#define SHPC_AVAIL_66MHZ_PCIX_SPEED_SHIFT 8
-#define SHPC_AVAIL_100MHZ_PCIX_SPEED_SHIFT 16
-#define SHPC_AVAIL_133MHZ_PCIX_SPEED_SHIFT 24
-#define SHPC_AVAIL_SPEED_MASK 0x1F
-
-/* Definitions used with the SHPC SHPC_SLOTS_AVAIL_II_REG register */
-#define SHPC_AVAIL_66MHZ_CONV_SPEED_SHIFT 0
-
-/* Register bits used with the SHPC SHPC_PROF_IF_SBCR_REG register */
-#define SHPC_SBCR_33MHZ_CONV_SPEED 0
-#define SHPC_SBCR_66MHZ_CONV_SPEED REG_BIT0
-#define SHPC_SBCR_66MHZ_PCIX_SPEED REG_BIT1
-#define SHPC_SBCR_100MHZ_PCIX_SPEED (REG_BIT0|REG_BIT1)
-#define SHPC_SBCR_133MHZ_PCIX_SPEED REG_BIT2
-#define SHPC_SBCR_SPEED_MASK (REG_BIT0|REG_BIT1|REG_BIT2)
-
-/* Register bits used with the SHPC SHPC_COMMAND_STATUS_REG register */
-#define SHPC_COMM_STS_ERR_INVALID_SPEED REG_BIT19
-#define SHPC_COMM_STS_ERR_INVALID_COMMAND REG_BIT18
-#define SHPC_COMM_STS_ERR_MRL_OPEN REG_BIT17
-#define SHPC_COMM_STS_ERR_MASK (REG_BIT17|REG_BIT18|REG_BIT19)
-#define SHPC_COMM_STS_CTRL_BUSY REG_BIT16
-#define SHPC_COMM_STS_SET_SPEED REG_BIT6
-
-/* Register bits used with the SHPC SHPC_CTRL_SERR_INT_REG register */
-#define SHPC_SERR_INT_GLOBAL_IRQ_MASK REG_BIT0
-#define SHPC_SERR_INT_GLOBAL_SERR_MASK REG_BIT1
-#define SHPC_SERR_INT_CMD_COMPLETE_MASK REG_BIT2
-#define SHPC_SERR_INT_ARBITER_SERR_MASK REG_BIT3
-#define SHPC_SERR_INT_CMD_COMPLETE_IRQ REG_BIT16
-#define SHPC_SERR_INT_ARBITER_IRQ REG_BIT17
-#define SHPC_SERR_INT_MASK_ALL (REG_BIT0|REG_BIT1|REG_BIT2|REG_BIT3)
-
-/* Register bits used with the SHPC SHPC_LOGICAL_SLOT_REGS register */
-#define SHPC_SLOT_POWER_ONLY REG_BIT0
-#define SHPC_SLOT_ENABLED REG_BIT1
-#define SHPC_SLOT_DISABLED (REG_BIT0 | REG_BIT1)
-#define SHPC_SLOT_STATE_MASK (REG_BIT0 | REG_BIT1)
-#define SHPC_SLOT_MRL_STATE_MASK REG_BIT8
-#define SHPC_SLOT_66MHZ_CONV_CAPABLE REG_BIT9
-#define SHPC_SLOT_CARD_EMPTY_MASK (REG_BIT10 | REG_BIT11)
-#define SHPC_SLOT_66MHZ_PCIX_CAPABLE REG_BIT12
-#define SHPC_SLOT_100MHZ_PCIX_CAPABLE REG_BIT13
-#define SHPC_SLOT_133MHZ_PCIX_CAPABLE (REG_BIT12 | REG_BIT13)
-#define SHPC_SLOT_PCIX_CAPABLE_MASK (REG_BIT12 | REG_BIT13)
-#define SHPC_SLOT_PCIX_CAPABLE_SHIFT 12
-#define SHPC_SLOT_PRESENCE_DETECTED REG_BIT16
-#define SHPC_SLOT_ISO_PWR_DETECTED REG_BIT17
-#define SHPC_SLOT_ATTN_DETECTED REG_BIT18
-#define SHPC_SLOT_MRL_DETECTED REG_BIT19
-#define SHPC_SLOT_POWER_DETECTED REG_BIT20
-#define SHPC_SLOT_PRESENCE_MASK REG_BIT24
-#define SHPC_SLOT_ISO_PWR_MASK REG_BIT25
-#define SHPC_SLOT_ATTN_MASK REG_BIT26
-#define SHPC_SLOT_MRL_MASK REG_BIT27
-#define SHPC_SLOT_POWER_MASK REG_BIT28
-#define SHPC_SLOT_MRL_SERR_MASK REG_BIT29
-#define SHPC_SLOT_POWER_SERR_MASK REG_BIT30
-#define SHPC_SLOT_MASK_ALL (REG_BIT24|REG_BIT25|REG_BIT26|\
- REG_BIT27|REG_BIT28|REG_BIT30)
-
-/* Register bits used with the SHPC SHPC_IRQ_LOCATOR_REG register. */
-#define SHPC_IRQ_CMD_COMPLETE REG_BIT0
-#define SHPC_IRQ_SLOT_N_PENDING REG_BIT1
-
-/* Register bits used with the SHPC SHPC_SERR_LOCATOR_REG register. */
-#define SHPC_IRQ_SERR_ARBITER_PENDING REG_BIT0
-#define SHPC_IRQ_SERR_SLOT_N_PENDING REG_BIT1
-
-/* Register bits used with the SHPC SHPC_SLOT_CONFIGURATION_REG register */
-#define SHPC_SLOT_CONFIG_MRL_SENSOR REG_BIT30
-#define SHPC_SLOT_CONFIG_ATTN_BUTTON REG_BIT31
-#define SHPC_SLOT_CONFIG_PHY_SLOT_NUM_SHIFT 16
-#define SHPC_SLOT_CONFIG_PHY_SLOT_NUM_MASK 0x3FF
-#define SHPC_SLOT_CONFIG_PHY_SLOT_NUM(reg) (((reg) >> 16) & 0x3FF)
-
-/* Max PCISHPC controller slots */
-#define MAX_SHPC_SLOTS 31
-
-/* PCISHPC controller command complete delay in microseconds. */
-#define SHPC_COMMAND_WAIT_TIME 10000
-
-/*
- * Power good wait time after issuing a command to change the slot state
- * to power only state.
- */
-#define SHPC_POWER_GOOD_WAIT_TIME 220000
-
-/* reset delay to 1 sec. */
-static int pcishpc_reset_delay = 1000000;
-
-/* PCISHPC controller softstate structure */
-typedef struct pcishpc_ctrl {
- dev_info_t *shpc_dip; /* DIP for SHPC Nexus */
- ddi_acc_handle_t shpc_config_hdl; /* SHPC DDI cfg handle */
- kmutex_t shpc_intr_mutex; /* Interrupt mutex lock */
- boolean_t interrupt_installed; /* Interrupt installed */
- boolean_t command_complete; /* Got a cmd complete IRQ */
- kcondvar_t cmd_comp_cv;
- boolean_t arbiter_timeout; /* Got a Arb timeout IRQ */
- kmutex_t shpc_mutex; /* Mutex for this SHPC */
- char nexus_path[MAXNAMELEN]; /* Pathname of Nexus */
- uint32_t shpc_bus; /* SHPC bus */
- uint32_t shpc_dev; /* SHPC device */
- uint32_t shpc_func; /* SHPC function */
- uint8_t shpc_dword_select; /* SHPC register offset */
- uint8_t shpc_dword_data_reg; /* SHPC data register */
- uint32_t shpc_slots_avail1_reg; /* SHPC Slots Available1 Reg */
- uint32_t shpc_slots_avail2_reg; /* SHPC Slots Available2 Reg */
- uint32_t numSlotsImpl; /* # of HP Slots Implemented */
- uint32_t numSlotsConn; /* # of HP Slots Connected */
- int currBusSpeed; /* Current Bus Speed */
- uint32_t deviceStart; /* 1st PCI Device # */
- uint32_t physStart; /* 1st Phys Device # */
- uint32_t deviceIncreases; /* Device # Increases */
- struct pcishpc *slots[MAX_SHPC_SLOTS]; /* Slot pointers */
- boolean_t has_attn; /* Do we have attn btn? */
- boolean_t has_mrl; /* Do we have MRL? */
- struct pcishpc_ctrl *nextp; /* Linked list pointer */
-} pcishpc_ctrl_t;
-
-/* PCISHPC slot softstate structure */
-typedef struct pcishpc {
- pcishpc_ctrl_t *ctrl; /* SHPC ctrl for this slot */
- hpc_slot_info_t slot_info; /* HPS framework slot info */
- hpc_slot_t slot_handle; /* HPS framework handle */
- hpc_slot_ops_t *slot_ops; /* HPS framework callbacks */
- uint32_t fault_led_state; /* Fault LED state */
- uint32_t power_led_state; /* Power LED state */
- uint32_t attn_led_state; /* Attn LED state */
- uint32_t active_led_state; /* Active LED state */
- hpc_slot_state_t slot_state; /* Slot State */
- uint32_t deviceNum; /* PCI device num for slot */
- uint32_t slotNum; /* SHPC slot number */
- uint32_t phy_slot_num; /* physical slot number */
- uint32_t slot_events; /* Slot event(s) IRQ */
- kcondvar_t attn_btn_cv; /* ATTN button pressed intr */
- boolean_t attn_btn_pending;
- kthread_t *attn_btn_threadp; /* ATTN button event thread */
- boolean_t attn_btn_thread_exit;
- struct pcishpc *nextp; /* Linked list pointer */
-} pcishpc_t;
-/* mutex to protect the shpc_head and shpc_ctrl_head linked lists */
-static kmutex_t pcishpc_list_mutex;
-
-/* Pointer to a linked list of shpc slot softstate structures */
-static pcishpc_t *pcishpc_head = NULL;
-
-/* Pointer to a linked list of shpc controller softstate structures */
-static pcishpc_ctrl_t *pcishpc_ctrl_head = NULL;
-
-/* mutex to protect access to the controller */
-static kmutex_t pcishpc_control_mutex;
-
-/* SHPC static function prototypes */
-static pcishpc_ctrl_t *pcishpc_create_controller(dev_info_t *dip);
-static int pcishpc_destroy_controller(dev_info_t *dip);
-static pcishpc_ctrl_t *pcishpc_get_controller(dev_info_t *dip);
-static pcishpc_t *pcishpc_create_slot(pcishpc_ctrl_t *ctrl_p);
-static int pcishpc_destroy_slots(pcishpc_ctrl_t *ctrl_p);
-static pcishpc_t *pcishpc_hpc_get_slot_state(hpc_slot_t slot);
-static int pcishpc_setup_controller(pcishpc_ctrl_t *ctrl_p);
-static int pcishpc_register_slot(pcishpc_ctrl_t *ctrl_p, int slot);
-static int pcishpc_connect(caddr_t ops_arg,
- hpc_slot_t slot_hdl, void *data,
- uint_t flags);
-static int pcishpc_disconnect(caddr_t ops_arg,
- hpc_slot_t slot_hdl, void *data,
- uint_t flags);
-static int pcishpc_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl,
- int request, caddr_t arg);
-static int pcishpc_setled(pcishpc_t *pcishpc_p, hpc_led_t led,
- hpc_led_state_t state);
-static int pcishpc_set_power_state(pcishpc_t *pcishpc_p,
- hpc_slot_state_t state);
-static int pcishpc_set_bus_speed(pcishpc_t *pcishpc_p);
-static int pcishpc_probe_controller(pcishpc_ctrl_t *pcishpc_p);
-static int pcishpc_get_pci_info(pcishpc_ctrl_t *pcishpc_p);
-static void pcishpc_get_slot_state(pcishpc_t *pcishpc_p);
-static int pcishpc_process_intr(pcishpc_ctrl_t *ctrl_p);
-static int pcishpc_enable_irqs(pcishpc_ctrl_t *ctrl_p);
-static int pcishpc_disable_irqs(pcishpc_ctrl_t *ctrl_p);
-static void pcishpc_set_soft_int(pcishpc_ctrl_t *ctrl_p);
-static int pcishpc_wait_busy(pcishpc_ctrl_t *ctrl_p);
-static int pcishpc_issue_command(pcishpc_ctrl_t *ctrl_p,
- uint32_t cmd_code);
-static int pcishpc_led_shpc_to_hpc(int state);
-static int pcishpc_led_hpc_to_shpc(int state);
-static int pcishpc_slot_shpc_to_hpc(int state);
-static int pcishpc_slot_hpc_to_shpc(int state);
-static char *pcishpc_textledstate(hpc_led_state_t state);
-static char *pcishpc_textslotstate(hpc_slot_state_t state);
-static char *pcishpc_textrequest(int request);
-static int pcishpc_set_slot_state(pcishpc_t *pcishpc_p);
-static void pcishpc_dump_regs(pcishpc_ctrl_t *ctrl_p);
-static void pcishpc_write_reg(pcishpc_ctrl_t *ctrl_p, int reg,
- uint32_t data);
-static uint32_t pcishpc_read_reg(pcishpc_ctrl_t *ctrl_p, int reg);
-static void pcishpc_debug(char *fmt, ...);
-
-static void pcishpc_attn_btn_handler(pcishpc_t *pcishpc_p);
-static void pcishpc_set_slot_name(pcishpc_ctrl_t *ctrl_p, int slot);
-
-static int pcishpc_debug_enabled = 0;
-
-/* Module operations information for the kernel */
-extern struct mod_ops mod_miscops;
-static struct modlmisc modlmisc = {
- &mod_miscops,
- "PCI SHPC hotplug module",
-};
-
-/* Module linkage information for the kernel */
-static struct modlinkage modlinkage = {
- MODREV_1,
- &modlmisc,
- NULL
-};
-
-int
-_init(void)
-{
- int rc;
-
- if ((rc = mod_install(&modlinkage)) != 0) {
- pcishpc_debug("pcishpc: install error=%d", rc);
- return (rc);
- }
-
- /* Init the shpc driver list mutex. */
- mutex_init(&pcishpc_list_mutex, NULL, MUTEX_DRIVER, NULL);
- /* Init the shpc control mutex. */
- mutex_init(&pcishpc_control_mutex, NULL, MUTEX_DRIVER, NULL);
-
- pcishpc_debug("pcishpc: installed");
- return (rc);
-}
-
-int
-_fini(void)
-{
- pcishpc_debug("pcishpc: _fini called()");
- /* XXX - to be fixed later */
- return (EBUSY);
-}
-
-int
-_info(struct modinfo *modinfop)
-{
- pcishpc_debug("pcishpc: _info called()");
- return (mod_info(&modlinkage, modinfop));
-}
-
-
-/*
- * pcishpc_create_controller()
- *
- * This function allocates and creates an SHPC controller state structure
- * and adds it to the linked list of controllers.
- */
-static pcishpc_ctrl_t *
-pcishpc_create_controller(dev_info_t *dip)
-{
- pcishpc_ctrl_t *ctrl_p;
-
- pcishpc_debug("pcishpc: create controller for %s#%d",
- ddi_driver_name(dip), ddi_get_instance(dip));
-
- ctrl_p = kmem_zalloc(sizeof (pcishpc_ctrl_t), KM_SLEEP);
-
- ctrl_p->interrupt_installed = B_FALSE;
- ctrl_p->shpc_dip = dip;
-
- (void) ddi_pathname(dip, ctrl_p->nexus_path);
-
- /* Get the PCI BUS,DEVICE,FUNCTION for this SHPC controller. */
- if (pcishpc_get_pci_info(ctrl_p) != DDI_SUCCESS) {
-
- pcishpc_debug("pcishpc_create_controller() "
- "Error: pcishpc_get_pci_info() failed");
- kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t));
- return (NULL);
- }
-
- if (pci_config_setup(dip, &ctrl_p->shpc_config_hdl) != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_create_controller() "
- "Error: Unable to map SHPC PCI Config registers");
- kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t));
- return (NULL);
- }
-
- /* Make sure the SHPC is listed in the PCI capibilities list. */
- if (pcishpc_probe_controller(ctrl_p) != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_create_controller() "
- "Error: Unable to find SHPC controller");
- pci_config_teardown(&ctrl_p->shpc_config_hdl);
- kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t));
- return (NULL);
- }
-
- /* Init the interrupt mutex */
- mutex_init(&ctrl_p->shpc_intr_mutex, NULL, MUTEX_DRIVER,
- (void *)PCISHPC_INTR_PRI);
-
- /* Interrupts are now enabled. */
- ctrl_p->interrupt_installed = B_TRUE;
-
- /* Init the shpc controller's mutex. */
- mutex_init(&ctrl_p->shpc_mutex, NULL, MUTEX_DRIVER, NULL);
-
- mutex_enter(&pcishpc_list_mutex);
-
- /* Insert new softstate into linked list of current soft states. */
- ctrl_p->nextp = pcishpc_ctrl_head;
- pcishpc_ctrl_head = ctrl_p;
-
- mutex_exit(&pcishpc_list_mutex);
-
- pcishpc_debug("pcishpc_create_controller() success");
-
- return (ctrl_p);
-}
-
-
-/*
- * pcishpc_probe_controller()
- *
- * This function probes to make sure there is indeed an SHPC controller.
- */
-static int
-pcishpc_probe_controller(pcishpc_ctrl_t *ctrl_p)
-{
- uint8_t cap_ptr;
- uint8_t cap_id;
- uint16_t status;
-
- status = pci_config_get16(ctrl_p->shpc_config_hdl, PCI_CONF_STAT);
- if (!(status & PCI_STAT_CAP)) {
- return (DDI_FAILURE);
- }
-
- /* Get a pointer to the PCI capabilities list. */
- cap_ptr = pci_config_get8(ctrl_p->shpc_config_hdl, PCI_BCNF_CAP_PTR);
-
- cap_ptr &= 0xFC;
-
- /* Walk PCI capabilities list searching for the SHPC capability. */
- while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
- cap_id = pci_config_get8(ctrl_p->shpc_config_hdl, cap_ptr);
-
- pcishpc_debug("pcishpc_probe_controller() capability @ "
- "pointer=%02x (id=%02x)", cap_ptr, cap_id);
-
- if (cap_id == PCI_CAP_ID_PCI_HOTPLUG) {
- /* Save the SHPC register offset. */
- ctrl_p->shpc_dword_select = cap_ptr+2;
- /* Save the SHPC data register. */
- ctrl_p->shpc_dword_data_reg = cap_ptr+4;
- break;
- }
-
- /* Get the pointer to the next capability. */
- cap_ptr = pci_config_get8(ctrl_p->shpc_config_hdl,
- cap_ptr+1);
-
- cap_ptr &= 0xFC;
- }
-
- if (cap_ptr == PCI_CAP_NEXT_PTR_NULL) {
- return (DDI_FAILURE);
- }
-
- pcishpc_debug("pcishpc_probe_controller() Found SHPC capibility");
-
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_destroy_controller()
- *
- * This function deallocates all of the SHPC controller resources.
- */
-static int
-pcishpc_destroy_controller(dev_info_t *dip)
-{
- pcishpc_ctrl_t *ctrl_p;
- pcishpc_ctrl_t **ctrl_pp;
-
- pcishpc_debug("pcishpc_destroy_controller() called(dip=%p)", dip);
-
- mutex_enter(&pcishpc_list_mutex);
-
- ctrl_pp = &pcishpc_ctrl_head;
-
- /* Walk the linked list of softstates. */
- while ((ctrl_p = *ctrl_pp) != NULL) {
- if (ctrl_p->shpc_dip == dip) {
- /*
- * Deallocate the slot state structures for
- * this controller.
- */
- (void) pcishpc_destroy_slots(ctrl_p);
-
- *ctrl_pp = ctrl_p->nextp;
-
- pci_config_teardown(&ctrl_p->shpc_config_hdl);
-
- cv_destroy(&ctrl_p->cmd_comp_cv);
-
- mutex_destroy(&ctrl_p->shpc_mutex);
- mutex_destroy(&ctrl_p->shpc_intr_mutex);
- kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t));
- mutex_exit(&pcishpc_list_mutex);
-
- pcishpc_debug("pcishpc_destroy_controller() success");
- return (DDI_SUCCESS);
- }
- ctrl_pp = &(ctrl_p->nextp);
- }
-
- mutex_exit(&pcishpc_list_mutex);
-
- pcishpc_debug("pcishpc_destroy_controller() not found");
-
- return (DDI_FAILURE);
-}
-
-
-/*
- * pcishpc_intr()
- *
- * This is the SHPC controller interrupt handler.
- */
-int
-pcishpc_intr(dev_info_t *dip)
-{
- pcishpc_ctrl_t *ctrl_p = pcishpc_get_controller(dip);
- int slot;
- uint32_t irq_locator, irq_serr_locator, reg;
- boolean_t slot_event = B_FALSE;
-
- pcishpc_debug("pcishpc_intr() called");
-
- if (ctrl_p->interrupt_installed == B_TRUE) {
- mutex_enter(&ctrl_p->shpc_intr_mutex);
-
- pcishpc_debug("pcishpc_intr() interrupt received");
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG);
-
- if (reg & SHPC_SERR_INT_CMD_COMPLETE_IRQ) {
- pcishpc_debug("pcishpc_intr() "
- "SHPC_SERR_INT_CMD_COMPLETE_IRQ detected");
- ctrl_p->command_complete = B_TRUE;
- cv_signal(&ctrl_p->cmd_comp_cv);
- }
-
- if (reg & SHPC_SERR_INT_ARBITER_IRQ) {
- pcishpc_debug("pcishpc_intr() SHPC_SERR_INT_ARBITER_IRQ"
- " detected");
- ctrl_p->arbiter_timeout = B_TRUE;
- }
-
- /* Write back the SERR INT register to acknowledge the IRQs. */
- pcishpc_write_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG, reg);
-
- irq_locator = pcishpc_read_reg(ctrl_p, SHPC_IRQ_LOCATOR_REG);
-
- irq_serr_locator = pcishpc_read_reg(ctrl_p,
- SHPC_SERR_LOCATOR_REG);
-
- /* Check for slot events that might have occured. */
- for (slot = 0; slot < ctrl_p->numSlotsImpl; slot++) {
- if ((irq_locator & (SHPC_IRQ_SLOT_N_PENDING<<slot)) ||
- (irq_serr_locator &
- (SHPC_IRQ_SERR_SLOT_N_PENDING<<slot))) {
- pcishpc_debug("pcishpc_intr() slot %d and "
- "pending IRQ", slot+1);
-
- /*
- * Note that we will need to generate a
- * slot event interrupt.
- */
- slot_event = B_TRUE;
-
- reg = pcishpc_read_reg(ctrl_p,
- SHPC_LOGICAL_SLOT_REGS+slot);
-
- /* Record any pending slot interrupts/events. */
- ctrl_p->slots[slot]->slot_events |= reg;
-
- /* Acknoledge any slot interrupts */
- pcishpc_write_reg(ctrl_p,
- SHPC_LOGICAL_SLOT_REGS+slot, reg);
- }
- }
-
- if (slot_event == B_TRUE) {
- pcishpc_debug("pcishpc_intr() slot(s) have event(s)");
- (void) pcishpc_process_intr(ctrl_p);
- } else {
- pcishpc_debug("pcishpc_intr() No slot event(s)");
- }
-
- mutex_exit(&ctrl_p->shpc_intr_mutex);
-
- pcishpc_debug("pcishpc_intr() claimed");
-
- return (DDI_INTR_CLAIMED);
- }
-
- pcishpc_debug("pcishpc_intr() unclaimed");
-
- return (DDI_INTR_UNCLAIMED);
-}
-
-/*
- * pcishpc_process_intr()
- *
- * This is the SHPC soft interrupt handler.
- */
-static int
-pcishpc_process_intr(pcishpc_ctrl_t *ctrl_p)
-{
- int slot;
-
- mutex_enter(&ctrl_p->shpc_mutex);
-
- pcishpc_debug("pcishpc_process_intr() called");
-
- /* XXX - add event handling code here */
- for (slot = 0; slot < ctrl_p->numSlotsImpl; slot++) {
- if (ctrl_p->slots[slot]->slot_events &
- SHPC_SLOT_PRESENCE_DETECTED)
- pcishpc_debug("slot %d: SHPC_SLOT_PRESENCE_DETECTED",
- slot+1);
-
- if (ctrl_p->slots[slot]->slot_events &
- SHPC_SLOT_ISO_PWR_DETECTED)
- pcishpc_debug("slot %d: SHPC_SLOT_ISO_PWR_DETECTED",
- slot+1);
-
- if (ctrl_p->slots[slot]->slot_events &
- SHPC_SLOT_ATTN_DETECTED) {
- pcishpc_debug("slot %d: SHPC_SLOT_ATTN_DETECTED",
- slot+1);
- /*
- * if ATTN button event is still pending
- * then cancel it
- */
- if (ctrl_p->slots[slot]->attn_btn_pending == B_TRUE)
- ctrl_p->slots[slot]->attn_btn_pending = B_FALSE;
-
- /* wake up the ATTN event handler */
- cv_signal(&ctrl_p->slots[slot]->attn_btn_cv);
- }
-
- if (ctrl_p->slots[slot]->slot_events & SHPC_SLOT_MRL_DETECTED)
- pcishpc_debug("slot %d: SHPC_SLOT_MRL_DETECTED",
- slot+1);
-
- if (ctrl_p->slots[slot]->slot_events & SHPC_SLOT_POWER_DETECTED)
- pcishpc_debug("slot %d: SHPC_SLOT_POWER_DETECTED",
- slot+1);
-
- /* Clear the events now that we've processed all of them. */
- ctrl_p->slots[slot]->slot_events = 0;
- }
-
- mutex_exit(&ctrl_p->shpc_mutex);
-
- return (DDI_INTR_CLAIMED);
-}
-
-
-/*
- * pcishpc_get_controller()
- *
- * This function retrieves the hot plug SHPC controller soft state.
- */
-static pcishpc_ctrl_t *
-pcishpc_get_controller(dev_info_t *dip)
-{
- pcishpc_ctrl_t *ctrl_p;
-
- pcishpc_debug("pcishpc_get_controller() called (dip=%p)", dip);
-
- mutex_enter(&pcishpc_list_mutex);
-
- ctrl_p = pcishpc_ctrl_head;
-
- while (ctrl_p) {
- if (ctrl_p->shpc_dip == dip)
- break;
- ctrl_p = ctrl_p->nextp;
- }
-
- mutex_exit(&pcishpc_list_mutex);
-
- pcishpc_debug("pcishpc_get_controller() (ctrl_p=%llx)", ctrl_p);
-
- return (ctrl_p);
-}
-
-
-/*
- * pcishpc_hpc_get_slot_state()
- *
- * This function retrieves the hot plug SHPC soft state from the
- * the HPS framework slot handle.
- */
-static pcishpc_t *
-pcishpc_hpc_get_slot_state(hpc_slot_t slot)
-{
- pcishpc_t *pcishpc_p;
-
- pcishpc_debug("pcishpc_hpc_get_slot_state() called (hpc_slot=%x)",
- slot);
-
- mutex_enter(&pcishpc_list_mutex);
-
- pcishpc_p = pcishpc_head;
-
- while (pcishpc_p) {
- if (pcishpc_p->slot_handle == slot) {
- pcishpc_debug("pcishpc_hpc_get_slot_state() found "
- "(pcishpc=%x)", pcishpc_p);
- mutex_exit(&pcishpc_list_mutex);
- return (pcishpc_p);
- }
- pcishpc_p = pcishpc_p->nextp;
- }
-
- mutex_exit(&pcishpc_list_mutex);
-
- pcishpc_debug("pcishpc_hpc_get_slot_state() failed (slot=%x)", slot);
-
- return (NULL);
-}
-
-
-/*
- * pcishpc_get_pci_info()
- *
- * Read the PCI Bus, PCI Device, and PCI function for the SHPC controller.
- */
-static int
-pcishpc_get_pci_info(pcishpc_ctrl_t *pcishpc_p)
-{
- pci_regspec_t *regspec;
- int reglen;
-
- pcishpc_debug("pcishpc_get_pci_info() called");
-
- if (ddi_getlongprop(DDI_DEV_T_NONE, pcishpc_p->shpc_dip,
- DDI_PROP_DONTPASS, "reg", (caddr_t)&regspec, &reglen)
- != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_get_pci_info() failed to get regspec.");
- return (DDI_FAILURE);
- }
-
- pcishpc_p->shpc_bus = PCI_REG_BUS_G(regspec[0].pci_phys_hi);
- pcishpc_p->shpc_dev = PCI_REG_DEV_G(regspec[0].pci_phys_hi);
- pcishpc_p->shpc_func = PCI_REG_FUNC_G(regspec[0].pci_phys_hi);
-
- kmem_free(regspec, reglen);
-
- pcishpc_debug("pcishpc_get_pci_info() %s%d: bus=%d, dev=%d, func=%d",
- ddi_driver_name(pcishpc_p->shpc_dip),
- ddi_get_instance(pcishpc_p->shpc_dip),
- pcishpc_p->shpc_bus, pcishpc_p->shpc_dev,
- pcishpc_p->shpc_func);
-
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_init()
- *
- * Install and configure an SHPC controller and register the HotPlug slots
- * with the Solaris HotPlug framework. This function is usually called by
- * a PCI bridge Nexus driver that has a built in SHPC controller.
- */
-int
-pcishpc_init(dev_info_t *dip)
-{
- pcishpc_ctrl_t *ctrl_p;
- int i;
-
- pcishpc_debug("pcishpc_init() called from %s#%d",
- ddi_driver_name(dip), ddi_get_instance(dip));
-
- mutex_enter(&pcishpc_control_mutex);
-
- if (pcishpc_get_controller(dip) != NULL) {
- pcishpc_debug("pcishpc_init() shpc instance already "
- "initialized!");
- mutex_exit(&pcishpc_control_mutex);
- return (DDI_SUCCESS);
- }
-
- /* Initialize soft state structure for the SHPC instance. */
- ctrl_p = pcishpc_create_controller(dip);
-
- if (ctrl_p == NULL) {
- pcishpc_debug("pcishpc_init() failed to create shpc softstate");
- mutex_exit(&pcishpc_control_mutex);
- return (DDI_FAILURE);
- }
-
- if (pcishpc_setup_controller(ctrl_p) != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_init() failed to setup controller");
- (void) pcishpc_destroy_controller(dip);
- mutex_exit(&pcishpc_control_mutex);
- return (DDI_FAILURE);
- }
-
-#if 0
- pcishpc_debug("%s%d: P2P bridge register dump:",
- ddi_driver_name(dip), ddi_get_instance(dip));
-
- for (i = 0; i < 0x100; i += 4) {
- pcishpc_debug("SHPC Cfg reg 0x%02x: %08x", i,
- pci_config_get32(ctrl_p->shpc_config_hdl, i));
- }
-#endif
-
- /* Setup each HotPlug slot on this SHPC controller. */
- for (i = 0; i < ctrl_p->numSlotsImpl; i++) {
- if (pcishpc_register_slot(ctrl_p, i) != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_init() failed to register "
- "slot %d", i);
- (void) pcishpc_destroy_controller(dip);
- mutex_exit(&pcishpc_control_mutex);
- return (DDI_FAILURE);
- }
- }
-
- (void) pcishpc_enable_irqs(ctrl_p);
-
- if (pcishpc_debug_enabled) {
- /* Dump out the SHPC registers. */
- pcishpc_dump_regs(ctrl_p);
- }
-
- mutex_exit(&pcishpc_control_mutex);
-
- pcishpc_debug("pcishpc_init() success(dip=%p)", dip);
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_enable_irqs()
- *
- * Enable/unmask the different IRQ's we support from the SHPC controller.
- */
-static int
-pcishpc_enable_irqs(pcishpc_ctrl_t *ctrl_p)
-{
- uint32_t reg;
- int slot;
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG);
-
- /* Enable all interrupts. */
- reg &= ~SHPC_SERR_INT_MASK_ALL;
-
- pcishpc_write_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG, reg);
-
- /* Unmask the interrupts for each slot. */
- for (slot = 0; slot < ctrl_p->numSlotsImpl; slot++) {
- ctrl_p->slots[slot]->slot_events = 0;
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot);
- if ((reg & SHPC_SLOT_STATE_MASK) == SHPC_SLOT_ENABLED) {
- reg &= ~(SHPC_SLOT_MASK_ALL | SHPC_SLOT_MRL_SERR_MASK);
- ctrl_p->numSlotsConn++;
- if (ctrl_p->currBusSpeed == -1)
- ctrl_p->currBusSpeed = pcishpc_read_reg(ctrl_p,
- SHPC_PROF_IF_SBCR_REG) &
- SHPC_SBCR_SPEED_MASK;
- } else {
- reg &= ~(SHPC_SLOT_MASK_ALL);
- }
-
- /* Enable/Unmask all slot interrupts. */
- pcishpc_write_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot, reg);
- }
-
- pcishpc_debug("pcishpc_enable_irqs: ctrl_p 0x%p, "
- "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p,
- ctrl_p->currBusSpeed, ctrl_p->numSlotsConn);
-
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_disable_irqs()
- *
- * Disable/Mask the different IRQ's we support from the SHPC controller.
- */
-static int
-pcishpc_disable_irqs(pcishpc_ctrl_t *ctrl_p)
-{
- uint32_t reg;
- int slot;
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG);
-
- /* Mask all interrupts. */
- reg |= SHPC_SERR_INT_MASK_ALL;
-
- pcishpc_write_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG, reg);
-
- /* Unmask the interrupts for each slot. */
- for (slot = 0; slot < ctrl_p->numSlotsImpl; slot++) {
- reg = pcishpc_read_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot);
-
- /* Disable/Mask all slot interrupts. */
- reg |= SHPC_SLOT_MASK_ALL;
-
- pcishpc_write_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot, reg);
- }
-
- pcishpc_debug("pcishpc_disable_irqs: ctrl_p 0x%p, "
- "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p,
- ctrl_p->currBusSpeed, ctrl_p->numSlotsConn);
-
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_register_slot()
- *
- * Create and register a slot with the Solaris HotPlug framework.
- */
-static int
-pcishpc_register_slot(pcishpc_ctrl_t *ctrl_p, int slot)
-{
- pcishpc_t *pcishpc_p;
-
- pcishpc_p = pcishpc_create_slot(ctrl_p);
-
- ctrl_p->slots[slot] = pcishpc_p;
-
- pcishpc_p->slot_ops = hpc_alloc_slot_ops(KM_SLEEP);
-
- pcishpc_p->slot_ops->hpc_version = HPC_SLOT_OPS_VERSION;
-
- pcishpc_p->slotNum = slot;
-
- /* Setup the PCI device # for this SHPC slot. */
- if (ctrl_p->deviceIncreases)
- pcishpc_p->deviceNum = ctrl_p->deviceStart + pcishpc_p->slotNum;
- else
- pcishpc_p->deviceNum = ctrl_p->deviceStart - pcishpc_p->slotNum;
-
- /* Setup the HPS framework slot ops callbacks for the SHPC driver. */
- pcishpc_p->slot_ops->hpc_op_connect = pcishpc_connect;
- pcishpc_p->slot_ops->hpc_op_disconnect = pcishpc_disconnect;
- pcishpc_p->slot_ops->hpc_op_control = pcishpc_pci_control;
- /* PCI HPC drivers do not support the insert/remove callbacks. */
- pcishpc_p->slot_ops->hpc_op_insert = NULL;
- pcishpc_p->slot_ops->hpc_op_remove = NULL;
-
- /* Setup the HPS framework slot information. */
- pcishpc_p->slot_info.version = HPC_SLOT_OPS_VERSION;
- pcishpc_p->slot_info.slot_type = HPC_SLOT_TYPE_PCI;
- /* Do not auto enable the deivce in this slot. */
- pcishpc_p->slot_info.slot_flags = HPC_SLOT_NO_AUTO_ENABLE |
- HPC_SLOT_CREATE_DEVLINK;
-
- pcishpc_p->slot_info.slot.pci.device_number = pcishpc_p->deviceNum;
- pcishpc_p->slot_info.slot.pci.slot_capabilities = HPC_SLOT_64BITS;
-
- /* setup thread for handling ATTN button events */
- if (ctrl_p->has_attn) {
- pcishpc_debug("pcishpc_register_slot: "
- "setting up ATTN button event "
- "handler thread for slot %d\n", slot);
- cv_init(&pcishpc_p->attn_btn_cv, NULL, CV_DRIVER, NULL);
- pcishpc_p->attn_btn_pending = B_FALSE;
- pcishpc_p->attn_btn_threadp = thread_create(NULL, 0,
- pcishpc_attn_btn_handler,
- (void *)pcishpc_p, 0, &p0, TS_RUN, minclsyspri);
- pcishpc_p->attn_btn_thread_exit = B_FALSE;
- }
-
- /* setup the slot name (used for ap-id) */
- pcishpc_set_slot_name(ctrl_p, slot);
-
- pcishpc_get_slot_state(pcishpc_p);
-
- /* Register this SHPC slot with the HPS framework. */
- if (hpc_slot_register(ctrl_p->shpc_dip, ctrl_p->nexus_path,
- &pcishpc_p->slot_info, &pcishpc_p->slot_handle,
- pcishpc_p->slot_ops, (caddr_t)pcishpc_p, 0) != 0) {
-
- pcishpc_debug("pcishpc_register_slot() failed to Register "
- "slot");
-
- hpc_free_slot_ops(pcishpc_p->slot_ops);
- pcishpc_p->slot_ops = NULL;
-
- return (DDI_FAILURE);
- }
-
- pcishpc_debug("pcishpc_register_slot() success for slot %d", slot);
-
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_create_slot()
- *
- * Allocate and add a new HotPlug slot state structure to the linked list.
- */
-static pcishpc_t *
-pcishpc_create_slot(pcishpc_ctrl_t *ctrl_p)
-{
- pcishpc_t *pcishpc_p;
-
- pcishpc_debug("pcishpc_create_slot() called(ctrl_p=%x)", ctrl_p);
-
- /* Allocate a new slot structure. */
- pcishpc_p = kmem_zalloc(sizeof (pcishpc_t), KM_SLEEP);
-
- pcishpc_p->ctrl = ctrl_p;
-
- mutex_enter(&pcishpc_list_mutex);
-
- /* Insert new slot into linked list of current slots. */
- pcishpc_p->nextp = pcishpc_head;
- pcishpc_head = pcishpc_p;
-
- mutex_exit(&pcishpc_list_mutex);
-
- pcishpc_debug("pcishpc_create_slot() success");
- return (pcishpc_p);
-}
-
-/*
- * pcishpc_setup_controller()
- *
- * Get the number of HotPlug Slots, and the PCI device information
- * for this HotPlug controller.
- */
-static int
-pcishpc_setup_controller(pcishpc_ctrl_t *ctrl_p)
-{
- uint32_t config;
- dev_info_t *ppdip;
-
- config = pcishpc_read_reg(ctrl_p, SHPC_SLOT_CONFIGURATION_REG);
-
- /* Get the number of HotPlug slots implemented */
- ctrl_p->numSlotsImpl = ((config)&31);
-
- /*
- * Initilize the current bus speed and number of hotplug slots
- * currently connected.
- */
- ctrl_p->currBusSpeed = -1;
- ctrl_p->numSlotsConn = 0;
-
- /* Save the value of Slots Available 1 and 2 registers */
- ctrl_p->shpc_slots_avail1_reg = pcishpc_read_reg(ctrl_p,
- SHPC_SLOTS_AVAIL_I_REG);
- ctrl_p->shpc_slots_avail2_reg = pcishpc_read_reg(ctrl_p,
- SHPC_SLOTS_AVAIL_II_REG);
-
- /* Get the first PCI device Number used. */
- /*
- * PCI-X I/O boat workaround.
- * The register doesn't set up the correct value.
- */
- ppdip = ddi_get_parent(ddi_get_parent(ctrl_p->shpc_dip));
- if ((ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS,
- "vendor-id", -1) == 0x108e) &&
- (ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS,
- "device-id", -1) == 0x9010))
- ctrl_p->deviceStart = 4;
- else
- ctrl_p->deviceStart = ((config>>8)&31);
-
- /* Get the first Physical device number. */
- ctrl_p->physStart = ((config>>16)&0x7ff);
- /* Check if the device numbers increase or decrease. */
- ctrl_p->deviceIncreases = ((config>>29)&0x1);
-
- ctrl_p->has_attn =
- (config & SHPC_SLOT_CONFIG_ATTN_BUTTON) ? B_TRUE : B_FALSE;
- ctrl_p->has_mrl =
- (config & SHPC_SLOT_CONFIG_MRL_SENSOR) ? B_TRUE : B_FALSE;
-
- cv_init(&ctrl_p->cmd_comp_cv, NULL, CV_DRIVER, NULL);
- ctrl_p->command_complete = B_FALSE;
- ctrl_p->arbiter_timeout = B_FALSE;
-
- if (ctrl_p->numSlotsImpl > MAX_SHPC_SLOTS) {
- pcishpc_debug("pcishpc_setup_controller() too many SHPC "
- "slots error");
- return (DDI_FAILURE);
- }
-
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_uninit()
- * Unload the HogPlug controller driver and deallocate all resources.
- */
-int
-pcishpc_uninit(dev_info_t *dip)
-{
- pcishpc_ctrl_t *ctrl_p;
-
- pcishpc_debug("pcishpc_uninit() called(dip=%p)", dip);
-
- mutex_enter(&pcishpc_control_mutex);
-
- ctrl_p = pcishpc_get_controller(dip);
-
- if (!ctrl_p) {
- pcishpc_debug("pcishpc_uninit() Unable to find softstate");
- mutex_exit(&pcishpc_control_mutex);
- return (DDI_FAILURE);
- }
-
- (void) pcishpc_disable_irqs(ctrl_p);
- ctrl_p->interrupt_installed = B_FALSE;
-
- (void) pcishpc_destroy_controller(dip);
-
- mutex_exit(&pcishpc_control_mutex);
-
- pcishpc_debug("pcishpc_uninit() success(dip=%p)", dip);
-
- return (DDI_SUCCESS);
-}
-
-/*
- * pcishpc_destroy_slots()
- *
- * Free up all of the slot resources for this controller.
- */
-static int
-pcishpc_destroy_slots(pcishpc_ctrl_t *ctrl_p)
-{
- pcishpc_t *pcishpc_p;
- pcishpc_t **pcishpc_pp;
-
- pcishpc_debug("pcishpc_destroy_slots() called(ctrl_p=%p)", ctrl_p);
-
- pcishpc_pp = &pcishpc_head;
-
- while ((pcishpc_p = *pcishpc_pp) != NULL) {
- if (pcishpc_p->ctrl == ctrl_p) {
- if (pcishpc_p->attn_btn_threadp != NULL) {
- mutex_enter(&ctrl_p->shpc_mutex);
- pcishpc_p->attn_btn_thread_exit = B_TRUE;
- cv_signal(&pcishpc_p->attn_btn_cv);
- pcishpc_debug("pcishpc_destroy_slots: "
- "waiting for ATTN thread exit\n");
- cv_wait(&pcishpc_p->attn_btn_cv,
- &ctrl_p->shpc_mutex);
- pcishpc_debug("pcishpc_destroy_slots: "
- "ATTN thread exit\n");
- cv_destroy(&pcishpc_p->attn_btn_cv);
- pcishpc_p->attn_btn_threadp = NULL;
- mutex_exit(&ctrl_p->shpc_mutex);
- }
-
- *pcishpc_pp = pcishpc_p->nextp;
-
- pcishpc_debug("pcishpc_destroy_slots() (shpc_p=%p) "
- "destroyed", pcishpc_p);
- if (pcishpc_p->slot_ops)
- if (hpc_slot_unregister(
- &pcishpc_p->slot_handle) != 0) {
- pcishpc_debug("pcishpc_destroy_slots() "
- "failed to unregister slot");
- return (DDI_FAILURE);
- } else {
- hpc_free_slot_ops(pcishpc_p->slot_ops);
- pcishpc_p->slot_ops = NULL;
- }
- kmem_free(pcishpc_p, sizeof (pcishpc_t));
- } else
- pcishpc_pp = &(pcishpc_p->nextp);
- }
-
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pcishpc_connect()
- *
- * Called by Hot Plug Services to connect a slot on the bus.
- */
-/*ARGSUSED*/
-static int
-pcishpc_connect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags)
-{
- pcishpc_t *pcishpc_p;
- uint32_t status;
-
- pcishpc_debug("pcishpc_connect called()");
-
- pcishpc_p = pcishpc_hpc_get_slot_state(slot_hdl);
-
- if (!pcishpc_p) {
- pcishpc_debug("pcishpc_connect() "
- "Failed to find soft state for slot_hdl %x", slot_hdl);
- return (HPC_ERR_FAILED);
- }
-
- mutex_enter(&pcishpc_p->ctrl->shpc_mutex);
-
- /* make sure the MRL sensor is closed */
- status = pcishpc_read_reg(pcishpc_p->ctrl,
- SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum);
-
- if (status & SHPC_SLOT_MRL_STATE_MASK) {
- pcishpc_debug("pcishpc_connect() failed: MRL open");
- goto cleanup;
- }
-
- if (pcishpc_set_power_state(pcishpc_p, HPC_SLOT_CONNECTED) !=
- DDI_SUCCESS) {
- pcishpc_debug("pcishpc_connect() failed: set power state");
- goto cleanup;
- }
-
- mutex_exit(&pcishpc_p->ctrl->shpc_mutex);
-
- pcishpc_debug("pcishpc_connect() success!");
-
- return (HPC_SUCCESS);
-
-cleanup:
- mutex_exit(&pcishpc_p->ctrl->shpc_mutex);
- return (HPC_ERR_FAILED);
-}
-
-
-/*
- * pcishpc_set_power_state()
- *
- * Changed a slot's power state.
- */
-static int
-pcishpc_set_power_state(pcishpc_t *pcishpc_p, hpc_slot_state_t state)
-{
- pcishpc_get_slot_state(pcishpc_p);
-
- /* Check to see if the slot is already in this state. */
- if (pcishpc_p->slot_state == state) {
- pcishpc_debug("pcishpc_set_power_state() slot already in "
- "this state");
- return (DDI_SUCCESS);
- }
-
- if ((pcishpc_p->slot_state == HPC_SLOT_EMPTY) &&
- ((state == HPC_SLOT_CONNECTED) ||
- (state == HPC_SLOT_DISCONNECTED))) {
- pcishpc_debug("pcishpc_set_power_state() slot in "
- "empty state");
- return (DDI_FAILURE);
- }
-
- /* Set the Power LED to blink. */
- (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_BLINK);
-
- /* Turn all other LEDS off. */
- (void) pcishpc_setled(pcishpc_p, HPC_FAULT_LED, HPC_LED_OFF);
- (void) pcishpc_setled(pcishpc_p, HPC_ATTN_LED, HPC_LED_OFF);
- (void) pcishpc_setled(pcishpc_p, HPC_ACTIVE_LED, HPC_LED_OFF);
-
- /* Set the slot state to the new slot state. */
- pcishpc_p->slot_state = state;
-
- /* Set the bus speed only if the bus segment is not running */
- if (state == HPC_SLOT_CONNECTED) {
- if (pcishpc_set_bus_speed(pcishpc_p) != DDI_SUCCESS)
- return (DDI_FAILURE);
-
- pcishpc_p->ctrl->numSlotsConn++;
- } else {
- if (--pcishpc_p->ctrl->numSlotsConn == 0)
- pcishpc_p->ctrl->currBusSpeed = -1;
- }
-
- pcishpc_debug("pcishpc_set_power_state(): ctrl_p 0x%p, "
- "pcishpc_p 0x%p, slot state 0x%x, current bus speed 0x%x, "
- "slots connected 0x%x\n", pcishpc_p->ctrl, pcishpc_p, state,
- pcishpc_p->ctrl->currBusSpeed, pcishpc_p->ctrl->numSlotsConn);
-
- /* Mask or Unmask MRL Sensor SEER bit based on new slot state */
- if (pcishpc_p->ctrl->has_mrl == B_TRUE) {
- uint32_t reg;
-
- reg = pcishpc_read_reg(pcishpc_p->ctrl,
- SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum);
- reg = (state == HPC_SLOT_CONNECTED) ?
- (reg & ~SHPC_SLOT_MRL_SERR_MASK) :
- (reg | SHPC_SLOT_MRL_SERR_MASK);
-
- pcishpc_write_reg(pcishpc_p->ctrl,
- SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum, reg);
- }
-
- /* Update the hardweare slot state. */
- if (pcishpc_set_slot_state(pcishpc_p) != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_set_power_state() failed");
- (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_OFF);
- pcishpc_get_slot_state(pcishpc_p);
- return (DDI_FAILURE);
- }
-
- /* Turn the Power LED ON for a connected slot. */
- if (state == HPC_SLOT_CONNECTED) {
- (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_ON);
- }
-
- /* Turn the Power LED OFF for a disconnected slot. */
- if (state == HPC_SLOT_DISCONNECTED) {
- (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_OFF);
- }
-
- /* Turn all other LEDS off. */
- (void) pcishpc_setled(pcishpc_p, HPC_FAULT_LED, HPC_LED_OFF);
- (void) pcishpc_setled(pcishpc_p, HPC_ATTN_LED, HPC_LED_OFF);
- (void) pcishpc_setled(pcishpc_p, HPC_ACTIVE_LED, HPC_LED_OFF);
-
- pcishpc_debug("pcishpc_set_power_state() success!");
-
- pcishpc_get_slot_state(pcishpc_p);
-
- /* delay after powerON to let the device initialize itself */
- delay(drv_usectohz(pcishpc_reset_delay));
-
- return (DDI_SUCCESS);
-}
-
-/*
- * pcishpc_set_bus_speed()
- *
- * Set the bus speed and mode.
- */
-static int
-pcishpc_set_bus_speed(pcishpc_t *pcishpc_p)
-{
- pcishpc_ctrl_t *ctrl_p = pcishpc_p->ctrl;
- int curr_speed = ctrl_p->currBusSpeed;
- int speed = -1;
- int avail_slots;
- uint32_t status;
-
- /* Make sure that the slot is in a correct state */
- status = pcishpc_read_reg(ctrl_p,
- SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum);
-
- /* Return failure if the slot is empty */
- if ((status & SHPC_SLOT_CARD_EMPTY_MASK) ==
- SHPC_SLOT_CARD_EMPTY_MASK) {
- pcishpc_debug("pcishpc_set_bus_speed() failed: "
- "the slot is empty.");
- return (DDI_FAILURE);
- }
-
- /* Return failure if the slot is not in disabled state */
- if ((status & SHPC_SLOT_STATE_MASK) != SHPC_SLOT_DISABLED) {
- pcishpc_debug("pcishpc_set_bus_speed() failed: "
- "incorrect slot state.");
- return (DDI_FAILURE);
- }
-
- /* Set the "power-only" mode for the slot */
- if (pcishpc_issue_command(ctrl_p, ((1+pcishpc_p->slotNum)<<8) |
- SHPC_SLOT_POWER_ONLY) != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_set_bus_speed() failed to set "
- "the slot %d in the power-only mode", pcishpc_p->slotNum);
- return (DDI_FAILURE);
- }
-
- /* Wait for power good */
- delay(drv_usectohz(SHPC_POWER_GOOD_WAIT_TIME));
-
- /* Make sure that the slot is in "power-only" state */
- status = pcishpc_read_reg(ctrl_p,
- SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum);
-
- if ((status & SHPC_SLOT_STATE_MASK) != SHPC_SLOT_POWER_ONLY) {
- pcishpc_debug("pcishpc_set_bus_speed() "
- "power-only failed: incorrect slot state.");
- return (DDI_FAILURE);
- }
-
- /*
- * Check if SHPC has available slots and select the highest
- * available bus speed for the slot.
- *
- * The bus speed codes are:
- * 100 - 133Mhz; <--+
- * 011 - 100Mhz; <--+ PCI-X
- * 010 - 66Mhz; <--+
- *
- * 001 - 66Mhz; <--+
- * 000 - 33Mhz <--+ Conv PCI
- */
- switch (status & SHPC_SLOT_PCIX_CAPABLE_MASK) {
- case SHPC_SLOT_133MHZ_PCIX_CAPABLE:
- avail_slots = (ctrl_p->shpc_slots_avail1_reg >>
- SHPC_AVAIL_133MHZ_PCIX_SPEED_SHIFT) & SHPC_AVAIL_SPEED_MASK;
-
- if (((curr_speed == -1) && avail_slots) ||
- (curr_speed == SHPC_SBCR_133MHZ_PCIX_SPEED)) {
- speed = SHPC_SBCR_133MHZ_PCIX_SPEED;
- break;
- }
- /* FALLTHROUGH */
- case SHPC_SLOT_100MHZ_PCIX_CAPABLE:
- avail_slots = (ctrl_p->shpc_slots_avail1_reg >>
- SHPC_AVAIL_100MHZ_PCIX_SPEED_SHIFT) & SHPC_AVAIL_SPEED_MASK;
-
- if (((curr_speed == -1) && avail_slots) ||
- (curr_speed == SHPC_SBCR_100MHZ_PCIX_SPEED)) {
- speed = SHPC_SBCR_100MHZ_PCIX_SPEED;
- break;
- }
- /* FALLTHROUGH */
- case SHPC_SLOT_66MHZ_PCIX_CAPABLE:
- avail_slots = (ctrl_p->shpc_slots_avail1_reg >>
- SHPC_AVAIL_66MHZ_PCIX_SPEED_SHIFT) & SHPC_AVAIL_SPEED_MASK;
-
- if (((curr_speed == -1) && avail_slots) ||
- (curr_speed == SHPC_SBCR_66MHZ_PCIX_SPEED)) {
- speed = SHPC_SBCR_66MHZ_PCIX_SPEED;
- break;
- }
- /* FALLTHROUGH */
- default:
- avail_slots = (ctrl_p->shpc_slots_avail2_reg >>
- SHPC_AVAIL_66MHZ_CONV_SPEED_SHIFT) & SHPC_AVAIL_SPEED_MASK;
-
- if ((status & SHPC_SLOT_66MHZ_CONV_CAPABLE) &&
- (((curr_speed == -1) && avail_slots) ||
- (curr_speed == SHPC_SBCR_66MHZ_CONV_SPEED))) {
- speed = SHPC_SBCR_66MHZ_CONV_SPEED;
- } else {
- avail_slots = (ctrl_p->shpc_slots_avail1_reg >>
- SHPC_AVAIL_33MHZ_CONV_SPEED_SHIFT) &
- SHPC_AVAIL_SPEED_MASK;
-
- if (((curr_speed == -1) && (avail_slots)) ||
- (curr_speed == SHPC_SBCR_33MHZ_CONV_SPEED)) {
- speed = SHPC_SBCR_33MHZ_CONV_SPEED;
- } else {
- pcishpc_debug("pcishpc_set_bus_speed() "
- " failed to set the bus speed, slot# %d",
- pcishpc_p->slotNum);
- return (DDI_FAILURE);
- }
- }
- break;
- }
-
- /*
- * If the bus segment is already running, check to see the card
- * in the slot can support the current bus speed.
- */
- if (curr_speed == speed) {
- /*
- * Check to see there is any slot available for the current
- * bus speed. Otherwise, we need fail the current slot connect
- * request.
- */
- return ((avail_slots <= ctrl_p->numSlotsConn) ?
- DDI_FAILURE : DDI_SUCCESS);
- }
-
- /* Set the bus speed */
- if (pcishpc_issue_command(ctrl_p, SHPC_COMM_STS_SET_SPEED |
- speed) == DDI_FAILURE) {
- pcishpc_debug("pcishpc_set_bus_speed() failed "
- "to set bus %d speed", pcishpc_p->slotNum);
- return (DDI_FAILURE);
- }
-
- /* Check the current bus speed */
- status = pcishpc_read_reg(ctrl_p, SHPC_PROF_IF_SBCR_REG) &
- SHPC_SBCR_SPEED_MASK;
- if ((status & SHPC_SBCR_SPEED_MASK) != speed) {
- pcishpc_debug("pcishpc_set_bus_speed() an incorrect "
- "bus speed, slot = 0x%x, speed = 0x%x",
- pcishpc_p->slotNum, status & SHPC_SBCR_SPEED_MASK);
- return (DDI_FAILURE);
- }
-
-
- /* Save the current bus speed */
- ctrl_p->currBusSpeed = speed;
-
- return (DDI_SUCCESS);
-}
-
-/*
- * pcishpc_disconnect()
- *
- * Called by Hot Plug Services to disconnect a slot on the bus.
- */
-/*ARGSUSED*/
-static int
-pcishpc_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data,
- uint_t flags)
-{
- pcishpc_t *pcishpc_p;
-
- pcishpc_debug("pcishpc_disconnect called()");
-
- pcishpc_p = pcishpc_hpc_get_slot_state(slot_hdl);
-
- if (!pcishpc_p) {
- pcishpc_debug("pcishpc_disconnect() "
- "Failed to find soft state for slot_hdl %x", slot_hdl);
- return (HPC_ERR_FAILED);
- }
-
- mutex_enter(&pcishpc_p->ctrl->shpc_mutex);
-
- if (pcishpc_set_power_state(pcishpc_p, HPC_SLOT_DISCONNECTED)
- != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_disconnect() failed");
- goto cleanup;
- }
-
- mutex_exit(&pcishpc_p->ctrl->shpc_mutex);
-
- pcishpc_debug("pcishpc_disconnect() success!");
-
- return (HPC_SUCCESS);
-
-cleanup:
- mutex_exit(&pcishpc_p->ctrl->shpc_mutex);
- return (HPC_ERR_FAILED);
-}
-
-
-/*
- * pcishpc_pci_control()
- *
- * Called by Hot Plug Services to perform a attachment point specific
- * operation on a Hot Pluggable Standard PCI Slot.
- */
-/*ARGSUSED*/
-static int
-pcishpc_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl, int request,
- caddr_t arg)
-{
- hpc_slot_state_t *hpc_slot_state;
- hpc_board_type_t *hpc_board_type;
- hpc_led_info_t *hpc_led_info;
- pcishpc_t *pcishpc_p;
- int ret = HPC_SUCCESS;
-
- pcishpc_debug("pcishpc_pci_control called(Request %s)",
- pcishpc_textrequest(request));
-
- pcishpc_p = pcishpc_hpc_get_slot_state(slot_hdl);
-
- if (!pcishpc_p) {
- pcishpc_debug("pcishpc_pci_control() Error: "
- "Failed to find soft state for slot_hdl %x", slot_hdl);
- return (HPC_ERR_FAILED);
- }
-
- mutex_enter(&pcishpc_p->ctrl->shpc_mutex);
-
- switch (request) {
- case HPC_CTRL_GET_SLOT_STATE:
- hpc_slot_state = (hpc_slot_state_t *)arg;
- pcishpc_get_slot_state(pcishpc_p);
- *hpc_slot_state = pcishpc_p->slot_state;
- pcishpc_debug("pcishpc_pci_control() - "
- "HPC_CTRL_GET_SLOT_STATE (state=%s)",
- pcishpc_textslotstate(pcishpc_p->slot_state));
- break;
-
- case HPC_CTRL_GET_BOARD_TYPE:
- hpc_board_type = (hpc_board_type_t *)arg;
- pcishpc_debug("pcishpc_pci_control() - "
- "HPC_CTRL_GET_BOARD_TYPE");
- pcishpc_get_slot_state(pcishpc_p);
- /*
- * The HPS framework does not know what board
- * type is plugged in.
- */
- if (pcishpc_p->slot_state == HPC_SLOT_EMPTY)
- *hpc_board_type = HPC_BOARD_UNKNOWN;
- else
- *hpc_board_type = HPC_BOARD_PCI_HOTPLUG;
- break;
-
- case HPC_CTRL_GET_LED_STATE:
- hpc_led_info = (hpc_led_info_t *)arg;
-
- pcishpc_get_slot_state(pcishpc_p);
-
- switch (hpc_led_info->led) {
- case HPC_FAULT_LED:
- hpc_led_info->state =
- pcishpc_p->fault_led_state;
- pcishpc_debug("pcishpc_pci_control() - "
- "GET_LED FAULT (state=%s)",
- pcishpc_textledstate(
- hpc_led_info->state));
- break;
-
- case HPC_POWER_LED:
- hpc_led_info->state =
- pcishpc_p->power_led_state;
- pcishpc_debug("pcishpc_pci_control() - "
- "GET_LED POWER (state=%s)",
- pcishpc_textledstate(
- hpc_led_info->state));
- break;
-
- case HPC_ATTN_LED:
- hpc_led_info->state =
- pcishpc_p->attn_led_state;
- pcishpc_debug("pcishpc_pci_control() - "
- "GET_LED ATTN(state = %s)",
- pcishpc_textledstate(
- hpc_led_info->state));
- break;
-
- case HPC_ACTIVE_LED:
- hpc_led_info->state =
- pcishpc_p->active_led_state;
- pcishpc_debug("pcishpc_pci_control() - "
- "GET_LED ACTIVE(state = %s)",
- pcishpc_textledstate(
- hpc_led_info->state));
- break;
-
- default:
- pcishpc_debug("pcishpc_pci_control() "
- "Error: GET_LED - "
- "Invalid LED %x",
- hpc_led_info->led);
- ret = HPC_ERR_NOTSUPPORTED;
- break;
- }
- break;
-
- case HPC_CTRL_SET_LED_STATE:
- hpc_led_info = (hpc_led_info_t *)arg;
- switch (hpc_led_info->led) {
- case HPC_ATTN_LED:
- (void) pcishpc_setled(pcishpc_p,
- hpc_led_info->led,
- hpc_led_info->state);
- break;
- case HPC_POWER_LED:
- pcishpc_debug("pcishpc_pci_control() "
- "Error: SET_LED - power LED");
- ret = HPC_ERR_NOTSUPPORTED;
- break;
- case HPC_FAULT_LED:
- case HPC_ACTIVE_LED:
- break;
- default:
- pcishpc_debug("pcishpc_pci_control() "
- "Error: SET_LED - Unknown LED %x",
- hpc_led_info->led);
- ret = HPC_ERR_NOTSUPPORTED;
- break;
- }
- break;
-
- case HPC_CTRL_DEV_UNCONFIG_FAILURE:
- case HPC_CTRL_DEV_CONFIG_FAILURE:
- pcishpc_debug("pcishpc_pci_control() Config/Unconfig "
- "failed.");
- (void) pcishpc_setled(pcishpc_p, HPC_ATTN_LED,
- HPC_LED_BLINK);
- break;
-
- case HPC_CTRL_ENABLE_AUTOCFG:
- case HPC_CTRL_DISABLE_AUTOCFG:
- case HPC_CTRL_DISABLE_SLOT:
- case HPC_CTRL_DEV_UNCONFIGURED:
- case HPC_CTRL_ENABLE_SLOT:
- case HPC_CTRL_DISABLE_ENUM:
- case HPC_CTRL_DEV_UNCONFIG_START:
- case HPC_CTRL_DEV_CONFIG_START:
- case HPC_CTRL_DEV_CONFIGURED:
- pcishpc_debug("pcishpc_pci_control() - %s",
- pcishpc_textrequest(request));
- break;
-
- case HPC_CTRL_ENABLE_ENUM:
- default:
- pcishpc_debug("pcishpc_pci_control() - Error: "
- "request (%d) NOT SUPPORTED", request);
- ret = HPC_ERR_NOTSUPPORTED;
- break;
- }
-
- mutex_exit(&pcishpc_p->ctrl->shpc_mutex);
- return (ret);
-}
-
-
-/*
- * pcishpc_setled()
- *
- * Change the state of a slot's LED.
- */
-static int
-pcishpc_setled(pcishpc_t *pcishpc_p, hpc_led_t led, hpc_led_state_t state)
-{
- switch (led) {
- case HPC_FAULT_LED:
- pcishpc_debug("pcishpc_setled() - HPC_FAULT_LED "
- "(set %s)", pcishpc_textledstate(state));
- pcishpc_p->fault_led_state = state;
- break;
-
- case HPC_POWER_LED:
- pcishpc_debug("pcishpc_setled() - HPC_POWER_LED "
- "(set %s)", pcishpc_textledstate(state));
- pcishpc_p->power_led_state = state;
- break;
-
- case HPC_ATTN_LED:
- pcishpc_debug("pcishpc_setled() - HPC_ATTN_LED "
- "(set %s)", pcishpc_textledstate(state));
- pcishpc_p->attn_led_state = state;
- break;
-
- case HPC_ACTIVE_LED:
- pcishpc_debug("pcishpc_setled() - HPC_ACTIVE_LED "
- "(set %s)", pcishpc_textledstate(state));
- pcishpc_p->active_led_state = state;
- break;
- }
-
- return (pcishpc_set_slot_state(pcishpc_p));
-}
-
-
-/*
- * pcishpc_set_slot_state()
- *
- * Updates the slot's state and leds.
- */
-static int
-pcishpc_set_slot_state(pcishpc_t *pcishpc_p)
-{
- uint32_t reg;
- uint32_t cmd_code;
- hpc_slot_state_t slot_state;
-
- reg = pcishpc_read_reg(pcishpc_p->ctrl,
- SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum);
-
- /* Default all states to unchanged. */
- cmd_code = ((1+pcishpc_p->slotNum)<<8);
-
- /* Has the slot state changed? */
- if ((reg & SHPC_SLOT_CARD_EMPTY_MASK) == SHPC_SLOT_CARD_EMPTY_MASK)
- slot_state = HPC_SLOT_EMPTY;
- else
- slot_state = pcishpc_slot_shpc_to_hpc(reg & 3);
- if (pcishpc_p->slot_state != slot_state) {
- pcishpc_debug("pcishpc_set_slot_state() Slot State changed");
- /* Set the new slot state in the Slot operation command. */
- cmd_code |= pcishpc_slot_hpc_to_shpc(pcishpc_p->slot_state);
- }
-
- /* Has the Power LED state changed? */
- if (pcishpc_p->power_led_state != pcishpc_led_shpc_to_hpc((reg>>2)&3)) {
- pcishpc_debug("pcishpc_set_slot_state() Power LED State "
- "changed");
- /* Set the new power led state in the Slot operation command. */
- cmd_code |=
- (pcishpc_led_hpc_to_shpc(pcishpc_p->power_led_state)
- << 2);
- }
-
- /* Has the Attn LED state changed? */
- if (pcishpc_p->attn_led_state != pcishpc_led_shpc_to_hpc((reg>>4)&3)) {
- pcishpc_debug("pcishpc_set_slot_state() Attn LED State "
- "changed");
- /* Set the new attn led state in the Slot operation command. */
- cmd_code |= (pcishpc_led_hpc_to_shpc(pcishpc_p->attn_led_state)
- << 4);
- }
-
- return (pcishpc_issue_command(pcishpc_p->ctrl, cmd_code));
-}
-
-
-/*
- * pcishpc_wait_busy()
- *
- * Wait until the SHPC controller is not busy.
- */
-static int
-pcishpc_wait_busy(pcishpc_ctrl_t *ctrl_p)
-{
- uint32_t status;
-
- /* Wait until SHPC controller is NOT busy */
- /*CONSTCOND*/
- while (1) {
- status = pcishpc_read_reg(ctrl_p, SHPC_COMMAND_STATUS_REG);
-
- /* Is there an MRL Sensor error? */
- if ((status & SHPC_COMM_STS_ERR_MASK) ==
- SHPC_COMM_STS_ERR_MRL_OPEN) {
- pcishpc_debug("pcishpc_wait_busy() ERROR: MRL Sensor "
- "error");
- break;
- }
-
- /* Is there an Invalid command error? */
- if ((status & SHPC_COMM_STS_ERR_MASK) ==
- SHPC_COMM_STS_ERR_INVALID_COMMAND) {
- pcishpc_debug("pcishpc_wait_busy() ERROR: Invalid "
- "command error");
- break;
- }
-
- /* Is there an Invalid Speed/Mode error? */
- if ((status & SHPC_COMM_STS_ERR_MASK) ==
- SHPC_COMM_STS_ERR_INVALID_SPEED) {
- pcishpc_debug("pcishpc_wait_busy() ERROR: Invalid "
- "Speed/Mode error");
- break;
- }
-
- /* Is the SHPC controller not BUSY? */
- if (!(status & SHPC_COMM_STS_CTRL_BUSY)) {
- /* Return Success. */
- return (DDI_SUCCESS);
- }
-
- pcishpc_debug("pcishpc_wait_busy() SHPC controller busy. "
- "Waiting");
-
- /* Wait before polling the status register again. */
- delay(drv_usectohz(SHPC_COMMAND_WAIT_TIME));
- }
-
- return (DDI_FAILURE);
-}
-
-
-/*
- * pcishpc_issue_command()
- *
- * Sends a command to the SHPC controller.
- */
-static int
-pcishpc_issue_command(pcishpc_ctrl_t *ctrl_p, uint32_t cmd_code)
-{
- int retCode;
-
- pcishpc_debug("pcishpc_issue_command() cmd_code=%02x", cmd_code);
-
- mutex_enter(&ctrl_p->shpc_intr_mutex);
-
- ctrl_p->command_complete = B_FALSE;
-
- /* Write the command to the SHPC controller. */
- pcishpc_write_reg(ctrl_p, SHPC_COMMAND_STATUS_REG, cmd_code);
-
- while (ctrl_p->command_complete == B_FALSE)
- cv_wait(&ctrl_p->cmd_comp_cv, &ctrl_p->shpc_intr_mutex);
-
- /* Wait until the SHPC controller processes the command. */
- retCode = pcishpc_wait_busy(ctrl_p);
-
- /* Make sure the command completed. */
- if (retCode == DDI_SUCCESS) {
- /* Did the command fail to generate the command complete IRQ? */
- if (ctrl_p->command_complete != B_TRUE) {
- pcishpc_debug("pcishpc_issue_command() Failed on "
- "generate cmd complete IRQ");
- retCode = DDI_FAILURE;
- }
- }
-
- mutex_exit(&ctrl_p->shpc_intr_mutex);
-
- if (retCode == DDI_FAILURE)
- pcishpc_debug("pcishpc_issue_command() Failed on cmd_code=%02x",
- cmd_code);
- else
- pcishpc_debug("pcishpc_issue_command() Success on "
- "cmd_code=%02x", cmd_code);
-
- return (retCode);
-}
-
-/*
- * pcishpc_led_shpc_to_hpc()
- *
- * Convert from SHPC indicator status to HPC indicator status.
- */
-static int
-pcishpc_led_shpc_to_hpc(int state)
-{
- switch (state) {
- case 1: /* SHPC On bits b01 */
- return (HPC_LED_ON);
- case 2: /* SHPC Blink bits b10 */
- return (HPC_LED_BLINK);
- case 3: /* SHPC Off bits b11 */
- return (HPC_LED_OFF);
- }
-
- return (HPC_LED_OFF);
-}
-
-
-/*
- * pcishpc_led_hpc_to_shpc()
- *
- * Convert from HPC indicator status to SHPC indicator status.
- */
-static int
-pcishpc_led_hpc_to_shpc(int state)
-{
- switch (state) {
- case HPC_LED_ON:
- return (1); /* SHPC On bits b01 */
- case HPC_LED_BLINK:
- return (2); /* SHPC Blink bits b10 */
- case HPC_LED_OFF:
- return (3); /* SHPC Off bits b11 */
- }
-
- return (3); /* SHPC Off bits b11 */
-}
-
-/*
- * pcishpc_slot_shpc_to_hpc()
- *
- * Convert from SHPC slot state to HPC slot state.
- */
-static int
-pcishpc_slot_shpc_to_hpc(int state)
-{
- switch (state) {
- case 0: /* SHPC Reserved */
- return (HPC_SLOT_EMPTY);
-
- case 1: /* SHPC Powered Only */
- return (HPC_SLOT_UNKNOWN);
-
- case 2: /* SHPC Enabled */
- return (HPC_SLOT_CONNECTED);
-
- case 3: /* SHPC Disabled */
- return (HPC_SLOT_DISCONNECTED);
- }
-
- /* Unknown slot state. */
- return (HPC_SLOT_UNKNOWN);
-}
-
-
-/*
- * pcishpc_slot_hpc_to_shpc()
- *
- * Convert from HPC slot state to SHPC slot state.
- */
-static int
-pcishpc_slot_hpc_to_shpc(int state)
-{
- switch (state) {
- case HPC_SLOT_EMPTY:
- return (0); /* SHPC Reserved */
-
- case HPC_SLOT_UNKNOWN:
- return (1); /* SHPC Powered Only */
-
- case HPC_SLOT_CONNECTED:
- return (2); /* SHPC Enabled */
-
- case HPC_SLOT_DISCONNECTED:
- return (3); /* SHPC Disabled */
- }
-
- /* Known slot state is reserved. */
- return (0);
-}
-
-
-/*
- * pcishpc_get_slot_state()
- *
- * Get the state of the slot.
- */
-static void
-pcishpc_get_slot_state(pcishpc_t *pcishpc_p)
-{
- uint32_t reg;
-
- /* Read the logical slot register for this Slot. */
- reg = pcishpc_read_reg(pcishpc_p->ctrl,
- SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum);
-
- /* Convert from the SHPC slot state to the HPC slot state. */
- if ((reg & SHPC_SLOT_CARD_EMPTY_MASK) == SHPC_SLOT_CARD_EMPTY_MASK)
- pcishpc_p->slot_state = HPC_SLOT_EMPTY;
- else
- pcishpc_p->slot_state = pcishpc_slot_shpc_to_hpc(reg & 3);
-
- /* Convert from the SHPC Power LED state to the HPC Power LED state. */
- pcishpc_p->power_led_state = pcishpc_led_shpc_to_hpc((reg>>2)&3);
-
- /* Convert from the SHPC Attn LED state to the HPC Attn LED state. */
- pcishpc_p->attn_led_state = pcishpc_led_shpc_to_hpc((reg>>4)&3);
-
- /* We don't have a fault LED so just default it to OFF. */
- pcishpc_p->fault_led_state = HPC_LED_OFF;
-
- /* We don't have an active LED so just default it to OFF. */
- pcishpc_p->active_led_state = HPC_LED_OFF;
-}
-
-/*
- * pcishpc_textledstate()
- *
- * Convert the led state into a text message.
- */
-static char *
-pcishpc_textledstate(hpc_led_state_t state)
-{
- /* Convert an HPC led state into a textual string. */
- switch (state) {
- case HPC_LED_OFF:
- return ("off");
-
- case HPC_LED_ON:
- return ("on");
-
- case HPC_LED_BLINK:
- return ("blink");
- }
- return ("unknown");
-}
-
-/*
- * pcishpc_textrequest()
- *
- * Convert the request into a text message.
- */
-static char *
-pcishpc_textrequest(int request)
-{
- /* Convert an HPC request into a textual string. */
- switch (request) {
- case HPC_CTRL_GET_LED_STATE:
- return ("HPC_CTRL_GET_LED_STATE");
- case HPC_CTRL_SET_LED_STATE:
- return ("HPC_CTRL_SET_LED_STATE");
- case HPC_CTRL_GET_SLOT_STATE:
- return ("HPC_CTRL_GET_SLOT_STATE");
- case HPC_CTRL_DEV_CONFIGURED:
- return ("HPC_CTRL_DEV_CONFIGURED");
- case HPC_CTRL_DEV_UNCONFIGURED:
- return ("HPC_CTRL_DEV_UNCONFIGURED");
- case HPC_CTRL_GET_BOARD_TYPE:
- return ("HPC_CTRL_GET_BOARD_TYPE");
- case HPC_CTRL_DISABLE_AUTOCFG:
- return ("HPC_CTRL_DISABLE_AUTOCFG");
- case HPC_CTRL_ENABLE_AUTOCFG:
- return ("HPC_CTRL_ENABLE_AUTOCFG");
- case HPC_CTRL_DISABLE_SLOT:
- return ("HPC_CTRL_DISABLE_SLOT");
- case HPC_CTRL_ENABLE_SLOT:
- return ("HPC_CTRL_ENABLE_SLOT");
- case HPC_CTRL_DISABLE_ENUM:
- return ("HPC_CTRL_DISABLE_ENUM");
- case HPC_CTRL_ENABLE_ENUM:
- return ("HPC_CTRL_ENABLE_ENUM");
- case HPC_CTRL_DEV_CONFIG_FAILURE:
- return ("HPC_CTRL_DEV_CONFIG_FAILURE");
- case HPC_CTRL_DEV_UNCONFIG_FAILURE:
- return ("HPC_CTRL_DEV_UNCONFIG_FAILURE");
- case HPC_CTRL_DEV_CONFIG_START:
- return ("HPC_CTRL_DEV_CONFIG_START");
- case HPC_CTRL_DEV_UNCONFIG_START:
- return ("HPC_CTRL_DEV_UNCONFIG_START");
- }
- return ("Unknown");
-}
-
-/*
- * pcishpc_textslotstate()
- *
- * Convert the request into a text message.
- */
-static char *
-pcishpc_textslotstate(hpc_slot_state_t state)
-{
- /* Convert an HPC slot state into a textual string. */
- switch (state) {
- case HPC_SLOT_EMPTY:
- return ("HPC_SLOT_EMPTY");
- case HPC_SLOT_DISCONNECTED:
- return ("HPC_SLOT_DISCONNECTED");
- case HPC_SLOT_CONNECTED:
- return ("HPC_SLOT_CONNECTED");
- case HPC_SLOT_UNKNOWN:
- return ("HPC_SLOT_UNKNOWN");
- }
- return ("Unknown");
-}
-
-
-/*
- * pcishpc_write_reg()
- *
- * Write to a SHPC controller register.
- */
-static void
-pcishpc_write_reg(pcishpc_ctrl_t *ctrl_p, int reg, uint32_t data)
-{
- /* Setup the SHPC dword select register. */
- pci_config_put8(ctrl_p->shpc_config_hdl,
- ctrl_p->shpc_dword_select, (uint8_t)reg);
-
- /* Read back the SHPC dword select register and verify. */
- if (pci_config_get8(ctrl_p->shpc_config_hdl,
- ctrl_p->shpc_dword_select) != (uint8_t)reg) {
- pcishpc_debug("pcishpc_write_reg() - Failed writing "
- "DWORD select reg");
- return;
- }
-
- /* Write to the SHPC dword data register. */
- pci_config_put32(ctrl_p->shpc_config_hdl,
- ctrl_p->shpc_dword_data_reg, data);
-
- /*
- * Issue a read of the VendorID/DeviceID just to force the previous
- * write to complete. This is probably not necessary, but it does
- * help enforce ordering if there is an issue.
- */
- (void) pci_config_get16(ctrl_p->shpc_config_hdl, PCI_CONF_VENID);
-}
-
-
-/*
- * pcishpc_read_reg()
- *
- * Read from a SHPC controller register.
- */
-static uint32_t
-pcishpc_read_reg(pcishpc_ctrl_t *ctrl_p, int reg)
-{
- /* Setup the SHPC dword select register. */
- pci_config_put8(ctrl_p->shpc_config_hdl,
- ctrl_p->shpc_dword_select, (uint8_t)reg);
-
- /* Read back the SHPC dword select register and verify. */
- if (pci_config_get8(ctrl_p->shpc_config_hdl,
- ctrl_p->shpc_dword_select) != (uint8_t)reg) {
- pcishpc_debug("pcishpc_read_reg() - Failed writing DWORD "
- "select reg");
- return (0xFFFFFFFF);
- }
-
- /* Read from the SHPC dword data register. */
- return (pci_config_get32(ctrl_p->shpc_config_hdl,
- ctrl_p->shpc_dword_data_reg));
-}
-
-
-/*
- * pcishpc_debug()
- *
- * Controls debug output if enabled.
- */
-static void
-pcishpc_debug(char *fmt, ...)
-{
- va_list ap;
-
- if (pcishpc_debug_enabled) {
- va_start(ap, fmt);
- vcmn_err(CE_WARN, fmt, ap);
- va_end(ap);
- }
-}
-
-
-/*
- * pcishpc_dump_regs()
- *
- * Dumps all of the SHPC controller registers.
- */
-static void
-pcishpc_dump_regs(pcishpc_ctrl_t *ctrl_p)
-{
- int slot, numSlots;
- uint32_t reg;
- char *state;
-
- cmn_err(CE_WARN, "pcishpc_dump_regs() called:");
- cmn_err(CE_WARN, "================================================"
- "==========");
-
- cmn_err(CE_WARN, "SHPC Base Offset "
- ": 0x%08x", pcishpc_read_reg(ctrl_p, SHPC_BASE_OFFSET_REG));
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_SLOTS_AVAIL_I_REG);
-
- cmn_err(CE_WARN, "Number of PCIX slots avail (33 Mhz) : %d",
- (reg & 31));
-
- cmn_err(CE_WARN, "Number of PCIX slots avail (66 Mhz) : %d",
- ((reg>>8) & 31));
-
- cmn_err(CE_WARN, "Number of PCIX slots avail (100 Mhz) : %d",
- ((reg>>16) & 31));
-
- cmn_err(CE_WARN, "Number of PCIX slots avail (133 Mhz) : %d",
- ((reg>>24) & 31));
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_SLOTS_AVAIL_II_REG);
-
- cmn_err(CE_WARN, "Number of conventional PCI slots (66 Mhz) : %d",
- (reg & 31));
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_SLOT_CONFIGURATION_REG);
-
- numSlots = (reg & 31);
-
- cmn_err(CE_WARN, "Number of Slots connected to this port : %d",
- numSlots);
-
- cmn_err(CE_WARN, "PCI Device # for First HotPlug Slot : %d",
- ((reg>>8) & 31));
-
- cmn_err(CE_WARN, "Physical Slot # for First PCI Device # : %d",
- ((reg>>16) & 0x7ff));
-
- cmn_err(CE_WARN, "Physical Slot Number Up/Down "
- ": %d", ((reg>>29) & 0x1));
-
- cmn_err(CE_WARN, "MRL Sensor Implemented "
- ": %s", (reg & SHPC_SLOT_CONFIG_MRL_SENSOR) ? "Yes" :
- "No");
-
- cmn_err(CE_WARN, "Attention Button Implemented "
- ": %s", (reg & SHPC_SLOT_CONFIG_ATTN_BUTTON) ? "Yes" :
- "No");
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_PROF_IF_SBCR_REG);
-
- switch (reg & 7) {
- case 0:
- state = "33Mhz Conventional PCI";
- break;
- case 1:
- state = "66Mhz Conventional PCI";
- break;
- case 2:
- state = "66Mhz PCI-X";
- break;
- case 3:
- state = "100Mhz PCI-X";
- break;
- case 4:
- state = "133Mhz PCI-X";
- break;
- default:
- state = "Reserved (Error)";
- break;
- }
-
- cmn_err(CE_WARN, "Current Port Operation Mode "
- ": %s", state);
-
- cmn_err(CE_WARN, "SHPC Interrupt Message Number "
- ": %d", ((reg>>16) &31));
-
- cmn_err(CE_WARN, "SHPC Programming Interface "
- ": %d", ((reg>>24) & 0xff));
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_COMMAND_STATUS_REG);
-
- cmn_err(CE_WARN, "SHPC Command Code "
- ": %d", (reg & 0xff));
-
- cmn_err(CE_WARN, "SHPC Target Slot "
- ": %d", ((reg>>8) & 31));
-
- cmn_err(CE_WARN, "SHPC Controller Busy "
- ": %s", ((reg>>16) & 1) ? "Yes" : "No");
-
- cmn_err(CE_WARN, "SHPC Controller Err: MRL Sensor "
- ": %s", ((reg>>17) & 1) ? "Yes" : "No");
-
- cmn_err(CE_WARN, "SHPC Controller Err: Invalid Command : %s",
- ((reg>>18) & 1) ? "Yes" : "No");
-
- cmn_err(CE_WARN, "SHPC Controller Err: Invalid Speed/Mode : %s",
- ((reg>>19) & 1) ? "Yes" : "No");
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_IRQ_LOCATOR_REG);
-
- cmn_err(CE_WARN, "Command Completion Interrupt Pending : %s",
- (reg & SHPC_IRQ_CMD_COMPLETE) ? "Yes" : "No");
-
- for (slot = 0; slot < numSlots; slot++) {
- cmn_err(CE_WARN, "Slot %d Interrupt Pending "
- ": %s", slot+1,
- (reg & (SHPC_IRQ_SLOT_N_PENDING<<slot)) ? "Yes" : "No");
- }
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_SERR_LOCATOR_REG);
-
- cmn_err(CE_WARN, "Arbiter SERR Pending "
- ": %s", (reg & SHPC_IRQ_SERR_ARBITER_PENDING) ?
- "Yes" : "No");
-
- for (slot = 0; slot < numSlots; slot++) {
- cmn_err(CE_WARN, "Slot %d SERR Pending "
- ": %s", slot+1, (reg &
- (SHPC_IRQ_SERR_SLOT_N_PENDING<<slot)) ?
- "Yes" : "No");
- }
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG);
-
- cmn_err(CE_WARN, "Global Interrupt Mask "
- ": %s", (reg & SHPC_SERR_INT_GLOBAL_IRQ_MASK) ?
- "Yes" : "No");
-
- cmn_err(CE_WARN, "Global SERR Mask "
- ": %s", (reg & SHPC_SERR_INT_GLOBAL_SERR_MASK) ?
- "Yes" : "No");
-
- cmn_err(CE_WARN, "Command Completion Interrupt Mask "
- ": %s", (reg & SHPC_SERR_INT_CMD_COMPLETE_MASK) ?
- "Yes" : "No");
-
- cmn_err(CE_WARN, "Arbiter SERR Mask "
- ": %s", (reg & SHPC_SERR_INT_ARBITER_SERR_MASK) ?
- "Yes" : "No");
-
- cmn_err(CE_WARN, "Command Completion Detected "
- ": %s", (reg & SHPC_SERR_INT_CMD_COMPLETE_IRQ) ?
- "Yes" : "No");
-
- cmn_err(CE_WARN, "Arbiter Timeout Detected "
- ": %s", (reg & SHPC_SERR_INT_ARBITER_IRQ) ?
- "Yes" : "No");
-
-
- for (slot = 0; slot < numSlots; slot++) {
- cmn_err(CE_WARN, "Logical Slot %d Registers:", slot+1);
- cmn_err(CE_WARN, "------------------------------------");
-
- reg = pcishpc_read_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot);
-
- cmn_err(CE_WARN, "Slot %d state "
- ": %s", slot+1,
- pcishpc_textslotstate(pcishpc_slot_shpc_to_hpc(
- (reg & 3))));
-
- cmn_err(CE_WARN, "Slot %d Power Indicator State "
- ": %s", slot+1,
- pcishpc_textledstate(pcishpc_led_shpc_to_hpc(
- (reg>>2) &3)));
-
- cmn_err(CE_WARN, "Slot %d Attention Indicator State "
- ": %s", slot+1,
- pcishpc_textledstate(pcishpc_led_shpc_to_hpc(
- (reg>>4)&3)));
-
- cmn_err(CE_WARN, "Slot %d Power Fault "
- ": %s", slot+1, ((reg>>6)&1) ? "Fault Detected" :
- "No Fault");
- cmn_err(CE_WARN, "Slot %d Attention Button "
- ": %s", slot+1, ((reg>>7)&1) ? "Depressed" :
- "Not Depressed");
- cmn_err(CE_WARN, "Slot %d MRL Sensor "
- ": %s", slot+1, ((reg>>8)&1) ? "Not Closed" :
- "Closed");
- cmn_err(CE_WARN, "Slot %d 66mhz Capable "
- ": %s", slot+1, ((reg>>9)&1) ? "66mhz" : "33mgz");
-
- switch ((reg>>10)&3) {
- case 0:
- state = "Card Present 7.5W";
- break;
- case 1:
- state = "Card Present 15W";
- break;
- case 2:
- state = "Card Present 25W";
- break;
- case 3:
- state = "Slot Empty";
- break;
- }
-
- cmn_err(CE_WARN, "Slot %d PRSNT1#/PRSNT2# "
- ": %s", slot+1, state);
-
- switch ((reg>>12)&3) {
- case 0:
- state = "Non PCI-X";
- break;
- case 1:
- state = "66mhz PCI-X";
- break;
- case 2:
- state = "Reserved";
- break;
- case 3:
- state = "133mhz PCI-X";
- break;
- }
-
- cmn_err(CE_WARN, "Slot %d Card Presence Change Detected : %s",
- slot+1, (reg & SHPC_SLOT_PRESENCE_DETECTED) ? "Yes" :
- "No");
- cmn_err(CE_WARN, "Slot %d Isolated Power Fault Detected : %s",
- slot+1, (reg & SHPC_SLOT_ISO_PWR_DETECTED) ? "Yes" :
- "No");
- cmn_err(CE_WARN, "Slot %d Attention Button Press Detected"
- ": %s", slot+1,
- (reg & SHPC_SLOT_ATTN_DETECTED) ? "Yes" : "No");
- cmn_err(CE_WARN, "Slot %d MRL Sensor Change Detected "
- ": %s", slot+1,
- (reg & SHPC_SLOT_MRL_DETECTED) ? "Yes" : "No");
- cmn_err(CE_WARN, "Slot %d Connected Power Fault Detected"
- ": %s", slot+1,
- (reg & SHPC_SLOT_POWER_DETECTED) ? "Yes" : "No");
-
- cmn_err(CE_WARN, "Slot %d Card Presence IRQ Masked "
- ": %s", slot+1,
- (reg & SHPC_SLOT_PRESENCE_MASK) ? "Yes" : "No");
- cmn_err(CE_WARN, "Slot %d Isolated Power Fault IRQ Masked"
- ": %s", slot+1,
- (reg & SHPC_SLOT_ISO_PWR_MASK) ? "Yes" : "No");
- cmn_err(CE_WARN, "Slot %d Attention Button IRQ Masked "
- ": %s", slot+1, (reg & SHPC_SLOT_ATTN_MASK) ? "Yes" :
- "No");
- cmn_err(CE_WARN, "Slot %d MRL Sensor IRQ Masked "
- ": %s", slot+1,
- (reg & SHPC_SLOT_MRL_MASK) ? "Yes" : "No");
- cmn_err(CE_WARN, "Slot %d Connected Power Fault IRQ Masked"
- " : %s", slot+1,
- (reg & SHPC_SLOT_POWER_MASK) ? "Yes" : "No");
- cmn_err(CE_WARN, "Slot %d MRL Sensor SERR Masked "
- ": %s", slot+1,
- (reg & SHPC_SLOT_MRL_SERR_MASK) ? "Yes" : "No");
- cmn_err(CE_WARN, "Slot %d Connected Power Fault SERR Masked :"
- "%s", slot+1,
- (reg & SHPC_SLOT_POWER_SERR_MASK) ? "Yes" : "No");
- }
-}
-
-static void
-pcishpc_attn_btn_handler(pcishpc_t *pcishpc_p)
-{
- hpc_led_state_t power_led_state;
- callb_cpr_t cprinfo;
-
- pcishpc_debug("pcishpc_attn_btn_handler: thread started\n");
-
- CALLB_CPR_INIT(&cprinfo, &pcishpc_p->ctrl->shpc_mutex,
- callb_generic_cpr, "pcishpc_attn_btn_handler");
-
- mutex_enter(&pcishpc_p->ctrl->shpc_mutex);
-
- /* wait for ATTN button event */
- cv_wait(&pcishpc_p->attn_btn_cv, &pcishpc_p->ctrl->shpc_mutex);
-
- while (pcishpc_p->attn_btn_thread_exit == B_FALSE) {
- if (pcishpc_p->attn_btn_pending == B_TRUE) {
- /* get the current state of power LED */
- power_led_state = pcishpc_p->power_led_state;
-
- /* Blink the Power LED while we wait for 5 seconds */
- (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED,
- HPC_LED_BLINK);
-
- /* wait for 5 seconds before taking any action */
- if (cv_timedwait(&pcishpc_p->attn_btn_cv,
- &pcishpc_p->ctrl->shpc_mutex,
- ddi_get_lbolt() + SEC_TO_TICK(5)) == -1) {
- /*
- * It is a time out;
- * make sure the ATTN pending flag is
- * still ON before sending the event
- * to HPS framework.
- */
- if (pcishpc_p->attn_btn_pending == B_TRUE) {
- /*
- * send the ATTN button event
- * to HPS framework
- */
- pcishpc_p->attn_btn_pending = B_FALSE;
- (void) hpc_slot_event_notify(
- pcishpc_p->slot_handle,
- HPC_EVENT_SLOT_ATTN,
- HPC_EVENT_NORMAL);
- }
- }
-
- /* restore the power LED state ??? XXX */
- (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED,
- power_led_state);
- continue;
- }
-
- /* wait for another ATTN button event */
- cv_wait(&pcishpc_p->attn_btn_cv, &pcishpc_p->ctrl->shpc_mutex);
- }
-
- pcishpc_debug("pcishpc_attn_btn_handler: thread exit\n");
- cv_signal(&pcishpc_p->attn_btn_cv);
- CALLB_CPR_EXIT(&cprinfo);
- thread_exit();
-}
-
-/*
- * setup slot name/slot-number info.
- */
-static void
-pcishpc_set_slot_name(pcishpc_ctrl_t *ctrl_p, int slot)
-{
- pcishpc_t *p = ctrl_p->slots[slot];
- uchar_t *slotname_data;
- int *slotnum;
- uint_t count;
- int len;
- uchar_t *s;
- uint32_t bit_mask;
- int pci_id_cnt, pci_id_bit;
- int slots_before, found;
- int invalid_slotnum = 0;
-
- if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->shpc_dip,
- DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) ==
- DDI_PROP_SUCCESS) {
- p->phy_slot_num = slotnum[0];
- ddi_prop_free(slotnum);
- } else {
- if (ctrl_p->deviceIncreases)
- p->phy_slot_num = ctrl_p->physStart + slot;
- else
- p->phy_slot_num = ctrl_p->physStart - slot;
-
- if ((ndi_prop_update_int(DDI_DEV_T_NONE, ctrl_p->shpc_dip,
- "physical-slot#", p->phy_slot_num)) != DDI_SUCCESS) {
- pcishpc_debug("pcishpc_set_slot_name(): failed to "
- "create phyical-slot#%d", p->phy_slot_num);
- }
- }
-
- if (!p->phy_slot_num) { /* platform may not have initialized it */
- p->phy_slot_num = pci_config_get8(ctrl_p->shpc_config_hdl,
- PCI_BCNF_SECBUS);
- invalid_slotnum = 1;
- }
-
- /*
- * construct the slot_name:
- * if "slot-names" property exists then use that name
- * else if valid slot number exists then it is "pci<slot-num>".
- * else it will be "pci<sec-bus-number>dev<dev-number>"
- */
- if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->shpc_dip, DDI_PROP_DONTPASS,
- "slot-names", (caddr_t)&slotname_data,
- &len) == DDI_PROP_SUCCESS) {
-
- bit_mask = slotname_data[3] | (slotname_data[2] << 8) |
- (slotname_data[1] << 16) | (slotname_data[0] << 24);
-
- pci_id_bit = 1;
- pci_id_cnt = slots_before = found = 0;
-
- /*
- * Walk the bit mask until we find the bit that corresponds
- * to our slots device number. We count how many bits
- * we find before we find our slot's bit.
- */
- while (!found && (pci_id_cnt < 32)) {
-
- while (p->deviceNum != pci_id_cnt) {
-
- /*
- * Find the next bit set.
- */
- while (!(bit_mask & pci_id_bit) &&
- (pci_id_cnt < 32)) {
- pci_id_bit = pci_id_bit << 1;
- pci_id_cnt++;
- }
-
- if (p->deviceNum != pci_id_cnt)
- slots_before++;
- else
- found = 1;
- }
- }
-
- if (pci_id_cnt < 32) {
-
- /*
- * Set ptr to first string.
- */
- s = slotname_data + 4;
-
- /*
- * Increment past all the strings for the slots
- * before ours.
- */
- while (slots_before) {
- while (*s != NULL)
- s++;
- s++;
- slots_before--;
- }
-
- (void) sprintf(p->slot_info.pci_slot_name, (char *)s);
-
- kmem_free(slotname_data, len);
- return;
- }
-
- /* slot-names entry not found */
- pcishpc_debug("pcishpc_set_slot_name(): "
- "No slot-names entry found for slot #%d",
- p->phy_slot_num);
- kmem_free(slotname_data, len);
- }
-
- if (invalid_slotnum)
- (void) sprintf(p->slot_info.pci_slot_name, "pci%d",
- p->deviceNum);
- else
- (void) sprintf(p->slot_info.pci_slot_name, "pci%d",
- p->phy_slot_num);
-}
diff --git a/usr/src/uts/common/io/pciex/hotplug/pcie_hp.c b/usr/src/uts/common/io/pciex/hotplug/pcie_hp.c
new file mode 100644
index 0000000000..55cc077a5b
--- /dev/null
+++ b/usr/src/uts/common/io/pciex/hotplug/pcie_hp.c
@@ -0,0 +1,1300 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains the common hotplug code that is used by Standard
+ * PCIe and PCI HotPlug Controller code.
+ *
+ * NOTE: This file is compiled and delivered through misc/pcie module.
+ */
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/debug.h>
+#include <sys/vtrace.h>
+#include <sys/autoconf.h>
+#include <sys/varargs.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/time.h>
+#include <sys/note.h>
+#include <sys/callb.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/sysevent.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/dr.h>
+#include <sys/pci_impl.h>
+#include <sys/pci_cap.h>
+#include <sys/hotplug/pci/pcicfg.h>
+#include <sys/hotplug/pci/pcie_hp.h>
+#include <sys/hotplug/pci/pciehpc.h>
+#include <sys/hotplug/pci/pcishpc.h>
+#include <io/pciex/pcieb.h>
+
+/* Local functions prototype */
+static int pcie_hp_list_occupants(dev_info_t *dip, void *arg);
+static int pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip,
+ char *cn_name);
+static int pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num);
+static int pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg);
+static int pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg);
+static int pcie_hp_match_dev_func(dev_info_t *dip, void *hdl);
+static boolean_t pcie_hp_match_dev(dev_info_t *dip, int dev_num);
+static int pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num,
+ int *func_num);
+static int pcie_hp_create_port_name_num(dev_info_t *dip,
+ ddi_hp_cn_info_t *cn_info);
+static int pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num,
+ int func_num);
+
+/*
+ * Global functions (called by other drivers/modules)
+ */
+
+/*
+ * return description text for led state
+ */
+char *
+pcie_led_state_text(pcie_hp_led_state_t state)
+{
+ switch (state) {
+ case PCIE_HP_LED_ON:
+ return (PCIEHPC_PROP_VALUE_ON);
+ case PCIE_HP_LED_OFF:
+ return (PCIEHPC_PROP_VALUE_OFF);
+ case PCIE_HP_LED_BLINK:
+ default:
+ return (PCIEHPC_PROP_VALUE_BLINK);
+ }
+}
+
+/*
+ * return description text for slot condition
+ */
+char *
+pcie_slot_condition_text(ap_condition_t condition)
+{
+ switch (condition) {
+ case AP_COND_UNKNOWN:
+ return (PCIEHPC_PROP_VALUE_UNKNOWN);
+ case AP_COND_OK:
+ return (PCIEHPC_PROP_VALUE_OK);
+ case AP_COND_FAILING:
+ return (PCIEHPC_PROP_VALUE_FAILING);
+ case AP_COND_FAILED:
+ return (PCIEHPC_PROP_VALUE_FAILED);
+ case AP_COND_UNUSABLE:
+ return (PCIEHPC_PROP_VALUE_UNUSABLE);
+ default:
+ return (PCIEHPC_PROP_VALUE_UNKNOWN);
+ }
+}
+
+/*
+ * routine to copy in a nvlist from userland
+ */
+int
+pcie_copyin_nvlist(char *packed_buf, size_t packed_sz, nvlist_t **nvlp)
+{
+ int ret = DDI_SUCCESS;
+ char *packed;
+ nvlist_t *dest = NULL;
+
+ if (packed_buf == NULL || packed_sz == 0)
+ return (DDI_EINVAL);
+
+ /* copyin packed nvlist */
+ if ((packed = kmem_alloc(packed_sz, KM_SLEEP)) == NULL)
+ return (DDI_ENOMEM);
+
+ if (copyin(packed_buf, packed, packed_sz) != 0) {
+ cmn_err(CE_WARN, "pcie_copyin_nvlist: copyin failed.\n");
+ ret = DDI_FAILURE;
+ goto copyin_cleanup;
+ }
+
+ /* unpack packed nvlist */
+ if ((ret = nvlist_unpack(packed, packed_sz, &dest, KM_SLEEP)) != 0) {
+ cmn_err(CE_WARN, "pcie_copyin_nvlist: nvlist_unpack "
+ "failed with err %d\n", ret);
+ switch (ret) {
+ case EINVAL:
+ case ENOTSUP:
+ ret = DDI_EINVAL;
+ goto copyin_cleanup;
+ case ENOMEM:
+ ret = DDI_ENOMEM;
+ goto copyin_cleanup;
+ default:
+ ret = DDI_FAILURE;
+ goto copyin_cleanup;
+ }
+ }
+ *nvlp = dest;
+copyin_cleanup:
+ kmem_free(packed, packed_sz);
+ return (ret);
+}
+
+/*
+ * routine to copy out a nvlist to userland
+ */
+int
+pcie_copyout_nvlist(nvlist_t *nvl, char *packed_buf, size_t *buf_sz)
+{
+ int err = 0;
+ char *buf = NULL;
+ size_t packed_sz;
+
+ if (nvl == NULL || packed_buf == NULL || buf_sz == NULL)
+ return (DDI_EINVAL);
+
+ /* pack nvlist, the library will allocate memory */
+ if ((err = nvlist_pack(nvl, &buf, &packed_sz, NV_ENCODE_NATIVE, 0))
+ != 0) {
+ cmn_err(CE_WARN, "pcie_copyout_nvlist: nvlist_pack "
+ "failed with err %d\n", err);
+ switch (err) {
+ case EINVAL:
+ case ENOTSUP:
+ return (DDI_EINVAL);
+ case ENOMEM:
+ return (DDI_ENOMEM);
+ default:
+ return (DDI_FAILURE);
+ }
+ }
+ if (packed_sz > *buf_sz) {
+ return (DDI_EINVAL);
+ }
+
+ /* copyout packed nvlist */
+ if (copyout(buf, packed_buf, packed_sz) != 0) {
+ cmn_err(CE_WARN, "pcie_copyout_nvlist: copyout " "failed.\n");
+ kmem_free(buf, packed_sz);
+ return (DDI_FAILURE);
+ }
+
+ *buf_sz = packed_sz;
+ kmem_free(buf, packed_sz);
+ return (DDI_SUCCESS);
+}
+
+/*
+ * init bus_hp_op entry and init hotpluggable slots & virtual ports
+ */
+int
+pcie_hp_init(dev_info_t *dip, caddr_t arg)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ int ret = DDI_SUCCESS, count;
+ dev_info_t *cdip;
+
+ if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
+ /* Init hotplug controller */
+ ret = pciehpc_init(dip, arg);
+ } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
+ ret = pcishpc_init(dip);
+ }
+
+ if (ret != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "pcie_hp_init: initialize hotplug "
+ "controller failed with %d\n", ret);
+ return (ret);
+ }
+
+ ndi_devi_enter(dip, &count);
+
+ /* Create port for the first level children */
+ cdip = ddi_get_child(dip);
+ while (cdip != NULL) {
+ if ((ret = pcie_hp_register_port(cdip, dip, NULL))
+ != DDI_SUCCESS) {
+ /* stop and cleanup */
+ break;
+ }
+ cdip = ddi_get_next_sibling(cdip);
+ }
+ ndi_devi_exit(dip, count);
+ if (ret != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "pcie_hp_init: initialize virtual "
+ "hotplug port failed with %d\n", ret);
+ (void) pcie_hp_uninit(dip);
+
+ return (ret);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * uninit the hotpluggable slots and virtual ports
+ */
+int
+pcie_hp_uninit(dev_info_t *dip)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ pcie_hp_unreg_port_t arg;
+
+ /*
+ * Must set arg.rv to NDI_SUCCESS so that if there's no port
+ * under this dip, we still return success thus the bridge
+ * driver can be successfully detached.
+ *
+ * Note that during the probe PCI configurator calls
+ * ndi_devi_offline() to detach driver for a new probed bridge,
+ * so that it can reprogram the resources for the bridge,
+ * ndi_devi_offline() calls into pcieb_detach() which in turn
+ * calls into this function. In this case there are no ports
+ * created under a new probe bridge dip, as ports are only
+ * created after the configurator finishing probing, thus the
+ * ndi_hp_walk_cn() will see no ports when this is called
+ * from the PCI configurtor.
+ */
+ arg.nexus_dip = dip;
+ arg.connector_num = DDI_HP_CN_NUM_NONE;
+ arg.rv = NDI_SUCCESS;
+
+ /* tear down all virtual hotplug handles */
+ ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg);
+
+ if (arg.rv != NDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p))
+ (void) pciehpc_uninit(dip);
+ else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p))
+ (void) pcishpc_uninit(dip);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * interrupt handler
+ */
+int
+pcie_hp_intr(dev_info_t *dip)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ int ret = DDI_INTR_UNCLAIMED;
+
+ if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p))
+ ret = pciehpc_intr(dip);
+ else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p))
+ ret = pcishpc_intr(dip);
+
+ return (ret);
+}
+
+/*
+ * Probe the given PCIe/PCI Hotplug Connection (CN).
+ */
+/*ARGSUSED*/
+int
+pcie_hp_probe(pcie_hp_slot_t *slot_p)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ dev_info_t *dip = ctrl_p->hc_dip;
+
+ /*
+ * Call the configurator to probe a given PCI hotplug
+ * Hotplug Connection (CN).
+ */
+ if (pcicfg_configure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0)
+ != PCICFG_SUCCESS) {
+ PCIE_DBG("pcie_hp_probe() failed\n");
+ return (DDI_FAILURE);
+ }
+ slot_p->hs_condition = AP_COND_OK;
+ pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip),
+ slot_p->hs_minor), slot_p->hs_device_num);
+
+ /*
+ * Create ports for the newly probed devices.
+ * Note, this is only for the first level children because the
+ * descendants' ports will be created during bridge driver attach.
+ */
+ return (pcie_hp_register_ports_for_dev(dip, slot_p->hs_device_num));
+}
+
+/*
+ * Unprobe the given PCIe/PCI Hotplug Connection (CN):
+ * 1. remove all child device nodes
+ * 2. unregister all dependent ports
+ */
+/*ARGSUSED*/
+int
+pcie_hp_unprobe(pcie_hp_slot_t *slot_p)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ dev_info_t *dip = ctrl_p->hc_dip;
+ pcie_hp_unreg_port_t arg;
+
+ /*
+ * Call the configurator to unprobe a given PCI hotplug
+ * Hotplug Connection (CN).
+ */
+ if (pcicfg_unconfigure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0)
+ != PCICFG_SUCCESS) {
+ PCIE_DBG("pcie_hp_unprobe() failed\n");
+ return (DDI_FAILURE);
+ }
+ slot_p->hs_condition = AP_COND_UNKNOWN;
+ pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip),
+ slot_p->hs_minor));
+
+ /*
+ * Remove ports for the unprobed devices.
+ * Note, this is only for the first level children because the
+ * descendants' ports were already removed during bridge driver dettach.
+ */
+ arg.nexus_dip = dip;
+ arg.connector_num = slot_p->hs_info.cn_num;
+ arg.rv = NDI_SUCCESS;
+ ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg);
+
+ return (arg.rv == NDI_SUCCESS) ? (DDI_SUCCESS) : (DDI_FAILURE);
+}
+
+/* Read-only probe: no hardware register programming. */
+int
+pcie_read_only_probe(dev_info_t *dip, char *cn_name, dev_info_t **pcdip)
+{
+ long dev, func;
+ int ret;
+ char *sp;
+ dev_info_t *cdip;
+
+ *pcdip = NULL;
+ /*
+ * Parse the string of a pci Port name and get the device number
+ * and function number.
+ */
+ if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0)
+ return (DDI_EINVAL);
+ if (ddi_strtol(sp + 1, NULL, 10, &func) != 0)
+ return (DDI_EINVAL);
+
+ ret = pcicfg_configure(dip, (int)dev, (int)func,
+ PCICFG_FLAG_READ_ONLY);
+ if (ret == PCICFG_SUCCESS) {
+ cdip = pcie_hp_devi_find(dip, (int)dev, (int)func);
+ *pcdip = cdip;
+ }
+ return (ret);
+}
+
+/* Read-only unprobe: no hardware register programming. */
+int
+pcie_read_only_unprobe(dev_info_t *dip, char *cn_name)
+{
+ long dev, func;
+ int ret;
+ char *sp;
+
+ /*
+ * Parse the string of a pci Port name and get the device number
+ * and function number.
+ */
+ if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0)
+ return (DDI_EINVAL);
+ if (ddi_strtol(sp + 1, NULL, 10, &func) != 0)
+ return (DDI_EINVAL);
+
+ ret = pcicfg_unconfigure(dip, (int)dev, (int)func,
+ PCICFG_FLAG_READ_ONLY);
+
+ return (ret);
+}
+
+/* Control structure used to find a device in the devinfo tree */
+struct pcie_hp_find_ctrl {
+ uint_t device;
+ uint_t function;
+ dev_info_t *dip;
+};
+
+/*
+ * find a devinfo node with specified device and function number
+ * in the device tree under 'dip'
+ */
+dev_info_t *
+pcie_hp_devi_find(dev_info_t *dip, uint_t device, uint_t function)
+{
+ struct pcie_hp_find_ctrl ctrl;
+ int count;
+
+ ctrl.device = device;
+ ctrl.function = function;
+ ctrl.dip = NULL;
+
+ ndi_devi_enter(dip, &count);
+ ddi_walk_devs(ddi_get_child(dip), pcie_hp_match_dev_func,
+ (void *)&ctrl);
+ ndi_devi_exit(dip, count);
+
+ return (ctrl.dip);
+}
+
+/*
+ * routine to create 'pci-occupant' property for a hotplug slot
+ */
+void
+pcie_hp_create_occupant_props(dev_info_t *dip, dev_t dev, int pci_dev)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ pcie_hp_ctrl_t *ctrl_p = (pcie_hp_ctrl_t *)bus_p->bus_hp_ctrl;
+ pcie_hp_slot_t *slotp;
+ pcie_hp_cn_cfg_t cn_cfg;
+ pcie_hp_occupant_info_t *occupant;
+ int circular, i;
+
+ ndi_devi_enter(dip, &circular);
+
+ if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) {
+ slotp = (ctrl_p && (pci_dev == 0)) ?
+ ctrl_p->hc_slots[pci_dev] : NULL;
+ } else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) {
+ if (ctrl_p) {
+ int slot_num;
+
+ slot_num = (ctrl_p->hc_device_increases) ?
+ (pci_dev - ctrl_p->hc_device_start) :
+ (pci_dev + ctrl_p->hc_device_start);
+
+ slotp = ctrl_p->hc_slots[slot_num];
+ } else {
+ slotp = NULL;
+ }
+ }
+
+ if (slotp == NULL)
+ return;
+
+ occupant = kmem_alloc(sizeof (pcie_hp_occupant_info_t), KM_SLEEP);
+ occupant->i = 0;
+
+ cn_cfg.flag = B_FALSE;
+ cn_cfg.rv = NDI_SUCCESS;
+ cn_cfg.dip = NULL;
+ cn_cfg.slotp = (void *)slotp;
+ cn_cfg.cn_private = (void *)occupant;
+
+ ddi_walk_devs(ddi_get_child(dip), pcie_hp_list_occupants,
+ (void *)&cn_cfg);
+
+ if (occupant->i == 0) {
+ /* no occupants right now, need to create stub property */
+ char *c[] = { "" };
+ (void) ddi_prop_update_string_array(dev, dip, "pci-occupant",
+ c, 1);
+ } else {
+ (void) ddi_prop_update_string_array(dev, dip, "pci-occupant",
+ occupant->id, occupant->i);
+ }
+
+ for (i = 0; i < occupant->i; i++)
+ kmem_free(occupant->id[i], sizeof (char[MAXPATHLEN]));
+
+ kmem_free(occupant, sizeof (pcie_hp_occupant_info_t));
+
+ ndi_devi_exit(dip, circular);
+}
+
+/*
+ * routine to remove 'pci-occupant' property for a hotplug slot
+ */
+void
+pcie_hp_delete_occupant_props(dev_info_t *dip, dev_t dev)
+{
+ (void) ddi_prop_remove(dev, dip, "pci-occupant");
+}
+
+/*
+ * general code to create a minor node, called from hotplug controller
+ * drivers.
+ */
+int
+pcie_create_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot)
+{
+ dev_info_t *dip = ctrl_p->hc_dip;
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[slot];
+ ddi_hp_cn_info_t *info_p = &slot_p->hs_info;
+
+ if (ddi_create_minor_node(dip, info_p->cn_name,
+ S_IFCHR, slot_p->hs_minor,
+ DDI_NT_PCI_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+ (void) ddi_prop_update_int(DDI_DEV_T_NONE,
+ dip, "ap-names", 1 << slot_p->hs_device_num);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * general code to remove a minor node, called from hotplug controller
+ * drivers.
+ */
+void
+pcie_remove_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot)
+{
+ ddi_remove_minor_node(ctrl_p->hc_dip,
+ ctrl_p->hc_slots[slot]->hs_info.cn_name);
+}
+
+/*
+ * Local functions (called within this file)
+ */
+
+/*
+ * Register ports for all the children with device number device_num
+ */
+static int
+pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num)
+{
+ dev_info_t *cdip;
+ int rv;
+
+ for (cdip = ddi_get_child(dip); cdip;
+ cdip = ddi_get_next_sibling(cdip)) {
+ if (pcie_hp_match_dev(cdip, device_num)) {
+ /*
+ * Found the newly probed device under the
+ * current slot. Register a port for it.
+ */
+ if ((rv = pcie_hp_register_port(cdip, dip, NULL))
+ != DDI_SUCCESS)
+ return (rv);
+ } else {
+ continue;
+ }
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Unregister ports of a pci bridge dip, get called from ndi_hp_walk_cn()
+ *
+ * If connector_num is specified, then unregister the slot's dependent ports
+ * only; Otherwise, unregister all ports of a pci bridge dip.
+ */
+static int
+pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg)
+{
+ pcie_hp_unreg_port_t *unreg_arg = (pcie_hp_unreg_port_t *)arg;
+ dev_info_t *dip = unreg_arg->nexus_dip;
+ int rv = NDI_SUCCESS;
+
+ if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT) {
+ unreg_arg->rv = rv;
+ return (DDI_WALK_CONTINUE);
+ }
+
+ if (unreg_arg->connector_num != DDI_HP_CN_NUM_NONE) {
+ /* Unregister ports for all unprobed devices under a slot. */
+ if (unreg_arg->connector_num == info->cn_num_dpd_on) {
+
+ rv = ndi_hp_unregister(dip, info->cn_name);
+ }
+ } else {
+
+ /* Unregister all ports of a pci bridge dip. */
+ rv = ndi_hp_unregister(dip, info->cn_name);
+ }
+
+ unreg_arg->rv = rv;
+ if (rv == NDI_SUCCESS)
+ return (DDI_WALK_CONTINUE);
+ else
+ return (DDI_WALK_TERMINATE);
+}
+
+/*
+ * Find a port according to cn_name and get the port's state.
+ */
+static int
+pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg)
+{
+ pcie_hp_port_state_t *port = (pcie_hp_port_state_t *)arg;
+
+ if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT)
+ return (DDI_WALK_CONTINUE);
+
+ if (strcmp(info->cn_name, port->cn_name) == 0) {
+ /* Matched. */
+ port->cn_state = info->cn_state;
+ port->rv = DDI_SUCCESS;
+
+ return (DDI_WALK_TERMINATE);
+ }
+
+ return (DDI_WALK_CONTINUE);
+}
+
+/*
+ * Find the physical slot with the given device number;
+ * return the slot if found.
+ */
+static pcie_hp_slot_t *
+pcie_find_physical_slot(dev_info_t *dip, int dev_num)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ pcie_hp_ctrl_t *ctrl = PCIE_GET_HP_CTRL(dip);
+
+ if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
+ /* PCIe has only one slot */
+ return (dev_num == 0) ? (ctrl->hc_slots[0]) : (NULL);
+ } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
+ for (int slot = 0; slot < ctrl->hc_num_slots_impl; slot++) {
+ if (ctrl->hc_slots[slot]->hs_device_num == dev_num) {
+ /* found */
+ return (ctrl->hc_slots[slot]);
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * setup slot name/slot-number info for the port which is being registered.
+ */
+static int
+pcie_hp_create_port_name_num(dev_info_t *dip, ddi_hp_cn_info_t *cn_info)
+{
+ int ret, dev_num, func_num, name_len;
+ dev_info_t *pdip = ddi_get_parent(dip);
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(pdip);
+ pcie_hp_slot_t *slot;
+ pcie_req_id_t bdf;
+ char tmp[PCIE_HP_DEV_FUNC_NUM_STRING_LEN];
+
+ ret = pcie_get_bdf_from_dip(dip, &bdf);
+ if (ret != DDI_SUCCESS) {
+ return (ret);
+ }
+ if (PCIE_IS_RP(bus_p) || PCIE_IS_SWD(bus_p) ||
+ PCIE_IS_PCI2PCIE(bus_p)) {
+ /*
+ * It is under a PCIe device, devcie number is always 0;
+ * function number might > 8 in ARI supported case.
+ */
+ dev_num = 0;
+ func_num = (bdf & ((~PCI_REG_BUS_M) >> 8));
+ } else {
+ dev_num = (bdf & (PCI_REG_DEV_M >> 8)) >> 3;
+ func_num = bdf & (PCI_REG_FUNC_M >> 8);
+ }
+ /*
+ * The string length of dev_num and func_num must be no longer than 4
+ * including the string end mark. (With ARI case considered, e.g.,
+ * dev_num=0x0, func_num=0xff.)
+ */
+ (void) snprintf(tmp, PCIE_HP_DEV_FUNC_NUM_STRING_LEN, "%x%x",
+ dev_num, func_num);
+ /*
+ * Calculate the length of cn_name.
+ * The format of pci port name is: pci.d,f
+ * d stands for dev_num, f stands for func_num. So the length of the
+ * name string can be calculated as following.
+ */
+ name_len = strlen(tmp) + PCIE_HP_PORT_NAME_STRING_LEN + 1;
+
+ cn_info->cn_name = (char *)kmem_zalloc(name_len, KM_SLEEP);
+ (void) snprintf(cn_info->cn_name, name_len, "pci.%x,%x",
+ dev_num, func_num);
+ cn_info->cn_num = (dev_num << 8) | func_num;
+ slot = pcie_find_physical_slot(pdip, dev_num);
+
+ cn_info->cn_num_dpd_on = slot ?
+ slot->hs_info.cn_num : DDI_HP_CN_NUM_NONE;
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Extract device and function number from port name, whose format is
+ * something like 'pci.1,0'
+ */
+static int
+pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num, int *func_num)
+{
+ int name_len, ret;
+ long d, f;
+ char *sp;
+
+ /* some checks for the input name */
+ name_len = strlen(cn_name);
+ if ((name_len <= PCIE_HP_PORT_NAME_STRING_LEN) ||
+ (name_len > (PCIE_HP_PORT_NAME_STRING_LEN +
+ PCIE_HP_DEV_FUNC_NUM_STRING_LEN - 1)) ||
+ (strncmp("pci.", cn_name, 4) != 0)) {
+ return (DDI_EINVAL);
+ }
+ ret = ddi_strtol(cn_name + 4, &sp, 10, &d);
+ if (ret != DDI_SUCCESS)
+ return (ret);
+
+ if (strncmp(",", sp, 1) != 0)
+ return (DDI_EINVAL);
+
+ ret = ddi_strtol(sp + 1, NULL, 10, &f);
+ if (ret != DDI_SUCCESS)
+ return (ret);
+ *dev_num = (int)d;
+ *func_num = (int)f;
+
+ return (ret);
+}
+
+/*
+ * Check/copy cn_name and set connection numbers.
+ * If it is a valid name, then setup cn_info for the newly created port.
+ */
+static int
+pcie_hp_setup_port_name_num(dev_info_t *pdip, char *cn_name,
+ ddi_hp_cn_info_t *cn_info)
+{
+ int dev_num, func_num, ret;
+ pcie_hp_slot_t *slot;
+
+ if ((ret = pcie_hp_get_df_from_port_name(cn_name, &dev_num, &func_num))
+ != DDI_SUCCESS)
+ return (ret);
+
+ if (pcie_hp_check_hardware_existence(pdip, dev_num, func_num) ==
+ DDI_SUCCESS) {
+ cn_info->cn_state = DDI_HP_CN_STATE_PRESENT;
+ } else {
+ cn_info->cn_state = DDI_HP_CN_STATE_EMPTY;
+ }
+
+ cn_info->cn_name = ddi_strdup(cn_name, KM_SLEEP);
+ cn_info->cn_num = (dev_num << 8) | func_num;
+
+ slot = pcie_find_physical_slot(pdip, dev_num);
+ if (slot) {
+ cn_info->cn_num_dpd_on = slot->hs_info.cn_num;
+ } else {
+ cn_info->cn_num_dpd_on = DDI_HP_CN_NUM_NONE;
+ }
+ return (DDI_SUCCESS);
+}
+
+static int
+ndi2ddi(int n)
+{
+ int ret;
+
+ switch (n) {
+ case NDI_SUCCESS:
+ ret = DDI_SUCCESS;
+ break;
+ case NDI_NOMEM:
+ ret = DDI_ENOMEM;
+ break;
+ case NDI_BUSY:
+ ret = DDI_EBUSY;
+ break;
+ case NDI_EINVAL:
+ ret = DDI_EINVAL;
+ break;
+ case NDI_ENOTSUP:
+ ret = DDI_ENOTSUP;
+ break;
+ case NDI_FAILURE:
+ default:
+ ret = DDI_FAILURE;
+ break;
+ }
+ return (ret);
+}
+
+/*
+ * Common routine to create and register a new port
+ *
+ * Create an empty port if dip is NULL, and cn_name needs to be specified in
+ * this case. Otherwise, create a port mapping to the specified dip, and cn_name
+ * is not needed in this case.
+ */
+static int
+pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip, char *cn_name)
+{
+ ddi_hp_cn_info_t *cn_info;
+ int ret;
+
+ ASSERT((dip == NULL) != (cn_name == NULL));
+ cn_info = kmem_zalloc(sizeof (ddi_hp_cn_info_t), KM_SLEEP);
+ if (dip != NULL)
+ ret = pcie_hp_create_port_name_num(dip, cn_info);
+ else
+ ret = pcie_hp_setup_port_name_num(pdip, cn_name, cn_info);
+
+ if (ret != DDI_SUCCESS) {
+ kmem_free(cn_info, sizeof (ddi_hp_cn_info_t));
+ return (ret);
+ }
+
+ cn_info->cn_child = dip;
+ cn_info->cn_type = DDI_HP_CN_TYPE_VIRTUAL_PORT;
+ cn_info->cn_type_str = DDI_HP_CN_TYPE_STR_PORT;
+
+ ret = ndi_hp_register(pdip, cn_info);
+
+ kmem_free(cn_info->cn_name, strlen(cn_info->cn_name) + 1);
+ kmem_free(cn_info, sizeof (ddi_hp_cn_info_t));
+
+ return (ndi2ddi(ret));
+}
+
+/* Check if there is a piece of hardware exist corresponding to the cn_name */
+static int
+pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num, int func_num)
+{
+
+ /*
+ * VHPTODO:
+ * According to device and function number, check if there is a hardware
+ * device exists. Currently, this function can not be reached before
+ * we enable state transition to or from "Port-Empty" or "Port-Present"
+ * states. When the pci device type project is integrated, we are going
+ * to call the pci config space access interfaces introduced by it.
+ */
+ _NOTE(ARGUNUSED(dip, dev_num, func_num));
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Dispatch hotplug commands to different hotplug controller drivers, including
+ * physical and virtual hotplug operations.
+ */
+/* ARGSUSED */
+int
+pcie_hp_common_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
+ void *arg, void *result)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ int ret = DDI_SUCCESS;
+
+ PCIE_DBG("pcie_hp_common_ops: dip=%p cn_name=%s op=%x arg=%p\n",
+ dip, cn_name, op, arg);
+
+ switch (op) {
+ case DDI_HPOP_CN_CREATE_PORT:
+ {
+ /* create an empty port */
+ return (pcie_hp_register_port(NULL, dip, cn_name));
+ }
+ case DDI_HPOP_CN_CHANGE_STATE:
+ {
+ ddi_hp_cn_state_t curr_state;
+ ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
+ pcie_hp_port_state_t state_arg;
+
+ if (target_state < DDI_HP_CN_STATE_PORT_EMPTY) {
+ /* this is for physical slot state change */
+ break;
+ }
+ PCIE_DBG("pcie_hp_common_ops: change port state"
+ " dip=%p cn_name=%s"
+ " op=%x arg=%p\n", (void *)dip, cn_name, op, arg);
+
+ state_arg.rv = DDI_FAILURE;
+ state_arg.cn_name = cn_name;
+ ndi_hp_walk_cn(dip, pcie_hp_get_port_state, &state_arg);
+ if (state_arg.rv != DDI_SUCCESS) {
+ /* can not find the port */
+ return (DDI_EINVAL);
+ }
+ curr_state = state_arg.cn_state;
+ /*
+ * Check if this is for changing port's state: change to/from
+ * PORT_EMPTY/PRESENT states.
+ */
+ if (curr_state < target_state) {
+ /* Upgrade state */
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_PORT_EMPTY:
+ if (target_state ==
+ DDI_HP_CN_STATE_PORT_PRESENT) {
+ int dev_num, func_num;
+
+ ret = pcie_hp_get_df_from_port_name(
+ cn_name, &dev_num, &func_num);
+ if (ret != DDI_SUCCESS)
+ goto port_state_done;
+
+ ret = pcie_hp_check_hardware_existence(
+ dip, dev_num, func_num);
+ } else if (target_state ==
+ DDI_HP_CN_STATE_OFFLINE) {
+ ret = pcie_read_only_probe(dip,
+ cn_name, (dev_info_t **)result);
+ } else
+ ret = DDI_EINVAL;
+
+ goto port_state_done;
+ case DDI_HP_CN_STATE_PORT_PRESENT:
+ if (target_state ==
+ DDI_HP_CN_STATE_OFFLINE)
+ ret = pcie_read_only_probe(dip,
+ cn_name, (dev_info_t **)result);
+ else
+ ret = DDI_EINVAL;
+
+ goto port_state_done;
+ default:
+ ASSERT("unexpected state");
+ }
+ } else {
+ /* Downgrade state */
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_PORT_PRESENT:
+ {
+ int dev_num, func_num;
+
+ ret = pcie_hp_get_df_from_port_name(cn_name,
+ &dev_num, &func_num);
+ if (ret != DDI_SUCCESS)
+ goto port_state_done;
+
+ ret = pcie_hp_check_hardware_existence(dip,
+ dev_num, func_num);
+
+ goto port_state_done;
+ }
+ case DDI_HP_CN_STATE_OFFLINE:
+ ret = pcie_read_only_unprobe(dip, cn_name);
+
+ goto port_state_done;
+ default:
+ ASSERT("unexpected state");
+ }
+ }
+port_state_done:
+ *(ddi_hp_cn_state_t *)result = curr_state;
+ return (ret);
+ }
+ default:
+ break;
+ }
+
+ if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
+ /* PCIe hotplug */
+ ret = pciehpc_hp_ops(dip, cn_name, op, arg, result);
+ } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
+ /* PCI SHPC hotplug */
+ ret = pcishpc_hp_ops(dip, cn_name, op, arg, result);
+ } else {
+ cmn_err(CE_WARN, "pcie_hp_common_ops: op is not supported."
+ " dip=%p cn_name=%s"
+ " op=%x arg=%p\n", (void *)dip, cn_name, op, arg);
+ ret = DDI_ENOTSUP;
+ }
+
+#if defined(__i386) || defined(__amd64)
+ /*
+ * like in attach, since hotplugging can change error registers,
+ * we need to ensure that the proper bits are set on this port
+ * after a configure operation
+ */
+ if ((ret == DDI_SUCCESS) && (op == DDI_HPOP_CN_CHANGE_STATE) &&
+ (*(ddi_hp_cn_state_t *)arg == DDI_HP_CN_STATE_ENABLED))
+ pcieb_intel_error_workaround(dip);
+#endif
+
+ return (ret);
+}
+
+/*
+ * pcie_hp_match_dev_func:
+ * Match dip's PCI device number and function number with input ones.
+ */
+static int
+pcie_hp_match_dev_func(dev_info_t *dip, void *hdl)
+{
+ struct pcie_hp_find_ctrl *ctrl = (struct pcie_hp_find_ctrl *)hdl;
+ pci_regspec_t *pci_rp;
+ int length;
+ int pci_dev, pci_func;
+
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
+ ctrl->dip = NULL;
+ return (DDI_WALK_TERMINATE);
+ }
+
+ /* get the PCI device address info */
+ pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
+ pci_func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
+
+ /*
+ * free the memory allocated by ddi_prop_lookup_int_array
+ */
+ ddi_prop_free(pci_rp);
+
+ if ((pci_dev == ctrl->device) && (pci_func == ctrl->function)) {
+ /* found the match for the specified device address */
+ ctrl->dip = dip;
+ return (DDI_WALK_TERMINATE);
+ }
+
+ /*
+ * continue the walk to the next sibling to look for a match.
+ */
+ return (DDI_WALK_PRUNECHILD);
+}
+
+/*
+ * pcie_hp_match_dev:
+ * Match the dip's pci device number with the input dev_num
+ */
+static boolean_t
+pcie_hp_match_dev(dev_info_t *dip, int dev_num)
+{
+ pci_regspec_t *pci_rp;
+ int length;
+ int pci_dev;
+
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
+ return (B_FALSE);
+ }
+
+ /* get the PCI device address info */
+ pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
+
+ /*
+ * free the memory allocated by ddi_prop_lookup_int_array
+ */
+ ddi_prop_free(pci_rp);
+
+ if (pci_dev == dev_num) {
+ /* found the match for the specified device address */
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Callback function to match with device number in order to list
+ * occupants under a specific slot
+ */
+static int
+pcie_hp_list_occupants(dev_info_t *dip, void *arg)
+{
+ pcie_hp_cn_cfg_t *cn_cfg_p = (pcie_hp_cn_cfg_t *)arg;
+ pcie_hp_occupant_info_t *occupant =
+ (pcie_hp_occupant_info_t *)cn_cfg_p->cn_private;
+ pcie_hp_slot_t *slot_p =
+ (pcie_hp_slot_t *)cn_cfg_p->slotp;
+ int pci_dev;
+ pci_regspec_t *pci_rp;
+ int length;
+ major_t major;
+
+ /*
+ * Get the PCI device number information from the devinfo
+ * node. Since the node may not have the address field
+ * setup (this is done in the DDI_INITCHILD of the parent)
+ * we look up the 'reg' property to decode that information.
+ */
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
+ (uint_t *)&length) != DDI_PROP_SUCCESS) {
+ cn_cfg_p->rv = DDI_FAILURE;
+ cn_cfg_p->dip = dip;
+ return (DDI_WALK_TERMINATE);
+ }
+
+ /* get the pci device id information */
+ pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
+
+ /*
+ * free the memory allocated by ddi_prop_lookup_int_array
+ */
+ ddi_prop_free(pci_rp);
+
+ /*
+ * Match the node for the device number of the slot.
+ */
+ if (pci_dev == slot_p->hs_device_num) {
+
+ major = ddi_driver_major(dip);
+
+ /*
+ * If the node is not yet attached, then don't list it
+ * as an occupant. This is valid, since nothing can be
+ * consuming it until it is attached, and cfgadm will
+ * ask for the property explicitly which will cause it
+ * to be re-freshed right before checking with rcm.
+ */
+ if ((major == DDI_MAJOR_T_NONE) || !i_ddi_devi_attached(dip))
+ return (DDI_WALK_PRUNECHILD);
+
+ /*
+ * If we have used all our occupants then print mesage
+ * and terminate walk.
+ */
+ if (occupant->i >= PCIE_HP_MAX_OCCUPANTS) {
+ cmn_err(CE_WARN,
+ "pcie (%s%d): unable to list all occupants",
+ ddi_driver_name(ddi_get_parent(dip)),
+ ddi_get_instance(ddi_get_parent(dip)));
+ return (DDI_WALK_TERMINATE);
+ }
+
+ /*
+ * No need to hold the dip as ddi_walk_devs
+ * has already arranged that for us.
+ */
+ occupant->id[occupant->i] =
+ kmem_alloc(sizeof (char[MAXPATHLEN]), KM_SLEEP);
+ (void) ddi_pathname(dip, (char *)occupant->id[occupant->i]);
+ occupant->i++;
+ }
+
+ /*
+ * continue the walk to the next sibling to look for a match
+ * or to find other nodes if this card is a multi-function card.
+ */
+ return (DDI_WALK_PRUNECHILD);
+}
+
+/*
+ * Generate the System Event for ESC_DR_REQ.
+ * One of the consumers is pcidr, it calls to libcfgadm to perform a
+ * configure or unconfigure operation to the AP.
+ */
+void
+pcie_hp_gen_sysevent_req(char *slot_name, int hint,
+ dev_info_t *self, int kmflag)
+{
+ sysevent_id_t eid;
+ nvlist_t *ev_attr_list = NULL;
+ char cn_path[MAXPATHLEN];
+ char *ap_id;
+ int err, ap_id_len;
+
+ /*
+ * Minor device name (AP) will be bus path
+ * concatenated with slot name
+ */
+ (void) strcpy(cn_path, "/devices");
+ (void) ddi_pathname(self, cn_path + strlen("/devices"));
+
+ ap_id_len = strlen(cn_path) + strlen(":") +
+ strlen(slot_name) + 1;
+ ap_id = kmem_zalloc(ap_id_len, kmflag);
+ if (ap_id == NULL) {
+ cmn_err(CE_WARN,
+ "%s%d: Failed to allocate memory for AP ID: %s:%s",
+ ddi_driver_name(self), ddi_get_instance(self),
+ cn_path, slot_name);
+
+ return;
+ }
+
+ (void) strcpy(ap_id, cn_path);
+ (void) strcat(ap_id, ":");
+ (void) strcat(ap_id, slot_name);
+
+ err = nvlist_alloc(&ev_attr_list, NV_UNIQUE_NAME_TYPE, kmflag);
+ if (err != 0) {
+ cmn_err(CE_WARN,
+ "%s%d: Failed to allocate memory "
+ "for event attributes%s", ddi_driver_name(self),
+ ddi_get_instance(self), ESC_DR_REQ);
+
+ kmem_free(ap_id, ap_id_len);
+ return;
+ }
+
+ switch (hint) {
+
+ case SE_INVESTIGATE_RES: /* fall through */
+ case SE_INCOMING_RES: /* fall through */
+ case SE_OUTGOING_RES: /* fall through */
+
+ err = nvlist_add_string(ev_attr_list, DR_REQ_TYPE,
+ SE_REQ2STR(hint));
+
+ if (err != 0) {
+ cmn_err(CE_WARN,
+ "%s%d: Failed to add attr [%s] "
+ "for %s event", ddi_driver_name(self),
+ ddi_get_instance(self),
+ DR_REQ_TYPE, ESC_DR_REQ);
+
+ goto done;
+ }
+ break;
+
+ default:
+ cmn_err(CE_WARN, "%s%d: Unknown hint on sysevent",
+ ddi_driver_name(self), ddi_get_instance(self));
+
+ goto done;
+ }
+
+ /*
+ * Add attachment point as attribute (common attribute)
+ */
+
+ err = nvlist_add_string(ev_attr_list, DR_AP_ID, ap_id);
+
+ if (err != 0) {
+ cmn_err(CE_WARN, "%s%d: Failed to add attr [%s] for %s event",
+ ddi_driver_name(self), ddi_get_instance(self),
+ DR_AP_ID, EC_DR);
+
+ goto done;
+ }
+
+
+ /*
+ * Log this event with sysevent framework.
+ */
+
+ err = ddi_log_sysevent(self, DDI_VENDOR_SUNW, EC_DR,
+ ESC_DR_REQ, ev_attr_list, &eid,
+ ((kmflag == KM_SLEEP) ? DDI_SLEEP : DDI_NOSLEEP));
+ if (err != 0) {
+ cmn_err(CE_WARN, "%s%d: Failed to log %s event",
+ ddi_driver_name(self), ddi_get_instance(self), EC_DR);
+ }
+
+done:
+ nvlist_free(ev_attr_list);
+ kmem_free(ap_id, ap_id_len);
+}
diff --git a/usr/src/uts/common/io/pciex/hotplug/pciehpc.c b/usr/src/uts/common/io/pciex/hotplug/pciehpc.c
new file mode 100644
index 0000000000..900e36b383
--- /dev/null
+++ b/usr/src/uts/common/io/pciex/hotplug/pciehpc.c
@@ -0,0 +1,2285 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains Standard PCI Express HotPlug functionality that is
+ * compatible with the PCI Express ver 1.1 specification.
+ *
+ * NOTE: This file is compiled and delivered through misc/pcie module.
+ */
+
+#include <sys/types.h>
+#include <sys/note.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/debug.h>
+#include <sys/vtrace.h>
+#include <sys/autoconf.h>
+#include <sys/varargs.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/time.h>
+#include <sys/callb.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/sysevent/dr.h>
+#include <sys/pci_impl.h>
+#include <sys/hotplug/pci/pcie_hp.h>
+#include <sys/hotplug/pci/pciehpc.h>
+
+typedef struct pciehpc_prop {
+ char *prop_name;
+ char *prop_value;
+} pciehpc_prop_t;
+
+static pciehpc_prop_t pciehpc_props[] = {
+ { PCIEHPC_PROP_LED_FAULT, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_LED_POWER, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_LED_ATTN, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_LED_ACTIVE, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_CARD_TYPE, PCIEHPC_PROP_VALUE_TYPE },
+ { PCIEHPC_PROP_BOARD_TYPE, PCIEHPC_PROP_VALUE_TYPE },
+ { PCIEHPC_PROP_SLOT_CONDITION, PCIEHPC_PROP_VALUE_TYPE }
+};
+
+/* Local functions prototype */
+static int pciehpc_hpc_init(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_hpc_uninit(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_slotinfo_init(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_enable_intr(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_disable_intr(pcie_hp_ctrl_t *ctrl_p);
+static pcie_hp_ctrl_t *pciehpc_create_controller(dev_info_t *dip);
+static void pciehpc_destroy_controller(dev_info_t *dip);
+static int pciehpc_register_slot(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_unregister_slot(pcie_hp_ctrl_t *ctrl_p);
+static int pciehpc_slot_get_property(pcie_hp_slot_t *slot_p,
+ ddi_hp_property_t *arg, ddi_hp_property_t *rval);
+static int pciehpc_slot_set_property(pcie_hp_slot_t *slot_p,
+ ddi_hp_property_t *arg, ddi_hp_property_t *rval);
+static void pciehpc_issue_hpc_command(pcie_hp_ctrl_t *ctrl_p, uint16_t control);
+static void pciehpc_attn_btn_handler(pcie_hp_ctrl_t *ctrl_p);
+static pcie_hp_led_state_t pciehpc_led_state_to_hpc(uint16_t state);
+static pcie_hp_led_state_t pciehpc_get_led_state(pcie_hp_ctrl_t *ctrl_p,
+ pcie_hp_led_t led);
+static void pciehpc_set_led_state(pcie_hp_ctrl_t *ctrl_p, pcie_hp_led_t led,
+ pcie_hp_led_state_t state);
+
+static int pciehpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state);
+static int pciehpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state);
+static int pciehpc_change_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state);
+static int
+ pciehpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result);
+static int
+ pciehpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result);
+static int pciehpc_slot_probe(pcie_hp_slot_t *slot_p);
+static int pciehpc_slot_unprobe(pcie_hp_slot_t *slot_p);
+
+#ifdef DEBUG
+static void pciehpc_dump_hpregs(pcie_hp_ctrl_t *ctrl_p);
+#endif /* DEBUG */
+
+/*
+ * Global functions (called by other drivers/modules)
+ */
+
+/*
+ * Initialize Hot Plug Controller if present. The arguments are:
+ * dip - Devinfo node pointer to the hot plug bus node
+ * regops - register ops to access HPC registers for non-standard
+ * HPC hw implementations (e.g: HPC in host PCI-E brdiges)
+ * This is NULL for standard HPC in PCIe bridges.
+ * Returns:
+ * DDI_SUCCESS for successful HPC initialization
+ * DDI_FAILURE for errors or if HPC hw not found
+ */
+int
+pciehpc_init(dev_info_t *dip, caddr_t arg)
+{
+ pcie_hp_regops_t *regops = (pcie_hp_regops_t *)(void *)arg;
+ pcie_hp_ctrl_t *ctrl_p;
+
+ PCIE_DBG("pciehpc_init() called (dip=%p)\n", (void *)dip);
+
+ /* Make sure that it is not already initialized */
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) != NULL) {
+ PCIE_DBG("%s%d: pciehpc instance already initialized!\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+ return (DDI_SUCCESS);
+ }
+
+ /* Allocate a new hotplug controller and slot structures */
+ ctrl_p = pciehpc_create_controller(dip);
+
+ /* setup access handle for HPC regs */
+ if (regops != NULL) {
+ /* HPC access is non-standard; use the supplied reg ops */
+ ctrl_p->hc_regops = *regops;
+ }
+
+ /*
+ * Setup resource maps for this bus node.
+ */
+ (void) pci_resource_setup(dip);
+
+ PCIE_DISABLE_ERRORS(dip);
+
+ /*
+ * Set the platform specific hot plug mode.
+ */
+ ctrl_p->hc_ops.init_hpc_hw = pciehpc_hpc_init;
+ ctrl_p->hc_ops.uninit_hpc_hw = pciehpc_hpc_uninit;
+ ctrl_p->hc_ops.init_hpc_slotinfo = pciehpc_slotinfo_init;
+ ctrl_p->hc_ops.uninit_hpc_slotinfo = pciehpc_slotinfo_uninit;
+ ctrl_p->hc_ops.poweron_hpc_slot = pciehpc_slot_poweron;
+ ctrl_p->hc_ops.poweroff_hpc_slot = pciehpc_slot_poweroff;
+
+ ctrl_p->hc_ops.enable_hpc_intr = pciehpc_enable_intr;
+ ctrl_p->hc_ops.disable_hpc_intr = pciehpc_disable_intr;
+
+#if defined(__i386) || defined(__amd64)
+ pciehpc_update_ops(ctrl_p);
+#endif
+
+ /* initialize hot plug controller hw */
+ if ((ctrl_p->hc_ops.init_hpc_hw)(ctrl_p) != DDI_SUCCESS)
+ goto cleanup1;
+
+ /* initialize slot information soft state structure */
+ if ((ctrl_p->hc_ops.init_hpc_slotinfo)(ctrl_p) != DDI_SUCCESS)
+ goto cleanup2;
+
+ /* register the hot plug slot with DDI HP framework */
+ if (pciehpc_register_slot(ctrl_p) != DDI_SUCCESS)
+ goto cleanup3;
+
+ /* create minor node for this slot */
+ if (pcie_create_minor_node(ctrl_p, 0) != DDI_SUCCESS)
+ goto cleanup4;
+
+ /* HPC initialization is complete now */
+ ctrl_p->hc_flags = PCIE_HP_INITIALIZED_FLAG;
+
+#ifdef DEBUG
+ /* For debug, dump the HPC registers */
+ pciehpc_dump_hpregs(ctrl_p);
+#endif /* DEBUG */
+
+ /* enable hot plug interrupts/event */
+ (void) (ctrl_p->hc_ops.enable_hpc_intr)(ctrl_p);
+
+ return (DDI_SUCCESS);
+cleanup4:
+ (void) pciehpc_unregister_slot(ctrl_p);
+cleanup3:
+ (void) (ctrl_p->hc_ops.uninit_hpc_slotinfo)(ctrl_p);
+
+cleanup2:
+ (void) (ctrl_p->hc_ops.uninit_hpc_hw)(ctrl_p);
+
+cleanup1:
+ PCIE_ENABLE_ERRORS(dip);
+ (void) pci_resource_destroy(dip);
+
+ pciehpc_destroy_controller(dip);
+ return (DDI_FAILURE);
+}
+
+/*
+ * Uninitialize HPC soft state structure and free up any resources
+ * used for the HPC instance.
+ */
+int
+pciehpc_uninit(dev_info_t *dip)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+
+ PCIE_DBG("pciehpc_uninit() called (dip=%p)\n", (void *)dip);
+
+ /* get the soft state structure for this dip */
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) {
+ return (DDI_FAILURE);
+ }
+
+ pcie_remove_minor_node(ctrl_p, 0);
+
+ /* disable interrupts */
+ (void) (ctrl_p->hc_ops.disable_hpc_intr)(ctrl_p);
+
+ /* unregister the slot */
+ (void) pciehpc_unregister_slot(ctrl_p);
+
+ /* uninit any slot info data structures */
+ (void) (ctrl_p->hc_ops.uninit_hpc_slotinfo)(ctrl_p);
+
+ /* uninitialize hpc, remove interrupt handler, etc. */
+ (void) (ctrl_p->hc_ops.uninit_hpc_hw)(ctrl_p);
+
+ PCIE_ENABLE_ERRORS(dip);
+
+ /*
+ * Destroy resource maps for this bus node.
+ */
+ (void) pci_resource_destroy(dip);
+
+ /* destroy the soft state structure */
+ pciehpc_destroy_controller(dip);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pciehpc_intr()
+ *
+ * Interrupt handler for PCI-E Hot plug controller interrupts.
+ *
+ * Note: This is only for native mode hot plug. This is called
+ * by the nexus driver at interrupt context. Interrupt Service Routine
+ * registration is done by the nexus driver for both hot plug and
+ * non-hot plug interrupts. This function is called from the ISR
+ * of the nexus driver to handle hot-plug interrupts.
+ */
+int
+pciehpc_intr(dev_info_t *dip)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+ pcie_hp_slot_t *slot_p;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ uint16_t status, control;
+
+ /* get the soft state structure for this dip */
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
+ return (DDI_INTR_UNCLAIMED);
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* make sure the controller soft state is initialized */
+ if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ /* if it is not NATIVE hot plug mode then return */
+ if (bus_p->bus_hp_curr_mode != PCIE_NATIVE_HP_MODE) {
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ slot_p = ctrl_p->hc_slots[0];
+
+ /* read the current slot status register */
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ /* check if there are any hot plug interrupts occurred */
+ if (!(status & PCIE_SLOTSTS_STATUS_EVENTS)) {
+ /* no hot plug events occurred */
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ /* clear the interrupt status bits */
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
+
+ /* check for CMD COMPLETE interrupt */
+ if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
+ PCIE_DBG("pciehpc_intr(): CMD COMPLETED interrupt received\n");
+ /* wake up any one waiting for Command Completion event */
+ cv_signal(&ctrl_p->hc_cmd_comp_cv);
+ }
+
+ /* check for ATTN button interrupt */
+ if (status & PCIE_SLOTSTS_ATTN_BTN_PRESSED) {
+ PCIE_DBG("pciehpc_intr(): ATTN BUTTON interrupt received\n");
+
+ /* if ATTN button event is still pending then cancel it */
+ if (slot_p->hs_attn_btn_pending == B_TRUE)
+ slot_p->hs_attn_btn_pending = B_FALSE;
+ else
+ slot_p->hs_attn_btn_pending = B_TRUE;
+
+ /* wake up the ATTN event handler */
+ cv_signal(&slot_p->hs_attn_btn_cv);
+ }
+
+ /* check for power fault interrupt */
+ if (status & PCIE_SLOTSTS_PWR_FAULT_DETECTED) {
+
+ PCIE_DBG("pciehpc_intr(): POWER FAULT interrupt received"
+ " on slot %d\n", slot_p->hs_phy_slot_num);
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ if (control & PCIE_SLOTCTL_PWR_FAULT_EN) {
+ slot_p->hs_condition = AP_COND_FAILED;
+
+ /* disable power fault detction interrupt */
+ pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
+ PCIE_SLOTCTL, control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
+
+ /*
+ * Send the event to DDI Hotplug framework, power off
+ * the slot
+ */
+ (void) ndi_hp_state_change_req(dip,
+ slot_p->hs_info.cn_name,
+ DDI_HP_CN_STATE_EMPTY, DDI_HP_REQ_ASYNC);
+
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED,
+ PCIE_HP_LED_ON);
+ }
+ }
+
+ /* check for MRL SENSOR CHANGED interrupt */
+ if (status & PCIE_SLOTSTS_MRL_SENSOR_CHANGED) {
+ /* For now (phase-I), no action is taken on this event */
+ PCIE_DBG("pciehpc_intr(): MRL SENSOR CHANGED interrupt received"
+ " on slot %d\n", slot_p->hs_phy_slot_num);
+ }
+
+ /* check for PRESENCE CHANGED interrupt */
+ if (status & PCIE_SLOTSTS_PRESENCE_CHANGED) {
+
+ PCIE_DBG("pciehpc_intr(): PRESENCE CHANGED interrupt received"
+ " on slot %d\n", slot_p->hs_phy_slot_num);
+
+ if (status & PCIE_SLOTSTS_PRESENCE_DETECTED) {
+ /*
+ * card is inserted into the slot, ask DDI Hotplug
+ * framework to change state to Present.
+ */
+ (void) ndi_hp_state_change_req(dip,
+ slot_p->hs_info.cn_name,
+ DDI_HP_CN_STATE_PRESENT,
+ DDI_HP_REQ_ASYNC);
+ } else { /* card is removed from the slot */
+ cmn_err(CE_NOTE, "pciehpc (%s%d): card is removed"
+ " from the slot %s",
+ ddi_driver_name(dip),
+ ddi_get_instance(dip),
+ slot_p->hs_info.cn_name);
+
+ if (slot_p->hs_info.cn_state ==
+ DDI_HP_CN_STATE_ENABLED) {
+ /* Card is removed when slot is enabled */
+ slot_p->hs_condition = AP_COND_FAILED;
+ } else {
+ slot_p->hs_condition = AP_COND_UNKNOWN;
+ }
+ /* make sure to disable power fault detction intr */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ if (control & PCIE_SLOTCTL_PWR_FAULT_EN)
+ pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
+ PCIE_SLOTCTL,
+ control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
+
+ /*
+ * Ask DDI Hotplug framework to change state to Empty
+ */
+ (void) ndi_hp_state_change_req(dip,
+ slot_p->hs_info.cn_name,
+ DDI_HP_CN_STATE_EMPTY,
+ DDI_HP_REQ_ASYNC);
+ }
+ }
+
+ /* check for DLL state changed interrupt */
+ if (ctrl_p->hc_dll_active_rep &&
+ (status & PCIE_SLOTSTS_DLL_STATE_CHANGED)) {
+ PCIE_DBG("pciehpc_intr(): DLL STATE CHANGED interrupt received"
+ " on slot %d\n", slot_p->hs_phy_slot_num);
+
+ cv_signal(&slot_p->hs_dll_active_cv);
+ }
+
+ mutex_exit(&ctrl_p->hc_mutex);
+
+ return (DDI_INTR_CLAIMED);
+}
+
+/*
+ * Handle hotplug commands
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/* ARGSUSED */
+int
+pciehpc_hp_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
+ void *arg, void *result)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+ pcie_hp_slot_t *slot_p;
+ int ret = DDI_SUCCESS;
+
+ PCIE_DBG("pciehpc_hp_ops: dip=%p cn_name=%s op=%x arg=%p\n",
+ dip, cn_name, op, arg);
+
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
+ return (DDI_FAILURE);
+
+ slot_p = ctrl_p->hc_slots[0];
+
+ if (strcmp(cn_name, slot_p->hs_info.cn_name) != 0)
+ return (DDI_EINVAL);
+
+ switch (op) {
+ case DDI_HPOP_CN_GET_STATE:
+ {
+ mutex_enter(&slot_p->hs_ctrl->hc_mutex);
+
+ /* get the current slot state */
+ pciehpc_get_slot_state(slot_p);
+
+ *((ddi_hp_cn_state_t *)result) = slot_p->hs_info.cn_state;
+
+ mutex_exit(&slot_p->hs_ctrl->hc_mutex);
+ break;
+ }
+ case DDI_HPOP_CN_CHANGE_STATE:
+ {
+ ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
+
+ mutex_enter(&slot_p->hs_ctrl->hc_mutex);
+
+ ret = pciehpc_change_slot_state(slot_p, target_state);
+ *(ddi_hp_cn_state_t *)result = slot_p->hs_info.cn_state;
+
+ mutex_exit(&slot_p->hs_ctrl->hc_mutex);
+ break;
+ }
+ case DDI_HPOP_CN_PROBE:
+
+ ret = pciehpc_slot_probe(slot_p);
+
+ break;
+ case DDI_HPOP_CN_UNPROBE:
+ ret = pciehpc_slot_unprobe(slot_p);
+
+ break;
+ case DDI_HPOP_CN_GET_PROPERTY:
+ ret = pciehpc_slot_get_property(slot_p,
+ (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
+ break;
+ case DDI_HPOP_CN_SET_PROPERTY:
+ ret = pciehpc_slot_set_property(slot_p,
+ (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
+ break;
+ default:
+ ret = DDI_ENOTSUP;
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ * Get the current state of the slot from the hw.
+ *
+ * The slot state should have been initialized before this function gets called.
+ */
+void
+pciehpc_get_slot_state(pcie_hp_slot_t *slot_p)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t control, status;
+ ddi_hp_cn_state_t curr_state = slot_p->hs_info.cn_state;
+
+ /* read the Slot Control Register */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ slot_p->hs_fault_led_state = PCIE_HP_LED_OFF; /* no fault led */
+ slot_p->hs_active_led_state = PCIE_HP_LED_OFF; /* no active led */
+
+ /* read the current Slot Status Register */
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ /* get POWER led state */
+ slot_p->hs_power_led_state =
+ pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control));
+
+ /* get ATTN led state */
+ slot_p->hs_attn_led_state =
+ pciehpc_led_state_to_hpc(pcie_slotctl_attn_indicator_get(control));
+
+ if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
+ /* no device present; slot is empty */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;
+
+ return;
+ }
+
+ /* device is present */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_PRESENT;
+
+ if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
+ /*
+ * Device is powered on. Set to "ENABLED" state (skip
+ * POWERED state) because there is not a explicit "enable"
+ * action exists for PCIe.
+ * If it is already in "POWERED" state, then keep it until
+ * user explicitly change it to other states.
+ */
+ if (curr_state == DDI_HP_CN_STATE_POWERED) {
+ slot_p->hs_info.cn_state = curr_state;
+ } else {
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_ENABLED;
+ }
+ }
+}
+
+/*
+ * setup slot name/slot-number info.
+ */
+void
+pciehpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uchar_t *slotname_data;
+ int *slotnum;
+ uint_t count;
+ int len;
+ int invalid_slotnum = 0;
+ uint32_t slot_capabilities;
+
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->hc_dip,
+ DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) ==
+ DDI_PROP_SUCCESS) {
+ slot_p->hs_phy_slot_num = slotnum[0];
+ ddi_prop_free(slotnum);
+ } else {
+ slot_capabilities = pciehpc_reg_get32(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCAP);
+ slot_p->hs_phy_slot_num =
+ PCIE_SLOTCAP_PHY_SLOT_NUM(slot_capabilities);
+ }
+
+ /* platform may not have initialized it */
+ if (!slot_p->hs_phy_slot_num) {
+ PCIE_DBG("%s#%d: Invalid slot number!\n",
+ ddi_driver_name(ctrl_p->hc_dip),
+ ddi_get_instance(ctrl_p->hc_dip));
+ slot_p->hs_phy_slot_num = pciehpc_reg_get8(ctrl_p,
+ PCI_BCNF_SECBUS);
+ invalid_slotnum = 1;
+ }
+ slot_p->hs_info.cn_num = slot_p->hs_phy_slot_num;
+ slot_p->hs_info.cn_num_dpd_on = DDI_HP_CN_NUM_NONE;
+
+ /*
+ * construct the slot_name:
+ * if "slot-names" property exists then use that name
+ * else if valid slot number exists then it is "pcie<slot-num>".
+ * else it will be "pcie<sec-bus-number>dev0"
+ */
+ if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->hc_dip, DDI_PROP_DONTPASS,
+ "slot-names", (caddr_t)&slotname_data, &len) == DDI_PROP_SUCCESS) {
+ char tmp_name[256];
+
+ /*
+ * Note: for PCI-E slots, the device number is always 0 so the
+ * first (and only) string is the slot name for this slot.
+ */
+ (void) snprintf(tmp_name, sizeof (tmp_name),
+ (char *)slotname_data + 4);
+ slot_p->hs_info.cn_name = ddi_strdup(tmp_name, KM_SLEEP);
+ kmem_free(slotname_data, len);
+ } else {
+ if (invalid_slotnum) {
+ /* use device number ie. 0 */
+ slot_p->hs_info.cn_name = ddi_strdup("pcie0",
+ KM_SLEEP);
+ } else {
+ char tmp_name[256];
+
+ (void) snprintf(tmp_name, sizeof (tmp_name), "pcie%d",
+ slot_p->hs_phy_slot_num);
+ slot_p->hs_info.cn_name = ddi_strdup(tmp_name,
+ KM_SLEEP);
+ }
+ }
+}
+
+/*
+ * Read/Write access to HPC registers. If platform nexus has non-standard
+ * HPC access mechanism then regops functions are used to do reads/writes.
+ */
+uint8_t
+pciehpc_reg_get8(pcie_hp_ctrl_t *ctrl_p, uint_t off)
+{
+ if (ctrl_p->hc_regops.get != NULL) {
+ return ((uint8_t)ctrl_p->hc_regops.get(
+ ctrl_p->hc_regops.cookie, (off_t)off));
+ } else {
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ return (pci_config_get8(bus_p->bus_cfg_hdl, off));
+ }
+}
+
+uint16_t
+pciehpc_reg_get16(pcie_hp_ctrl_t *ctrl_p, uint_t off)
+{
+ if (ctrl_p->hc_regops.get != NULL) {
+ return ((uint16_t)ctrl_p->hc_regops.get(
+ ctrl_p->hc_regops.cookie, (off_t)off));
+ } else {
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ return (pci_config_get16(bus_p->bus_cfg_hdl, off));
+ }
+}
+
+uint32_t
+pciehpc_reg_get32(pcie_hp_ctrl_t *ctrl_p, uint_t off)
+{
+ if (ctrl_p->hc_regops.get != NULL) {
+ return ((uint32_t)ctrl_p->hc_regops.get(
+ ctrl_p->hc_regops.cookie, (off_t)off));
+ } else {
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ return (pci_config_get32(bus_p->bus_cfg_hdl, off));
+ }
+}
+
+void
+pciehpc_reg_put8(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint8_t val)
+{
+ if (ctrl_p->hc_regops.put != NULL) {
+ ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
+ (off_t)off, (uint_t)val);
+ } else {
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ pci_config_put8(bus_p->bus_cfg_hdl, off, val);
+ }
+}
+
+void
+pciehpc_reg_put16(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint16_t val)
+{
+ if (ctrl_p->hc_regops.put != NULL) {
+ ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
+ (off_t)off, (uint_t)val);
+ } else {
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ pci_config_put16(bus_p->bus_cfg_hdl, off, val);
+ }
+}
+
+void
+pciehpc_reg_put32(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint32_t val)
+{
+ if (ctrl_p->hc_regops.put != NULL) {
+ ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
+ (off_t)off, (uint_t)val);
+ } else {
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ pci_config_put32(bus_p->bus_cfg_hdl, off, val);
+ }
+}
+
+/*
+ * ************************************************************************
+ * *** Local functions (called within this file)
+ * *** PCIe Native Hotplug mode specific functions
+ * ************************************************************************
+ */
+
+/*
+ * Initialize HPC hardware, install interrupt handler, etc. It doesn't
+ * enable hot plug interrupts.
+ *
+ * (Note: It is called only from pciehpc_init().)
+ */
+static int
+pciehpc_hpc_init(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t reg;
+
+ /* read the Slot Control Register */
+ reg = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ /* disable all interrupts */
+ reg &= ~(PCIE_SLOTCTL_INTR_MASK);
+ pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
+ PCIE_SLOTCTL, reg);
+
+ /* clear any interrupt status bits */
+ reg = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Uninitialize HPC hardware, uninstall interrupt handler, etc.
+ *
+ * (Note: It is called only from pciehpc_uninit().)
+ */
+static int
+pciehpc_hpc_uninit(pcie_hp_ctrl_t *ctrl_p)
+{
+ /* disable interrupts */
+ (void) pciehpc_disable_intr(ctrl_p);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Setup slot information for use with DDI HP framework.
+ */
+static int
+pciehpc_slotinfo_init(pcie_hp_ctrl_t *ctrl_p)
+{
+ uint32_t slot_capabilities, link_capabilities;
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ mutex_enter(&ctrl_p->hc_mutex);
+ /*
+ * setup DDI HP framework slot information structure
+ */
+ slot_p->hs_device_num = 0;
+
+ slot_p->hs_info.cn_type = DDI_HP_CN_TYPE_PCIE;
+ slot_p->hs_info.cn_type_str = (ctrl_p->hc_regops.get == NULL) ?
+ PCIE_NATIVE_HP_TYPE : PCIE_PROP_HP_TYPE;
+ slot_p->hs_info.cn_child = NULL;
+
+ slot_p->hs_minor =
+ PCI_MINOR_NUM(ddi_get_instance(ctrl_p->hc_dip),
+ slot_p->hs_device_num);
+ slot_p->hs_condition = AP_COND_UNKNOWN;
+
+ /* read Slot Capabilities Register */
+ slot_capabilities = pciehpc_reg_get32(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCAP);
+
+ /* set slot-name/slot-number info */
+ pciehpc_set_slot_name(ctrl_p);
+
+ /* check if Attn Button present */
+ ctrl_p->hc_has_attn = (slot_capabilities & PCIE_SLOTCAP_ATTN_BUTTON) ?
+ B_TRUE : B_FALSE;
+
+ /* check if Manual Retention Latch sensor present */
+ ctrl_p->hc_has_mrl = (slot_capabilities & PCIE_SLOTCAP_MRL_SENSOR) ?
+ B_TRUE : B_FALSE;
+
+ /*
+ * PCI-E version 1.1 defines EMI Lock Present bit
+ * in Slot Capabilities register. Check for it.
+ */
+ ctrl_p->hc_has_emi_lock = (slot_capabilities &
+ PCIE_SLOTCAP_EMI_LOCK_PRESENT) ? B_TRUE : B_FALSE;
+
+ link_capabilities = pciehpc_reg_get32(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_LINKCAP);
+ ctrl_p->hc_dll_active_rep = (link_capabilities &
+ PCIE_LINKCAP_DLL_ACTIVE_REP_CAPABLE) ? B_TRUE : B_FALSE;
+ if (ctrl_p->hc_dll_active_rep)
+ cv_init(&slot_p->hs_dll_active_cv, NULL, CV_DRIVER, NULL);
+
+ /* setup thread for handling ATTN button events */
+ if (ctrl_p->hc_has_attn) {
+ PCIE_DBG("pciehpc_slotinfo_init: setting up ATTN button event "
+ "handler thread for slot %d\n", slot_p->hs_phy_slot_num);
+
+ cv_init(&slot_p->hs_attn_btn_cv, NULL, CV_DRIVER, NULL);
+ slot_p->hs_attn_btn_pending = B_FALSE;
+ slot_p->hs_attn_btn_threadp = thread_create(NULL, 0,
+ pciehpc_attn_btn_handler,
+ (void *)ctrl_p, 0, &p0, TS_RUN, minclsyspri);
+ slot_p->hs_attn_btn_thread_exit = B_FALSE;
+ }
+
+ /* get current slot state from the hw */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;
+ pciehpc_get_slot_state(slot_p);
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_ENABLED)
+ slot_p->hs_condition = AP_COND_OK;
+
+ mutex_exit(&ctrl_p->hc_mutex);
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+pciehpc_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+
+ if (slot_p->hs_attn_btn_threadp != NULL) {
+ mutex_enter(&ctrl_p->hc_mutex);
+ slot_p->hs_attn_btn_thread_exit = B_TRUE;
+ cv_signal(&slot_p->hs_attn_btn_cv);
+ PCIE_DBG("pciehpc_slotinfo_uninit: "
+ "waiting for ATTN thread exit\n");
+ cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
+ PCIE_DBG("pciehpc_slotinfo_uninit: ATTN thread exit\n");
+ cv_destroy(&slot_p->hs_attn_btn_cv);
+ slot_p->hs_attn_btn_threadp = NULL;
+ mutex_exit(&ctrl_p->hc_mutex);
+ }
+
+ if (ctrl_p->hc_dll_active_rep)
+ cv_destroy(&slot_p->hs_dll_active_cv);
+ if (slot_p->hs_info.cn_name)
+ kmem_free(slot_p->hs_info.cn_name,
+ strlen(slot_p->hs_info.cn_name) + 1);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Enable hot plug interrupts.
+ * Note: this is only for Native hot plug mode.
+ */
+static int
+pciehpc_enable_intr(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t reg;
+
+ /* clear any interrupt status bits */
+ reg = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
+
+ /* read the Slot Control Register */
+ reg = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ /*
+ * enable interrupts: power fault detection interrupt is enabled
+ * only when the slot is powered ON
+ */
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED)
+ pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
+ PCIE_SLOTCTL, reg | PCIE_SLOTCTL_INTR_MASK);
+ else
+ pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
+ PCIE_SLOTCTL, reg | (PCIE_SLOTCTL_INTR_MASK &
+ ~PCIE_SLOTCTL_PWR_FAULT_EN));
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Disable hot plug interrupts.
+ * Note: this is only for Native hot plug mode.
+ */
+static int
+pciehpc_disable_intr(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t reg;
+
+ /* read the Slot Control Register */
+ reg = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ /* disable all interrupts */
+ reg &= ~(PCIE_SLOTCTL_INTR_MASK);
+ pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTCTL, reg);
+
+ /* clear any interrupt status bits */
+ reg = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Allocate a new hotplug controller and slot structures for HPC
+ * associated with this dip.
+ */
+static pcie_hp_ctrl_t *
+pciehpc_create_controller(dev_info_t *dip)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ ctrl_p = kmem_zalloc(sizeof (pcie_hp_ctrl_t), KM_SLEEP);
+ ctrl_p->hc_dip = dip;
+
+ /* Allocate a new slot structure. */
+ ctrl_p->hc_slots[0] = kmem_zalloc(sizeof (pcie_hp_slot_t), KM_SLEEP);
+ ctrl_p->hc_slots[0]->hs_num = 0;
+ ctrl_p->hc_slots[0]->hs_ctrl = ctrl_p;
+
+ /* Initialize the interrupt mutex */
+ mutex_init(&ctrl_p->hc_mutex, NULL, MUTEX_DRIVER,
+ (void *)PCIE_INTR_PRI);
+
+ /* Initialize synchronization conditional variable */
+ cv_init(&ctrl_p->hc_cmd_comp_cv, NULL, CV_DRIVER, NULL);
+ ctrl_p->hc_cmd_pending = B_FALSE;
+
+ bus_p->bus_hp_curr_mode = PCIE_NATIVE_HP_MODE;
+ PCIE_SET_HP_CTRL(dip, ctrl_p);
+
+ return (ctrl_p);
+}
+
+/*
+ * Remove the HPC controller and slot structures
+ */
+static void
+pciehpc_destroy_controller(dev_info_t *dip)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ /* get the soft state structure for this dip */
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
+ return;
+
+ PCIE_SET_HP_CTRL(dip, NULL);
+ bus_p->bus_hp_curr_mode = PCIE_NONE_HP_MODE;
+
+ mutex_destroy(&ctrl_p->hc_mutex);
+ cv_destroy(&ctrl_p->hc_cmd_comp_cv);
+ kmem_free(ctrl_p->hc_slots[0], sizeof (pcie_hp_slot_t));
+ kmem_free(ctrl_p, sizeof (pcie_hp_ctrl_t));
+}
+
+/*
+ * Register the PCI-E hot plug slot with DDI HP framework.
+ */
+static int
+pciehpc_register_slot(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ dev_info_t *dip = ctrl_p->hc_dip;
+
+ /* register the slot with DDI HP framework */
+ if (ndi_hp_register(dip, &slot_p->hs_info) != NDI_SUCCESS) {
+ PCIE_DBG("pciehpc_register_slot() failed to register slot %d\n",
+ slot_p->hs_phy_slot_num);
+ return (DDI_FAILURE);
+ }
+
+ pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip),
+ slot_p->hs_minor), slot_p->hs_device_num);
+
+ PCIE_DBG("pciehpc_register_slot(): registered slot %d\n",
+ slot_p->hs_phy_slot_num);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Unregister the PCI-E hot plug slot from DDI HP framework.
+ */
+static int
+pciehpc_unregister_slot(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ dev_info_t *dip = ctrl_p->hc_dip;
+
+ pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip),
+ slot_p->hs_minor));
+
+ /* unregister the slot with DDI HP framework */
+ if (ndi_hp_unregister(dip, slot_p->hs_info.cn_name) != NDI_SUCCESS) {
+ PCIE_DBG("pciehpc_unregister_slot() "
+ "failed to unregister slot %d\n", slot_p->hs_phy_slot_num);
+ return (DDI_FAILURE);
+ }
+
+ PCIE_DBG("pciehpc_unregister_slot(): unregistered slot %d\n",
+ slot_p->hs_phy_slot_num);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pciehpc_slot_poweron()
+ *
+ * Poweron/Enable the slot.
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/*ARGSUSED*/
+static int
+pciehpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t status, control;
+
+ ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(slot_p);
+
+ /* check if the slot is already in the 'enabled' state */
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
+ /* slot is already in the 'enabled' state */
+ PCIE_DBG("pciehpc_slot_poweron() slot %d already enabled\n",
+ slot_p->hs_phy_slot_num);
+
+ *result = slot_p->hs_info.cn_state;
+ return (DDI_SUCCESS);
+ }
+
+ /* read the Slot Status Register */
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ /* make sure the MRL switch is closed if present */
+ if ((ctrl_p->hc_has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) {
+ /* MRL switch is open */
+ cmn_err(CE_WARN, "MRL switch is open on slot %d\n",
+ slot_p->hs_phy_slot_num);
+ goto cleanup;
+ }
+
+ /* make sure the slot has a device present */
+ if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
+ /* slot is empty */
+ PCIE_DBG("slot %d is empty\n", slot_p->hs_phy_slot_num);
+ goto cleanup;
+ }
+
+ /* get the current state of Slot Control Register */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ /*
+ * Enable power to the slot involves:
+ * 1. Set power LED to blink and ATTN led to OFF.
+ * 2. Set power control ON in Slot Control Reigster and
+ * wait for Command Completed Interrupt or 1 sec timeout.
+ * 3. If Data Link Layer State Changed events are supported
+ * then wait for the event to indicate Data Layer Link
+ * is active. The time out value for this event is 1 second.
+ * This is specified in PCI-E version 1.1.
+ * 4. Set power LED to be ON.
+ */
+
+ /* 1. set power LED to blink & ATTN led to OFF */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
+
+ /* 2. set power control to ON */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+ control &= ~PCIE_SLOTCTL_PWR_CONTROL;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+ /* 3. wait for DLL State Change event, if it's supported */
+ if (ctrl_p->hc_dll_active_rep) {
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_LINKSTS);
+
+ if (!(status & PCIE_LINKSTS_DLL_LINK_ACTIVE)) {
+ /* wait 1 sec for the DLL State Changed event */
+ (void) cv_timedwait(&slot_p->hs_dll_active_cv,
+ &ctrl_p->hc_mutex,
+ ddi_get_lbolt() +
+ SEC_TO_TICK(PCIE_HP_DLL_STATE_CHANGE_TIMEOUT));
+
+ /* check Link status */
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off +
+ PCIE_LINKSTS);
+ if (!(status & PCIE_LINKSTS_DLL_LINK_ACTIVE))
+ goto cleanup2;
+ }
+ }
+
+ /* wait 1 sec for link to come up */
+ delay(drv_usectohz(1000000));
+
+ /* check power is really turned ON */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ if (control & PCIE_SLOTCTL_PWR_CONTROL) {
+ PCIE_DBG("slot %d fails to turn on power on connect\n",
+ slot_p->hs_phy_slot_num);
+
+ goto cleanup1;
+ }
+
+ /* clear power fault status */
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+ status |= PCIE_SLOTSTS_PWR_FAULT_DETECTED;
+ pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTSTS,
+ status);
+
+ /* enable power fault detection interrupt */
+ control |= PCIE_SLOTCTL_PWR_FAULT_EN;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+ /* 4. Set power LED to be ON */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_ON);
+
+ /* if EMI is present, turn it ON */
+ if (ctrl_p->hc_has_emi_lock) {
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ if (!(status & PCIE_SLOTSTS_EMI_LOCK_SET)) {
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+ control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+ /* wait 1 sec after toggling the state of EMI lock */
+ delay(drv_usectohz(1000000));
+ }
+ }
+
+ *result = slot_p->hs_info.cn_state =
+ DDI_HP_CN_STATE_POWERED;
+
+ return (DDI_SUCCESS);
+
+cleanup2:
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ /* if power is ON, set power control to OFF */
+ if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
+ control |= PCIE_SLOTCTL_PWR_CONTROL;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+ }
+
+cleanup1:
+ /* set power led to OFF */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
+
+cleanup:
+ return (DDI_FAILURE);
+}
+
+/*ARGSUSED*/
+static int
+pciehpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t status, control;
+
+ ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(slot_p);
+
+ /* check if the slot is not in the "enabled' state */
+ if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
+ /* slot is in the 'disabled' state */
+ PCIE_DBG("pciehpc_slot_poweroff(): "
+ "slot %d already disabled\n", slot_p->hs_phy_slot_num);
+ ASSERT(slot_p->hs_power_led_state == PCIE_HP_LED_OFF);
+
+ *result = slot_p->hs_info.cn_state;
+ return (DDI_SUCCESS);
+ }
+
+ /* read the Slot Status Register */
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ /* make sure the slot has a device present */
+ if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
+ /* slot is empty */
+ PCIE_DBG("pciehpc_slot_poweroff(): slot %d is empty\n",
+ slot_p->hs_phy_slot_num);
+ goto cleanup;
+ }
+
+ /*
+ * Disable power to the slot involves:
+ * 1. Set power LED to blink.
+ * 2. Set power control OFF in Slot Control Reigster and
+ * wait for Command Completed Interrupt or 1 sec timeout.
+ * 3. Set POWER led and ATTN led to be OFF.
+ */
+
+ /* 1. set power LED to blink */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);
+
+ /* disable power fault detection interrupt */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+ control &= ~PCIE_SLOTCTL_PWR_FAULT_EN;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+ /* 2. set power control to OFF */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+ control |= PCIE_SLOTCTL_PWR_CONTROL;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+#ifdef DEBUG
+ /* check for power control bit to be OFF */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+ ASSERT(control & PCIE_SLOTCTL_PWR_CONTROL);
+#endif
+
+ /* 3. Set power LED to be OFF */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
+
+ /* if EMI is present, turn it OFF */
+ if (ctrl_p->hc_has_emi_lock) {
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ if (status & PCIE_SLOTSTS_EMI_LOCK_SET) {
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+ control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+ /* wait 1 sec after toggling the state of EMI lock */
+ delay(drv_usectohz(1000000));
+ }
+ }
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(slot_p);
+
+ *result = slot_p->hs_info.cn_state;
+
+ return (DDI_SUCCESS);
+
+cleanup:
+ return (DDI_FAILURE);
+}
+
+/*
+ * pciehpc_slot_probe()
+ *
+ * Probe the slot.
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/*ARGSUSED*/
+static int
+pciehpc_slot_probe(pcie_hp_slot_t *slot_p)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ int ret = DDI_SUCCESS;
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(slot_p);
+
+ /*
+ * Probe a given PCIe Hotplug Connection (CN).
+ */
+ PCIE_DISABLE_ERRORS(ctrl_p->hc_dip);
+ ret = pcie_hp_probe(slot_p);
+
+ if (ret != DDI_SUCCESS) {
+ PCIE_DBG("pciehpc_slot_probe() failed\n");
+
+ /* turn the ATTN led ON for configure failure */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_ON);
+
+ /* if power to the slot is still on then set Power led to ON */
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED)
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
+ PCIE_HP_LED_ON);
+
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_FAILURE);
+ }
+
+ PCIE_ENABLE_ERRORS(ctrl_p->hc_dip);
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(slot_p);
+
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pciehpc_slot_unprobe()
+ *
+ * Unprobe the slot.
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/*ARGSUSED*/
+static int
+pciehpc_slot_unprobe(pcie_hp_slot_t *slot_p)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ int ret;
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(slot_p);
+
+ /*
+ * Unprobe a given PCIe Hotplug Connection (CN).
+ */
+ PCIE_DISABLE_ERRORS(ctrl_p->hc_dip);
+ ret = pcie_hp_unprobe(slot_p);
+
+ if (ret != DDI_SUCCESS) {
+ PCIE_DBG("pciehpc_slot_unprobe() failed\n");
+
+ /* if power to the slot is still on then set Power led to ON */
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED)
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
+ PCIE_HP_LED_ON);
+
+ PCIE_ENABLE_ERRORS(ctrl_p->hc_dip);
+
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_FAILURE);
+ }
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(slot_p);
+
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_SUCCESS);
+}
+
+static int
+pciehpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state;
+ int rv = DDI_SUCCESS;
+
+ if (target_state > DDI_HP_CN_STATE_ENABLED) {
+ return (DDI_EINVAL);
+ }
+
+ curr_state = slot_p->hs_info.cn_state;
+ while ((curr_state < target_state) && (rv == DDI_SUCCESS)) {
+
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_EMPTY:
+ /*
+ * From EMPTY to PRESENT, just check the hardware
+ * slot state.
+ */
+ pciehpc_get_slot_state(slot_p);
+ curr_state = slot_p->hs_info.cn_state;
+ if (curr_state < DDI_HP_CN_STATE_PRESENT)
+ rv = DDI_FAILURE;
+ break;
+ case DDI_HP_CN_STATE_PRESENT:
+ rv = (slot_p->hs_ctrl->hc_ops.poweron_hpc_slot)(slot_p,
+ &curr_state);
+
+ break;
+ case DDI_HP_CN_STATE_POWERED:
+ curr_state = slot_p->hs_info.cn_state =
+ DDI_HP_CN_STATE_ENABLED;
+ break;
+ default:
+ /* should never reach here */
+ ASSERT("unknown devinfo state");
+ }
+ }
+
+ return (rv);
+}
+
+static int
+pciehpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state;
+ int rv = DDI_SUCCESS;
+
+
+ curr_state = slot_p->hs_info.cn_state;
+ while ((curr_state > target_state) && (rv == DDI_SUCCESS)) {
+
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_PRESENT:
+ /*
+ * From PRESENT to EMPTY, just check hardware slot
+ * state.
+ */
+ pciehpc_get_slot_state(slot_p);
+ curr_state = slot_p->hs_info.cn_state;
+ if (curr_state >= DDI_HP_CN_STATE_PRESENT)
+ rv = DDI_FAILURE;
+ break;
+ case DDI_HP_CN_STATE_POWERED:
+ rv = (slot_p->hs_ctrl->hc_ops.poweroff_hpc_slot)(
+ slot_p, &curr_state);
+
+ break;
+ case DDI_HP_CN_STATE_ENABLED:
+ curr_state = slot_p->hs_info.cn_state =
+ DDI_HP_CN_STATE_POWERED;
+
+ break;
+ default:
+ /* should never reach here */
+ ASSERT("unknown devinfo state");
+ }
+ }
+
+ return (rv);
+}
+
+/* Change slot state to a target state */
+static int
+pciehpc_change_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state;
+ int rv;
+
+ pciehpc_get_slot_state(slot_p);
+ curr_state = slot_p->hs_info.cn_state;
+
+ if (curr_state == target_state) {
+ return (DDI_SUCCESS);
+ }
+ if (curr_state < target_state) {
+
+ rv = pciehpc_upgrade_slot_state(slot_p, target_state);
+ } else {
+ rv = pciehpc_downgrade_slot_state(slot_p, target_state);
+ }
+
+ return (rv);
+}
+
+int
+pciehpc_slot_get_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
+ ddi_hp_property_t *rval)
+{
+ ddi_hp_property_t request, result;
+#ifdef _SYSCALL32_IMPL
+ ddi_hp_property32_t request32, result32;
+#endif
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ nvlist_t *prop_list;
+ nvlist_t *prop_rlist; /* nvlist for return values */
+ nvpair_t *prop_pair;
+ char *name, *value;
+ int ret = DDI_SUCCESS;
+ int i, n;
+ boolean_t get_all_prop = B_FALSE;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(arg, &request, sizeof (ddi_hp_property_t)) ||
+ copyin(rval, &result, sizeof (ddi_hp_property_t)))
+ return (DDI_FAILURE);
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ bzero(&request, sizeof (request));
+ bzero(&result, sizeof (result));
+ if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)) ||
+ copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
+ return (DDI_FAILURE);
+ request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
+ request.buf_size = request32.buf_size;
+ result.nvlist_buf = (char *)(uintptr_t)result32.nvlist_buf;
+ result.buf_size = result32.buf_size;
+ }
+#endif
+
+ if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
+ &prop_list)) != DDI_SUCCESS)
+ return (ret);
+
+ if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
+ ret = DDI_ENOMEM;
+ goto get_prop_cleanup;
+ }
+
+ /* check whether the requested property is "all" or "help" */
+ prop_pair = nvlist_next_nvpair(prop_list, NULL);
+ if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+ n = sizeof (pciehpc_props) / sizeof (pciehpc_prop_t);
+
+ if (strcmp(name, PCIEHPC_PROP_ALL) == 0) {
+ (void) nvlist_remove_all(prop_list, PCIEHPC_PROP_ALL);
+
+ /*
+ * Add all properties into the request list, so that we
+ * will get the values in the following for loop.
+ */
+ for (i = 0; i < n; i++) {
+ if (nvlist_add_string(prop_list,
+ pciehpc_props[i].prop_name, "") != 0) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup1;
+ }
+ }
+ get_all_prop = B_TRUE;
+ } else if (strcmp(name, PCIEHPC_PROP_HELP) == 0) {
+ /*
+ * Empty the request list, and add help strings into the
+ * return list. We will pass the following for loop.
+ */
+ (void) nvlist_remove_all(prop_list, PCIEHPC_PROP_HELP);
+
+ for (i = 0; i < n; i++) {
+ if (nvlist_add_string(prop_rlist,
+ pciehpc_props[i].prop_name,
+ pciehpc_props[i].prop_value) != 0) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup1;
+ }
+ }
+ }
+ }
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* get the current slot state */
+ pciehpc_get_slot_state(slot_p);
+
+ /* for each requested property, get the value and add it to nvlist */
+ prop_pair = NULL;
+ while (prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+
+ if (strcmp(name, PCIEHPC_PROP_LED_FAULT) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_fault_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_LED_POWER) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_power_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_attn_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_LED_ACTIVE) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_active_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_CARD_TYPE) == 0) {
+ ddi_acc_handle_t handle;
+ dev_info_t *cdip;
+ uint8_t prog_class, base_class, sub_class;
+ int i;
+
+ mutex_exit(&ctrl_p->hc_mutex);
+ cdip = pcie_hp_devi_find(
+ ctrl_p->hc_dip, slot_p->hs_device_num, 0);
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ if ((slot_p->hs_info.cn_state
+ != DDI_HP_CN_STATE_ENABLED) || (cdip == NULL)) {
+ /*
+ * When getting all properties, just ignore the
+ * one that's not available under certain state.
+ */
+ if (get_all_prop)
+ continue;
+
+ ret = DDI_ENOTSUP;
+ goto get_prop_cleanup2;
+ }
+
+ if (pci_config_setup(cdip, &handle) != DDI_SUCCESS) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup2;
+ }
+
+ prog_class = pci_config_get8(handle,
+ PCI_CONF_PROGCLASS);
+ base_class = pci_config_get8(handle, PCI_CONF_BASCLASS);
+ sub_class = pci_config_get8(handle, PCI_CONF_SUBCLASS);
+ pci_config_teardown(&handle);
+
+ for (i = 0; i < class_pci_items; i++) {
+ if ((base_class == class_pci[i].base_class) &&
+ (sub_class == class_pci[i].sub_class) &&
+ (prog_class == class_pci[i].prog_class)) {
+ value = class_pci[i].short_desc;
+ break;
+ }
+ }
+ if (i == class_pci_items)
+ value = PCIEHPC_PROP_VALUE_UNKNOWN;
+ } else if (strcmp(name, PCIEHPC_PROP_BOARD_TYPE) == 0) {
+ if (slot_p->hs_info.cn_state <= DDI_HP_CN_STATE_EMPTY)
+ value = PCIEHPC_PROP_VALUE_UNKNOWN;
+ else
+ value = PCIEHPC_PROP_VALUE_PCIHOTPLUG;
+ } else if (strcmp(name, PCIEHPC_PROP_SLOT_CONDITION) == 0) {
+ value = pcie_slot_condition_text(slot_p->hs_condition);
+ } else {
+ /* unsupported property */
+ cmn_err(CE_WARN, "Unsupported property: %s\n", name);
+
+ ret = DDI_ENOTSUP;
+ goto get_prop_cleanup2;
+ }
+ if (nvlist_add_string(prop_rlist, name, value) != 0) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup2;
+ }
+ }
+
+ /* pack nvlist and copyout */
+ if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
+ &result.buf_size)) != DDI_SUCCESS) {
+ goto get_prop_cleanup2;
+ }
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyout(&result, rval, sizeof (ddi_hp_property_t)))
+ ret = DDI_FAILURE;
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ if (result.buf_size > UINT32_MAX) {
+ ret = DDI_FAILURE;
+ } else {
+ result32.buf_size = (uint32_t)result.buf_size;
+ if (copyout(&result32, rval,
+ sizeof (ddi_hp_property32_t)))
+ ret = DDI_FAILURE;
+ }
+ }
+#endif
+
+get_prop_cleanup2:
+ mutex_exit(&ctrl_p->hc_mutex);
+get_prop_cleanup1:
+ nvlist_free(prop_rlist);
+get_prop_cleanup:
+ nvlist_free(prop_list);
+ return (ret);
+}
+
+int
+pciehpc_slot_set_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
+ ddi_hp_property_t *rval)
+{
+ ddi_hp_property_t request, result;
+#ifdef _SYSCALL32_IMPL
+ ddi_hp_property32_t request32, result32;
+#endif
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ nvlist_t *prop_list;
+ nvlist_t *prop_rlist;
+ nvpair_t *prop_pair;
+ char *name, *value;
+ pcie_hp_led_state_t led_state;
+ int ret = DDI_SUCCESS;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(arg, &request, sizeof (ddi_hp_property_t)))
+ return (DDI_FAILURE);
+ if (rval &&
+ copyin(rval, &result, sizeof (ddi_hp_property_t)))
+ return (DDI_FAILURE);
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ bzero(&request, sizeof (request));
+ bzero(&result, sizeof (result));
+ if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)))
+ return (DDI_FAILURE);
+ if (rval &&
+ copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
+ return (DDI_FAILURE);
+ request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
+ request.buf_size = request32.buf_size;
+ if (rval) {
+ result.nvlist_buf =
+ (char *)(uintptr_t)result32.nvlist_buf;
+ result.buf_size = result32.buf_size;
+ }
+ }
+#endif
+
+ if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
+ &prop_list)) != DDI_SUCCESS)
+ return (ret);
+
+ /* check whether the requested property is "help" */
+ prop_pair = nvlist_next_nvpair(prop_list, NULL);
+ if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair) &&
+ (strcmp(nvpair_name(prop_pair), PCIEHPC_PROP_HELP) == 0)) {
+ if (!rval) {
+ ret = DDI_ENOTSUP;
+ goto set_prop_cleanup;
+ }
+
+ if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
+ ret = DDI_ENOMEM;
+ goto set_prop_cleanup;
+ }
+ if (nvlist_add_string(prop_rlist, PCIEHPC_PROP_LED_ATTN,
+ PCIEHPC_PROP_VALUE_LED) != 0) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ }
+
+ if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
+ &result.buf_size)) != DDI_SUCCESS) {
+ goto set_prop_cleanup1;
+ }
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyout(&result, rval,
+ sizeof (ddi_hp_property_t))) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ }
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ if (result.buf_size > UINT32_MAX) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ } else {
+ result32.buf_size = (uint32_t)result.buf_size;
+ if (copyout(&result32, rval,
+ sizeof (ddi_hp_property32_t))) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ }
+ }
+ }
+#endif
+set_prop_cleanup1:
+ nvlist_free(prop_rlist);
+ nvlist_free(prop_list);
+ return (ret);
+ }
+
+ /* Validate the request */
+ prop_pair = NULL;
+ while (prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+ if (nvpair_type(prop_pair) != DATA_TYPE_STRING) {
+ cmn_err(CE_WARN, "Unexpected data type of setting "
+ "property %s.\n", name);
+ ret = DDI_EINVAL;
+ goto set_prop_cleanup;
+ }
+ if (nvpair_value_string(prop_pair, &value)) {
+ cmn_err(CE_WARN, "Get string value failed for property "
+ "%s.\n", name);
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup;
+ }
+
+ if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
+ if ((strcmp(value, PCIEHPC_PROP_VALUE_ON) != 0) &&
+ (strcmp(value, PCIEHPC_PROP_VALUE_OFF) != 0) &&
+ (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) != 0)) {
+ cmn_err(CE_WARN, "Unsupported value of setting "
+ "property %s\n", name);
+ ret = DDI_ENOTSUP;
+ goto set_prop_cleanup;
+ }
+ } else {
+ cmn_err(CE_WARN, "Unsupported property: %s\n", name);
+ ret = DDI_ENOTSUP;
+ goto set_prop_cleanup;
+ }
+ }
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* get the current slot state */
+ pciehpc_get_slot_state(slot_p);
+
+ /* set each property */
+ prop_pair = NULL;
+ while (prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+
+ if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
+ if (strcmp(value, PCIEHPC_PROP_VALUE_ON) == 0)
+ led_state = PCIE_HP_LED_ON;
+ else if (strcmp(value, PCIEHPC_PROP_VALUE_OFF) == 0)
+ led_state = PCIE_HP_LED_OFF;
+ else if (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) == 0)
+ led_state = PCIE_HP_LED_BLINK;
+
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED,
+ led_state);
+ }
+ }
+
+ mutex_exit(&ctrl_p->hc_mutex);
+set_prop_cleanup:
+ nvlist_free(prop_list);
+ return (ret);
+}
+
+/*
+ * Send a command to the PCI-E Hot Plug Controller.
+ *
+ * NOTES: The PCI-E spec defines the following semantics for issuing hot plug
+ * commands.
+ * 1) If Command Complete events/interrupts are supported then software
+ * waits for Command Complete event after issuing a command (i.e writing
+ * to the Slot Control register). The command completion could take as
+ * long as 1 second so software should be prepared to wait for 1 second
+ * before issuing another command.
+ *
+ * 2) If Command Complete events/interrupts are not supported then
+ * software could issue multiple Slot Control writes without any delay
+ * between writes.
+ */
+static void
+pciehpc_issue_hpc_command(pcie_hp_ctrl_t *ctrl_p, uint16_t control)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t status;
+ uint32_t slot_cap;
+
+ /*
+ * PCI-E version 1.1 spec defines No Command Completed
+ * Support bit (bit#18) in Slot Capabilities register. If this
+ * bit is set then slot doesn't support notification of command
+ * completion events.
+ */
+ slot_cap = pciehpc_reg_get32(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCAP);
+
+ /*
+ * If no Command Completion event is supported or it is ACPI
+ * hot plug mode then just issue the command and return.
+ */
+ if ((slot_cap & PCIE_SLOTCAP_NO_CMD_COMP_SUPP) ||
+ (bus_p->bus_hp_curr_mode == PCIE_ACPI_HP_MODE)) {
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
+ return;
+ }
+
+ /*
+ * **************************************
+ * Command Complete events are supported.
+ * **************************************
+ */
+
+ /*
+ * If HPC is not yet initialized then just poll for the Command
+ * Completion interrupt.
+ */
+ if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
+ int retry = PCIE_HP_CMD_WAIT_RETRY;
+
+ /* write the command to the HPC */
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
+
+ /* poll for status completion */
+ while (retry--) {
+ /* wait for 10 msec before checking the status */
+ delay(drv_usectohz(PCIE_HP_CMD_WAIT_TIME));
+
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
+ /* clear the status bits */
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
+ break;
+ }
+ }
+ return;
+ }
+
+ /* HPC is already initialized */
+
+ ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
+
+ /*
+ * If previous command is still pending then wait for its
+ * completion. i.e cv_wait()
+ */
+
+ while (ctrl_p->hc_cmd_pending == B_TRUE)
+ cv_wait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex);
+
+ /*
+ * Issue the command and wait for Command Completion or
+ * the 1 sec timeout.
+ */
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
+
+ ctrl_p->hc_cmd_pending = B_TRUE;
+
+ if (cv_timedwait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex,
+ ddi_get_lbolt() + SEC_TO_TICK(1)) == -1) {
+
+ /* it is a timeout */
+ PCIE_DBG("pciehpc_issue_hpc_command: Command Complete"
+ " interrupt is not received for slot %d\n",
+ slot_p->hs_phy_slot_num);
+
+ /* clear the status info in case interrupts are disabled? */
+ status = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS);
+
+ if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
+ /* clear the status bits */
+ pciehpc_reg_put16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
+ }
+ }
+
+ ctrl_p->hc_cmd_pending = B_FALSE;
+
+ /* wake up any one waiting for issuing another command to HPC */
+ cv_signal(&ctrl_p->hc_cmd_comp_cv);
+}
+
+/*
+ * pciehcp_attn_btn_handler()
+ *
+ * This handles ATTN button pressed event as per the PCI-E 1.1 spec.
+ */
+static void
+pciehpc_attn_btn_handler(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ pcie_hp_led_state_t power_led_state;
+ callb_cpr_t cprinfo;
+
+ PCIE_DBG("pciehpc_attn_btn_handler: thread started\n");
+
+ CALLB_CPR_INIT(&cprinfo, &ctrl_p->hc_mutex, callb_generic_cpr,
+ "pciehpc_attn_btn_handler");
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* wait for ATTN button event */
+ cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
+
+ while (slot_p->hs_attn_btn_thread_exit == B_FALSE) {
+ if (slot_p->hs_attn_btn_pending == B_TRUE) {
+ /* get the current state of power LED */
+ power_led_state = pciehpc_get_led_state(ctrl_p,
+ PCIE_HP_POWER_LED);
+
+ /* Blink the Power LED while we wait for 5 seconds */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
+ PCIE_HP_LED_BLINK);
+
+ /* wait for 5 seconds before taking any action */
+ if (cv_timedwait(&slot_p->hs_attn_btn_cv,
+ &ctrl_p->hc_mutex,
+ ddi_get_lbolt() + SEC_TO_TICK(5)) == -1) {
+ /*
+ * It is a time out; make sure the ATTN pending
+ * flag is still ON before sending the event to
+ * DDI HP framework.
+ */
+ if (slot_p->hs_attn_btn_pending == B_TRUE) {
+ int hint;
+
+ slot_p->hs_attn_btn_pending = B_FALSE;
+ pciehpc_get_slot_state(slot_p);
+
+ if (slot_p->hs_info.cn_state <=
+ DDI_HP_CN_STATE_PRESENT) {
+ /*
+ * Insertion.
+ */
+ hint = SE_INCOMING_RES;
+ } else {
+ /*
+ * Want to remove;
+ */
+ hint = SE_OUTGOING_RES;
+ }
+
+ /*
+ * We can't call ddihp_cn_gen_sysevent
+ * here since it's not a DDI interface.
+ */
+ pcie_hp_gen_sysevent_req(
+ slot_p->hs_info.cn_name,
+ hint,
+ ctrl_p->hc_dip,
+ KM_SLEEP);
+ }
+ }
+
+ /* restore the power LED state */
+ pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
+ power_led_state);
+ continue;
+ }
+
+ /* wait for another ATTN button event */
+ cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
+ }
+
+ PCIE_DBG("pciehpc_attn_btn_handler: thread exit\n");
+ cv_signal(&slot_p->hs_attn_btn_cv);
+ CALLB_CPR_EXIT(&cprinfo);
+ thread_exit();
+}
+
+/*
+ * convert LED state from PCIE HPC definition to pcie_hp_led_state_t
+ * definition.
+ */
+static pcie_hp_led_state_t
+pciehpc_led_state_to_hpc(uint16_t state)
+{
+ switch (state) {
+ case PCIE_SLOTCTL_INDICATOR_STATE_ON:
+ return (PCIE_HP_LED_ON);
+ case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
+ return (PCIE_HP_LED_BLINK);
+ case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
+ default:
+ return (PCIE_HP_LED_OFF);
+ }
+}
+
+/*
+ * Get the state of an LED.
+ */
+static pcie_hp_led_state_t
+pciehpc_get_led_state(pcie_hp_ctrl_t *ctrl_p, pcie_hp_led_t led)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t control, state;
+
+ /* get the current state of Slot Control register */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ switch (led) {
+ case PCIE_HP_POWER_LED:
+ state = pcie_slotctl_pwr_indicator_get(control);
+ break;
+ case PCIE_HP_ATTN_LED:
+ state = pcie_slotctl_attn_indicator_get(control);
+ break;
+ default:
+ PCIE_DBG("pciehpc_get_led_state() invalid LED %d\n", led);
+ return (PCIE_HP_LED_OFF);
+ }
+
+ switch (state) {
+ case PCIE_SLOTCTL_INDICATOR_STATE_ON:
+ return (PCIE_HP_LED_ON);
+
+ case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
+ return (PCIE_HP_LED_BLINK);
+
+ case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
+ default:
+ return (PCIE_HP_LED_OFF);
+ }
+}
+
+/*
+ * Set the state of an LED. It updates both hw and sw state.
+ */
+static void
+pciehpc_set_led_state(pcie_hp_ctrl_t *ctrl_p, pcie_hp_led_t led,
+ pcie_hp_led_state_t state)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t control;
+
+ /* get the current state of Slot Control register */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ switch (led) {
+ case PCIE_HP_POWER_LED:
+ /* clear led mask */
+ control &= ~PCIE_SLOTCTL_PWR_INDICATOR_MASK;
+ slot_p->hs_power_led_state = state;
+ break;
+ case PCIE_HP_ATTN_LED:
+ /* clear led mask */
+ control &= ~PCIE_SLOTCTL_ATTN_INDICATOR_MASK;
+ slot_p->hs_attn_led_state = state;
+ break;
+ default:
+ PCIE_DBG("pciehpc_set_led_state() invalid LED %d\n", led);
+ return;
+ }
+
+ switch (state) {
+ case PCIE_HP_LED_ON:
+ if (led == PCIE_HP_POWER_LED)
+ control = pcie_slotctl_pwr_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_ON);
+ else if (led == PCIE_HP_ATTN_LED)
+ control = pcie_slotctl_attn_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_ON);
+ break;
+ case PCIE_HP_LED_OFF:
+ if (led == PCIE_HP_POWER_LED)
+ control = pcie_slotctl_pwr_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_OFF);
+ else if (led == PCIE_HP_ATTN_LED)
+ control = pcie_slotctl_attn_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_OFF);
+ break;
+ case PCIE_HP_LED_BLINK:
+ if (led == PCIE_HP_POWER_LED)
+ control = pcie_slotctl_pwr_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
+ else if (led == PCIE_HP_ATTN_LED)
+ control = pcie_slotctl_attn_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
+ break;
+
+ default:
+ PCIE_DBG("pciehpc_set_led_state() invalid LED state %d\n",
+ state);
+ return;
+ }
+
+ /* update the Slot Control Register */
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+#ifdef DEBUG
+ /* get the current state of Slot Control register */
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ PCIE_DBG("pciehpc_set_led_state: slot %d power-led %s attn-led %s\n",
+ slot_p->hs_phy_slot_num, pcie_led_state_text(
+ pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control))),
+ pcie_led_state_text(pciehpc_led_state_to_hpc(
+ pcie_slotctl_attn_indicator_get(control))));
+#endif
+}
+
+#ifdef DEBUG
+/*
+ * Dump PCI-E Hot Plug registers.
+ */
+static void
+pciehpc_dump_hpregs(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uint16_t control;
+ uint32_t capabilities;
+
+ if (!pcie_debug_flags)
+ return;
+
+ capabilities = pciehpc_reg_get32(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCAP);
+
+ control = pciehpc_reg_get16(ctrl_p,
+ bus_p->bus_pcie_off + PCIE_SLOTCTL);
+
+ PCIE_DBG("pciehpc_dump_hpregs: Found PCI-E hot plug slot %d\n",
+ slot_p->hs_phy_slot_num);
+
+ PCIE_DBG("Attention Button Present = %s\n",
+ capabilities & PCIE_SLOTCAP_ATTN_BUTTON ? "Yes":"No");
+
+ PCIE_DBG("Power controller Present = %s\n",
+ capabilities & PCIE_SLOTCAP_POWER_CONTROLLER ? "Yes":"No");
+
+ PCIE_DBG("MRL Sensor Present = %s\n",
+ capabilities & PCIE_SLOTCAP_MRL_SENSOR ? "Yes":"No");
+
+ PCIE_DBG("Attn Indicator Present = %s\n",
+ capabilities & PCIE_SLOTCAP_ATTN_INDICATOR ? "Yes":"No");
+
+ PCIE_DBG("Power Indicator Present = %s\n",
+ capabilities & PCIE_SLOTCAP_PWR_INDICATOR ? "Yes":"No");
+
+ PCIE_DBG("HotPlug Surprise = %s\n",
+ capabilities & PCIE_SLOTCAP_HP_SURPRISE ? "Yes":"No");
+
+ PCIE_DBG("HotPlug Capable = %s\n",
+ capabilities & PCIE_SLOTCAP_HP_CAPABLE ? "Yes":"No");
+
+ PCIE_DBG("Physical Slot Number = %d\n",
+ PCIE_SLOTCAP_PHY_SLOT_NUM(capabilities));
+
+ PCIE_DBG("Attn Button interrupt Enabled = %s\n",
+ control & PCIE_SLOTCTL_ATTN_BTN_EN ? "Yes":"No");
+
+ PCIE_DBG("Power Fault interrupt Enabled = %s\n",
+ control & PCIE_SLOTCTL_PWR_FAULT_EN ? "Yes":"No");
+
+ PCIE_DBG("MRL Sensor INTR Enabled = %s\n",
+ control & PCIE_SLOTCTL_MRL_SENSOR_EN ? "Yes":"No");
+
+ PCIE_DBG("Presence interrupt Enabled = %s\n",
+ control & PCIE_SLOTCTL_PRESENCE_CHANGE_EN ? "Yes":"No");
+
+ PCIE_DBG("Cmd Complete interrupt Enabled = %s\n",
+ control & PCIE_SLOTCTL_CMD_INTR_EN ? "Yes":"No");
+
+ PCIE_DBG("HotPlug interrupt Enabled = %s\n",
+ control & PCIE_SLOTCTL_HP_INTR_EN ? "Yes":"No");
+
+ PCIE_DBG("Power Indicator LED = %s", pcie_led_state_text(
+ pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control))));
+
+ PCIE_DBG("Attn Indicator LED = %s\n",
+ pcie_led_state_text(pciehpc_led_state_to_hpc(
+ pcie_slotctl_attn_indicator_get(control))));
+}
+#endif /* DEBUG */
diff --git a/usr/src/uts/common/io/pciex/hotplug/pcishpc.c b/usr/src/uts/common/io/pciex/hotplug/pcishpc.c
new file mode 100644
index 0000000000..40c6f71a46
--- /dev/null
+++ b/usr/src/uts/common/io/pciex/hotplug/pcishpc.c
@@ -0,0 +1,2645 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains PCI HotPlug functionality that is compatible with the
+ * PCI SHPC specification 1.x.
+ *
+ * NOTE: This file is compiled and delivered through misc/pcie module.
+ */
+
+#include <sys/note.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/kstat.h>
+#include <sys/debug.h>
+#include <sys/vtrace.h>
+#include <sys/autoconf.h>
+#include <sys/varargs.h>
+#include <sys/hwconf.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/callb.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/sysevent/dr.h>
+#include <sys/ndi_impldefs.h>
+#include <sys/pci_impl.h>
+#include <sys/hotplug/pci/pcie_hp.h>
+#include <sys/hotplug/pci/pcishpc.h>
+
+typedef struct pcishpc_prop {
+ char *prop_name;
+ char *prop_value;
+} pcishpc_prop_t;
+
+static pcishpc_prop_t pcishpc_props[] = {
+ { PCIEHPC_PROP_LED_FAULT, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_LED_POWER, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_LED_ATTN, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_LED_ACTIVE, PCIEHPC_PROP_VALUE_LED },
+ { PCIEHPC_PROP_CARD_TYPE, PCIEHPC_PROP_VALUE_TYPE },
+ { PCIEHPC_PROP_BOARD_TYPE, PCIEHPC_PROP_VALUE_TYPE },
+ { PCIEHPC_PROP_SLOT_CONDITION, PCIEHPC_PROP_VALUE_TYPE }
+};
+
+/* reset delay to 1 sec. */
+static int pcishpc_reset_delay = 1000000;
+
+/* Local function prototype */
+static pcie_hp_ctrl_t *pcishpc_create_controller(dev_info_t *dip);
+static int pcishpc_setup_controller(pcie_hp_ctrl_t *ctrl_p);
+static int pcishpc_destroy_controller(dev_info_t *dip);
+static pcie_hp_slot_t *pcishpc_create_slot(pcie_hp_ctrl_t *ctrl_p);
+static int pcishpc_register_slot(pcie_hp_ctrl_t *ctrl_p, int slot);
+static int pcishpc_destroy_slots(pcie_hp_ctrl_t *ctrl_p);
+static int pcishpc_enable_irqs(pcie_hp_ctrl_t *ctrl_p);
+static int pcishpc_disable_irqs(pcie_hp_ctrl_t *ctrl_p);
+static int pcishpc_slot_get_property(pcie_hp_slot_t *slot_p,
+ ddi_hp_property_t *arg, ddi_hp_property_t *rval);
+static int pcishpc_slot_set_property(pcie_hp_slot_t *slot_p,
+ ddi_hp_property_t *arg, ddi_hp_property_t *rval);
+static int pcishpc_issue_command(pcie_hp_ctrl_t *ctrl_p,
+ uint32_t cmd_code);
+static int pcishpc_wait_busy(pcie_hp_ctrl_t *ctrl_p);
+static void pcishpc_attn_btn_handler(pcie_hp_slot_t *slot_p);
+static void pcishpc_get_slot_state(pcie_hp_slot_t *slot_p);
+static int pcishpc_set_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t new_slot_state);
+static void pcishpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p, int slot);
+static int pcishpc_set_bus_speed(pcie_hp_slot_t *slot_p);
+static int pcishpc_setled(pcie_hp_slot_t *slot_p, pcie_hp_led_t led,
+ pcie_hp_led_state_t state);
+static int pcishpc_led_shpc_to_hpc(int state);
+static int pcishpc_led_hpc_to_shpc(int state);
+static int pcishpc_slot_shpc_to_hpc(int shpc_state);
+static int pcishpc_slot_hpc_to_shpc(int state);
+static char *pcishpc_slot_textslotstate(ddi_hp_cn_state_t state);
+static char *pcishpc_slot_textledstate(pcie_hp_led_state_t state);
+
+static uint32_t pcishpc_read_reg(pcie_hp_ctrl_t *ctrl_p, int reg);
+static void pcishpc_write_reg(pcie_hp_ctrl_t *ctrl_p, int reg,
+ uint32_t data);
+
+static int pcishpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state);
+static int pcishpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state);
+static int pcishpc_change_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state);
+
+static int pcishpc_slot_poweron(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t *result_state);
+static int pcishpc_slot_poweroff(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t *result_state);
+static int pcishpc_slot_probe(pcie_hp_slot_t *slot_p);
+static int pcishpc_slot_unprobe(pcie_hp_slot_t *slot_p);
+#ifdef DEBUG
+static void pcishpc_dump_regs(pcie_hp_ctrl_t *ctrl_p);
+#endif /* DEBUG */
+
+
+/*
+ * Global functions (called by other drivers/modules)
+ */
+
+/*
+ * pcishpc_init()
+ *
+ * Install and configure an SHPC controller and register the HotPlug slots
+ * with the Solaris HotPlug framework. This function is usually called by
+ * a PCI bridge Nexus driver that has a built in SHPC controller.
+ */
+int
+pcishpc_init(dev_info_t *dip)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ pcie_hp_ctrl_t *ctrl_p;
+ int i;
+
+ PCIE_DBG("pcishpc_init() called from %s#%d\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) != NULL) {
+ PCIE_DBG("pcishpc_init() shpc instance already "
+ "initialized!\n");
+ return (DDI_SUCCESS);
+ }
+
+ /* Initialize soft state structure for the SHPC instance. */
+ ctrl_p = pcishpc_create_controller(dip);
+
+ if (ctrl_p == NULL) {
+ PCIE_DBG("pcishpc_init() failed to create shpc softstate\n");
+ return (DDI_FAILURE);
+ }
+
+ if (pcishpc_setup_controller(ctrl_p) != DDI_SUCCESS) {
+ PCIE_DBG("pcishpc_init() failed to setup controller\n");
+ goto cleanup;
+ }
+
+ /*
+ * Setup resource maps for this bus node.
+ */
+ (void) pci_resource_setup(dip);
+
+#ifdef DEBUG
+ PCIE_DBG("%s%d: P2P bridge register dump:\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ for (i = 0; i < 0x100; i += 4) {
+ PCIE_DBG("SHPC Cfg reg 0x%02x: %08x\n", i,
+ pci_config_get32(bus_p->bus_cfg_hdl, i));
+ }
+#endif /* DEBUG */
+
+ /* Setup each HotPlug slot on this SHPC controller. */
+ for (i = 0; i < ctrl_p->hc_num_slots_impl; i++) {
+ if (pcishpc_register_slot(ctrl_p, i) != DDI_SUCCESS) {
+ PCIE_DBG("pcishpc_init() failed to register "
+ "slot %d\n", i);
+ goto cleanup1;
+ }
+ if (pcie_create_minor_node(ctrl_p, i) != DDI_SUCCESS) {
+ PCIE_DBG("pcishpc_init() failed to create "
+ "minor node for slot %d\n", i);
+ goto cleanup1;
+ }
+ }
+
+ (void) pcishpc_enable_irqs(ctrl_p);
+
+#ifdef DEBUG
+ /* Dump out the SHPC registers. */
+ pcishpc_dump_regs(ctrl_p);
+#endif /* DEBUG */
+
+ PCIE_DBG("pcishpc_init() success(dip=%p)\n", dip);
+ return (DDI_SUCCESS);
+
+cleanup1:
+ for (i = 0; i < ctrl_p->hc_num_slots_impl; i++) {
+ if (ctrl_p->hc_slots[i] == NULL)
+ continue;
+
+ pcie_remove_minor_node(ctrl_p, i);
+ }
+ (void) pci_resource_destroy(dip);
+cleanup:
+ (void) pcishpc_destroy_controller(dip);
+ return (DDI_FAILURE);
+}
+
+/*
+ * pcishpc_uninit()
+ * Unload the HogPlug controller driver and deallocate all resources.
+ */
+int
+pcishpc_uninit(dev_info_t *dip)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+ int i;
+
+ PCIE_DBG("pcishpc_uninit() called(dip=%p)\n", dip);
+
+ ctrl_p = PCIE_GET_HP_CTRL(dip);
+
+ if (!ctrl_p) {
+ PCIE_DBG("pcishpc_uninit() Unable to find softstate\n");
+ return (DDI_FAILURE);
+ }
+
+ for (i = 0; i < PCIE_HP_MAX_SLOTS; i++) {
+ if (ctrl_p->hc_slots[i] == NULL)
+ continue;
+
+ pcie_remove_minor_node(ctrl_p, i);
+ }
+
+ (void) pcishpc_disable_irqs(ctrl_p);
+ ctrl_p->hc_flags = 0;
+
+ /*
+ * Destroy resource maps for this bus node.
+ */
+ (void) pci_resource_destroy(dip);
+
+ (void) pcishpc_destroy_controller(dip);
+
+ PCIE_DBG("pcishpc_uninit() success(dip=%p)\n", dip);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pcishpc_intr()
+ *
+ * This is the SHPC controller interrupt handler.
+ */
+int
+pcishpc_intr(dev_info_t *dip)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+ uint32_t irq_locator, irq_serr_locator, reg;
+ int slot;
+
+ PCIE_DBG("pcishpc_intr() called\n");
+
+ /* get the soft state structure for this dip */
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
+ return (DDI_INTR_UNCLAIMED);
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
+ PCIE_DBG("pcishpc_intr() unclaimed\n");
+ mutex_exit(&ctrl_p->hc_mutex);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ PCIE_DBG("pcishpc_intr() interrupt received\n");
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);
+
+ if (reg & PCI_HP_SERR_INT_CMD_COMPLETE_IRQ) {
+ PCIE_DBG("pcishpc_intr() "
+ "PCI_HP_SERR_INT_CMD_COMPLETE_IRQ detected\n");
+ ctrl_p->hc_cmd_pending = B_FALSE;
+ cv_signal(&ctrl_p->hc_cmd_comp_cv);
+ }
+
+ if (reg & PCI_HP_SERR_INT_ARBITER_IRQ) {
+ PCIE_DBG("pcishpc_intr() PCI_HP_SERR_INT_ARBITER_IRQ "
+ "detected\n");
+ ctrl_p->hc_arbiter_timeout = B_TRUE;
+ }
+
+ /* Write back the SERR INT register to acknowledge the IRQs. */
+ pcishpc_write_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG, reg);
+
+ irq_locator = pcishpc_read_reg(ctrl_p, PCI_HP_IRQ_LOCATOR_REG);
+ irq_serr_locator = pcishpc_read_reg(ctrl_p, PCI_HP_SERR_LOCATOR_REG);
+
+ /* Check for slot events that might have occured. */
+ for (slot = 0; slot < ctrl_p->hc_num_slots_impl; slot++) {
+ if ((irq_locator & (PCI_HP_IRQ_SLOT_N_PENDING<<slot)) ||
+ (irq_serr_locator &
+ (PCI_HP_IRQ_SERR_SLOT_N_PENDING<<slot))) {
+ PCIE_DBG("pcishpc_intr() slot %d and "
+ "pending IRQ\n", slot+1);
+
+ reg = pcishpc_read_reg(ctrl_p,
+ PCI_HP_LOGICAL_SLOT_REGS+slot);
+
+ if (reg & PCI_HP_SLOT_PRESENCE_DETECTED)
+ PCIE_DBG("slot %d: "
+ "PCI_HP_SLOT_PRESENCE_DETECTED\n",
+ slot+1);
+
+ if (reg & PCI_HP_SLOT_ISO_PWR_DETECTED)
+ PCIE_DBG("slot %d: "
+ "PCI_HP_SLOT_ISO_PWR_DETECTED\n",
+ slot+1);
+
+ if (reg & PCI_HP_SLOT_ATTN_DETECTED) {
+ PCIE_DBG("slot %d: "
+ "PCI_HP_SLOT_ATTN_DETECTED\n", slot+1);
+
+ /*
+ * if ATTN button event is still pending
+ * then cancel it
+ */
+ if (ctrl_p->hc_slots[slot]->
+ hs_attn_btn_pending == B_TRUE)
+ ctrl_p->hc_slots[slot]->
+ hs_attn_btn_pending = B_FALSE;
+
+ /* wake up the ATTN event handler */
+ cv_signal(&ctrl_p->hc_slots[slot]->
+ hs_attn_btn_cv);
+ }
+
+ if (reg & PCI_HP_SLOT_MRL_DETECTED)
+ PCIE_DBG("slot %d: "
+ "PCI_HP_SLOT_MRL_DETECTED\n", slot+1);
+
+ if (reg & PCI_HP_SLOT_POWER_DETECTED)
+ PCIE_DBG("slot %d: "
+ "PCI_HP_SLOT_POWER_DETECTED\n", slot+1);
+
+ /* Acknoledge any slot interrupts */
+ pcishpc_write_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot,
+ reg);
+ }
+ }
+
+ mutex_exit(&ctrl_p->hc_mutex);
+
+ PCIE_DBG("pcishpc_intr() claimed\n");
+
+ return (DDI_INTR_CLAIMED);
+}
+
+int
+pcishpc_slot_get_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
+ ddi_hp_property_t *rval)
+{
+ ddi_hp_property_t request, result;
+#ifdef _SYSCALL32_IMPL
+ ddi_hp_property32_t request32, result32;
+#endif
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ nvlist_t *prop_list;
+ nvlist_t *prop_rlist; /* nvlist for return values */
+ nvpair_t *prop_pair;
+ char *name, *value;
+ int ret = DDI_SUCCESS;
+ int i, n;
+ boolean_t get_all_prop = B_FALSE;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(arg, &request, sizeof (ddi_hp_property_t)) ||
+ copyin(rval, &result, sizeof (ddi_hp_property_t)))
+ return (DDI_FAILURE);
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ bzero(&request, sizeof (request));
+ bzero(&result, sizeof (result));
+ if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)) ||
+ copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
+ return (DDI_FAILURE);
+ request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
+ request.buf_size = request32.buf_size;
+ result.nvlist_buf = (char *)(uintptr_t)result32.nvlist_buf;
+ result.buf_size = result32.buf_size;
+ }
+#endif
+
+ if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
+ &prop_list)) != DDI_SUCCESS)
+ return (ret);
+
+ if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
+ ret = DDI_ENOMEM;
+ goto get_prop_cleanup;
+ }
+
+ /* check whether the requested property is "all" or "help" */
+ prop_pair = nvlist_next_nvpair(prop_list, NULL);
+ if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+ n = sizeof (pcishpc_props) / sizeof (pcishpc_prop_t);
+
+ if (strcmp(name, PCIEHPC_PROP_ALL) == 0) {
+ (void) nvlist_remove_all(prop_list, PCIEHPC_PROP_ALL);
+
+ /*
+ * Add all properties into the request list, so that we
+ * will get the values in the following for loop.
+ */
+ for (i = 0; i < n; i++) {
+ if (nvlist_add_string(prop_list,
+ pcishpc_props[i].prop_name, "") != 0) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup1;
+ }
+ }
+ get_all_prop = B_TRUE;
+ } else if (strcmp(name, PCIEHPC_PROP_HELP) == 0) {
+ /*
+ * Empty the request list, and add help strings into the
+ * return list. We will pass the following for loop.
+ */
+ (void) nvlist_remove_all(prop_list, PCIEHPC_PROP_HELP);
+
+ for (i = 0; i < n; i++) {
+ if (nvlist_add_string(prop_rlist,
+ pcishpc_props[i].prop_name,
+ pcishpc_props[i].prop_value) != 0) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup1;
+ }
+ }
+ }
+ }
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ /* for each requested property, get the value and add it to nvlist */
+ prop_pair = NULL;
+ while (prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+
+ if (strcmp(name, PCIEHPC_PROP_LED_FAULT) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_fault_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_LED_POWER) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_power_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_attn_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_LED_ACTIVE) == 0) {
+ value = pcie_led_state_text(
+ slot_p->hs_active_led_state);
+ } else if (strcmp(name, PCIEHPC_PROP_CARD_TYPE) == 0) {
+ ddi_acc_handle_t handle;
+ dev_info_t *cdip;
+ uint8_t prog_class, base_class, sub_class;
+ int i;
+
+ mutex_exit(&ctrl_p->hc_mutex);
+ cdip = pcie_hp_devi_find(
+ ctrl_p->hc_dip, slot_p->hs_device_num, 0);
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ if ((slot_p->hs_info.cn_state !=
+ DDI_HP_CN_STATE_ENABLED) || (cdip == NULL)) {
+ /*
+ * When getting all properties, just ignore the
+ * one that's not available under certain state.
+ */
+ if (get_all_prop)
+ continue;
+
+ ret = DDI_ENOTSUP;
+ goto get_prop_cleanup2;
+ }
+
+ if (pci_config_setup(cdip, &handle) != DDI_SUCCESS) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup2;
+ }
+
+ prog_class = pci_config_get8(handle,
+ PCI_CONF_PROGCLASS);
+ base_class = pci_config_get8(handle, PCI_CONF_BASCLASS);
+ sub_class = pci_config_get8(handle, PCI_CONF_SUBCLASS);
+ pci_config_teardown(&handle);
+
+ for (i = 0; i < class_pci_items; i++) {
+ if ((base_class == class_pci[i].base_class) &&
+ (sub_class == class_pci[i].sub_class) &&
+ (prog_class == class_pci[i].prog_class)) {
+ value = class_pci[i].short_desc;
+ break;
+ }
+ }
+ if (i == class_pci_items)
+ value = PCIEHPC_PROP_VALUE_UNKNOWN;
+ } else if (strcmp(name, PCIEHPC_PROP_BOARD_TYPE) == 0) {
+ if (slot_p->hs_info.cn_state <= DDI_HP_CN_STATE_EMPTY)
+ value = PCIEHPC_PROP_VALUE_UNKNOWN;
+ else
+ value = PCIEHPC_PROP_VALUE_PCIHOTPLUG;
+ } else if (strcmp(name, PCIEHPC_PROP_SLOT_CONDITION) == 0) {
+ value = pcie_slot_condition_text(slot_p->hs_condition);
+ } else {
+ /* unsupported property */
+ cmn_err(CE_WARN, "Unsupported property: %s\n", name);
+
+ ret = DDI_ENOTSUP;
+ goto get_prop_cleanup2;
+ }
+ if (nvlist_add_string(prop_rlist, name, value) != 0) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup2;
+ }
+ }
+
+ // pack nvlist and copyout
+ if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
+ &result.buf_size)) != DDI_SUCCESS) {
+ goto get_prop_cleanup2;
+ }
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyout(&result, rval, sizeof (ddi_hp_property_t))) {
+ ret = DDI_FAILURE;
+ goto get_prop_cleanup2;
+ }
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ if (result.buf_size > UINT32_MAX) {
+ ret = DDI_FAILURE;
+ } else {
+ result32.buf_size = (uint32_t)result.buf_size;
+ if (copyout(&result32, rval,
+ sizeof (ddi_hp_property32_t)))
+ ret = DDI_FAILURE;
+ }
+ }
+#endif
+
+get_prop_cleanup2:
+ mutex_exit(&ctrl_p->hc_mutex);
+get_prop_cleanup1:
+ nvlist_free(prop_rlist);
+get_prop_cleanup:
+ nvlist_free(prop_list);
+ return (ret);
+}
+
+int
+pcishpc_slot_set_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
+ ddi_hp_property_t *rval)
+{
+ ddi_hp_property_t request, result;
+#ifdef _SYSCALL32_IMPL
+ ddi_hp_property32_t request32, result32;
+#endif
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ nvlist_t *prop_list;
+ nvlist_t *prop_rlist;
+ nvpair_t *prop_pair;
+ char *name, *value;
+ pcie_hp_led_state_t led_state;
+ int ret = DDI_SUCCESS;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(arg, &request, sizeof (ddi_hp_property_t)))
+ return (DDI_FAILURE);
+ if (rval &&
+ copyin(rval, &result, sizeof (ddi_hp_property_t)))
+ return (DDI_FAILURE);
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ bzero(&request, sizeof (request));
+ bzero(&result, sizeof (result));
+ if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)))
+ return (DDI_FAILURE);
+ if (rval &&
+ copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
+ return (DDI_FAILURE);
+ request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
+ request.buf_size = request32.buf_size;
+ if (rval) {
+ result.nvlist_buf =
+ (char *)(uintptr_t)result32.nvlist_buf;
+ result.buf_size = result32.buf_size;
+ }
+ }
+#endif
+
+ if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
+ &prop_list)) != DDI_SUCCESS)
+ return (ret);
+
+ /* check whether the requested property is "help" */
+ prop_pair = nvlist_next_nvpair(prop_list, NULL);
+ if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair) &&
+ (strcmp(nvpair_name(prop_pair), PCIEHPC_PROP_HELP) == 0)) {
+ if (!rval) {
+ ret = DDI_ENOTSUP;
+ goto set_prop_cleanup;
+ }
+
+ if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
+ ret = DDI_ENOMEM;
+ goto set_prop_cleanup;
+ }
+ if (nvlist_add_string(prop_rlist, PCIEHPC_PROP_LED_ATTN,
+ PCIEHPC_PROP_VALUE_LED) != 0) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ }
+
+ if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
+ &result.buf_size)) != DDI_SUCCESS) {
+ goto set_prop_cleanup1;
+ }
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyout(&result, rval,
+ sizeof (ddi_hp_property_t))) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ }
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ if (result.buf_size > UINT32_MAX) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ } else {
+ result32.buf_size = (uint32_t)result.buf_size;
+ if (copyout(&result32, rval,
+ sizeof (ddi_hp_property32_t))) {
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup1;
+ }
+ }
+ }
+#endif
+set_prop_cleanup1:
+ nvlist_free(prop_rlist);
+ nvlist_free(prop_list);
+ return (ret);
+ }
+
+ /* Validate the request */
+ prop_pair = NULL;
+ while (prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+ if (nvpair_type(prop_pair) != DATA_TYPE_STRING) {
+ cmn_err(CE_WARN, "Unexpected data type of setting "
+ "property %s.\n", name);
+ ret = DDI_EINVAL;
+ goto set_prop_cleanup;
+ }
+ if (nvpair_value_string(prop_pair, &value)) {
+ cmn_err(CE_WARN, "Get string value failed for property "
+ "%s.\n", name);
+ ret = DDI_FAILURE;
+ goto set_prop_cleanup;
+ }
+
+ if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
+ if ((strcmp(value, PCIEHPC_PROP_VALUE_ON) != 0) &&
+ (strcmp(value, PCIEHPC_PROP_VALUE_OFF) != 0) &&
+ (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) != 0)) {
+ cmn_err(CE_WARN, "Unsupported value of setting "
+ "property %s\n", name);
+ ret = DDI_ENOTSUP;
+ goto set_prop_cleanup;
+ }
+ } else {
+ cmn_err(CE_WARN, "Unsupported property: %s\n", name);
+ ret = DDI_ENOTSUP;
+ goto set_prop_cleanup;
+ }
+ }
+
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ // set each property
+ prop_pair = NULL;
+ while (prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) {
+ name = nvpair_name(prop_pair);
+
+ if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
+ if (strcmp(value, PCIEHPC_PROP_VALUE_ON) == 0)
+ led_state = PCIE_HP_LED_ON;
+ else if (strcmp(value, PCIEHPC_PROP_VALUE_OFF) == 0)
+ led_state = PCIE_HP_LED_OFF;
+ else if (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) == 0)
+ led_state = PCIE_HP_LED_BLINK;
+
+ (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED,
+ led_state);
+ }
+ }
+
+ mutex_exit(&ctrl_p->hc_mutex);
+set_prop_cleanup:
+ nvlist_free(prop_list);
+ return (ret);
+}
+
+/*
+ * pcishpc_hp_ops()
+ *
+ * Handle hotplug commands
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/* ARGSUSED */
+int
+pcishpc_hp_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
+ void *arg, void *result)
+{
+ pcie_hp_slot_t *slot_p = NULL;
+ pcie_hp_ctrl_t *ctrl_p;
+ int ret = DDI_SUCCESS, i;
+
+ PCIE_DBG("pcishpc_hp_ops: dip=%p cn_name=%s op=%x arg=%p\n",
+ dip, cn_name, op, arg);
+
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
+ return (DDI_FAILURE);
+
+ for (i = 0; i < PCIE_HP_MAX_SLOTS && ctrl_p->hc_slots[i]; i++) {
+ if (strcmp(ctrl_p->hc_slots[i]->hs_info.cn_name, cn_name)
+ == 0) {
+ /* Match with a physical slot, found */
+ slot_p = ctrl_p->hc_slots[i];
+ break;
+ }
+ }
+ if (!slot_p) {
+ PCIE_DBG("pcishpc_hp_ops: Failed to find the slot under"
+ "dip %p with name: %s; op=%x arg=%p\n",
+ dip, cn_name, op, arg);
+ return (DDI_EINVAL);
+ }
+ switch (op) {
+ case DDI_HPOP_CN_GET_STATE:
+ {
+ mutex_enter(&ctrl_p->hc_mutex);
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ *((ddi_hp_cn_state_t *)result) = slot_p->hs_info.cn_state;
+
+ mutex_exit(&ctrl_p->hc_mutex);
+ break;
+ }
+ case DDI_HPOP_CN_CHANGE_STATE:
+ {
+ ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
+
+ mutex_enter(&slot_p->hs_ctrl->hc_mutex);
+
+ ret = pcishpc_change_slot_state(slot_p, target_state);
+ *((ddi_hp_cn_state_t *)result) = slot_p->hs_info.cn_state;
+
+ mutex_exit(&slot_p->hs_ctrl->hc_mutex);
+ break;
+ }
+ case DDI_HPOP_CN_PROBE:
+ ret = pcishpc_slot_probe(slot_p);
+
+ break;
+ case DDI_HPOP_CN_UNPROBE:
+ ret = pcishpc_slot_unprobe(slot_p);
+
+ break;
+ case DDI_HPOP_CN_GET_PROPERTY:
+ ret = pcishpc_slot_get_property(slot_p,
+ (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
+ break;
+ case DDI_HPOP_CN_SET_PROPERTY:
+ ret = pcishpc_slot_set_property(slot_p,
+ (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
+ break;
+ default:
+ ret = DDI_ENOTSUP;
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ * Local functions (called within this file)
+ */
+
+/*
+ * pcishpc_create_controller()
+ *
+ * This function allocates and creates an SHPC controller state structure
+ * and adds it to the linked list of controllers.
+ */
+static pcie_hp_ctrl_t *
+pcishpc_create_controller(dev_info_t *dip)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ pcie_hp_ctrl_t *ctrl_p;
+
+ PCIE_DBG("pcishpc: create controller for %s#%d\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ ctrl_p = kmem_zalloc(sizeof (pcie_hp_ctrl_t), KM_SLEEP);
+ ctrl_p->hc_dip = dip;
+
+ cv_init(&ctrl_p->hc_cmd_comp_cv, NULL, CV_DRIVER, NULL);
+
+ /* Init the shpc controller's mutex. */
+ mutex_init(&ctrl_p->hc_mutex, NULL, MUTEX_DRIVER, NULL);
+
+ /* HPC initialization is complete now */
+ ctrl_p->hc_flags = PCIE_HP_INITIALIZED_FLAG;
+ bus_p->bus_hp_curr_mode = PCIE_PCI_HP_MODE;
+
+ PCIE_SET_HP_CTRL(dip, ctrl_p);
+
+ PCIE_DBG("pcishpc_create_controller() success\n");
+
+ return (ctrl_p);
+}
+
+
+/*
+ * pcishpc_setup_controller()
+ *
+ * Get the number of HotPlug Slots, and the PCI device information
+ * for this HotPlug controller.
+ */
+static int
+pcishpc_setup_controller(pcie_hp_ctrl_t *ctrl_p)
+{
+ uint32_t config;
+ dev_info_t *ppdip;
+
+ config = pcishpc_read_reg(ctrl_p, PCI_HP_SLOT_CONFIGURATION_REG);
+
+ /* Get the number of HotPlug slots implemented */
+ ctrl_p->hc_num_slots_impl = ((config)&31);
+
+ /*
+ * Initilize the current bus speed and number of hotplug slots
+ * currently connected.
+ */
+ ctrl_p->hc_curr_bus_speed = -1;
+ ctrl_p->hc_num_slots_connected = 0;
+
+ /*
+ * Get the first PCI device Number used.
+ *
+ * PCI-X I/O boat workaround.
+ * The register doesn't set up the correct value.
+ */
+ ppdip = ddi_get_parent(ddi_get_parent(ctrl_p->hc_dip));
+ if ((ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS,
+ "vendor-id", -1) == 0x108e) &&
+ (ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS,
+ "device-id", -1) == 0x9010))
+ ctrl_p->hc_device_start = 4;
+ else
+ ctrl_p->hc_device_start = ((config>>8)&31);
+
+ /* Get the first Physical device number. */
+ ctrl_p->hc_phys_start = ((config>>16)&0x7ff);
+
+ /* Check if the device numbers increase or decrease. */
+ ctrl_p->hc_device_increases = ((config>>29)&0x1);
+
+ ctrl_p->hc_has_attn =
+ (config & PCI_HP_SLOT_CONFIG_ATTN_BUTTON) ? B_TRUE : B_FALSE;
+ ctrl_p->hc_has_mrl =
+ (config & PCI_HP_SLOT_CONFIG_MRL_SENSOR) ? B_TRUE : B_FALSE;
+
+ ctrl_p->hc_cmd_pending = B_FALSE;
+ ctrl_p->hc_arbiter_timeout = B_FALSE;
+
+ if (ctrl_p->hc_num_slots_impl > PCIE_HP_MAX_SLOTS) {
+ PCIE_DBG("pcishpc_setup_controller() too many SHPC "
+ "slots error\n");
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * pcishpc_destroy_controller()
+ *
+ * This function deallocates all of the SHPC controller resources.
+ */
+static int
+pcishpc_destroy_controller(dev_info_t *dip)
+{
+ pcie_hp_ctrl_t *ctrl_p;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ PCIE_DBG("pcishpc_destroy_controller() called(dip=%p)\n", dip);
+
+ /* get the soft state structure for this dip */
+ if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) {
+ PCIE_DBG("pcishpc_destroy_controller() not found\n");
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Deallocate the slot state structures for this controller.
+ */
+ (void) pcishpc_destroy_slots(ctrl_p);
+ cv_destroy(&ctrl_p->hc_cmd_comp_cv);
+ mutex_destroy(&ctrl_p->hc_mutex);
+ kmem_free(ctrl_p, sizeof (pcie_hp_ctrl_t));
+ bus_p->bus_hp_curr_mode = PCIE_NONE_HP_MODE;
+
+ PCIE_DBG("pcishpc_destroy_controller() success\n");
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pcishpc_create_slot()
+ *
+ * Allocate and add a new HotPlug slot state structure to the linked list.
+ */
+static pcie_hp_slot_t *
+pcishpc_create_slot(pcie_hp_ctrl_t *ctrl_p)
+{
+ pcie_hp_slot_t *slot_p;
+
+ PCIE_DBG("pcishpc_create_slot() called(ctrl_p=%x)\n", ctrl_p);
+
+ /* Allocate a new slot structure. */
+ slot_p = kmem_zalloc(sizeof (pcie_hp_slot_t), KM_SLEEP);
+ slot_p->hs_ctrl = ctrl_p;
+
+ /* Assign an initial value */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;
+
+ PCIE_DBG("pcishpc_create_slot() success\n");
+ return (slot_p);
+}
+
+/*
+ * pcishpc_register_slot()
+ *
+ * Create and register a slot with the Solaris HotPlug framework.
+ */
+static int
+pcishpc_register_slot(pcie_hp_ctrl_t *ctrl_p, int slot)
+{
+ dev_info_t *dip = ctrl_p->hc_dip;
+ pcie_hp_slot_t *slot_p;
+
+ slot_p = pcishpc_create_slot(ctrl_p);
+ ctrl_p->hc_slots[slot] = slot_p;
+ slot_p->hs_num = slot;
+
+ /* Setup the PCI device # for this SHPC slot. */
+ if (ctrl_p->hc_device_increases)
+ slot_p->hs_device_num = ctrl_p->hc_device_start +
+ slot_p->hs_num;
+ else
+ slot_p->hs_device_num = ctrl_p->hc_device_start -
+ slot_p->hs_num;
+
+ /* Setup the DDI HP framework slot information. */
+ slot_p->hs_info.cn_type = DDI_HP_CN_TYPE_PCI;
+ slot_p->hs_info.cn_type_str = PCIE_PCI_HP_TYPE;
+ slot_p->hs_info.cn_child = NULL;
+
+ slot_p->hs_minor = PCI_MINOR_NUM(
+ ddi_get_instance(dip), slot_p->hs_device_num);
+ slot_p->hs_condition = AP_COND_UNKNOWN;
+
+ /* setup thread for handling ATTN button events */
+ if (ctrl_p->hc_has_attn) {
+ PCIE_DBG("pcishpc_register_slot: "
+ "setting up ATTN button event "
+ "handler thread for slot %d\n", slot);
+
+ cv_init(&slot_p->hs_attn_btn_cv, NULL, CV_DRIVER, NULL);
+ slot_p->hs_attn_btn_pending = B_FALSE;
+ slot_p->hs_attn_btn_threadp = thread_create(NULL, 0,
+ pcishpc_attn_btn_handler,
+ (void *)slot_p, 0, &p0, TS_RUN, minclsyspri);
+ slot_p->hs_attn_btn_thread_exit = B_FALSE;
+ }
+
+ /* setup the slot name (used for ap-id) */
+ pcishpc_set_slot_name(ctrl_p, slot);
+
+ pcishpc_get_slot_state(slot_p);
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_ENABLED)
+ slot_p->hs_condition = AP_COND_OK;
+
+ /* register the slot with DDI HP framework */
+ if (ndi_hp_register(dip, &slot_p->hs_info) != NDI_SUCCESS) {
+ PCIE_DBG("pciehpc_register_slot() failed to register slot %d\n",
+ slot_p->hs_phy_slot_num);
+ return (DDI_FAILURE);
+ }
+
+ pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip),
+ slot_p->hs_minor), slot_p->hs_device_num);
+
+ PCIE_DBG("pcishpc_register_slot() success for slot %d\n", slot);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pcishpc_destroy_slots()
+ *
+ * Free up all of the slot resources for this controller.
+ */
+static int
+pcishpc_destroy_slots(pcie_hp_ctrl_t *ctrl_p)
+{
+ dev_info_t *dip = ctrl_p->hc_dip;
+ pcie_hp_slot_t *slot_p;
+ int i;
+
+ PCIE_DBG("pcishpc_destroy_slots() called(ctrl_p=%p)\n", ctrl_p);
+
+ for (i = 0; i < PCIE_HP_MAX_SLOTS; i++) {
+ if ((slot_p = ctrl_p->hc_slots[i]) == NULL)
+ continue;
+
+ if (slot_p->hs_attn_btn_threadp != NULL) {
+ mutex_enter(&ctrl_p->hc_mutex);
+ slot_p->hs_attn_btn_thread_exit = B_TRUE;
+ cv_signal(&slot_p->hs_attn_btn_cv);
+ PCIE_DBG("pcishpc_destroy_slots: "
+ "waiting for ATTN thread exit\n");
+ cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
+ PCIE_DBG("pcishpc_destroy_slots: "
+ "ATTN thread exit\n");
+ cv_destroy(&slot_p->hs_attn_btn_cv);
+ slot_p->hs_attn_btn_threadp = NULL;
+ mutex_exit(&ctrl_p->hc_mutex);
+ }
+
+ PCIE_DBG("pcishpc_destroy_slots() (shpc_p=%p)\n"
+ "destroyed", slot_p);
+
+ pcie_hp_delete_occupant_props(dip,
+ makedevice(ddi_driver_major(dip),
+ slot_p->hs_minor));
+
+ /* unregister the slot with DDI HP framework */
+ if (ndi_hp_unregister(dip, slot_p->hs_info.cn_name) !=
+ NDI_SUCCESS) {
+ PCIE_DBG("pcishpc_destroy_slots() "
+ "failed to unregister slot %d\n",
+ slot_p->hs_phy_slot_num);
+ return (DDI_FAILURE);
+ }
+ kmem_free(slot_p->hs_info.cn_name,
+ strlen(slot_p->hs_info.cn_name) + 1);
+ kmem_free(slot_p, sizeof (pcie_hp_slot_t));
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pcishpc_enable_irqs()
+ *
+ * Enable/unmask the different IRQ's we support from the SHPC controller.
+ */
+static int
+pcishpc_enable_irqs(pcie_hp_ctrl_t *ctrl_p)
+{
+ uint32_t reg;
+ int slot;
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);
+
+ /* Enable all interrupts. */
+ reg &= ~PCI_HP_SERR_INT_MASK_ALL;
+
+ pcishpc_write_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG, reg);
+
+ /* Unmask the interrupts for each slot. */
+ for (slot = 0; slot < ctrl_p->hc_num_slots_impl; slot++) {
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot);
+ if ((reg & PCI_HP_SLOT_STATE_MASK) == PCI_HP_SLOT_ENABLED) {
+ reg &= ~(PCI_HP_SLOT_MASK_ALL |
+ PCI_HP_SLOT_MRL_SERR_MASK);
+ ctrl_p->hc_num_slots_connected++;
+ if (ctrl_p->hc_curr_bus_speed == -1)
+ ctrl_p->hc_curr_bus_speed =
+ pcishpc_read_reg(ctrl_p,
+ PCI_HP_PROF_IF_SBCR_REG) &
+ PCI_HP_SBCR_SPEED_MASK;
+ } else {
+ reg &= ~(PCI_HP_SLOT_MASK_ALL);
+ }
+
+ /* Enable/Unmask all slot interrupts. */
+ pcishpc_write_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot, reg);
+ }
+
+ PCIE_DBG("pcishpc_enable_irqs: ctrl_p 0x%p, "
+ "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p,
+ ctrl_p->hc_curr_bus_speed, ctrl_p->hc_num_slots_connected);
+
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * pcishpc_disable_irqs()
+ *
+ * Disable/Mask the different IRQ's we support from the SHPC controller.
+ */
+static int
+pcishpc_disable_irqs(pcie_hp_ctrl_t *ctrl_p)
+{
+ uint32_t reg;
+ int slot;
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);
+
+ /* Mask all interrupts. */
+ reg |= PCI_HP_SERR_INT_MASK_ALL;
+
+ pcishpc_write_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG, reg);
+
+ /* Unmask the interrupts for each slot. */
+ for (slot = 0; slot < ctrl_p->hc_num_slots_impl; slot++) {
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot);
+
+ /* Disable/Mask all slot interrupts. */
+ reg |= PCI_HP_SLOT_MASK_ALL;
+
+ pcishpc_write_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot, reg);
+ }
+
+ PCIE_DBG("pcishpc_disable_irqs: ctrl_p 0x%p, "
+ "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p,
+ ctrl_p->hc_curr_bus_speed, ctrl_p->hc_num_slots_connected);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pcishpc_slot_poweron()
+ *
+ * Poweron/Enable the slot.
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/*ARGSUSED*/
+static int
+pcishpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result_state)
+{
+ uint32_t status;
+
+ PCIE_DBG("pcishpc_slot_poweron called()\n");
+
+ ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ /* check if the slot is already in the 'enabled' state */
+ if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
+ /* slot is already in the 'enabled' state */
+ PCIE_DBG("pcishpc_slot_poweron() slot %d already enabled\n",
+ slot_p->hs_phy_slot_num);
+
+ *result_state = slot_p->hs_info.cn_state;
+ return (DDI_SUCCESS);
+ }
+
+ if (slot_p->hs_info.cn_state == DDI_HP_CN_STATE_EMPTY) {
+ PCIE_DBG("pcishpc_slot_poweron() slot in empty state\n");
+ goto cleanup;
+ }
+
+ /* make sure the MRL sensor is closed */
+ status = pcishpc_read_reg(slot_p->hs_ctrl,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
+
+ if (status & PCI_HP_SLOT_MRL_STATE_MASK) {
+ PCIE_DBG("pcishpc_slot_poweron() failed: MRL open\n");
+ goto cleanup;
+ }
+
+ /* Set the Power LED to blink */
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);
+
+ /* Turn all other LEDS off */
+ (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);
+
+ /* Set the bus speed only if the bus segment is not running */
+ if (pcishpc_set_bus_speed(slot_p) != DDI_SUCCESS) {
+ PCIE_DBG("pcishpc_slot_poweron() setting speed failed\n");
+ goto cleanup;
+ }
+
+ slot_p->hs_ctrl->hc_num_slots_connected++;
+
+ PCIE_DBG("pcishpc_slot_poweron(): slot_p 0x%p, slot state 0x%x, "
+ "current bus speed 0x%x, slots connected 0x%x\n", slot_p,
+ slot_p->hs_info.cn_state, slot_p->hs_ctrl->hc_curr_bus_speed,
+ slot_p->hs_ctrl->hc_num_slots_connected);
+
+ /* Mask or Unmask MRL Sensor SEER bit based on new slot state */
+ if (slot_p->hs_ctrl->hc_has_mrl == B_TRUE) {
+ uint32_t reg;
+
+ reg = pcishpc_read_reg(slot_p->hs_ctrl,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
+
+ pcishpc_write_reg(slot_p->hs_ctrl,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num,
+ reg & ~PCI_HP_SLOT_MRL_SERR_MASK);
+ }
+
+ /* Update the hardware slot state. */
+ if (pcishpc_set_slot_state(slot_p,
+ DDI_HP_CN_STATE_ENABLED) != DDI_SUCCESS) {
+ PCIE_DBG("pcishpc_slot_poweron() failed\n");
+
+ pcishpc_get_slot_state(slot_p);
+ goto cleanup;
+ }
+ /* Update the current state. It will be used in pcishpc_setled() */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_ENABLED;
+
+ /* Turn the Power LED ON for a enabled slot. */
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_ON);
+
+ /* Turn all other LEDS off. */
+ (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);
+
+ /* delay after powerON to let the device initialize itself */
+ delay(drv_usectohz(pcishpc_reset_delay));
+
+ PCIE_DBG("pcishpc_slot_poweron() success!\n");
+
+ /*
+ * Want to show up as POWERED state for now. It will be updated to
+ * ENABLED state when user explicitly enable the slot.
+ */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED;
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+ /*
+ * It should be poweron'ed now. Have a check here in case any
+ * hardware problems.
+ */
+ if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
+ PCIE_DBG("pcishpc_slot_poweron() failed after hardware"
+ " registers all programmed.\n");
+
+ goto cleanup;
+ }
+
+ *result_state = slot_p->hs_info.cn_state;
+
+ return (DDI_SUCCESS);
+
+cleanup:
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
+ return (DDI_FAILURE);
+}
+
+/*ARGSUSED*/
+static int
+pcishpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result_state)
+{
+ PCIE_DBG("pcishpc_slot_poweroff called()\n");
+
+ ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ /* check if the slot is not in the "enabled" or "powered" state */
+ if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
+ /* slot is in the 'disabled' state */
+ PCIE_DBG("pcishpc_slot_poweroff(): "
+ "slot %d already disabled\n", slot_p->hs_phy_slot_num);
+
+ *result_state = slot_p->hs_info.cn_state;
+ return (DDI_SUCCESS);
+ }
+
+ /* Set the Power LED to blink */
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);
+
+ /* Turn all other LEDS off */
+ (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);
+
+ if (--slot_p->hs_ctrl->hc_num_slots_connected == 0)
+ slot_p->hs_ctrl->hc_curr_bus_speed = -1;
+
+ PCIE_DBG("pcishpc_slot_poweroff(): slot_p 0x%p, slot state 0x%x, "
+ "current bus speed 0x%x, slots connected 0x%x\n", slot_p,
+ slot_p->hs_info.cn_state, slot_p->hs_ctrl->hc_curr_bus_speed,
+ slot_p->hs_ctrl->hc_num_slots_connected);
+
+ /* Mask or Unmask MRL Sensor SEER bit based on new slot state */
+ if (slot_p->hs_ctrl->hc_has_mrl == B_TRUE) {
+ uint32_t reg;
+
+ reg = pcishpc_read_reg(slot_p->hs_ctrl,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
+
+ pcishpc_write_reg(slot_p->hs_ctrl,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num,
+ reg | PCI_HP_SLOT_MRL_SERR_MASK);
+ }
+
+ /* Update the hardware slot state. */
+ if (pcishpc_set_slot_state(slot_p, DDI_HP_CN_STATE_PRESENT) !=
+ DDI_SUCCESS) {
+ PCIE_DBG("pcishpc_slot_poweroff() failed\n");
+
+ pcishpc_get_slot_state(slot_p);
+ goto cleanup;
+ }
+
+ /* Update the current state. It will be used in pcishpc_setled() */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_PRESENT;
+
+ /* Turn the Power LED OFF for a disabled slot. */
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
+
+ /* Turn all other LEDS off. */
+ (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
+ (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);
+
+ /* delay after powerON to let the device initialize itself */
+ delay(drv_usectohz(pcishpc_reset_delay));
+
+ pcishpc_get_slot_state(slot_p);
+ /*
+ * It should be poweroff'ed now. Have a check here in case any
+ * hardware problems.
+ */
+ if (slot_p->hs_info.cn_state > DDI_HP_CN_STATE_PRESENT) {
+ PCIE_DBG("pcishpc_slot_poweroff() failed after hardware"
+ " registers all programmed.\n");
+
+ goto cleanup;
+ }
+
+ PCIE_DBG("pcishpc_slot_poweroff() success!\n");
+
+ *result_state = slot_p->hs_info.cn_state;
+ return (DDI_SUCCESS);
+
+cleanup:
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
+ return (DDI_FAILURE);
+}
+
+/*
+ * pcishpc_slot_probe()
+ *
+ * Probe the slot.
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/*ARGSUSED*/
+static int
+pcishpc_slot_probe(pcie_hp_slot_t *slot_p)
+{
+ mutex_enter(&slot_p->hs_ctrl->hc_mutex);
+
+ PCIE_DBG("pcishpc_slot_probe called()\n");
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ /*
+ * Probe a given PCI Hotplug Connection (CN).
+ */
+ if (pcie_hp_probe(slot_p) != DDI_SUCCESS) {
+ (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED,
+ PCIE_HP_LED_BLINK);
+
+ PCIE_DBG("pcishpc_slot_probe() failed\n");
+
+ mutex_exit(&slot_p->hs_ctrl->hc_mutex);
+ return (DDI_FAILURE);
+ }
+
+ PCIE_DBG("pcishpc_slot_probe() success!\n");
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ mutex_exit(&slot_p->hs_ctrl->hc_mutex);
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pcishpc_slot_unprobe()
+ *
+ * Unprobe the slot.
+ *
+ * Note: This function is called by DDI HP framework at kernel context only
+ */
+/*ARGSUSED*/
+static int
+pcishpc_slot_unprobe(pcie_hp_slot_t *slot_p)
+{
+ mutex_enter(&slot_p->hs_ctrl->hc_mutex);
+
+ PCIE_DBG("pcishpc_slot_unprobe called()\n");
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ /*
+ * Unprobe a given PCI Hotplug Connection (CN).
+ */
+ if (pcie_hp_unprobe(slot_p) != DDI_SUCCESS) {
+ (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED,
+ PCIE_HP_LED_BLINK);
+
+ PCIE_DBG("pcishpc_slot_unprobe() failed\n");
+
+ mutex_exit(&slot_p->hs_ctrl->hc_mutex);
+ return (DDI_FAILURE);
+ }
+
+ PCIE_DBG("pcishpc_slot_unprobe() success!\n");
+
+ /* get the current slot state */
+ pcishpc_get_slot_state(slot_p);
+
+ mutex_exit(&slot_p->hs_ctrl->hc_mutex);
+ return (DDI_SUCCESS);
+}
+
+static int
+pcishpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state;
+ int rv = DDI_SUCCESS;
+
+ if (target_state > DDI_HP_CN_STATE_ENABLED) {
+ return (DDI_EINVAL);
+ }
+
+ curr_state = slot_p->hs_info.cn_state;
+ while ((curr_state < target_state) && (rv == DDI_SUCCESS)) {
+
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_EMPTY:
+ /*
+ * From EMPTY to PRESENT, just check the hardware
+ * slot state.
+ */
+ pcishpc_get_slot_state(slot_p);
+ curr_state = slot_p->hs_info.cn_state;
+ if (curr_state < DDI_HP_CN_STATE_PRESENT)
+ rv = DDI_FAILURE;
+ break;
+ case DDI_HP_CN_STATE_PRESENT:
+ rv = pcishpc_slot_poweron(slot_p, &curr_state);
+ break;
+ case DDI_HP_CN_STATE_POWERED:
+ curr_state = slot_p->hs_info.cn_state =
+ DDI_HP_CN_STATE_ENABLED;
+ break;
+ default:
+ /* should never reach here */
+ ASSERT("unknown devinfo state");
+ }
+ }
+
+ return (rv);
+}
+
+static int
+pcishpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state;
+ int rv = DDI_SUCCESS;
+
+
+ curr_state = slot_p->hs_info.cn_state;
+ while ((curr_state > target_state) && (rv == DDI_SUCCESS)) {
+
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_PRESENT:
+ /*
+ * From PRESENT to EMPTY, just check hardware
+ * slot state.
+ */
+ pcishpc_get_slot_state(slot_p);
+ curr_state = slot_p->hs_info.cn_state;
+ if (curr_state >= DDI_HP_CN_STATE_PRESENT)
+ rv = DDI_FAILURE;
+ break;
+ case DDI_HP_CN_STATE_POWERED:
+ rv = pcishpc_slot_poweroff(slot_p, &curr_state);
+
+ break;
+ case DDI_HP_CN_STATE_ENABLED:
+ curr_state = slot_p->hs_info.cn_state =
+ DDI_HP_CN_STATE_POWERED;
+
+ break;
+ default:
+ /* should never reach here */
+ ASSERT("unknown devinfo state");
+ }
+ }
+
+ return (rv);
+}
+
+/* Change slot state to a target state */
+static int
+pcishpc_change_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state;
+ int rv;
+
+ pcishpc_get_slot_state(slot_p);
+ curr_state = slot_p->hs_info.cn_state;
+
+ if (curr_state == target_state) {
+ return (DDI_SUCCESS);
+ }
+ if (curr_state < target_state) {
+
+ rv = pcishpc_upgrade_slot_state(slot_p, target_state);
+ } else {
+ rv = pcishpc_downgrade_slot_state(slot_p, target_state);
+ }
+
+ return (rv);
+}
+
+/*
+ * pcishpc_issue_command()
+ *
+ * Sends a command to the SHPC controller.
+ */
+static int
+pcishpc_issue_command(pcie_hp_ctrl_t *ctrl_p, uint32_t cmd_code)
+{
+ int retCode;
+
+ ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
+
+ PCIE_DBG("pcishpc_issue_command() cmd_code=%02x\n", cmd_code);
+
+ ctrl_p->hc_cmd_pending = B_TRUE;
+
+ /* Write the command to the SHPC controller. */
+ pcishpc_write_reg(ctrl_p, PCI_HP_COMMAND_STATUS_REG, cmd_code);
+
+ while (ctrl_p->hc_cmd_pending == B_TRUE)
+ cv_wait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex);
+
+ /* Wait until the SHPC controller processes the command. */
+ retCode = pcishpc_wait_busy(ctrl_p);
+
+ /* Make sure the command completed. */
+ if (retCode == DDI_SUCCESS) {
+ /* Did the command fail to generate the command complete IRQ? */
+ if (ctrl_p->hc_cmd_pending != B_FALSE) {
+ PCIE_DBG("pcishpc_issue_command() Failed on "
+ "generate cmd complete IRQ\n");
+ retCode = DDI_FAILURE;
+ }
+ }
+
+ if (retCode == DDI_FAILURE)
+ PCIE_DBG("pcishpc_issue_command() Failed on cmd_code=%02x\n",
+ cmd_code);
+ else
+ PCIE_DBG("pcishpc_issue_command() Success on "
+ "cmd_code=%02x\n", cmd_code);
+
+ return (retCode);
+}
+
+/*
+ * pcishpc_wait_busy()
+ *
+ * Wait until the SHPC controller is not busy.
+ */
+static int
+pcishpc_wait_busy(pcie_hp_ctrl_t *ctrl_p)
+{
+ uint32_t status;
+
+ /* Wait until SHPC controller is NOT busy */
+ for (;;) {
+ status = pcishpc_read_reg(ctrl_p, PCI_HP_COMMAND_STATUS_REG);
+
+ /* Is there an MRL Sensor error? */
+ if ((status & PCI_HP_COMM_STS_ERR_MASK) ==
+ PCI_HP_COMM_STS_ERR_MRL_OPEN) {
+ PCIE_DBG("pcishpc_wait_busy() ERROR: "
+ "MRL Sensor error\n");
+ break;
+ }
+
+ /* Is there an Invalid command error? */
+ if ((status & PCI_HP_COMM_STS_ERR_MASK) ==
+ PCI_HP_COMM_STS_ERR_INVALID_COMMAND) {
+ PCIE_DBG("pcishpc_wait_busy() ERROR: Invalid "
+ "command error\n");
+ break;
+ }
+
+ /* Is there an Invalid Speed/Mode error? */
+ if ((status & PCI_HP_COMM_STS_ERR_MASK) ==
+ PCI_HP_COMM_STS_ERR_INVALID_SPEED) {
+ PCIE_DBG("pcishpc_wait_busy() ERROR: Invalid "
+ "Speed/Mode error\n");
+ break;
+ }
+
+ /* Is the SHPC controller not BUSY? */
+ if (!(status & PCI_HP_COMM_STS_CTRL_BUSY)) {
+ /* Return Success. */
+ return (DDI_SUCCESS);
+ }
+
+ PCIE_DBG("pcishpc_wait_busy() SHPC controller busy. Waiting\n");
+
+ /* Wait before polling the status register again. */
+ delay(drv_usectohz(PCIE_HP_CMD_WAIT_TIME));
+ }
+
+ return (DDI_FAILURE);
+}
+
+static void
+pcishpc_attn_btn_handler(pcie_hp_slot_t *slot_p)
+{
+ pcie_hp_led_state_t hs_power_led_state;
+ callb_cpr_t cprinfo;
+
+ PCIE_DBG("pcishpc_attn_btn_handler: thread started\n");
+
+ CALLB_CPR_INIT(&cprinfo, &slot_p->hs_ctrl->hc_mutex,
+ callb_generic_cpr, "pcishpc_attn_btn_handler");
+
+ mutex_enter(&slot_p->hs_ctrl->hc_mutex);
+
+ /* wait for ATTN button event */
+ cv_wait(&slot_p->hs_attn_btn_cv, &slot_p->hs_ctrl->hc_mutex);
+
+ while (slot_p->hs_attn_btn_thread_exit == B_FALSE) {
+ if (slot_p->hs_attn_btn_pending == B_TRUE) {
+ /* get the current state of power LED */
+ hs_power_led_state = slot_p->hs_power_led_state;
+
+ /* Blink the Power LED while we wait for 5 seconds */
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED,
+ PCIE_HP_LED_BLINK);
+
+ /* wait for 5 seconds before taking any action */
+ if (cv_timedwait(&slot_p->hs_attn_btn_cv,
+ &slot_p->hs_ctrl->hc_mutex,
+ ddi_get_lbolt() + SEC_TO_TICK(5)) == -1) {
+ /*
+ * It is a time out;
+ * make sure the ATTN pending flag is
+ * still ON before sending the event
+ * to DDI HP framework.
+ */
+ if (slot_p->hs_attn_btn_pending == B_TRUE) {
+ int hint;
+
+ /* restore the power LED state */
+ (void) pcishpc_setled(slot_p,
+ PCIE_HP_POWER_LED,
+ hs_power_led_state);
+ /*
+ * send the ATTN button event
+ * to DDI HP framework
+ */
+ slot_p->hs_attn_btn_pending = B_FALSE;
+
+ pcishpc_get_slot_state(slot_p);
+
+ if (slot_p->hs_info.cn_state <=
+ DDI_HP_CN_STATE_PRESENT) {
+ /*
+ * Insertion.
+ */
+ hint = SE_INCOMING_RES;
+ } else {
+ /*
+ * Want to remove;
+ */
+ hint = SE_OUTGOING_RES;
+ }
+ pcie_hp_gen_sysevent_req(
+ slot_p->hs_info.cn_name,
+ hint,
+ slot_p->hs_ctrl->hc_dip,
+ KM_SLEEP);
+
+ continue;
+ }
+ }
+
+ /* restore the power LED state */
+ (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED,
+ hs_power_led_state);
+ continue;
+ }
+
+ /* wait for another ATTN button event */
+ cv_wait(&slot_p->hs_attn_btn_cv, &slot_p->hs_ctrl->hc_mutex);
+ }
+
+ PCIE_DBG("pcishpc_attn_btn_handler: thread exit\n");
+ cv_signal(&slot_p->hs_attn_btn_cv);
+ CALLB_CPR_EXIT(&cprinfo);
+ thread_exit();
+}
+
+/*
+ * pcishpc_get_slot_state()
+ *
+ * Get the state of the slot.
+ * The slot state should have been initialized before this function gets called.
+ */
+static void
+pcishpc_get_slot_state(pcie_hp_slot_t *slot_p)
+{
+ uint32_t reg;
+ ddi_hp_cn_state_t curr_state = slot_p->hs_info.cn_state;
+
+ /* Read the logical slot register for this Slot. */
+ reg = pcishpc_read_reg(slot_p->hs_ctrl,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
+
+ /* Convert from the SHPC slot state to the HPC slot state. */
+ slot_p->hs_info.cn_state = pcishpc_slot_shpc_to_hpc(reg);
+ if (curr_state == DDI_HP_CN_STATE_POWERED &&
+ slot_p->hs_info.cn_state > DDI_HP_CN_STATE_POWERED) {
+ /*
+ * Keep POWERED state if it is currently POWERED state because
+ * this driver does not really implement enable/disable
+ * slot operations. That is, when poweron, it actually enables
+ * the slot also.
+ * So, from hardware view, POWERED == ENABLED.
+ * But, when user explicitly change to POWERED state, it should
+ * be kept until user explicitly change to other states later.
+ */
+ slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED;
+ }
+
+ /* Convert from the SHPC Power LED state to the HPC Power LED state. */
+ slot_p->hs_power_led_state = pcishpc_led_shpc_to_hpc((reg>>2)&3);
+
+ /* Convert from the SHPC Attn LED state to the HPC Attn LED state. */
+ slot_p->hs_attn_led_state = pcishpc_led_shpc_to_hpc((reg>>4)&3);
+
+ /* We don't have a fault LED so just default it to OFF. */
+ slot_p->hs_fault_led_state = PCIE_HP_LED_OFF;
+
+ /* We don't have an active LED so just default it to OFF. */
+ slot_p->hs_active_led_state = PCIE_HP_LED_OFF;
+}
+
+/*
+ * pcishpc_set_slot_state()
+ *
+ * Updates the slot's state and leds.
+ */
+static int
+pcishpc_set_slot_state(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t new_slot_state)
+{
+ uint32_t reg, cmd_code;
+ ddi_hp_cn_state_t curr_state;
+
+ ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
+
+ reg = pcishpc_read_reg(slot_p->hs_ctrl,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
+
+ /* Default all states to unchanged. */
+ cmd_code = ((1 + slot_p->hs_num) << 8);
+
+ /* Has the slot state changed? */
+ curr_state = pcishpc_slot_shpc_to_hpc(reg);
+ if (curr_state != new_slot_state) {
+ PCIE_DBG("pcishpc_set_slot_state() Slot State changed");
+
+ /* Set the new slot state in the Slot operation command. */
+ cmd_code |= pcishpc_slot_hpc_to_shpc(new_slot_state);
+ }
+
+ /* Has the Power LED state changed? */
+ if (slot_p->hs_power_led_state != pcishpc_led_shpc_to_hpc((reg>>2)&3)) {
+ PCIE_DBG("pcishpc_set_slot_state() Power LED State changed\n");
+
+ /* Set the new power led state in the Slot operation command. */
+ cmd_code |=
+ (pcishpc_led_hpc_to_shpc(slot_p->hs_power_led_state) << 2);
+ }
+
+ /* Has the Attn LED state changed? */
+ if (slot_p->hs_attn_led_state != pcishpc_led_shpc_to_hpc((reg>>4)&3)) {
+ PCIE_DBG("pcishpc_set_slot_state() Attn LED State changed\n");
+
+ /* Set the new attn led state in the Slot operation command. */
+ cmd_code |=
+ (pcishpc_led_hpc_to_shpc(slot_p->hs_attn_led_state) << 4);
+ }
+
+ return (pcishpc_issue_command(slot_p->hs_ctrl, cmd_code));
+}
+
+/*
+ * setup slot name/slot-number info.
+ */
+static void
+pcishpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p, int slot)
+{
+ pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[slot];
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+ uchar_t *slotname_data;
+ int *slotnum;
+ uint_t count;
+ int len;
+ uchar_t *s;
+ uint32_t bit_mask;
+ int pci_id_cnt, pci_id_bit;
+ int slots_before, found;
+ int invalid_slotnum = 0;
+
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->hc_dip,
+ DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) ==
+ DDI_PROP_SUCCESS) {
+ slot_p->hs_phy_slot_num = slotnum[0];
+ ddi_prop_free(slotnum);
+ } else {
+ if (ctrl_p->hc_device_increases)
+ slot_p->hs_phy_slot_num = ctrl_p->hc_phys_start + slot;
+ else
+ slot_p->hs_phy_slot_num = ctrl_p->hc_phys_start - slot;
+
+ if ((ndi_prop_update_int(DDI_DEV_T_NONE, ctrl_p->hc_dip,
+ "physical-slot#", slot_p->hs_phy_slot_num)) != DDI_SUCCESS)
+ PCIE_DBG("pcishpc_set_slot_name(): failed to "
+ "create phyical-slot#%d\n",
+ slot_p->hs_phy_slot_num);
+ }
+
+ /* Platform may not have initialized it */
+ if (!slot_p->hs_phy_slot_num) {
+ slot_p->hs_phy_slot_num = pci_config_get8(bus_p->bus_cfg_hdl,
+ PCI_BCNF_SECBUS);
+ invalid_slotnum = 1;
+ }
+ slot_p->hs_info.cn_num = slot_p->hs_phy_slot_num;
+ slot_p->hs_info.cn_num_dpd_on = DDI_HP_CN_NUM_NONE;
+
+ /*
+ * construct the slot_name:
+ * if "slot-names" property exists then use that name
+ * else if valid slot number exists then it is "pci<slot-num>".
+ * else it will be "pci<sec-bus-number>dev<dev-number>"
+ */
+ if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->hc_dip, DDI_PROP_DONTPASS,
+ "slot-names", (caddr_t)&slotname_data, &len) == DDI_PROP_SUCCESS) {
+ bit_mask = slotname_data[3] | (slotname_data[2] << 8) |
+ (slotname_data[1] << 16) | (slotname_data[0] << 24);
+
+ pci_id_bit = 1;
+ pci_id_cnt = slots_before = found = 0;
+
+ /*
+ * Walk the bit mask until we find the bit that corresponds
+ * to our slots device number. We count how many bits
+ * we find before we find our slot's bit.
+ */
+ while (!found && (pci_id_cnt < 32)) {
+ while (slot_p->hs_device_num != pci_id_cnt) {
+
+ /*
+ * Find the next bit set.
+ */
+ while (!(bit_mask & pci_id_bit) &&
+ (pci_id_cnt < 32)) {
+ pci_id_bit = pci_id_bit << 1;
+ pci_id_cnt++;
+ }
+
+ if (slot_p->hs_device_num != pci_id_cnt)
+ slots_before++;
+ else
+ found = 1;
+ }
+ }
+
+ if (pci_id_cnt < 32) {
+
+ /*
+ * Set ptr to first string.
+ */
+ s = slotname_data + 4;
+
+ /*
+ * Increment past all the strings for the slots
+ * before ours.
+ */
+ while (slots_before) {
+ while (*s != NULL)
+ s++;
+ s++;
+ slots_before--;
+ }
+
+ slot_p->hs_info.cn_name = i_ddi_strdup((char *)s,
+ KM_SLEEP);
+ kmem_free(slotname_data, len);
+ return;
+ }
+
+ /* slot-names entry not found */
+ PCIE_DBG("pcishpc_set_slot_name(): "
+ "No slot-names entry found for slot #%d\n",
+ slot_p->hs_phy_slot_num);
+ kmem_free(slotname_data, len);
+ }
+
+ if (invalid_slotnum) {
+ char tmp_name[256];
+
+ (void) snprintf(tmp_name, sizeof (tmp_name), "pci%d",
+ slot_p->hs_device_num);
+ slot_p->hs_info.cn_name = i_ddi_strdup(tmp_name, KM_SLEEP);
+ } else {
+ char tmp_name[256];
+
+ (void) snprintf(tmp_name, sizeof (tmp_name), "pci%d",
+ slot_p->hs_phy_slot_num);
+ slot_p->hs_info.cn_name = i_ddi_strdup(tmp_name, KM_SLEEP);
+ }
+}
+
+/*
+ * pcishpc_set_bus_speed()
+ *
+ * Set the bus speed and mode.
+ */
+static int
+pcishpc_set_bus_speed(pcie_hp_slot_t *slot_p)
+{
+ pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
+ int curr_speed = ctrl_p->hc_curr_bus_speed;
+ int speed = -1;
+ int avail_slots;
+ uint32_t status, slots_avail1_reg, slots_avail2_reg;
+
+ ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
+
+ /* Make sure that the slot is in a correct state */
+ status = pcishpc_read_reg(ctrl_p,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
+
+ /* Return failure if the slot is empty */
+ if ((status & PCI_HP_SLOT_CARD_EMPTY_MASK) ==
+ PCI_HP_SLOT_CARD_EMPTY_MASK) {
+ PCIE_DBG("pcishpc_set_bus_speed() failed: "
+ "the slot is empty\n");
+ return (DDI_FAILURE);
+ }
+
+ /* Return failure if the slot is not in disabled state */
+ if ((status & PCI_HP_SLOT_STATE_MASK) != PCI_HP_SLOT_DISABLED) {
+ PCIE_DBG("pcishpc_set_bus_speed() failed: "
+ "incorrect slot state\n");
+ return (DDI_FAILURE);
+ }
+
+ /* Set the "power-only" mode for the slot */
+ if (pcishpc_issue_command(ctrl_p, ((1+slot_p->hs_num)<<8) |
+ PCI_HP_SLOT_POWER_ONLY) != DDI_SUCCESS) {
+ PCIE_DBG("pcishpc_set_bus_speed() failed to set "
+ "the slot %d in the power-only mode\n", slot_p->hs_num);
+ return (DDI_FAILURE);
+ }
+
+ /* Wait for power good */
+ delay(drv_usectohz(PCIE_HP_POWER_GOOD_WAIT_TIME));
+
+ /* Make sure that the slot is in "power-only" state */
+ status = pcishpc_read_reg(ctrl_p,
+ PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
+
+ if ((status & PCI_HP_SLOT_STATE_MASK) != PCI_HP_SLOT_POWER_ONLY) {
+ PCIE_DBG("pcishpc_set_bus_speed() "
+ "power-only failed: incorrect slot state\n");
+ return (DDI_FAILURE);
+ }
+
+ slots_avail1_reg = pcishpc_read_reg(ctrl_p,
+ PCI_HP_SLOTS_AVAIL_I_REG);
+ slots_avail2_reg = pcishpc_read_reg(ctrl_p,
+ PCI_HP_SLOTS_AVAIL_II_REG);
+
+ /*
+ * Check if SHPC has available slots and select the highest
+ * available bus speed for the slot.
+ *
+ * The bus speed codes are:
+ * 100 - 133Mhz; <--+
+ * 011 - 100Mhz; <--+ PCI-X
+ * 010 - 66Mhz; <--+
+ *
+ * 001 - 66Mhz; <--+
+ * 000 - 33Mhz <--+ Conv PCI
+ */
+ switch (status & PCI_HP_SLOT_PCIX_CAPABLE_MASK) {
+ case PCI_HP_SLOT_133MHZ_PCIX_CAPABLE:
+ avail_slots = (slots_avail1_reg >>
+ PCI_HP_AVAIL_133MHZ_PCIX_SPEED_SHIFT) &
+ PCI_HP_AVAIL_SPEED_MASK;
+
+ if (((curr_speed == -1) && avail_slots) ||
+ (curr_speed == PCI_HP_SBCR_133MHZ_PCIX_SPEED)) {
+ speed = PCI_HP_SBCR_133MHZ_PCIX_SPEED;
+ break;
+ }
+ /* FALLTHROUGH */
+ case PCI_HP_SLOT_100MHZ_PCIX_CAPABLE:
+ avail_slots = (slots_avail1_reg >>
+ PCI_HP_AVAIL_100MHZ_PCIX_SPEED_SHIFT) &
+ PCI_HP_AVAIL_SPEED_MASK;
+
+ if (((curr_speed == -1) && avail_slots) ||
+ (curr_speed == PCI_HP_SBCR_100MHZ_PCIX_SPEED)) {
+ speed = PCI_HP_SBCR_100MHZ_PCIX_SPEED;
+ break;
+ }
+ /* FALLTHROUGH */
+ case PCI_HP_SLOT_66MHZ_PCIX_CAPABLE:
+ avail_slots = (slots_avail1_reg >>
+ PCI_HP_AVAIL_66MHZ_PCIX_SPEED_SHIFT) &
+ PCI_HP_AVAIL_SPEED_MASK;
+
+ if (((curr_speed == -1) && avail_slots) ||
+ (curr_speed == PCI_HP_SBCR_66MHZ_PCIX_SPEED)) {
+ speed = PCI_HP_SBCR_66MHZ_PCIX_SPEED;
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ avail_slots = (slots_avail2_reg >>
+ PCI_HP_AVAIL_66MHZ_CONV_SPEED_SHIFT) &
+ PCI_HP_AVAIL_SPEED_MASK;
+
+ if ((status & PCI_HP_SLOT_66MHZ_CONV_CAPABLE) &&
+ (((curr_speed == -1) && avail_slots) ||
+ (curr_speed == PCI_HP_SBCR_66MHZ_CONV_SPEED))) {
+ speed = PCI_HP_SBCR_66MHZ_CONV_SPEED;
+ } else {
+ avail_slots = (slots_avail1_reg >>
+ PCI_HP_AVAIL_33MHZ_CONV_SPEED_SHIFT) &
+ PCI_HP_AVAIL_SPEED_MASK;
+
+ if (((curr_speed == -1) && (avail_slots)) ||
+ (curr_speed == PCI_HP_SBCR_33MHZ_CONV_SPEED)) {
+ speed = PCI_HP_SBCR_33MHZ_CONV_SPEED;
+ } else {
+ PCIE_DBG("pcishpc_set_bus_speed() "
+ " failed to set the bus speed, slot# %d\n",
+ slot_p->hs_num);
+ return (DDI_FAILURE);
+ }
+ }
+ break;
+ }
+
+ /*
+ * If the bus segment is already running, check to see the card
+ * in the slot can support the current bus speed.
+ */
+ if (curr_speed == speed) {
+ /*
+ * Check to see there is any slot available for the current
+ * bus speed. Otherwise, we need fail the current slot connect
+ * request.
+ */
+ return ((avail_slots <= ctrl_p->hc_num_slots_connected) ?
+ DDI_FAILURE : DDI_SUCCESS);
+ }
+
+ /* Set the bus speed */
+ if (pcishpc_issue_command(ctrl_p, PCI_HP_COMM_STS_SET_SPEED |
+ speed) == DDI_FAILURE) {
+ PCIE_DBG("pcishpc_set_bus_speed() failed "
+ "to set bus %d speed\n", slot_p->hs_num);
+ return (DDI_FAILURE);
+ }
+
+ /* Check the current bus speed */
+ status = pcishpc_read_reg(ctrl_p, PCI_HP_PROF_IF_SBCR_REG) &
+ PCI_HP_SBCR_SPEED_MASK;
+ if ((status & PCI_HP_SBCR_SPEED_MASK) != speed) {
+ PCIE_DBG("pcishpc_set_bus_speed() an incorrect "
+ "bus speed, slot = 0x%x, speed = 0x%x\n",
+ slot_p->hs_num, status & PCI_HP_SBCR_SPEED_MASK);
+ return (DDI_FAILURE);
+ }
+
+
+ /* Save the current bus speed */
+ ctrl_p->hc_curr_bus_speed = speed;
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pcishpc_setled()
+ *
+ * Change the state of a slot's LED.
+ */
+static int
+pcishpc_setled(pcie_hp_slot_t *slot_p, pcie_hp_led_t led,
+ pcie_hp_led_state_t state)
+{
+ ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
+
+ switch (led) {
+ case PCIE_HP_FAULT_LED:
+ PCIE_DBG("pcishpc_setled() - PCIE_HP_FAULT_LED "
+ "(set %s)\n", pcishpc_slot_textledstate(state));
+ slot_p->hs_fault_led_state = state;
+ break;
+
+ case PCIE_HP_POWER_LED:
+ PCIE_DBG("pcishpc_setled() - PCIE_HP_POWER_LED "
+ "(set %s)\n", pcishpc_slot_textledstate(state));
+ slot_p->hs_power_led_state = state;
+ break;
+
+ case PCIE_HP_ATTN_LED:
+ PCIE_DBG("pcishpc_setled() - PCIE_HP_ATTN_LED "
+ "(set %s)\n", pcishpc_slot_textledstate(state));
+ slot_p->hs_attn_led_state = state;
+ break;
+
+ case PCIE_HP_ACTIVE_LED:
+ PCIE_DBG("pcishpc_setled() - PCIE_HP_ACTIVE_LED "
+ "(set %s)\n", pcishpc_slot_textledstate(state));
+ slot_p->hs_active_led_state = state;
+ break;
+ }
+
+ return (pcishpc_set_slot_state(slot_p, slot_p->hs_info.cn_state));
+}
+
+/*
+ * pcishpc_led_shpc_to_hpc()
+ *
+ * Convert from SHPC indicator status to HPC indicator status.
+ */
+static int
+pcishpc_led_shpc_to_hpc(int state)
+{
+ switch (state) {
+ case 1: /* SHPC On bits b01 */
+ return (PCIE_HP_LED_ON);
+ case 2: /* SHPC Blink bits b10 */
+ return (PCIE_HP_LED_BLINK);
+ case 3: /* SHPC Off bits b11 */
+ return (PCIE_HP_LED_OFF);
+ }
+
+ return (PCIE_HP_LED_OFF);
+}
+
+
+/*
+ * pcishpc_led_hpc_to_shpc()
+ *
+ * Convert from HPC indicator status to SHPC indicator status.
+ */
+static int
+pcishpc_led_hpc_to_shpc(int state)
+{
+ switch (state) {
+ case PCIE_HP_LED_ON:
+ return (1); /* SHPC On bits b01 */
+ case PCIE_HP_LED_BLINK:
+ return (2); /* SHPC Blink bits b10 */
+ case PCIE_HP_LED_OFF:
+ return (3); /* SHPC Off bits b11 */
+ }
+
+ return (3); /* SHPC Off bits b11 */
+}
+
+/*
+ * pcishpc_slot_shpc_to_hpc()
+ *
+ * Convert from SHPC slot state to HPC slot state.
+ * The argument shpc_state is expected to be read from the slot register.
+ */
+static int
+pcishpc_slot_shpc_to_hpc(int shpc_state)
+{
+ if ((shpc_state & PCI_HP_SLOT_CARD_EMPTY_MASK) ==
+ PCI_HP_SLOT_CARD_EMPTY_MASK)
+ return (DDI_HP_CN_STATE_EMPTY);
+
+ switch (shpc_state & PCI_HP_SLOT_STATE_MASK) {
+ case PCI_HP_SLOT_POWER_ONLY: /* SHPC Powered Only */
+ return (DDI_HP_CN_STATE_POWERED);
+
+ case PCI_HP_SLOT_ENABLED: /* SHPC Enabled */
+ return (DDI_HP_CN_STATE_ENABLED);
+
+ case PCI_HP_SLOT_DISABLED: /* SHPC Disabled */
+ default : /* SHPC Reserved */
+ return (DDI_HP_CN_STATE_PRESENT);
+ }
+}
+
+/*
+ * pcishpc_slot_hpc_to_shpc()
+ *
+ * Convert from HPC slot state to SHPC slot state.
+ */
+static int
+pcishpc_slot_hpc_to_shpc(int state)
+{
+ switch (state) {
+ case DDI_HP_CN_STATE_EMPTY:
+ return (0);
+
+ case DDI_HP_CN_STATE_POWERED:
+ return (PCI_HP_SLOT_POWER_ONLY);
+
+ case DDI_HP_CN_STATE_ENABLED:
+ return (PCI_HP_SLOT_ENABLED);
+
+ default:
+ return (PCI_HP_SLOT_DISABLED);
+ }
+}
+
+/*
+ * pcishpc_slot_textslotstate()
+ *
+ * Convert the request into a text message.
+ */
+static char *
+pcishpc_slot_textslotstate(ddi_hp_cn_state_t state)
+{
+ /* Convert an HPC slot state into a textual string. */
+ if (state == DDI_HP_CN_STATE_EMPTY)
+ return ("HPC_SLOT_EMPTY");
+ else if (state == DDI_HP_CN_STATE_ENABLED)
+ return ("HPC_SLOT_ENABLED");
+ else if (state == DDI_HP_CN_STATE_POWERED)
+ return ("HPC_SLOT_POWERED_ONLY");
+ else
+ return ("HPC_SLOT_DISABLED");
+}
+
+
+/*
+ * pcishpc_slot_textledstate()
+ *
+ * Convert the led state into a text message.
+ */
+static char *
+pcishpc_slot_textledstate(pcie_hp_led_state_t state)
+{
+ /* Convert an HPC led state into a textual string. */
+ switch (state) {
+ case PCIE_HP_LED_OFF:
+ return ("off");
+
+ case PCIE_HP_LED_ON:
+ return ("on");
+
+ case PCIE_HP_LED_BLINK:
+ return ("blink");
+ }
+ return ("unknown");
+}
+
+
+/*
+ * pcishpc_read_reg()
+ *
+ * Read from a SHPC controller register.
+ */
+static uint32_t
+pcishpc_read_reg(pcie_hp_ctrl_t *ctrl_p, int reg)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ /* Setup the SHPC dword select register. */
+ pci_config_put8(bus_p->bus_cfg_hdl,
+ bus_p->bus_pci_hp_off + PCI_HP_DWORD_SELECT_OFF, (uint8_t)reg);
+
+ /* Read back the SHPC dword select register and verify. */
+ if (pci_config_get8(bus_p->bus_cfg_hdl, bus_p->bus_pci_hp_off +
+ PCI_HP_DWORD_SELECT_OFF) != (uint8_t)reg) {
+ PCIE_DBG("pcishpc_read_reg() - Failed writing DWORD "
+ "select reg\n");
+ return (0xFFFFFFFF);
+ }
+
+ /* Read from the SHPC dword data register. */
+ return (pci_config_get32(bus_p->bus_cfg_hdl,
+ bus_p->bus_pci_hp_off + PCI_HP_DWORD_DATA_OFF));
+}
+
+
+/*
+ * pcishpc_write_reg()
+ *
+ * Write to a SHPC controller register.
+ */
+static void
+pcishpc_write_reg(pcie_hp_ctrl_t *ctrl_p, int reg, uint32_t data)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
+
+ /* Setup the SHPC dword select register. */
+ pci_config_put8(bus_p->bus_cfg_hdl,
+ bus_p->bus_pci_hp_off + PCI_HP_DWORD_SELECT_OFF, (uint8_t)reg);
+
+ /* Read back the SHPC dword select register and verify. */
+ if (pci_config_get8(bus_p->bus_cfg_hdl, bus_p->bus_pci_hp_off +
+ PCI_HP_DWORD_SELECT_OFF) != (uint8_t)reg) {
+ PCIE_DBG("pcishpc_write_reg() - Failed writing "
+ "DWORD select reg\n");
+ return;
+ }
+
+ /* Write to the SHPC dword data register. */
+ pci_config_put32(bus_p->bus_cfg_hdl,
+ bus_p->bus_pci_hp_off + PCI_HP_DWORD_DATA_OFF, data);
+
+ /*
+ * Issue a read of the VendorID/DeviceID just to force the previous
+ * write to complete. This is probably not necessary, but it does
+ * help enforce ordering if there is an issue.
+ */
+ (void) pci_config_get16(bus_p->bus_cfg_hdl, PCI_CONF_VENID);
+}
+
+
+#ifdef DEBUG
+/*
+ * pcishpc_dump_regs()
+ *
+ * Dumps all of the SHPC controller registers.
+ */
+static void
+pcishpc_dump_regs(pcie_hp_ctrl_t *ctrl_p)
+{
+ int slot, numSlots;
+ uint32_t reg;
+ char *state;
+
+ if (!pcie_debug_flags)
+ return;
+
+ PCIE_DBG("pcishpc_dump_regs() called:\n");
+ PCIE_DBG("==========================================================");
+
+ PCIE_DBG("SHPC Base Offset "
+ ": 0x%08x\n", pcishpc_read_reg(ctrl_p, PCI_HP_BASE_OFFSET_REG));
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_SLOTS_AVAIL_I_REG);
+
+ PCIE_DBG("Number of PCIX slots avail (33 Mhz) : %d\n",
+ (reg & 31));
+
+ PCIE_DBG("Number of PCIX slots avail (66 Mhz) : %d\n",
+ ((reg>>8) & 31));
+
+ PCIE_DBG("Number of PCIX slots avail (100 Mhz) : %d\n",
+ ((reg>>16) & 31));
+
+ PCIE_DBG("Number of PCIX slots avail (133 Mhz) : %d\n",
+ ((reg>>24) & 31));
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_SLOTS_AVAIL_II_REG);
+
+ PCIE_DBG("Number of conventional PCI slots (66 Mhz) : %d\n",
+ (reg & 31));
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_SLOT_CONFIGURATION_REG);
+
+ numSlots = (reg & 31);
+
+ PCIE_DBG("Number of Slots connected to this port : %d\n",
+ numSlots);
+
+ PCIE_DBG("PCI Device # for First HotPlug Slot : %d\n",
+ ((reg>>8) & 31));
+
+ PCIE_DBG("Physical Slot # for First PCI Device # : %d\n",
+ ((reg>>16) & 0x7ff));
+
+ PCIE_DBG("Physical Slot Number Up/Down : %d\n",
+ ((reg>>29) & 0x1));
+
+ PCIE_DBG("MRL Sensor Implemented : %s\n",
+ (reg & PCI_HP_SLOT_CONFIG_MRL_SENSOR) ? "Yes" : "No");
+
+ PCIE_DBG("Attention Button Implemented : %s\n",
+ (reg & PCI_HP_SLOT_CONFIG_ATTN_BUTTON) ? "Yes" : "No");
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_PROF_IF_SBCR_REG);
+
+ switch (reg & 7) {
+ case 0:
+ state = "33Mhz Conventional PCI";
+ break;
+ case 1:
+ state = "66Mhz Conventional PCI";
+ break;
+ case 2:
+ state = "66Mhz PCI-X";
+ break;
+ case 3:
+ state = "100Mhz PCI-X";
+ break;
+ case 4:
+ state = "133Mhz PCI-X";
+ break;
+ default:
+ state = "Reserved (Error)";
+ break;
+ }
+
+ PCIE_DBG("Current Port Operation Mode : %s\n", state);
+
+ PCIE_DBG("SHPC Interrupt Message Number : %d\n",
+ ((reg>>16) &31));
+
+ PCIE_DBG("SHPC Programming Interface : %d\n",
+ ((reg>>24) & 0xff));
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_COMMAND_STATUS_REG);
+
+ PCIE_DBG("SHPC Command Code : %d\n",
+ (reg & 0xff));
+
+ PCIE_DBG("SHPC Target Slot : %d\n",
+ ((reg>>8) & 31));
+
+ PCIE_DBG("SHPC Controller Busy : %s\n",
+ ((reg>>16) & 1) ? "Yes" : "No");
+
+ PCIE_DBG("SHPC Controller Err: MRL Sensor : %s\n",
+ ((reg>>17) & 1) ? "Yes" : "No");
+
+ PCIE_DBG("SHPC Controller Err: Invalid Command : %s\n",
+ ((reg>>18) & 1) ? "Yes" : "No");
+
+ PCIE_DBG("SHPC Controller Err: Invalid Speed/Mode : %s\n",
+ ((reg>>19) & 1) ? "Yes" : "No");
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_IRQ_LOCATOR_REG);
+
+ PCIE_DBG("Command Completion Interrupt Pending : %s\n",
+ (reg & PCI_HP_IRQ_CMD_COMPLETE) ? "Yes" : "No");
+
+ for (slot = 0; slot < numSlots; slot++) {
+ PCIE_DBG("Slot %d Interrupt Pending : %s\n", slot+1,
+ (reg & (PCI_HP_IRQ_SLOT_N_PENDING<<slot)) ? "Yes" : "No");
+ }
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_SERR_LOCATOR_REG);
+
+ PCIE_DBG("Arbiter SERR Pending : %s\n",
+ (reg & PCI_HP_IRQ_SERR_ARBITER_PENDING) ? "Yes" : "No");
+
+ for (slot = 0; slot < numSlots; slot++) {
+ PCIE_DBG("Slot %d SERR Pending : %s\n",
+ slot+1, (reg &
+ (PCI_HP_IRQ_SERR_SLOT_N_PENDING<<slot)) ? "Yes" : "No");
+ }
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);
+
+ PCIE_DBG("Global Interrupt Mask : %s\n",
+ (reg & PCI_HP_SERR_INT_GLOBAL_IRQ_MASK) ? "Yes" : "No");
+
+ PCIE_DBG("Global SERR Mask : %s\n",
+ (reg & PCI_HP_SERR_INT_GLOBAL_SERR_MASK) ? "Yes" : "No");
+
+ PCIE_DBG("Command Completion Interrupt Mask : %s\n",
+ (reg & PCI_HP_SERR_INT_CMD_COMPLETE_MASK) ? "Yes" : "No");
+
+ PCIE_DBG("Arbiter SERR Mask : %s\n",
+ (reg & PCI_HP_SERR_INT_ARBITER_SERR_MASK) ? "Yes" : "No");
+
+ PCIE_DBG("Command Completion Detected : %s\n",
+ (reg & PCI_HP_SERR_INT_CMD_COMPLETE_IRQ) ? "Yes" : "No");
+
+ PCIE_DBG("Arbiter Timeout Detected : %s\n",
+ (reg & PCI_HP_SERR_INT_ARBITER_IRQ) ? "Yes" : "No");
+
+ for (slot = 0; slot < numSlots; slot++) {
+ PCIE_DBG("Logical Slot %d Registers:\n", slot+1);
+ PCIE_DBG("------------------------------------\n");
+
+ reg = pcishpc_read_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot);
+
+ PCIE_DBG("Slot %d state : %s\n", slot+1,
+ pcishpc_slot_textslotstate(pcishpc_slot_shpc_to_hpc(reg)));
+
+ PCIE_DBG("Slot %d Power Indicator State : %s\n", slot+1,
+ pcishpc_slot_textledstate(pcishpc_led_shpc_to_hpc(
+ (reg>>2) &3)));
+
+ PCIE_DBG("Slot %d Attention Indicator State : %s\n", slot+1,
+ pcishpc_slot_textledstate(pcishpc_led_shpc_to_hpc(
+ (reg>>4)&3)));
+
+ PCIE_DBG("Slot %d Power Fault : %s\n", slot+1,
+ ((reg>>6)&1) ? "Fault Detected" : "No Fault");
+ PCIE_DBG("Slot %d Attention Button : %s\n", slot+1,
+ ((reg>>7)&1) ? "Depressed" : "Not Depressed");
+ PCIE_DBG("Slot %d MRL Sensor : %s\n", slot+1,
+ ((reg>>8)&1) ? "Not Closed" : "Closed");
+ PCIE_DBG("Slot %d 66mhz Capable : %s\n", slot+1,
+ ((reg>>9)&1) ? "66mhz" : "33mgz");
+
+ switch ((reg>>10)&3) {
+ case 0:
+ state = "Card Present 7.5W";
+ break;
+ case 1:
+ state = "Card Present 15W";
+ break;
+ case 2:
+ state = "Card Present 25W";
+ break;
+ case 3:
+ state = "Slot Empty";
+ break;
+ }
+
+ PCIE_DBG("Slot %d PRSNT1#/PRSNT2# : %s\n", slot+1,
+ state);
+
+ switch ((reg>>12)&3) {
+ case 0:
+ state = "Non PCI-X";
+ break;
+ case 1:
+ state = "66mhz PCI-X";
+ break;
+ case 2:
+ state = "Reserved";
+ break;
+ case 3:
+ state = "133mhz PCI-X";
+ break;
+ }
+
+ PCIE_DBG("Slot %d Card Presence Change Detected : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_PRESENCE_DETECTED) ? "Yes" :
+ "No");
+ PCIE_DBG("Slot %d Isolated Power Fault Detected : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_ISO_PWR_DETECTED) ? "Yes" :
+ "No");
+ PCIE_DBG("Slot %d Attention Button Press Detected : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_ATTN_DETECTED) ? "Yes" : "No");
+ PCIE_DBG("Slot %d MRL Sensor Change Detected : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_MRL_DETECTED) ? "Yes" : "No");
+ PCIE_DBG("Slot %d Connected Power Fault Detected : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_POWER_DETECTED) ? "Yes" : "No");
+
+ PCIE_DBG("Slot %d Card Presence IRQ Masked : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_PRESENCE_MASK) ? "Yes" : "No");
+ PCIE_DBG("Slot %d Isolated Power Fault IRQ Masked : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_ISO_PWR_MASK) ? "Yes" : "No");
+ PCIE_DBG("Slot %d Attention Button IRQ Masked : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_ATTN_MASK) ? "Yes" : "No");
+ PCIE_DBG("Slot %d MRL Sensor IRQ Masked : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_MRL_MASK) ? "Yes" : "No");
+ PCIE_DBG("Slot %d Connected Power Fault IRQ Masked : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_POWER_MASK) ? "Yes" : "No");
+ PCIE_DBG("Slot %d MRL Sensor SERR Masked : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_MRL_SERR_MASK) ? "Yes" : "No");
+ PCIE_DBG("Slot %d Connected Power Fault SERR Masked : %s\n",
+ slot+1, (reg & PCI_HP_SLOT_POWER_SERR_MASK) ? "Yes" : "No");
+ }
+}
+#endif /* DEBUG */
diff --git a/usr/src/uts/common/io/pciex/pcie.c b/usr/src/uts/common/io/pciex/pcie.c
index 4f39719d6b..bf08608fa4 100644
--- a/usr/src/uts/common/io/pciex/pcie.c
+++ b/usr/src/uts/common/io/pciex/pcie.c
@@ -34,10 +34,15 @@
#include <sys/fm/util.h>
#include <sys/promif.h>
#include <sys/disp.h>
-#include <sys/pcie.h>
+#include <sys/stat.h>
+#include <sys/file.h>
#include <sys/pci_cap.h>
+#include <sys/pci_impl.h>
#include <sys/pcie_impl.h>
+#include <sys/hotplug/pci/pcie_hp.h>
+#include <sys/hotplug/pci/pcicfg.h>
+/* Local functions prototypes */
static void pcie_init_pfd(dev_info_t *);
static void pcie_fini_pfd(dev_info_t *);
@@ -48,6 +53,7 @@ static void pcie_check_io_mem_range(ddi_acc_handle_t, boolean_t *, boolean_t *);
#ifdef DEBUG
uint_t pcie_debug_flags = 0;
static void pcie_print_bus(pcie_bus_t *bus_p);
+void pcie_dbg(char *fmt, ...);
#endif /* DEBUG */
/* Variable to control default PCI-Express config settings */
@@ -114,7 +120,7 @@ uint32_t pcie_ecrc_value =
* If a particular platform wants to disable certain errors such as UR/MA,
* instead of using #defines have the platform's PCIe Root Complex driver set
* these masks using the pcie_get_XXX_mask and pcie_set_XXX_mask functions. For
- * x86 the closest thing to a PCIe root complex driver is NPE. For SPARC the
+ * x86 the closest thing to a PCIe root complex driver is NPE. For SPARC the
* closest PCIe root complex driver is PX.
*
* pcie_serr_disable_flag : disable SERR only (in RCR and command reg) x86
@@ -135,13 +141,14 @@ uint32_t pcie_aer_suce_severity = PCIE_AER_SUCE_SERR_ASSERT | \
PCIE_AER_SUCE_USC_MSG_DATA_ERR;
int pcie_max_mps = PCIE_DEVCTL_MAX_PAYLOAD_4096 >> 5;
+int pcie_disable_ari = 0;
static void pcie_scan_mps(dev_info_t *rc_dip, dev_info_t *dip,
int *max_supported);
static int pcie_get_max_supported(dev_info_t *dip, void *arg);
static int pcie_map_phys(dev_info_t *dip, pci_regspec_t *phys_spec,
caddr_t *addrp, ddi_acc_handle_t *handlep);
-static void pcie_unmap_phys(ddi_acc_handle_t *handlep, pci_regspec_t *ph);
+static void pcie_unmap_phys(ddi_acc_handle_t *handlep, pci_regspec_t *ph);
/*
* modload support
@@ -149,7 +156,7 @@ static void pcie_unmap_phys(ddi_acc_handle_t *handlep, pci_regspec_t *ph);
static struct modlmisc modlmisc = {
&mod_miscops, /* Type of module */
- "PCIE: PCI framework"
+ "PCI Express Framework Module"
};
static struct modlinkage modlinkage = {
@@ -199,6 +206,195 @@ _info(struct modinfo *modinfop)
return (mod_info(&modlinkage, modinfop));
}
+/* ARGSUSED */
+int
+pcie_init(dev_info_t *dip, caddr_t arg)
+{
+ int ret = DDI_SUCCESS;
+
+ /*
+ * Create a "devctl" minor node to support DEVCTL_DEVICE_*
+ * and DEVCTL_BUS_* ioctls to this bus.
+ */
+ if ((ret = ddi_create_minor_node(dip, "devctl", S_IFCHR,
+ PCI_MINOR_NUM(ddi_get_instance(dip), PCI_DEVCTL_MINOR),
+ DDI_NT_NEXUS, 0)) != DDI_SUCCESS) {
+ PCIE_DBG("Failed to create devctl minor node for %s%d\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ return (ret);
+ }
+
+ if ((ret = pcie_hp_init(dip, arg)) != DDI_SUCCESS) {
+ /*
+ * On a few x86 platforms, we observed unexpected hotplug
+ * initialization failures in recent years. Continue with
+ * a message printed because we don't want to stop PCI
+ * driver attach and system boot because of this hotplug
+ * initialization failure before we address all those issues.
+ */
+ cmn_err(CE_WARN, "%s%d: Failed setting hotplug framework\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+#if defined(__sparc)
+ ddi_remove_minor_node(dip, "devctl");
+
+ return (ret);
+#endif /* defined(__sparc) */
+ }
+
+ if ((pcie_ari_supported(dip) == PCIE_ARI_FORW_SUPPORTED) &&
+ (pcie_ari_is_enabled(dip) == PCIE_ARI_FORW_DISABLED))
+ (void) pcicfg_configure(dip, 0, PCICFG_ALL_FUNC,
+ PCICFG_FLAG_ENABLE_ARI);
+
+ return (DDI_SUCCESS);
+}
+
+/* ARGSUSED */
+int
+pcie_uninit(dev_info_t *dip)
+{
+ int ret = DDI_SUCCESS;
+
+ if (pcie_ari_is_enabled(dip) == PCIE_ARI_FORW_ENABLED)
+ (void) pcie_ari_disable(dip);
+
+ if ((ret = pcie_hp_uninit(dip)) != DDI_SUCCESS) {
+ PCIE_DBG("Failed to uninitialize hotplug for %s%d\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ return (ret);
+ }
+
+ ddi_remove_minor_node(dip, "devctl");
+
+ return (ret);
+}
+
+/* ARGSUSED */
+int
+pcie_intr(dev_info_t *dip)
+{
+ return (pcie_hp_intr(dip));
+}
+
+/* ARGSUSED */
+int
+pcie_open(dev_info_t *dip, dev_t *devp, int flags, int otyp, cred_t *credp)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ /*
+ * Make sure the open is for the right file type.
+ */
+ if (otyp != OTYP_CHR)
+ return (EINVAL);
+
+ /*
+ * Handle the open by tracking the device state.
+ */
+ if ((bus_p->bus_soft_state == PCI_SOFT_STATE_OPEN_EXCL) ||
+ ((flags & FEXCL) &&
+ (bus_p->bus_soft_state != PCI_SOFT_STATE_CLOSED))) {
+ return (EBUSY);
+ }
+
+ if (flags & FEXCL)
+ bus_p->bus_soft_state = PCI_SOFT_STATE_OPEN_EXCL;
+ else
+ bus_p->bus_soft_state = PCI_SOFT_STATE_OPEN;
+
+ return (0);
+}
+
+/* ARGSUSED */
+int
+pcie_close(dev_info_t *dip, dev_t dev, int flags, int otyp, cred_t *credp)
+{
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ if (otyp != OTYP_CHR)
+ return (EINVAL);
+
+ bus_p->bus_soft_state = PCI_SOFT_STATE_CLOSED;
+
+ return (0);
+}
+
+/* ARGSUSED */
+int
+pcie_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg, int mode,
+ cred_t *credp, int *rvalp)
+{
+ struct devctl_iocdata *dcp;
+ uint_t bus_state;
+ int rv = DDI_SUCCESS;
+
+ /*
+ * We can use the generic implementation for devctl ioctl
+ */
+ switch (cmd) {
+ case DEVCTL_DEVICE_GETSTATE:
+ case DEVCTL_DEVICE_ONLINE:
+ case DEVCTL_DEVICE_OFFLINE:
+ case DEVCTL_BUS_GETSTATE:
+ return (ndi_devctl_ioctl(dip, cmd, arg, mode, 0));
+ default:
+ break;
+ }
+
+ /*
+ * read devctl ioctl data
+ */
+ if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
+ return (EFAULT);
+
+ switch (cmd) {
+ case DEVCTL_BUS_QUIESCE:
+ if (ndi_get_bus_state(dip, &bus_state) == NDI_SUCCESS)
+ if (bus_state == BUS_QUIESCED)
+ break;
+ (void) ndi_set_bus_state(dip, BUS_QUIESCED);
+ break;
+ case DEVCTL_BUS_UNQUIESCE:
+ if (ndi_get_bus_state(dip, &bus_state) == NDI_SUCCESS)
+ if (bus_state == BUS_ACTIVE)
+ break;
+ (void) ndi_set_bus_state(dip, BUS_ACTIVE);
+ break;
+ case DEVCTL_BUS_RESET:
+ case DEVCTL_BUS_RESETALL:
+ case DEVCTL_DEVICE_RESET:
+ rv = ENOTSUP;
+ break;
+ default:
+ rv = ENOTTY;
+ }
+
+ ndi_dc_freehdl(dcp);
+ return (rv);
+}
+
+/* ARGSUSED */
+int
+pcie_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
+ int flags, char *name, caddr_t valuep, int *lengthp)
+{
+ if (dev == DDI_DEV_T_ANY)
+ goto skip;
+
+ if (PCIE_IS_HOTPLUG_CAPABLE(dip) &&
+ strcmp(name, "pci-occupant") == 0) {
+ int pci_dev = PCI_MINOR_NUM_TO_PCI_DEVNUM(getminor(dev));
+
+ pcie_hp_create_occupant_props(dip, dev, pci_dev);
+ }
+
+skip:
+ return (ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp));
+}
+
/*
* PCI-Express child device initialization.
* This function enables generic pci-express interrupts and error
@@ -312,6 +508,13 @@ pcie_initchild(dev_info_t *cdip)
pcie_enable_errors(cdip);
}
+ bus_p->bus_ari = B_FALSE;
+ if ((pcie_ari_is_enabled(ddi_get_parent(cdip))
+ == PCIE_ARI_FORW_ENABLED) && (pcie_ari_device(cdip)
+ == PCIE_ARI_DEVICE)) {
+ bus_p->bus_ari = B_TRUE;
+ }
+
if (pcie_initchild_mps(cdip) == DDI_FAILURE)
return (DDI_FAILURE);
@@ -528,6 +731,7 @@ void
pcie_rc_fini_bus(dev_info_t *dip)
{
pcie_bus_t *bus_p = (pcie_bus_t *)ndi_get_bus_private(dip, B_FALSE);
+
ndi_set_bus_private(dip, B_FALSE, NULL, NULL);
kmem_free(bus_p, sizeof (pcie_bus_t));
}
@@ -552,7 +756,6 @@ pcie_init_bus(dev_info_t *cdip)
/* allocate memory for pcie bus data */
bus_p = kmem_zalloc(sizeof (pcie_bus_t), KM_SLEEP);
-
/* Set back pointer to dip */
bus_p->bus_dip = cdip;
@@ -561,8 +764,10 @@ pcie_init_bus(dev_info_t *cdip)
errstr = "Cannot setup config access";
goto fail;
}
+
bus_p->bus_cfg_hdl = eh;
bus_p->bus_fm_flags = 0;
+ bus_p->bus_soft_state = PCI_SOFT_STATE_CLOSED;
/* get device's bus/dev/function number */
if (pcie_get_bdf_from_dip(cdip, &bus_p->bus_bdf) != DDI_SUCCESS) {
@@ -588,6 +793,14 @@ pcie_init_bus(dev_info_t *cdip)
if (PCI_CAP_LOCATE(eh, PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_AER),
&bus_p->bus_aer_off) != DDI_SUCCESS)
bus_p->bus_aer_off = NULL;
+
+ /* Check and save PCIe hotplug capability information */
+ if ((PCIE_IS_RP(bus_p) || PCIE_IS_SWD(bus_p)) &&
+ (PCI_CAP_GET16(eh, NULL, bus_p->bus_pcie_off, PCIE_PCIECAP)
+ & PCIE_PCIECAP_SLOT_IMPL) &&
+ (PCI_CAP_GET32(eh, NULL, bus_p->bus_pcie_off, PCIE_SLOTCAP)
+ & PCIE_SLOTCAP_HP_CAPABLE))
+ bus_p->bus_hp_sup_modes |= PCIE_NATIVE_HP_MODE;
} else {
bus_p->bus_pcie_off = NULL;
bus_p->bus_dev_type = PCIE_PCIECAP_DEV_TYPE_PCI_PSEUDO;
@@ -608,6 +821,11 @@ pcie_init_bus(dev_info_t *cdip)
/* Save the Range information if device is a switch/bridge */
if (PCIE_IS_BDG(bus_p)) {
+ /* Check and save PCI hotplug (SHPC) capability information */
+ if ((PCI_CAP_LOCATE(eh, PCI_CAP_ID_PCI_HOTPLUG,
+ &bus_p->bus_pci_hp_off)) == DDI_SUCCESS)
+ bus_p->bus_hp_sup_modes |= PCIE_PCI_HP_MODE;
+
/* get "bus_range" property */
range_size = sizeof (pci_bus_range_t);
if (ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
@@ -661,6 +879,10 @@ pcie_init_bus(dev_info_t *cdip)
ndi_set_bus_private(cdip, B_TRUE, DEVI_PORT_TYPE_PCI, (void *)bus_p);
+ if (PCIE_IS_HOTPLUG_CAPABLE(cdip))
+ (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, cdip,
+ "hotplug-capable");
+
pcie_init_pfd(cdip);
bus_p->bus_mps = 0;
@@ -717,6 +939,10 @@ pcie_fini_bus(dev_info_t *cdip)
bus_p = PCIE_DIP2UPBUS(cdip);
ASSERT(bus_p);
+
+ if (PCIE_IS_HOTPLUG_CAPABLE(cdip))
+ (void) ndi_prop_remove(DDI_DEV_T_NONE, cdip, "hotplug-capable");
+
pci_config_teardown(&bus_p->bus_cfg_hdl);
ndi_set_bus_private(cdip, B_TRUE, NULL, NULL);
kmem_free(bus_p->bus_assigned_addr,
@@ -1025,7 +1251,7 @@ pcie_get_bdf_for_dma_xfer(dev_info_t *dip, dev_info_t *rdip)
/*
* As part of the probing, the PCI fcode interpreter may setup a DMA
* request if a given card has a fcode on it using dip and rdip of the
- * AP (attachment point) i.e, dip and rdip of px/pcieb driver. In this
+ * hotplug connector i.e, dip and rdip of px/pcieb driver. In this
* case, return a invalid value for the bdf since we cannot get to the
* bdf value of the actual device which will be initiating this DMA.
*/
@@ -1152,6 +1378,7 @@ pcie_initchild_mps(dev_info_t *cdip)
int max_payload_size;
pcie_bus_t *bus_p;
dev_info_t *pdip = ddi_get_parent(cdip);
+ uint8_t dev_type;
bus_p = PCIE_DIP2BUS(cdip);
if (bus_p == NULL) {
@@ -1160,6 +1387,21 @@ pcie_initchild_mps(dev_info_t *cdip)
return (DDI_FAILURE);
}
+ dev_type = bus_p->bus_dev_type;
+
+ /*
+ * For ARI Devices, only function zero's MPS needs to be set.
+ */
+ if ((dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
+ (pcie_ari_is_enabled(pdip) == PCIE_ARI_FORW_ENABLED)) {
+ pcie_req_id_t child_bdf;
+
+ if (pcie_get_bdf_from_dip(cdip, &child_bdf) == DDI_FAILURE)
+ return (DDI_FAILURE);
+ if ((child_bdf & PCIE_REQ_ID_ARI_FUNC_MASK) != 0)
+ return (DDI_SUCCESS);
+ }
+
if (PCIE_IS_RP(bus_p)) {
/*
* If this device is a root port, then the mps scan
@@ -1202,6 +1444,7 @@ pcie_initchild_mps(dev_info_t *cdip)
bus_p->bus_mps = mps;
}
+
return (DDI_SUCCESS);
}
@@ -1244,6 +1487,7 @@ pcie_scan_mps(dev_info_t *rc_dip, dev_info_t *dip, int *max_supported)
ddi_walk_devs(dip, pcie_get_max_supported,
(void *)&max_pay_load_supported);
ndi_devi_exit(ddi_get_parent(dip), circular_count);
+
*max_supported = max_pay_load_supported.highest_common_mps;
}
@@ -1313,7 +1557,7 @@ fail1:
* dip - dip of root complex
*
* Returns - DDI_SUCCESS if there is at least one root port otherwise
- * DDI_FAILURE.
+ * DDI_FAILURE.
*/
int
pcie_root_port(dev_info_t *dip)
@@ -1473,6 +1717,211 @@ pcie_get_rber_fatal(dev_info_t *dip)
return (rp_bus_p->bus_pfd->pe_rber_fatal);
}
+int
+pcie_ari_supported(dev_info_t *dip)
+{
+ uint32_t devcap2;
+ uint16_t pciecap;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ uint8_t dev_type;
+
+ PCIE_DBG("pcie_ari_supported: dip=%p\n", dip);
+
+ if (bus_p == NULL)
+ return (PCIE_ARI_FORW_NOT_SUPPORTED);
+
+ dev_type = bus_p->bus_dev_type;
+
+ if ((dev_type != PCIE_PCIECAP_DEV_TYPE_DOWN) &&
+ (dev_type != PCIE_PCIECAP_DEV_TYPE_ROOT))
+ return (PCIE_ARI_FORW_NOT_SUPPORTED);
+
+ if (pcie_disable_ari) {
+ PCIE_DBG("pcie_ari_supported: dip=%p: ARI Disabled\n", dip);
+ return (PCIE_ARI_FORW_NOT_SUPPORTED);
+ }
+
+ pciecap = PCIE_CAP_GET(16, bus_p, PCIE_PCIECAP);
+
+ if ((pciecap & PCIE_PCIECAP_VER_MASK) < PCIE_PCIECAP_VER_2_0) {
+ PCIE_DBG("pcie_ari_supported: dip=%p: Not 2.0\n", dip);
+ return (PCIE_ARI_FORW_NOT_SUPPORTED);
+ }
+
+ devcap2 = PCIE_CAP_GET(32, bus_p, PCIE_DEVCAP2);
+
+ PCIE_DBG("pcie_ari_supported: dip=%p: DevCap2=0x%x\n",
+ dip, devcap2);
+
+ if (devcap2 & PCIE_DEVCAP2_ARI_FORWARD) {
+ PCIE_DBG("pcie_ari_supported: "
+ "dip=%p: ARI Forwarding is supported\n", dip);
+ return (PCIE_ARI_FORW_SUPPORTED);
+ }
+ return (PCIE_ARI_FORW_NOT_SUPPORTED);
+}
+
+int
+pcie_ari_enable(dev_info_t *dip)
+{
+ uint16_t devctl2;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ PCIE_DBG("pcie_ari_enable: dip=%p\n", dip);
+
+ if (pcie_ari_supported(dip) == PCIE_ARI_FORW_NOT_SUPPORTED)
+ return (DDI_FAILURE);
+
+ devctl2 = PCIE_CAP_GET(16, bus_p, PCIE_DEVCTL2);
+ devctl2 |= PCIE_DEVCTL2_ARI_FORWARD_EN;
+ PCIE_CAP_PUT(16, bus_p, PCIE_DEVCTL2, devctl2);
+
+ PCIE_DBG("pcie_ari_enable: dip=%p: writing 0x%x to DevCtl2\n",
+ dip, devctl2);
+
+ return (DDI_SUCCESS);
+}
+
+int
+pcie_ari_disable(dev_info_t *dip)
+{
+ uint16_t devctl2;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ PCIE_DBG("pcie_ari_disable: dip=%p\n", dip);
+
+ if (pcie_ari_supported(dip) == PCIE_ARI_FORW_NOT_SUPPORTED)
+ return (DDI_FAILURE);
+
+ devctl2 = PCIE_CAP_GET(16, bus_p, PCIE_DEVCTL2);
+ devctl2 &= ~PCIE_DEVCTL2_ARI_FORWARD_EN;
+ PCIE_CAP_PUT(16, bus_p, PCIE_DEVCTL2, devctl2);
+
+ PCIE_DBG("pcie_ari_disable: dip=%p: writing 0x%x to DevCtl2\n",
+ dip, devctl2);
+
+ return (DDI_SUCCESS);
+}
+
+int
+pcie_ari_is_enabled(dev_info_t *dip)
+{
+ uint16_t devctl2;
+ pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+
+ PCIE_DBG("pcie_ari_is_enabled: dip=%p\n", dip);
+
+ if (pcie_ari_supported(dip) == PCIE_ARI_FORW_NOT_SUPPORTED)
+ return (PCIE_ARI_FORW_DISABLED);
+
+ devctl2 = PCIE_CAP_GET(32, bus_p, PCIE_DEVCTL2);
+
+ PCIE_DBG("pcie_ari_is_enabled: dip=%p: DevCtl2=0x%x\n",
+ dip, devctl2);
+
+ if (devctl2 & PCIE_DEVCTL2_ARI_FORWARD_EN) {
+ PCIE_DBG("pcie_ari_is_enabled: "
+ "dip=%p: ARI Forwarding is enabled\n", dip);
+ return (PCIE_ARI_FORW_ENABLED);
+ }
+
+ return (PCIE_ARI_FORW_DISABLED);
+}
+
+int
+pcie_ari_device(dev_info_t *dip)
+{
+ ddi_acc_handle_t handle;
+ uint16_t cap_ptr;
+
+ PCIE_DBG("pcie_ari_device: dip=%p\n", dip);
+
+ /*
+ * XXX - This function may be called before the bus_p structure
+ * has been populated. This code can be changed to remove
+ * pci_config_setup()/pci_config_teardown() when the RFE
+ * to populate the bus_p structures early in boot is putback.
+ */
+
+ /* First make sure it is a PCIe device */
+
+ if (pci_config_setup(dip, &handle) != DDI_SUCCESS)
+ return (PCIE_NOT_ARI_DEVICE);
+
+ if ((PCI_CAP_LOCATE(handle, PCI_CAP_ID_PCI_E, &cap_ptr))
+ != DDI_SUCCESS) {
+ pci_config_teardown(&handle);
+ return (PCIE_NOT_ARI_DEVICE);
+ }
+
+ /* Locate the ARI Capability */
+
+ if ((PCI_CAP_LOCATE(handle, PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_ARI),
+ &cap_ptr)) == DDI_FAILURE) {
+ pci_config_teardown(&handle);
+ return (PCIE_NOT_ARI_DEVICE);
+ }
+
+ /* ARI Capability was found so it must be a ARI device */
+ PCIE_DBG("pcie_ari_device: ARI Device dip=%p\n", dip);
+
+ pci_config_teardown(&handle);
+ return (PCIE_ARI_DEVICE);
+}
+
+int
+pcie_ari_get_next_function(dev_info_t *dip, int *func)
+{
+ uint32_t val;
+ uint16_t cap_ptr, next_function;
+ ddi_acc_handle_t handle;
+
+ /*
+ * XXX - This function may be called before the bus_p structure
+ * has been populated. This code can be changed to remove
+ * pci_config_setup()/pci_config_teardown() when the RFE
+ * to populate the bus_p structures early in boot is putback.
+ */
+
+ if (pci_config_setup(dip, &handle) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ if ((PCI_CAP_LOCATE(handle,
+ PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_ARI), &cap_ptr)) == DDI_FAILURE) {
+ pci_config_teardown(&handle);
+ return (DDI_FAILURE);
+ }
+
+ val = PCI_CAP_GET32(handle, NULL, cap_ptr, PCIE_ARI_CAP);
+
+ next_function = (val >> PCIE_ARI_CAP_NEXT_FUNC_SHIFT) &
+ PCIE_ARI_CAP_NEXT_FUNC_MASK;
+
+ pci_config_teardown(&handle);
+
+ *func = next_function;
+
+ return (DDI_SUCCESS);
+}
+
+dev_info_t *
+pcie_func_to_dip(dev_info_t *dip, pcie_req_id_t function)
+{
+ pcie_req_id_t child_bdf;
+ dev_info_t *cdip;
+
+ for (cdip = ddi_get_child(dip); cdip;
+ cdip = ddi_get_next_sibling(cdip)) {
+
+ if (pcie_get_bdf_from_dip(cdip, &child_bdf) == DDI_FAILURE)
+ return (NULL);
+
+ if ((child_bdf & PCIE_REQ_ID_ARI_FUNC_MASK) == function)
+ return (cdip);
+ }
+ return (NULL);
+}
+
#ifdef DEBUG
static void
diff --git a/usr/src/uts/common/io/pciex/pcieb.c b/usr/src/uts/common/io/pciex/pcieb.c
index 298e895044..24d8ebc03d 100644
--- a/usr/src/uts/common/io/pciex/pcieb.c
+++ b/usr/src/uts/common/io/pciex/pcieb.c
@@ -39,18 +39,16 @@
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/fm/util.h>
-#include <sys/pcie.h>
#include <sys/pci_cap.h>
+#include <sys/pci_impl.h>
#include <sys/pcie_impl.h>
-#include <sys/hotplug/pci/pcihp.h>
-#include <sys/hotplug/pci/pciehpc.h>
-#include <sys/hotplug/pci/pcishpc.h>
#include <sys/open.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/promif.h> /* prom_printf */
#include <sys/disp.h>
#include <sys/pcie_pwr.h>
+#include <sys/hotplug/pci/pcie_hp.h>
#include "pcieb.h"
#ifdef PX_PLX
#include <io/pciex/pcieb_plx.h>
@@ -120,14 +118,13 @@ static struct bus_ops pcieb_bus_ops = {
i_ndi_busop_access_enter, /* (*bus_fm_access_enter)(); */
i_ndi_busop_access_exit, /* (*bus_fm_access_exit)(); */
pcie_bus_power, /* (*bus_power)(); */
- pcieb_intr_ops /* (*bus_intr_op)(); */
+ pcieb_intr_ops, /* (*bus_intr_op)(); */
+ pcie_hp_common_ops /* (*bus_hp_op)(); */
};
static int pcieb_open(dev_t *, int, int, cred_t *);
static int pcieb_close(dev_t, int, int, cred_t *);
static int pcieb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
-static int pcieb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
- caddr_t, int *);
static int pcieb_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
static uint_t pcieb_intr_handler(caddr_t arg1, caddr_t arg2);
@@ -138,9 +135,6 @@ static void pcieb_pwr_teardown(dev_info_t *dip);
static int pcieb_pwr_disable(dev_info_t *dip);
/* Hotplug related functions */
-static int pcieb_pciehpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle);
-static int pcieb_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle);
-static int pcieb_init_hotplug(pcieb_devstate_t *pcieb);
static void pcieb_id_props(pcieb_devstate_t *pcieb);
/*
@@ -161,7 +155,7 @@ static struct cb_ops pcieb_cb_ops = {
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
- pcieb_prop_op, /* cb_prop_op */
+ pcie_prop_op, /* cb_prop_op */
NULL, /* streamtab */
D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
CB_REV, /* rev */
@@ -194,7 +188,7 @@ static struct dev_ops pcieb_ops = {
static struct modldrv modldrv = {
&mod_driverops, /* Type of module */
- "PCIe to PCI nexus driver",
+ "PCIe bridge/switch driver",
&pcieb_ops, /* driver ops */
};
@@ -246,6 +240,36 @@ _info(struct modinfo *modinfop)
return (mod_info(&modlinkage, modinfop));
}
+/* ARGSUSED */
+static int
+pcieb_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ minor_t minor = getminor((dev_t)arg);
+ int instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
+ pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, instance);
+ int ret = DDI_SUCCESS;
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)(intptr_t)instance;
+ break;
+ case DDI_INFO_DEVT2DEVINFO:
+ if (pcieb == NULL) {
+ ret = DDI_FAILURE;
+ break;
+ }
+
+ *result = (void *)pcieb->pcieb_dip;
+ break;
+ default:
+ ret = DDI_FAILURE;
+ break;
+ }
+
+ return (ret);
+}
+
+
/*ARGSUSED*/
static int
pcieb_probe(dev_info_t *devi)
@@ -261,7 +285,6 @@ pcieb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
pcieb_devstate_t *pcieb;
pcie_bus_t *bus_p = PCIE_DIP2UPBUS(devi);
ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl;
- uint8_t dev_type = bus_p->bus_dev_type;
switch (cmd) {
case DDI_RESUME:
@@ -297,7 +320,6 @@ pcieb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
return (DDI_FAILURE);
pcieb = ddi_get_soft_state(pcieb_state, instance);
pcieb->pcieb_dip = devi;
- pcieb->pcieb_soft_state = PCIEB_SOFT_STATE_CLOSED;
if ((pcieb_fm_init(pcieb)) != DDI_SUCCESS) {
PCIEB_DEBUG(DBG_ATTACH, devi, "Failed in pcieb_fm_init\n");
@@ -356,37 +378,14 @@ pcieb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
pcieb_attach_plx_workarounds(pcieb);
#endif /* PX_PLX */
- /* Initialize hotplug */
- pcieb->pcieb_hotplug_capable = B_FALSE;
-
- if ((dev_type == PCIE_PCIECAP_DEV_TYPE_DOWN) ||
- (dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT) ||
- (dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) ||
- (dev_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE)) {
- (void) pcieb_init_hotplug(pcieb);
- }
+ if (pcie_init(devi, NULL) != DDI_SUCCESS)
+ goto fail;
/*
* Initialize interrupt handlers. Ignore return value.
*/
(void) pcieb_intr_attach(pcieb);
- if (pcieb->pcieb_hotplug_capable == B_FALSE) {
- /*
- * (for non hotplug bus) this would create ":devctl" minor
- * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls
- * to this bus.
- */
- if (ddi_create_minor_node(devi, "devctl", S_IFCHR,
- PCIHP_AP_MINOR_NUM(instance, PCIHP_DEVCTL_MINOR),
- DDI_NT_NEXUS, 0) != DDI_SUCCESS)
- goto fail;
- }
-
- PCIEB_DEBUG(DBG_ATTACH, devi,
- "pcieb_attach: this nexus %s hotplug slots\n",
- pcieb->pcieb_hotplug_capable == B_TRUE ? "has":"has no");
-
/* Do any platform specific workarounds needed at this time */
pcieb_plat_attach_workaround(devi);
@@ -429,19 +428,8 @@ pcieb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
/* remove interrupt handlers */
pcieb_intr_fini(pcieb);
- if (pcieb->pcieb_hotplug_capable == B_TRUE) {
- if (pcihp_uninit(devi) == DDI_FAILURE)
- error = DDI_FAILURE;
-
- if (pcieb->pcieb_hpc_type == HPC_PCIE)
- (void) pciehpc_uninit(devi);
- else if (pcieb->pcieb_hpc_type == HPC_SHPC)
- (void) pcishpc_uninit(devi);
-
- (void) ndi_prop_remove(DDI_DEV_T_NONE, devi, "hotplug-capable");
- } else {
- ddi_remove_minor_node(devi, "devctl");
- }
+ /* uninitialize inband PCI-E HPC if present */
+ (void) pcie_uninit(devi);
(void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type");
@@ -640,7 +628,7 @@ static int
pcieb_name_child(dev_info_t *child, char *name, int namelen)
{
pci_regspec_t *pci_rp;
- uint_t slot, func;
+ uint_t device, func;
char **unit_addr;
uint_t n;
@@ -677,13 +665,19 @@ pcieb_name_child(dev_info_t *child, char *name, int namelen)
}
/* copy the device identifications */
- slot = PCI_REG_DEV_G(pci_rp[0].pci_phys_hi);
+ device = PCI_REG_DEV_G(pci_rp[0].pci_phys_hi);
func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi);
+ if (pcie_ari_is_enabled(ddi_get_parent(child))
+ == PCIE_ARI_FORW_ENABLED) {
+ func = (device << 3) | func;
+ device = 0;
+ }
+
if (func != 0)
- (void) snprintf(name, namelen, "%x,%x", slot, func);
+ (void) snprintf(name, namelen, "%x,%x", device, func);
else
- (void) snprintf(name, namelen, "%x", slot);
+ (void) snprintf(name, namelen, "%x", device);
ddi_prop_free(pci_rp);
return (DDI_SUCCESS);
@@ -911,7 +905,7 @@ pcieb_intr_init(pcieb_devstate_t *pcieb, int intr_type)
(intr_type == DDI_INTR_TYPE_MSI) ? "MSI" : "INTx");
request = 0;
- if (pcieb->pcieb_hotplug_capable) {
+ if (PCIE_IS_HOTPLUG_ENABLED(dip)) {
request++;
is_hp = B_TRUE;
}
@@ -1220,202 +1214,52 @@ pcieb_fm_fini(pcieb_devstate_t *pcieb_p)
static int
pcieb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
{
- pcieb_devstate_t *pcieb_p;
- minor_t minor = getminor(*devp);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
-
- /*
- * Make sure the open is for the right file type.
- */
- if (otyp != OTYP_CHR)
- return (EINVAL);
+ int inst = PCI_MINOR_NUM_TO_INSTANCE(getminor(*devp));
+ pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, inst);
+ int rv;
- /*
- * Get the soft state structure for the device.
- */
- pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state,
- instance);
-
- if (pcieb_p == NULL)
+ if (pcieb == NULL)
return (ENXIO);
- if (pcieb_p->pcieb_hotplug_capable == B_TRUE)
- return ((pcihp_get_cb_ops())->cb_open(devp, flags,
- otyp, credp));
+ mutex_enter(&pcieb->pcieb_mutex);
+ rv = pcie_open(pcieb->pcieb_dip, devp, flags, otyp, credp);
+ mutex_exit(&pcieb->pcieb_mutex);
- /*
- * Handle the open by tracking the device state.
- */
- mutex_enter(&pcieb_p->pcieb_mutex);
- if (flags & FEXCL) {
- if (pcieb_p->pcieb_soft_state != PCIEB_SOFT_STATE_CLOSED) {
- mutex_exit(&pcieb_p->pcieb_mutex);
- return (EBUSY);
- }
- pcieb_p->pcieb_soft_state = PCIEB_SOFT_STATE_OPEN_EXCL;
- } else {
- if (pcieb_p->pcieb_soft_state == PCIEB_SOFT_STATE_OPEN_EXCL) {
- mutex_exit(&pcieb_p->pcieb_mutex);
- return (EBUSY);
- }
- pcieb_p->pcieb_soft_state = PCIEB_SOFT_STATE_OPEN;
- }
- mutex_exit(&pcieb_p->pcieb_mutex);
- return (0);
+ return (rv);
}
static int
pcieb_close(dev_t dev, int flags, int otyp, cred_t *credp)
{
- pcieb_devstate_t *pcieb_p;
- minor_t minor = getminor(dev);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
+ int inst = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
+ pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, inst);
+ int rv;
- if (otyp != OTYP_CHR)
- return (EINVAL);
-
- pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state,
- instance);
-
- if (pcieb_p == NULL)
+ if (pcieb == NULL)
return (ENXIO);
- if (pcieb_p->pcieb_hotplug_capable == B_TRUE)
- return ((pcihp_get_cb_ops())->cb_close(dev, flags,
- otyp, credp));
+ mutex_enter(&pcieb->pcieb_mutex);
+ rv = pcie_close(pcieb->pcieb_dip, dev, flags, otyp, credp);
+ mutex_exit(&pcieb->pcieb_mutex);
- mutex_enter(&pcieb_p->pcieb_mutex);
- pcieb_p->pcieb_soft_state = PCIEB_SOFT_STATE_CLOSED;
- mutex_exit(&pcieb_p->pcieb_mutex);
- return (0);
+ return (rv);
}
static int
pcieb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
int *rvalp)
{
- pcieb_devstate_t *pcieb_p;
- dev_info_t *self;
- struct devctl_iocdata *dcp;
- uint_t bus_state;
- int rv = 0;
- minor_t minor = getminor(dev);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
-
- pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state,
- instance);
-
- if (pcieb_p == NULL)
- return (ENXIO);
-
- self = pcieb_p->pcieb_dip;
- if (pcieb_p->pcieb_hotplug_capable == B_TRUE) {
- rv = ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd,
- arg, mode, credp, rvalp));
+ int inst = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
+ pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, inst);
+ int rv;
- pcieb_plat_ioctl_hotplug(self, rv, cmd);
- return (rv);
- }
-
- /*
- * We can use the generic implementation for these ioctls
- */
- switch (cmd) {
- case DEVCTL_DEVICE_GETSTATE:
- case DEVCTL_DEVICE_ONLINE:
- case DEVCTL_DEVICE_OFFLINE:
- case DEVCTL_BUS_GETSTATE:
- return (ndi_devctl_ioctl(self, cmd, arg, mode, 0));
- }
-
- /*
- * read devctl ioctl data
- */
- if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
- return (EFAULT);
-
- switch (cmd) {
-
- case DEVCTL_DEVICE_RESET:
- rv = ENOTSUP;
- break;
-
- case DEVCTL_BUS_QUIESCE:
- if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS)
- if (bus_state == BUS_QUIESCED)
- break;
- (void) ndi_set_bus_state(self, BUS_QUIESCED);
- break;
-
- case DEVCTL_BUS_UNQUIESCE:
- if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS)
- if (bus_state == BUS_ACTIVE)
- break;
- (void) ndi_set_bus_state(self, BUS_ACTIVE);
- break;
-
- case DEVCTL_BUS_RESET:
- rv = ENOTSUP;
- break;
-
- case DEVCTL_BUS_RESETALL:
- rv = ENOTSUP;
- break;
-
- default:
- rv = ENOTTY;
- }
-
- ndi_dc_freehdl(dcp);
- return (rv);
-}
-
-static int
-pcieb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
- int flags, char *name, caddr_t valuep, int *lengthp)
-{
- pcieb_devstate_t *pcieb_p;
- minor_t minor = getminor(dev);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
-
- pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state,
- instance);
-
- if (pcieb_p == NULL)
+ if (pcieb == NULL)
return (ENXIO);
- if (pcieb_p->pcieb_hotplug_capable == B_TRUE)
- return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op,
- flags, name, valuep, lengthp));
-
- return (ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp));
-}
-
-/*ARGSUSED*/
-static int
-pcieb_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
-{
- pcieb_devstate_t *pcieb_p; /* per pcieb state pointer */
- minor_t minor = getminor((dev_t)arg);
- int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
-
- pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state,
- instance);
+ /* To handle devctl and hotplug related ioctls */
+ rv = pcie_ioctl(pcieb->pcieb_dip, dev, cmd, arg, mode, credp, rvalp);
- switch (infocmd) {
- default:
- return (DDI_FAILURE);
-
- case DDI_INFO_DEVT2INSTANCE:
- *result = (void *)(intptr_t)instance;
- return (DDI_SUCCESS);
-
- case DDI_INFO_DEVT2DEVINFO:
- if (pcieb_p == NULL)
- return (DDI_FAILURE);
- *result = (void *)pcieb_p->pcieb_dip;
- return (DDI_SUCCESS);
- }
+ return (rv);
}
/*
@@ -1444,12 +1288,8 @@ pcieb_intr_handler(caddr_t arg1, caddr_t arg2)
if (isrc == PCIEB_INTR_SRC_UNKNOWN)
goto FAIL;
- if (isrc & PCIEB_INTR_SRC_HP) {
- if (pcieb_p->pcieb_hpc_type == HPC_PCIE)
- ret = pciehpc_intr(dip);
- else if (pcieb_p->pcieb_hpc_type == HPC_SHPC)
- ret = pcishpc_intr(dip);
- }
+ if (isrc & PCIEB_INTR_SRC_HP)
+ ret = pcie_intr(dip);
if (isrc & PCIEB_INTR_SRC_PME)
ret = DDI_INTR_CLAIMED;
@@ -1576,99 +1416,6 @@ pcieb_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
}
-/*ARGSUSED*/
-static int pcieb_pciehpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle)
-{
- uint16_t cap_ptr;
-
- if ((PCI_CAP_LOCATE(config_handle, PCI_CAP_ID_PCI_E, &cap_ptr)) !=
- DDI_FAILURE) {
- uint16_t slotimpl = PCI_CAP_GET16(config_handle, NULL, cap_ptr,
- PCIE_PCIECAP) & PCIE_PCIECAP_SLOT_IMPL;
- if (slotimpl)
- if (PCI_CAP_GET32(config_handle, NULL, cap_ptr,
- PCIE_SLOTCAP) & PCIE_SLOTCAP_HP_CAPABLE)
- return (DDI_SUCCESS);
- }
-
- return (DDI_FAILURE);
-}
-
-static int pcieb_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle)
-{
- return (pcieb_plat_pcishpc_probe(dip, config_handle));
-}
-
-/*
- * Initialize hotplug framework if we are hotpluggable.
- * Sets flag in the soft state if Hot Plug is supported and initialized
- * properly.
- */
-/*ARGSUSED*/
-static int
-pcieb_init_hotplug(pcieb_devstate_t *pcieb)
-{
- int rv = DDI_FAILURE;
- pcie_bus_t *bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip);
- ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl;
- uint8_t dev_type = bus_p->bus_dev_type;
-
-#ifdef PX_PLX
- uint16_t vid = bus_p->bus_dev_ven_id & 0xFFFF;
- uint16_t did = bus_p->bus_dev_ven_id >> 16;
- if ((vid == PXB_VENDOR_PLX) && (did == PXB_DEVICE_PLX_8532) &&
- (bus_p->bus_rev_id <= PXB_DEVICE_PLX_AA_REV))
- return (DDI_SUCCESS);
-#endif /* PX_PLX */
-
- if (((dev_type == PCIE_PCIECAP_DEV_TYPE_DOWN) ||
- (dev_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE) ||
- (dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT)) &&
- (pcieb_pciehpc_probe(pcieb->pcieb_dip,
- config_handle) == DDI_SUCCESS)) {
- pcieb->pcieb_hpc_type = HPC_PCIE;
- } else if ((dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) &&
- (pcieb_pcishpc_probe(pcieb->pcieb_dip,
- config_handle) == DDI_SUCCESS)) {
- pcieb->pcieb_hpc_type = HPC_SHPC;
- } else {
- pcieb->pcieb_hpc_type = HPC_NONE;
- return (DDI_SUCCESS);
- }
-
- pcieb->pcieb_hotplug_capable = B_TRUE;
-
- if (pcieb->pcieb_hpc_type == HPC_PCIE)
- rv = pciehpc_init(pcieb->pcieb_dip, NULL);
- else if (pcieb->pcieb_hpc_type == HPC_SHPC)
- rv = pcishpc_init(pcieb->pcieb_dip);
-
- if (rv != DDI_SUCCESS)
- goto fail;
-
- if (pcihp_init(pcieb->pcieb_dip) != DDI_SUCCESS) {
- if (pcieb->pcieb_hpc_type == HPC_PCIE)
- (void) pciehpc_uninit(pcieb->pcieb_dip);
- else if (pcieb->pcieb_hpc_type == HPC_SHPC)
- (void) pcishpc_uninit(pcieb->pcieb_dip);
-
- goto fail;
- }
-
- (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, pcieb->pcieb_dip,
- "hotplug-capable");
-
- return (DDI_SUCCESS);
-
-fail:
- pcieb->pcieb_hpc_type = HPC_NONE;
- pcieb->pcieb_hotplug_capable = B_FALSE;
- PCIEB_DEBUG(DBG_ATTACH, pcieb->pcieb_dip, "Failed setting hotplug"
- " framework\n");
-
- return (DDI_FAILURE);
-}
-
/*
* Power management related initialization specific to pcieb.
* Called by pcieb_attach()
@@ -1922,10 +1669,10 @@ pcieb_create_ranges_prop(dev_info_t *dip,
ddi_acc_handle_t config_handle)
{
uint32_t base, limit;
- pcieb_ranges_t ranges[PCIEB_RANGE_LEN];
+ ppb_ranges_t ranges[PCIEB_RANGE_LEN];
uint8_t io_base_lo, io_limit_lo;
uint16_t io_base_hi, io_limit_hi, mem_base, mem_limit;
- int i = 0, rangelen = sizeof (pcieb_ranges_t)/sizeof (int);
+ int i = 0, rangelen = sizeof (ppb_ranges_t)/sizeof (int);
io_base_lo = pci_config_get8(config_handle, PCI_BCNF_IO_BASE_LOW);
io_limit_lo = pci_config_get8(config_handle, PCI_BCNF_IO_LIMIT_LOW);
diff --git a/usr/src/uts/common/io/pciex/pcieb.h b/usr/src/uts/common/io/pciex/pcieb.h
index 17a9052172..7d8556cfc1 100644
--- a/usr/src/uts/common/io/pciex/pcieb.h
+++ b/usr/src/uts/common/io/pciex/pcieb.h
@@ -70,7 +70,6 @@ typedef enum { /* same sequence as pcieb_debug_sym[] */
#define PX_MDT_11 0x01
#define PX_MDT_22 0x10
-
#define NUM_LOGICAL_SLOTS 32
#define PCIEB_RANGE_LEN 2
#define PCIEB_32BIT_IO 1
@@ -82,30 +81,9 @@ typedef enum { /* same sequence as pcieb_debug_sym[] */
#define PCIEB_LADDR(lo, hi) (((uint16_t)(hi) << 16) | (uint16_t)(lo))
#define PCIEB_32bit_MEMADDR(addr) (PCIEB_LADDR(0, ((uint16_t)(addr) & 0xFFF0)))
-/*
- * The following typedef is used to represent an entry in the "ranges"
- * property of a device node.
- */
-typedef struct {
- uint32_t child_high;
- uint32_t child_mid;
- uint32_t child_low;
- uint32_t parent_high;
- uint32_t parent_mid;
- uint32_t parent_low;
- uint32_t size_high;
- uint32_t size_low;
-} pcieb_ranges_t;
-
-typedef enum { HPC_NONE, HPC_PCIE, HPC_SHPC, HPC_OUTBAND } pcieb_hpc_type_t;
-
typedef struct {
dev_info_t *pcieb_dip;
- /* Hotplug support */
- boolean_t pcieb_hotplug_capable;
- pcieb_hpc_type_t pcieb_hpc_type;
-
/* Interrupt support */
ddi_intr_handle_t *pcieb_htable; /* Intr Handlers */
int pcieb_htable_size; /* htable size */
@@ -114,7 +92,6 @@ typedef struct {
int pcieb_intr_type; /* (MSI | FIXED) */
int pcieb_isr_tab[4]; /* MSI source offset */
- uint_t pcieb_soft_state;
int pcieb_init_flags;
kmutex_t pcieb_mutex; /* Soft state mutex */
kmutex_t pcieb_intr_mutex; /* Intr handler mutex */
@@ -156,13 +133,13 @@ extern void *pcieb_state;
*/
#define NVIDIA_VENDOR_ID 0x10de /* Nvidia Vendor Id */
-#ifdef BCM_SW_WORKAROUNDS
+#ifdef PCIEB_BCM
/* Workaround for address space limitation in Broadcom 5714/5715 */
#define PCIEB_ADDR_LIMIT_LO 0ull
#define PCIEB_ADDR_LIMIT_HI ((1ull << 40) - 1)
-#endif /* BCM_SW_WORKAROUNDS */
+#endif /* PCIEB_BCM */
/*
* The following values are used to initialize the cache line size
@@ -177,7 +154,6 @@ extern void pcieb_plat_attach_workaround(dev_info_t *dip);
extern void pcieb_plat_intr_attach(pcieb_devstate_t *pcieb);
extern void pcieb_plat_initchild(dev_info_t *child);
extern void pcieb_plat_uninitchild(dev_info_t *child);
-extern void pcieb_plat_ioctl_hotplug(dev_info_t *dip, int rv, int cmd);
extern int pcieb_plat_ctlops(dev_info_t *rdip, ddi_ctl_enum_t ctlop,
void *arg);
extern int pcieb_plat_pcishpc_probe(dev_info_t *dip,
diff --git a/usr/src/uts/common/os/ddi_hp_impl.c b/usr/src/uts/common/os/ddi_hp_impl.c
new file mode 100644
index 0000000000..6aeb01aac4
--- /dev/null
+++ b/usr/src/uts/common/os/ddi_hp_impl.c
@@ -0,0 +1,1139 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Sun DDI hotplug implementation specific functions
+ */
+
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/debug.h>
+#include <sys/avintr.h>
+#include <sys/autoconf.h>
+#include <sys/ddi.h>
+#include <sys/sunndi.h>
+#include <sys/ndi_impldefs.h>
+#include <sys/sysevent.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/dr.h>
+#include <sys/fs/dv_node.h>
+
+/*
+ * Local function prototypes
+ */
+/* Connector operations */
+static int ddihp_cn_pre_change_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state);
+static int ddihp_cn_post_change_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t new_state);
+static int ddihp_cn_handle_state_change(ddi_hp_cn_handle_t *hdlp);
+static int ddihp_cn_change_children_state(ddi_hp_cn_handle_t *hdlp,
+ boolean_t online);
+static int ddihp_change_node_state(dev_info_t *dip, void *arg);
+/* Port operations */
+static int ddihp_port_change_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state);
+static int ddihp_port_upgrade_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state);
+static int ddihp_port_downgrade_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state);
+/* Misc routines */
+static void ddihp_update_last_change(ddi_hp_cn_handle_t *hdlp);
+static boolean_t ddihp_check_status_prop(dev_info_t *dip);
+
+/*
+ * Global functions (called within hotplug framework)
+ */
+
+/*
+ * Implement modctl() commands for hotplug.
+ * Called by modctl_hp() in modctl.c
+ */
+int
+ddihp_modctl(int hp_op, char *path, char *cn_name, uintptr_t arg,
+ uintptr_t rval)
+{
+ dev_info_t *dip;
+ ddi_hp_cn_handle_t *hdlp;
+ ddi_hp_op_t op = (ddi_hp_op_t)hp_op;
+ int count, rv, error;
+
+ /* Get the dip of nexus node */
+ dip = e_ddi_hold_devi_by_path(path, 0);
+
+ if (dip == NULL)
+ return (ENXIO);
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_modctl: dip %p op %x path %s "
+ "cn_name %s arg %p rval %p\n", (void *)dip, hp_op, path, cn_name,
+ (void *)arg, (void *)rval));
+
+ if (!NEXUS_HAS_HP_OP(dip)) {
+ ddi_release_devi(dip);
+ return (ENOTSUP);
+ }
+
+ /* Lock before access */
+ ndi_devi_enter(dip, &count);
+
+ hdlp = ddihp_cn_name_to_handle(dip, cn_name);
+
+ if (hp_op == DDI_HPOP_CN_CREATE_PORT) {
+ if (hdlp != NULL) {
+ /* this port already exists. */
+ error = EEXIST;
+
+ goto done;
+ }
+ rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
+ dip, cn_name, op, NULL, NULL);
+ } else {
+ if (hdlp == NULL) {
+ /* Invalid Connection name */
+ error = ENXIO;
+
+ goto done;
+ }
+ if (hp_op == DDI_HPOP_CN_CHANGE_STATE) {
+ ddi_hp_cn_state_t target_state = (ddi_hp_cn_state_t)arg;
+ ddi_hp_cn_state_t result_state = 0;
+
+ DDIHP_CN_OPS(hdlp, op, (void *)&target_state,
+ (void *)&result_state, rv);
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_modctl: target_state="
+ "%x, result_state=%x, rv=%x \n",
+ target_state, result_state, rv));
+ } else {
+ DDIHP_CN_OPS(hdlp, op, (void *)arg, (void *)rval, rv);
+ }
+ }
+ switch (rv) {
+ case DDI_SUCCESS:
+ error = 0;
+ break;
+ case DDI_EINVAL:
+ error = EINVAL;
+ break;
+ case DDI_EBUSY:
+ error = EBUSY;
+ break;
+ case DDI_ENOTSUP:
+ error = ENOTSUP;
+ break;
+ case DDI_ENOMEM:
+ error = ENOMEM;
+ break;
+ default:
+ error = EIO;
+ }
+
+done:
+ ndi_devi_exit(dip, count);
+
+ ddi_release_devi(dip);
+
+ return (error);
+}
+
+/*
+ * Return the state of Hotplug Connection (CN)
+ */
+int
+ddihp_cn_getstate(ddi_hp_cn_handle_t *hdlp)
+{
+ ddi_hp_cn_state_t new_state;
+ int ret;
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_getstate: pdip %p hdlp %p\n",
+ (void *)hdlp->cn_dip, (void *)hdlp));
+
+ ASSERT(DEVI_BUSY_OWNED(hdlp->cn_dip));
+
+ DDIHP_CN_OPS(hdlp, DDI_HPOP_CN_GET_STATE,
+ NULL, (void *)&new_state, ret);
+ if (ret != DDI_SUCCESS) {
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_getstate: "
+ "CN %p getstate command failed\n", (void *)hdlp));
+
+ return (ret);
+ }
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_getstate: hdlp %p "
+ "current Connection state %x new Connection state %x\n",
+ (void *)hdlp, hdlp->cn_info.cn_state, new_state));
+
+ if (new_state != hdlp->cn_info.cn_state) {
+ hdlp->cn_info.cn_state = new_state;
+ ddihp_update_last_change(hdlp);
+ }
+
+ return (ret);
+}
+
+/*
+ * Implementation function for unregistering the Hotplug Connection (CN)
+ */
+int
+ddihp_cn_unregister(ddi_hp_cn_handle_t *hdlp)
+{
+ dev_info_t *dip = hdlp->cn_dip;
+
+ DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_unregister: hdlp %p\n",
+ (void *)hdlp));
+
+ ASSERT(DEVI_BUSY_OWNED(dip));
+
+ (void) ddihp_cn_getstate(hdlp);
+
+ if (hdlp->cn_info.cn_state > DDI_HP_CN_STATE_OFFLINE) {
+ DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_unregister: dip %p, hdlp %p "
+ "state %x. Device busy, failed to unregister connection!\n",
+ (void *)dip, (void *)hdlp, hdlp->cn_info.cn_state));
+
+ return (DDI_EBUSY);
+ }
+
+ /* unlink the handle */
+ DDIHP_LIST_REMOVE(ddi_hp_cn_handle_t, (DEVI(dip)->devi_hp_hdlp), hdlp);
+
+ kmem_free(hdlp->cn_info.cn_name, strlen(hdlp->cn_info.cn_name) + 1);
+ kmem_free(hdlp, sizeof (ddi_hp_cn_handle_t));
+ return (DDI_SUCCESS);
+}
+
+/*
+ * For a given Connection name and the dip node where the Connection is
+ * supposed to be, find the corresponding hotplug handle.
+ */
+ddi_hp_cn_handle_t *
+ddihp_cn_name_to_handle(dev_info_t *dip, char *cn_name)
+{
+ ddi_hp_cn_handle_t *hdlp;
+
+ ASSERT(DEVI_BUSY_OWNED(dip));
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_name_to_handle: "
+ "dip %p cn_name to find: %s", (void *)dip, cn_name));
+ for (hdlp = DEVI(dip)->devi_hp_hdlp; hdlp; hdlp = hdlp->next) {
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_name_to_handle: "
+ "current cn_name: %s", hdlp->cn_info.cn_name));
+
+ if (strcmp(cn_name, hdlp->cn_info.cn_name) == 0) {
+ /* found */
+ return (hdlp);
+ }
+ }
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_name_to_handle: "
+ "failed to find cn_name"));
+ return (NULL);
+}
+
+/*
+ * Process the hotplug operations for Connector and also create Port
+ * upon user command.
+ */
+int
+ddihp_connector_ops(ddi_hp_cn_handle_t *hdlp, ddi_hp_op_t op,
+ void *arg, void *result)
+{
+ int rv = DDI_SUCCESS;
+ dev_info_t *dip = hdlp->cn_dip;
+
+ ASSERT(DEVI_BUSY_OWNED(dip));
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_connector_ops: pdip=%p op=%x "
+ "hdlp=%p arg=%p\n", (void *)dip, op, (void *)hdlp, arg));
+
+ if (op == DDI_HPOP_CN_CHANGE_STATE) {
+ ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
+
+ rv = ddihp_cn_pre_change_state(hdlp, target_state);
+ if (rv != DDI_SUCCESS) {
+ /* the state is not changed */
+ *((ddi_hp_cn_state_t *)result) =
+ hdlp->cn_info.cn_state;
+ return (rv);
+ }
+ }
+ ASSERT(NEXUS_HAS_HP_OP(dip));
+ rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
+ dip, hdlp->cn_info.cn_name, op, arg, result);
+
+ if (rv != DDI_SUCCESS) {
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_connector_ops: "
+ "bus_hp_op failed: pdip=%p cn_name:%s op=%x "
+ "hdlp=%p arg=%p\n", (void *)dip, hdlp->cn_info.cn_name,
+ op, (void *)hdlp, arg));
+ }
+ if (op == DDI_HPOP_CN_CHANGE_STATE) {
+ int rv_post;
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_connector_ops: "
+ "old_state=%x, new_state=%x, rv=%x\n",
+ hdlp->cn_info.cn_state, *(ddi_hp_cn_state_t *)result, rv));
+
+ /*
+ * After state change op is successfully done or
+ * failed at some stages, continue to do some jobs.
+ */
+ rv_post = ddihp_cn_post_change_state(hdlp,
+ *(ddi_hp_cn_state_t *)result);
+
+ if (rv_post != DDI_SUCCESS)
+ rv = rv_post;
+ }
+
+ return (rv);
+}
+
+/*
+ * Process the hotplug op for Port
+ */
+int
+ddihp_port_ops(ddi_hp_cn_handle_t *hdlp, ddi_hp_op_t op,
+ void *arg, void *result)
+{
+ int ret = DDI_SUCCESS;
+
+ ASSERT(DEVI_BUSY_OWNED(hdlp->cn_dip));
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_port_ops: pdip=%p op=%x hdlp=%p "
+ "arg=%p\n", (void *)hdlp->cn_dip, op, (void *)hdlp, arg));
+
+ switch (op) {
+ case DDI_HPOP_CN_GET_STATE:
+ {
+ int state;
+
+ state = hdlp->cn_info.cn_state;
+
+ if (hdlp->cn_info.cn_child == NULL) {
+ /* No child. Either present or empty. */
+ if (state >= DDI_HP_CN_STATE_PORT_PRESENT)
+ state = DDI_HP_CN_STATE_PORT_PRESENT;
+ else
+ state = DDI_HP_CN_STATE_PORT_EMPTY;
+
+ } else { /* There is a child of this Port */
+
+ /* Check DEVI(dip)->devi_node_state */
+ switch (i_ddi_node_state(hdlp->cn_info.cn_child)) {
+ case DS_INVAL:
+ case DS_PROTO:
+ case DS_LINKED:
+ case DS_BOUND:
+ case DS_INITIALIZED:
+ case DS_PROBED:
+ state = DDI_HP_CN_STATE_OFFLINE;
+ break;
+ case DS_ATTACHED:
+ state = DDI_HP_CN_STATE_MAINTENANCE;
+ break;
+ case DS_READY:
+ state = DDI_HP_CN_STATE_ONLINE;
+ break;
+ default:
+ /* should never reach here */
+ ASSERT("unknown devinfo state");
+ }
+ /*
+ * Check DEVI(dip)->devi_state in case the node is
+ * downgraded or quiesced.
+ */
+ if (state == DDI_HP_CN_STATE_ONLINE &&
+ ddi_get_devstate(hdlp->cn_info.cn_child) !=
+ DDI_DEVSTATE_UP)
+ state = DDI_HP_CN_STATE_MAINTENANCE;
+ }
+
+ *((ddi_hp_cn_state_t *)result) = state;
+
+ break;
+ }
+ case DDI_HPOP_CN_CHANGE_STATE:
+ {
+ ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
+ ddi_hp_cn_state_t curr_state = hdlp->cn_info.cn_state;
+
+ ret = ddihp_port_change_state(hdlp, target_state);
+ if (curr_state != hdlp->cn_info.cn_state) {
+ ddihp_update_last_change(hdlp);
+ }
+ *((ddi_hp_cn_state_t *)result) = hdlp->cn_info.cn_state;
+
+ break;
+ }
+ case DDI_HPOP_CN_REMOVE_PORT:
+ {
+ (void) ddihp_cn_getstate(hdlp);
+
+ if (hdlp->cn_info.cn_state != DDI_HP_CN_STATE_PORT_EMPTY) {
+ /* Only empty PORT can be removed by commands */
+ ret = DDI_EBUSY;
+
+ break;
+ }
+
+ ret = ddihp_cn_unregister(hdlp);
+ break;
+ }
+ default:
+ ret = DDI_ENOTSUP;
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ * Generate the system event with a possible hint
+ */
+/* ARGSUSED */
+void
+ddihp_cn_gen_sysevent(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_sysevent_t event_sub_class, int hint, int kmflag)
+{
+ dev_info_t *dip = hdlp->cn_dip;
+ char *cn_path, *ap_id;
+ char *ev_subclass = NULL;
+ nvlist_t *ev_attr_list = NULL;
+ sysevent_id_t eid;
+ int ap_id_len, err;
+
+ cn_path = kmem_zalloc(MAXPATHLEN, kmflag);
+ if (cn_path == NULL) {
+ cmn_err(CE_WARN,
+ "%s%d: Failed to allocate memory for hotplug"
+ " connection: %s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ hdlp->cn_info.cn_name);
+
+ return;
+ }
+
+ /*
+ * Minor device name will be bus path
+ * concatenated with connection name.
+ * One of consumers of the sysevent will pass it
+ * to cfgadm as AP ID.
+ */
+ (void) strcpy(cn_path, "/devices");
+ (void) ddi_pathname(dip, cn_path + strlen("/devices"));
+
+ ap_id_len = strlen(cn_path) + strlen(":") +
+ strlen(hdlp->cn_info.cn_name) + 1;
+ ap_id = kmem_zalloc(ap_id_len, kmflag);
+ if (ap_id == NULL) {
+ cmn_err(CE_WARN,
+ "%s%d: Failed to allocate memory for AP ID: %s:%s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ cn_path, hdlp->cn_info.cn_name);
+ kmem_free(cn_path, MAXPATHLEN);
+
+ return;
+ }
+
+ (void) strcpy(ap_id, cn_path);
+ (void) strcat(ap_id, ":");
+ (void) strcat(ap_id, hdlp->cn_info.cn_name);
+ kmem_free(cn_path, MAXPATHLEN);
+
+ err = nvlist_alloc(&ev_attr_list, NV_UNIQUE_NAME_TYPE, kmflag);
+
+ if (err != 0) {
+ cmn_err(CE_WARN,
+ "%s%d: Failed to allocate memory for event subclass %d\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ event_sub_class);
+ kmem_free(ap_id, ap_id_len);
+
+ return;
+ }
+
+ switch (event_sub_class) {
+ case DDI_HP_CN_STATE_CHANGE:
+ ev_subclass = ESC_DR_AP_STATE_CHANGE;
+
+ switch (hint) {
+ case SE_NO_HINT: /* fall through */
+ case SE_HINT_INSERT: /* fall through */
+ case SE_HINT_REMOVE:
+ err = nvlist_add_string(ev_attr_list, DR_HINT,
+ SE_HINT2STR(hint));
+
+ if (err != 0) {
+ cmn_err(CE_WARN, "%s%d: Failed to add attr [%s]"
+ " for %s event\n", ddi_driver_name(dip),
+ ddi_get_instance(dip), DR_HINT,
+ ESC_DR_AP_STATE_CHANGE);
+
+ goto done;
+ }
+ break;
+
+ default:
+ cmn_err(CE_WARN, "%s%d: Unknown hint on sysevent\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ goto done;
+ }
+
+ break;
+
+ /* event sub class: DDI_HP_CN_REQ */
+ case DDI_HP_CN_REQ:
+ ev_subclass = ESC_DR_REQ;
+
+ switch (hint) {
+ case SE_INVESTIGATE_RES: /* fall through */
+ case SE_INCOMING_RES: /* fall through */
+ case SE_OUTGOING_RES: /* fall through */
+ err = nvlist_add_string(ev_attr_list, DR_REQ_TYPE,
+ SE_REQ2STR(hint));
+
+ if (err != 0) {
+ cmn_err(CE_WARN,
+ "%s%d: Failed to add attr [%s] for %s \n"
+ "event", ddi_driver_name(dip),
+ ddi_get_instance(dip),
+ DR_REQ_TYPE, ESC_DR_REQ);
+
+ goto done;
+ }
+ break;
+
+ default:
+ cmn_err(CE_WARN, "%s%d: Unknown hint on sysevent\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ goto done;
+ }
+
+ break;
+
+ default:
+ cmn_err(CE_WARN, "%s%d: Unknown Event subclass\n",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+
+ goto done;
+ }
+
+ /*
+ * Add Hotplug Connection (CN) as attribute (common attribute)
+ */
+ err = nvlist_add_string(ev_attr_list, DR_AP_ID, ap_id);
+ if (err != 0) {
+ cmn_err(CE_WARN, "%s%d: Failed to add attr [%s] for %s event\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ DR_AP_ID, EC_DR);
+
+ goto done;
+ }
+
+ /*
+ * Log this event with sysevent framework.
+ */
+ err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, EC_DR,
+ ev_subclass, ev_attr_list, &eid,
+ ((kmflag == KM_SLEEP) ? DDI_SLEEP : DDI_NOSLEEP));
+
+ if (err != 0) {
+ cmn_err(CE_WARN, "%s%d: Failed to log %s event\n",
+ ddi_driver_name(dip), ddi_get_instance(dip), EC_DR);
+ }
+
+done:
+ nvlist_free(ev_attr_list);
+ kmem_free(ap_id, ap_id_len);
+}
+
+/*
+ * Local functions (called within this file)
+ */
+
+/*
+ * Connector operations
+ */
+
+/*
+ * Prepare to change state for a Connector: offline, unprobe, etc.
+ */
+static int
+ddihp_cn_pre_change_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state = hdlp->cn_info.cn_state;
+ dev_info_t *dip = hdlp->cn_dip;
+ int rv = DDI_SUCCESS;
+
+ if (curr_state > target_state &&
+ curr_state == DDI_HP_CN_STATE_ENABLED) {
+ /*
+ * If the Connection goes to a lower state from ENABLED,
+ * then offline all children under it.
+ */
+ rv = ddihp_cn_change_children_state(hdlp, B_FALSE);
+ if (rv != DDI_SUCCESS) {
+ cmn_err(CE_WARN,
+ "(%s%d): "
+ "failed to unconfigure the device in the"
+ " Connection %s\n", ddi_driver_name(dip),
+ ddi_get_instance(dip),
+ hdlp->cn_info.cn_name);
+
+ return (rv);
+ }
+ ASSERT(NEXUS_HAS_HP_OP(dip));
+ /*
+ * Remove all the children and their ports
+ * after they are offlined.
+ */
+ rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
+ dip, hdlp->cn_info.cn_name, DDI_HPOP_CN_UNPROBE,
+ NULL, NULL);
+ if (rv != DDI_SUCCESS) {
+ cmn_err(CE_WARN,
+ "(%s%d): failed"
+ " to unprobe the device in the Connector"
+ " %s\n", ddi_driver_name(dip),
+ ddi_get_instance(dip),
+ hdlp->cn_info.cn_name);
+
+ return (rv);
+ }
+
+ DDI_HP_NEXDBG((CE_CONT,
+ "ddihp_connector_ops (%s%d): device"
+ " is unconfigured and unprobed in Connector %s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ hdlp->cn_info.cn_name));
+ }
+
+ return (rv);
+}
+
+/*
+ * Jobs after change state of a Connector: update last change time,
+ * probe, online, sysevent, etc.
+ */
+static int
+ddihp_cn_post_change_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t new_state)
+{
+ int rv = DDI_SUCCESS;
+ ddi_hp_cn_state_t curr_state = hdlp->cn_info.cn_state;
+
+ /* Update the state in handle */
+ if (new_state != curr_state) {
+ hdlp->cn_info.cn_state = new_state;
+ ddihp_update_last_change(hdlp);
+ }
+
+ if (curr_state < new_state &&
+ new_state == DDI_HP_CN_STATE_ENABLED) {
+ /*
+ * Probe and online devices if state is
+ * upgraded to ENABLED.
+ */
+ rv = ddihp_cn_handle_state_change(hdlp);
+ }
+ if (curr_state != hdlp->cn_info.cn_state) {
+ /*
+ * For Connector, generate a sysevent on
+ * state change.
+ */
+ ddihp_cn_gen_sysevent(hdlp, DDI_HP_CN_STATE_CHANGE,
+ SE_NO_HINT, KM_SLEEP);
+ }
+
+ return (rv);
+}
+
+/*
+ * Handle Connector state change.
+ *
+ * This function is called after connector is upgraded to ENABLED sate.
+ * It probes the device plugged in the connector to setup devinfo nodes
+ * and then online the nodes.
+ */
+static int
+ddihp_cn_handle_state_change(ddi_hp_cn_handle_t *hdlp)
+{
+ dev_info_t *dip = hdlp->cn_dip;
+ int rv = DDI_SUCCESS;
+
+ ASSERT(DEVI_BUSY_OWNED(dip));
+ ASSERT(NEXUS_HAS_HP_OP(dip));
+ /*
+ * If the Connection went to state ENABLED from a lower state,
+ * probe it.
+ */
+ rv = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
+ dip, hdlp->cn_info.cn_name, DDI_HPOP_CN_PROBE, NULL, NULL);
+
+ if (rv != DDI_SUCCESS) {
+ ddi_hp_cn_state_t target_state = DDI_HP_CN_STATE_POWERED;
+ ddi_hp_cn_state_t result_state = 0;
+
+ /*
+ * Probe failed. Disable the connector so that it can
+ * be enabled again by a later try from userland.
+ */
+ (void) (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))(
+ dip, hdlp->cn_info.cn_name, DDI_HPOP_CN_CHANGE_STATE,
+ (void *)&target_state, (void *)&result_state);
+
+ if (result_state && result_state != hdlp->cn_info.cn_state) {
+ hdlp->cn_info.cn_state = result_state;
+ ddihp_update_last_change(hdlp);
+ }
+
+ cmn_err(CE_WARN,
+ "(%s%d): failed to probe the Connection %s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ hdlp->cn_info.cn_name);
+
+ return (rv);
+ }
+ /*
+ * Try to online all the children of CN.
+ */
+ (void) ddihp_cn_change_children_state(hdlp, B_TRUE);
+
+ DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_event_handler (%s%d): "
+ "device is configured in the Connection %s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ hdlp->cn_info.cn_name));
+ return (rv);
+}
+
+/*
+ * Online/Offline all the children under the Hotplug Connection (CN)
+ *
+ * Do online operation when the online parameter is true; otherwise do offline.
+ */
+static int
+ddihp_cn_change_children_state(ddi_hp_cn_handle_t *hdlp, boolean_t online)
+{
+ ddi_hp_cn_cfg_t cn_cfg;
+ dev_info_t *dip = hdlp->cn_dip;
+ dev_info_t *cdip;
+ ddi_hp_cn_handle_t *h;
+ int rv = DDI_SUCCESS;
+
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_change_children_state:"
+ " dip %p hdlp %p, online %x\n",
+ (void *)dip, (void *)hdlp, online));
+
+ cn_cfg.online = online;
+ ASSERT(DEVI_BUSY_OWNED(dip));
+
+ /*
+ * Return invalid if Connection state is < DDI_HP_CN_STATE_ENABLED
+ * when try to online children.
+ */
+ if (online && hdlp->cn_info.cn_state < DDI_HP_CN_STATE_ENABLED) {
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_cn_change_children_state: "
+ "Connector %p is not in probed state\n", (void *)hdlp));
+
+ return (DDI_EINVAL);
+ }
+
+ /* Now, online/offline all the devices depending on the Connector */
+
+ if (!online) {
+ /*
+ * For offline operation we need to firstly clean up devfs
+ * so as not to prevent driver detach.
+ */
+ (void) devfs_clean(dip, NULL, DV_CLEAN_FORCE);
+ }
+ for (h = DEVI(dip)->devi_hp_hdlp; h; h = h->next) {
+ if (h->cn_info.cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT)
+ continue;
+
+ if (h->cn_info.cn_num_dpd_on !=
+ hdlp->cn_info.cn_num)
+ continue;
+
+ cdip = h->cn_info.cn_child;
+ ASSERT(cdip);
+ if (online) {
+ /* online children */
+ if (!ddihp_check_status_prop(dip))
+ continue;
+
+ if (ndi_devi_online(cdip,
+ NDI_ONLINE_ATTACH | NDI_CONFIG) != NDI_SUCCESS) {
+ cmn_err(CE_WARN,
+ "(%s%d):"
+ " failed to attach driver for a device"
+ " (%s%d) under the Connection %s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ ddi_driver_name(cdip),
+ ddi_get_instance(cdip),
+ hdlp->cn_info.cn_name);
+ /*
+ * One of the devices failed to online, but we
+ * want to continue to online the rest siblings
+ * after mark the failure here.
+ */
+ rv = DDI_FAILURE;
+
+ continue;
+ }
+ cn_cfg.rv = NDI_SUCCESS;
+ if (ddi_get_child(cdip)) {
+ /* Continue to online grand children */
+ int c;
+
+ ndi_devi_enter(cdip, &c);
+ ddi_walk_devs(ddi_get_child(cdip),
+ ddihp_change_node_state,
+ (void *)&cn_cfg);
+ ndi_devi_exit(cdip, c);
+ }
+ if (cn_cfg.rv != NDI_SUCCESS) {
+ /*
+ * one of the grand children is not ONLINE'd.
+ */
+ cmn_err(CE_WARN,
+ "(%s%d):"
+ " failed to attach driver for a grandchild"
+ "device (%s%d) in the Connection %s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ ddi_driver_name(cdip),
+ ddi_get_instance(cdip),
+ hdlp->cn_info.cn_name);
+
+ rv = DDI_FAILURE;
+ }
+
+ } else {
+ /* offline children */
+ if (ndi_devi_offline(cdip, NDI_UNCONFIG) !=
+ NDI_SUCCESS) {
+ cmn_err(CE_WARN,
+ "(%s%d):"
+ " failed to dettach driver for the device"
+ " (%s%d) in the Connection %s\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ ddi_driver_name(cdip),
+ ddi_get_instance(cdip),
+ hdlp->cn_info.cn_name);
+
+ return (DDI_EBUSY);
+ }
+ }
+ }
+
+ return (rv);
+}
+
+/*
+ * This function is called to online or offline the dev_info nodes for an
+ * Hotplug Connection (CN).
+ */
+static int
+ddihp_change_node_state(dev_info_t *dip, void *arg)
+{
+ ddi_hp_cn_cfg_t *cn_cfg_p = (ddi_hp_cn_cfg_t *)arg;
+ int rv;
+
+ if (cn_cfg_p->online) {
+ /* It is online operation */
+ if (!ddihp_check_status_prop(dip))
+ return (DDI_WALK_PRUNECHILD);
+
+ rv = ndi_devi_online(dip, NDI_ONLINE_ATTACH | NDI_CONFIG);
+ } else {
+ /* It is offline operation */
+ (void) devfs_clean(ddi_get_parent(dip), NULL, DV_CLEAN_FORCE);
+ rv = ndi_devi_offline(dip, NDI_UNCONFIG);
+ }
+ if (rv != NDI_SUCCESS) {
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_change_devinfo_node_state:"
+ " failed op %x rv %d\n", cn_cfg_p->online, rv));
+ cn_cfg_p->rv = rv;
+
+ /* Failed to attach/detach the driver(s) */
+ return (DDI_WALK_PRUNECHILD);
+ }
+
+ /* Continue the walk */
+ return (DDI_WALK_CONTINUE);
+}
+
+/*
+ * Port operations
+ */
+
+/*
+ * Change Port state to target_state.
+ */
+static int
+ddihp_port_change_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state = hdlp->cn_info.cn_state;
+
+ if (target_state < DDI_HP_CN_STATE_PORT_EMPTY ||
+ target_state > DDI_HP_CN_STATE_ONLINE) {
+
+ return (DDI_EINVAL);
+ }
+
+ if (curr_state < target_state)
+ return (ddihp_port_upgrade_state(hdlp, target_state));
+ else if (curr_state > target_state)
+ return (ddihp_port_downgrade_state(hdlp, target_state));
+ else
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Upgrade port state to target_state.
+ */
+static int
+ddihp_port_upgrade_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state, new_state, result_state;
+ dev_info_t *cdip;
+ int rv = DDI_SUCCESS;
+
+ curr_state = hdlp->cn_info.cn_state;
+ while (curr_state < target_state) {
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_PORT_EMPTY:
+ /* Check the existence of the corresponding hardware */
+ new_state = DDI_HP_CN_STATE_PORT_PRESENT;
+ rv = ddihp_connector_ops(hdlp,
+ DDI_HPOP_CN_CHANGE_STATE,
+ (void *)&new_state, (void *)&result_state);
+ if (rv == DDI_SUCCESS) {
+ hdlp->cn_info.cn_state =
+ result_state;
+ }
+ break;
+ case DDI_HP_CN_STATE_PORT_PRESENT:
+ /* Read-only probe the corresponding hardware. */
+ new_state = DDI_HP_CN_STATE_OFFLINE;
+ rv = ddihp_connector_ops(hdlp,
+ DDI_HPOP_CN_CHANGE_STATE,
+ (void *)&new_state, &cdip);
+ if (rv == DDI_SUCCESS) {
+ hdlp->cn_info.cn_state =
+ DDI_HP_CN_STATE_OFFLINE;
+
+ ASSERT(hdlp->cn_info.cn_child == NULL);
+ hdlp->cn_info.cn_child = cdip;
+ }
+ break;
+ case DDI_HP_CN_STATE_OFFLINE:
+ /* fall through */
+ case DDI_HP_CN_STATE_MAINTENANCE:
+
+ cdip = hdlp->cn_info.cn_child;
+
+ rv = ndi_devi_online(cdip,
+ NDI_ONLINE_ATTACH | NDI_CONFIG);
+ if (rv == NDI_SUCCESS) {
+ hdlp->cn_info.cn_state =
+ DDI_HP_CN_STATE_ONLINE;
+ rv = DDI_SUCCESS;
+ } else {
+ rv = DDI_FAILURE;
+ DDI_HP_IMPLDBG((CE_CONT,
+ "ddihp_port_upgrade_state: "
+ "failed to online device %p at port: %s\n",
+ (void *)cdip, hdlp->cn_info.cn_name));
+ }
+ break;
+ case DDI_HP_CN_STATE_ONLINE:
+
+ break;
+ default:
+ /* should never reach here */
+ ASSERT("unknown devinfo state");
+ }
+ curr_state = hdlp->cn_info.cn_state;
+ if (rv != DDI_SUCCESS) {
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_port_upgrade_state: "
+ "failed curr_state=%x, target_state=%x \n",
+ curr_state, target_state));
+ return (rv);
+ }
+ }
+
+ return (rv);
+}
+
+/*
+ * Downgrade state to target_state
+ */
+static int
+ddihp_port_downgrade_state(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state)
+{
+ ddi_hp_cn_state_t curr_state, new_state, result_state;
+ dev_info_t *dip = hdlp->cn_dip;
+ dev_info_t *cdip;
+ int rv = DDI_SUCCESS;
+
+ curr_state = hdlp->cn_info.cn_state;
+ while (curr_state > target_state) {
+
+ switch (curr_state) {
+ case DDI_HP_CN_STATE_PORT_EMPTY:
+
+ break;
+ case DDI_HP_CN_STATE_PORT_PRESENT:
+ /* Check the existence of the corresponding hardware */
+ new_state = DDI_HP_CN_STATE_PORT_EMPTY;
+ rv = ddihp_connector_ops(hdlp,
+ DDI_HPOP_CN_CHANGE_STATE,
+ (void *)&new_state, (void *)&result_state);
+ if (rv == DDI_SUCCESS)
+ hdlp->cn_info.cn_state =
+ result_state;
+
+ break;
+ case DDI_HP_CN_STATE_OFFLINE:
+ /*
+ * Read-only unprobe the corresponding hardware:
+ * 1. release the assigned resource;
+ * 2. remove the node pointed by the port's cn_child
+ */
+ new_state = DDI_HP_CN_STATE_PORT_PRESENT;
+ rv = ddihp_connector_ops(hdlp,
+ DDI_HPOP_CN_CHANGE_STATE,
+ (void *)&new_state, (void *)&result_state);
+ if (rv == DDI_SUCCESS)
+ hdlp->cn_info.cn_state =
+ DDI_HP_CN_STATE_PORT_PRESENT;
+ break;
+ case DDI_HP_CN_STATE_MAINTENANCE:
+ /* fall through. */
+ case DDI_HP_CN_STATE_ONLINE:
+ cdip = hdlp->cn_info.cn_child;
+
+ (void) devfs_clean(dip, NULL, DV_CLEAN_FORCE);
+ rv = ndi_devi_offline(cdip, NDI_UNCONFIG);
+ if (rv == NDI_SUCCESS) {
+ hdlp->cn_info.cn_state =
+ DDI_HP_CN_STATE_OFFLINE;
+ rv = DDI_SUCCESS;
+ } else {
+ rv = DDI_EBUSY;
+ DDI_HP_IMPLDBG((CE_CONT,
+ "ddihp_port_downgrade_state: failed "
+ "to offline node, rv=%x, cdip=%p \n",
+ rv, (void *)cdip));
+ }
+
+ break;
+ default:
+ /* should never reach here */
+ ASSERT("unknown devinfo state");
+ }
+ curr_state = hdlp->cn_info.cn_state;
+ if (rv != DDI_SUCCESS) {
+ DDI_HP_IMPLDBG((CE_CONT,
+ "ddihp_port_downgrade_state: failed "
+ "curr_state=%x, target_state=%x \n",
+ curr_state, target_state));
+ return (rv);
+ }
+ }
+
+ return (rv);
+}
+
+/*
+ * Misc routines
+ */
+
+/* Update the last state change time */
+static void
+ddihp_update_last_change(ddi_hp_cn_handle_t *hdlp)
+{
+ time_t time;
+
+ if (drv_getparm(TIME, (void *)&time) != DDI_SUCCESS)
+ hdlp->cn_info.cn_last_change = (time_t)-1;
+ else
+ hdlp->cn_info.cn_last_change = (time32_t)time;
+}
+
+/*
+ * Check the device for a 'status' property. A conforming device
+ * should have a status of "okay", "disabled", "fail", or "fail-xxx".
+ *
+ * Return FALSE for a conforming device that is disabled or faulted.
+ * Return TRUE in every other case.
+ *
+ * 'status' property is NOT a bus specific property. It is defined in page 184,
+ * IEEE 1275 spec. The full name of the spec is "IEEE Standard for
+ * Boot (Initialization Configuration) Firmware: Core Requirements and
+ * Practices".
+ */
+static boolean_t
+ddihp_check_status_prop(dev_info_t *dip)
+{
+ char *status_prop;
+ boolean_t rv = B_TRUE;
+
+ /* try to get the 'status' property */
+ if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "status", &status_prop) == DDI_PROP_SUCCESS) {
+ /*
+ * test if the status is "disabled", "fail", or
+ * "fail-xxx".
+ */
+ if (strcmp(status_prop, "disabled") == 0) {
+ rv = B_FALSE;
+ DDI_HP_IMPLDBG((CE_CONT, "ddihp_check_status_prop "
+ "(%s%d): device is in disabled state",
+ ddi_driver_name(dip), ddi_get_instance(dip)));
+ } else if (strncmp(status_prop, "fail", 4) == 0) {
+ rv = B_FALSE;
+ cmn_err(CE_WARN,
+ "hotplug (%s%d): device is in fault state (%s)\n",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ status_prop);
+ }
+
+ ddi_prop_free(status_prop);
+ }
+
+ return (rv);
+}
diff --git a/usr/src/uts/common/os/ddi_hp_ndi.c b/usr/src/uts/common/os/ddi_hp_ndi.c
new file mode 100644
index 0000000000..d8cb0de3a2
--- /dev/null
+++ b/usr/src/uts/common/os/ddi_hp_ndi.c
@@ -0,0 +1,405 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Sun NDI hotplug interfaces
+ */
+
+#include <sys/note.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/debug.h>
+#include <sys/avintr.h>
+#include <sys/autoconf.h>
+#include <sys/sunndi.h>
+#include <sys/ndi_impldefs.h>
+#include <sys/ddi.h>
+#include <sys/disp.h>
+#include <sys/stat.h>
+#include <sys/callb.h>
+#include <sys/sysevent.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/dr.h>
+#include <sys/taskq.h>
+
+/* Local functions prototype */
+static void ddihp_cn_run_event(void *arg);
+static int ddihp_cn_req_handler(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state);
+
+/*
+ * Global functions (called by hotplug controller or nexus drivers)
+ */
+
+/*
+ * Register the Hotplug Connection (CN)
+ */
+int
+ndi_hp_register(dev_info_t *dip, ddi_hp_cn_info_t *info_p)
+{
+ ddi_hp_cn_handle_t *hdlp;
+ int count;
+
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_register: dip %p, info_p %p\n",
+ (void *)dip, (void *)info_p));
+
+ ASSERT(!servicing_interrupt());
+ if (servicing_interrupt())
+ return (NDI_FAILURE);
+
+ /* Validate the arguments */
+ if ((dip == NULL) || (info_p == NULL))
+ return (NDI_EINVAL);
+
+ if (!NEXUS_HAS_HP_OP(dip)) {
+ return (NDI_ENOTSUP);
+ }
+ /* Lock before access */
+ ndi_devi_enter(dip, &count);
+
+ hdlp = ddihp_cn_name_to_handle(dip, info_p->cn_name);
+ if (hdlp) {
+ /* This cn_name is already registered. */
+ ndi_devi_exit(dip, count);
+
+ return (NDI_SUCCESS);
+ }
+ /*
+ * Create and initialize hotplug Connection handle
+ */
+ hdlp = (ddi_hp_cn_handle_t *)kmem_zalloc(
+ (sizeof (ddi_hp_cn_handle_t)), KM_SLEEP);
+
+ /* Copy the Connection information */
+ hdlp->cn_dip = dip;
+ bcopy(info_p, &(hdlp->cn_info), sizeof (*info_p));
+
+ /* Copy cn_name */
+ hdlp->cn_info.cn_name = ddi_strdup(info_p->cn_name, KM_SLEEP);
+
+ if (ddihp_cn_getstate(hdlp) != DDI_SUCCESS) {
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_register: dip %p, hdlp %p"
+ "ddi_cn_getstate failed\n", (void *)dip, (void *)hdlp));
+
+ goto fail;
+ }
+
+ /*
+ * Append the handle to the list
+ */
+ DDIHP_LIST_APPEND(ddi_hp_cn_handle_t, (DEVI(dip)->devi_hp_hdlp),
+ hdlp);
+
+ ndi_devi_exit(dip, count);
+
+ return (NDI_SUCCESS);
+
+fail:
+ kmem_free(hdlp->cn_info.cn_name, strlen(hdlp->cn_info.cn_name) + 1);
+ kmem_free(hdlp, sizeof (ddi_hp_cn_handle_t));
+ ndi_devi_exit(dip, count);
+
+ return (NDI_FAILURE);
+}
+
+/*
+ * Unregister a Hotplug Connection (CN)
+ */
+int
+ndi_hp_unregister(dev_info_t *dip, char *cn_name)
+{
+ ddi_hp_cn_handle_t *hdlp;
+ int count;
+ int ret;
+
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_unregister: dip %p, cn name %s\n",
+ (void *)dip, cn_name));
+
+ ASSERT(!servicing_interrupt());
+ if (servicing_interrupt())
+ return (NDI_FAILURE);
+
+ /* Validate the arguments */
+ if ((dip == NULL) || (cn_name == NULL))
+ return (NDI_EINVAL);
+
+ ndi_devi_enter(dip, &count);
+
+ hdlp = ddihp_cn_name_to_handle(dip, cn_name);
+ if (hdlp == NULL) {
+ ndi_devi_exit(dip, count);
+ return (NDI_EINVAL);
+ }
+
+ switch (ddihp_cn_unregister(hdlp)) {
+ case DDI_SUCCESS:
+ ret = NDI_SUCCESS;
+ break;
+ case DDI_EINVAL:
+ ret = NDI_EINVAL;
+ break;
+ case DDI_EBUSY:
+ ret = NDI_BUSY;
+ break;
+ default:
+ ret = NDI_FAILURE;
+ break;
+ }
+
+ ndi_devi_exit(dip, count);
+
+ return (ret);
+}
+
+/*
+ * Notify the Hotplug Connection (CN) to change state.
+ * Flag:
+ * DDI_HP_REQ_SYNC Return after the change is finished.
+ * DDI_HP_REQ_ASYNC Return after the request is dispatched.
+ */
+int
+ndi_hp_state_change_req(dev_info_t *dip, char *cn_name,
+ ddi_hp_cn_state_t state, uint_t flag)
+{
+ ddi_hp_cn_async_event_entry_t *eventp;
+
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_state_change_req: dip %p "
+ "cn_name: %s, state %x, flag %x\n",
+ (void *)dip, cn_name, state, flag));
+
+ /* Validate the arguments */
+ if (dip == NULL || cn_name == NULL)
+ return (NDI_EINVAL);
+
+ if (!NEXUS_HAS_HP_OP(dip)) {
+ return (NDI_ENOTSUP);
+ }
+ /*
+ * If the request is to handle the event synchronously, then call
+ * the event handler without queuing the event.
+ */
+ if (flag & DDI_HP_REQ_SYNC) {
+ ddi_hp_cn_handle_t *hdlp;
+ int count;
+ int ret;
+
+ ASSERT(!servicing_interrupt());
+ if (servicing_interrupt())
+ return (NDI_FAILURE);
+
+ ndi_devi_enter(dip, &count);
+
+ hdlp = ddihp_cn_name_to_handle(dip, cn_name);
+ if (hdlp == NULL) {
+ ndi_devi_exit(dip, count);
+
+ return (NDI_EINVAL);
+ }
+
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_state_change_req: hdlp %p "
+ "calling ddihp_cn_req_handler() directly to handle "
+ "target_state %x\n", (void *)hdlp, state));
+
+ ret = ddihp_cn_req_handler(hdlp, state);
+
+ ndi_devi_exit(dip, count);
+
+ return (ret);
+ }
+
+ eventp = kmem_zalloc(sizeof (ddi_hp_cn_async_event_entry_t),
+ KM_NOSLEEP);
+ if (eventp == NULL)
+ return (NDI_NOMEM);
+
+ eventp->cn_name = ddi_strdup(cn_name, KM_NOSLEEP);
+ if (eventp->cn_name == NULL) {
+ kmem_free(eventp, sizeof (ddi_hp_cn_async_event_entry_t));
+ return (NDI_NOMEM);
+ }
+ eventp->dip = dip;
+ eventp->target_state = state;
+
+ /*
+ * Hold the parent's ref so that it won't disappear when the taskq is
+ * scheduled to run.
+ */
+ ndi_hold_devi(dip);
+
+ if (!taskq_dispatch(system_taskq, ddihp_cn_run_event, eventp,
+ TQ_NOSLEEP)) {
+ ndi_rele_devi(dip);
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_state_change_req: "
+ "taskq_dispatch failed! dip %p "
+ "target_state %x\n", (void *)dip, state));
+ return (NDI_NOMEM);
+ }
+
+ return (NDI_CLAIMED);
+}
+
+/*
+ * Walk the link of Hotplug Connection handles of a dip:
+ * DEVI(dip)->devi_hp_hdlp->[link of connections]
+ */
+void
+ndi_hp_walk_cn(dev_info_t *dip, int (*f)(ddi_hp_cn_info_t *,
+ void *), void *arg)
+{
+ int count;
+ ddi_hp_cn_handle_t *head, *curr, *prev;
+
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_walk_cn: dip %p arg %p\n",
+ (void *)dip, arg));
+
+ ASSERT(!servicing_interrupt());
+ if (servicing_interrupt())
+ return;
+
+ /* Validate the arguments */
+ if (dip == NULL)
+ return;
+
+ ndi_devi_enter(dip, &count);
+
+ head = DEVI(dip)->devi_hp_hdlp;
+ curr = head;
+ prev = NULL;
+ while (curr != NULL) {
+ DDI_HP_NEXDBG((CE_CONT, "ndi_hp_walk_cn: dip %p "
+ "current cn_name: %s\n",
+ (void *)dip, curr->cn_info.cn_name));
+ switch ((*f)(&(curr->cn_info), arg)) {
+ case DDI_WALK_TERMINATE:
+ ndi_devi_exit(dip, count);
+
+ return;
+ case DDI_WALK_CONTINUE:
+ default:
+ if (DEVI(dip)->devi_hp_hdlp != head) {
+ /*
+ * The current node is head and it is removed
+ * by last call to (*f)()
+ */
+ head = DEVI(dip)->devi_hp_hdlp;
+ curr = head;
+ prev = NULL;
+ } else if (prev && prev->next != curr) {
+ /*
+ * The current node is a middle node or tail
+ * node and it is removed by last call to
+ * (*f)()
+ */
+ curr = prev->next;
+ } else {
+ /* no removal accurred on curr node */
+ prev = curr;
+ curr = curr->next;
+ }
+ }
+ }
+ ndi_devi_exit(dip, count);
+}
+
+/*
+ * Local functions (called within this file)
+ */
+
+/*
+ * Wrapper function for ddihp_cn_req_handler() called from taskq
+ */
+static void
+ddihp_cn_run_event(void *arg)
+{
+ ddi_hp_cn_async_event_entry_t *eventp =
+ (ddi_hp_cn_async_event_entry_t *)arg;
+ dev_info_t *dip = eventp->dip;
+ ddi_hp_cn_handle_t *hdlp;
+ int count;
+
+ /* Lock before access */
+ ndi_devi_enter(dip, &count);
+
+ hdlp = ddihp_cn_name_to_handle(dip, eventp->cn_name);
+ if (hdlp) {
+ (void) ddihp_cn_req_handler(hdlp, eventp->target_state);
+ } else {
+ DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_run_event: no handle for "
+ "cn_name: %s dip %p. Request for target_state %x is"
+ " dropped. \n",
+ eventp->cn_name, (void *)dip, eventp->target_state));
+ }
+
+ ndi_devi_exit(dip, count);
+
+ /* Release the devi's ref that is held from interrupt context. */
+ ndi_rele_devi((dev_info_t *)DEVI(dip));
+ kmem_free(eventp->cn_name, strlen(eventp->cn_name) + 1);
+ kmem_free(eventp, sizeof (ddi_hp_cn_async_event_entry_t));
+}
+
+/*
+ * Handle state change request of a Hotplug Connection (CN)
+ */
+static int
+ddihp_cn_req_handler(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_state_t target_state)
+{
+ dev_info_t *dip = hdlp->cn_dip;
+ int ret = DDI_SUCCESS;
+
+ DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_req_handler:"
+ " hdlp %p, target_state %x\n",
+ (void *)hdlp, target_state));
+
+ ASSERT(DEVI_BUSY_OWNED(dip));
+
+ if (ddihp_cn_getstate(hdlp) != DDI_SUCCESS) {
+ DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_req_handler: dip %p, "
+ "hdlp %p ddi_cn_getstate failed\n", (void *)dip,
+ (void *)hdlp));
+
+ return (NDI_UNCLAIMED);
+ }
+ if (hdlp->cn_info.cn_state != target_state) {
+ ddi_hp_cn_state_t result_state = 0;
+
+ DDIHP_CN_OPS(hdlp, DDI_HPOP_CN_CHANGE_STATE,
+ (void *)&target_state, (void *)&result_state, ret);
+
+ DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_req_handler: dip %p, "
+ "hdlp %p changed state to %x, ret=%x\n",
+ (void *)dip, (void *)hdlp, result_state, ret));
+ }
+
+ if (ret == DDI_SUCCESS)
+ return (NDI_CLAIMED);
+ else
+ return (NDI_UNCLAIMED);
+}
diff --git a/usr/src/uts/common/os/devcfg.c b/usr/src/uts/common/os/devcfg.c
index 871b1c4c91..e36f63bd4c 100644
--- a/usr/src/uts/common/os/devcfg.c
+++ b/usr/src/uts/common/os/devcfg.c
@@ -407,6 +407,7 @@ i_ddi_free_node(dev_info_t *dip)
ASSERT(devi->devi_addr == NULL);
ASSERT(devi->devi_node_state == DS_PROTO);
ASSERT(devi->devi_child == NULL);
+ ASSERT(devi->devi_hp_hdlp == NULL);
#if defined(__x86) && !defined(__xpv)
for (gfxp = gfx_devinfo_list; gfxp; gfxp = gfxp->g_next) {
@@ -652,7 +653,7 @@ link_node(dev_info_t *dip)
* This is a temporary workaround for Bug 4618861.
* We keep the scsi_vhci nexus node on the left side of the devinfo
* tree (under the root nexus driver), so that virtual nodes under
- * scsi_vhci will be SUSPENDed first and RESUMEd last. This ensures
+ * scsi_vhci will be SUSPENDed first and RESUMEd last. This ensures
* that the pHCI nodes are active during times when their clients
* may be depending on them. This workaround embodies the knowledge
* that system PM and CPR both traverse the tree left-to-right during
@@ -695,6 +696,7 @@ unlink_node(dev_info_t *dip)
struct dev_info *devi = DEVI(dip);
struct dev_info *parent = devi->devi_parent;
dev_info_t **dipp;
+ ddi_hp_cn_handle_t *hdlp;
ASSERT(parent != NULL);
ASSERT(devi->devi_node_state == DS_LINKED);
@@ -737,6 +739,11 @@ unlink_node(dev_info_t *dip)
remove_from_dn_list(&orphanlist, dip);
}
+ /* Update parent's hotplug handle list */
+ for (hdlp = DEVI(parent)->devi_hp_hdlp; hdlp; hdlp = hdlp->next) {
+ if (hdlp->cn_info.cn_child == dip)
+ hdlp->cn_info.cn_child = NULL;
+ }
return (DDI_SUCCESS);
}
@@ -2453,7 +2460,7 @@ i_ddi_get_exported_classes(dev_info_t *dip, char ***classes)
n += get_class(ddi_driver_name(dip), buf);
unlock_hw_class_list();
- ASSERT(n == nclass); /* make sure buf wasn't overrun */
+ ASSERT(n == nclass); /* make sure buf wasn't overrun */
return (nclass);
}
@@ -3094,7 +3101,7 @@ debug_dtree(dev_info_t *devi, struct dev_info *adevi, char *service)
}
#else /* DEBUG */
#define debug_dtree(a1, a2, a3) /* nothing */
-#endif /* DEBUG */
+#endif /* DEBUG */
static void
ddi_optimize_dtree(dev_info_t *devi)
@@ -3288,7 +3295,7 @@ i_ddi_forceattach_drivers()
* when uhci/ohci reset themselves, it induces a port change on
* the ehci companion controller. Since there's no interrupt handler
* installed at the time, the moment that interrupt is unmasked, an
- * interrupt storm will occur. All this is averted when ehci is
+ * interrupt storm will occur. All this is averted when ehci is
* loaded first. And now you know..... the REST of the story.
*
* Regardless of platform, ehci needs to initialize first to avoid
@@ -4226,7 +4233,7 @@ reset_leaves(void)
* outstanding attach or detach operations in progress when quiesce_devices() or
* reset_leaves()is invoked. It must be called before the system becomes
* single-threaded because device attach and detach are multi-threaded
- * operations. (note that during system shutdown the system doesn't actually
+ * operations. (note that during system shutdown the system doesn't actually
* become single-thread since other threads still exist, but the shutdown thread
* will disable preemption for itself, raise it's pil, and stop all the other
* cpus in the system there by effectively making the system single-threaded.)
@@ -4420,7 +4427,7 @@ unbind_children_by_driver(dev_info_t *dip, void *arg)
* We are called either from rem_drv or update_drv when reloading
* a driver.conf file. In either case, we unbind persistent nodes
* and destroy .conf nodes. In the case of rem_drv, this will be
- * the final state. In the case of update_drv, i_ddi_bind_devs()
+ * the final state. In the case of update_drv, i_ddi_bind_devs()
* may be invoked later to re-enumerate (new) driver.conf rebind
* persistent nodes.
*/
@@ -5371,7 +5378,7 @@ devi_config_one(dev_info_t *pdip, char *devnm, dev_info_t **cdipp,
* We may have a genericname on a system that creates drivername
* nodes (from .conf files). Find the drivername by nodeid. If we
* can't find a node with devnm as the node name then we search by
- * drivername. This allows an implementation to supply a genericly
+ * drivername. This allows an implementation to supply a genericly
* named boot path (disk) and locate drivename nodes (sd). The
* NDI_PROMNAME flag does not apply to /devices/pseudo paths.
*/
@@ -8480,7 +8487,7 @@ e_ddi_unretire_device(char *path)
static int
mark_and_fence(dev_info_t *dip, void *arg)
{
- char *fencepath = (char *)arg;
+ char *fencepath = (char *)arg;
/*
* We have already decided to retire this device. The various
@@ -8541,7 +8548,7 @@ i_ddi_check_retire(dev_info_t *dip)
(void *)dip, path));
/*
- * Check if this device is in the "retired" store i.e. should
+ * Check if this device is in the "retired" store i.e. should
* be retired. If not, we have nothing to do.
*/
if (e_ddi_device_retired(path) == 0) {
diff --git a/usr/src/uts/common/os/modctl.c b/usr/src/uts/common/os/modctl.c
index 8ec4bc3f7d..1ddec74d87 100644
--- a/usr/src/uts/common/os/modctl.c
+++ b/usr/src/uts/common/os/modctl.c
@@ -174,7 +174,7 @@ mod_setup(void)
num_devs = read_binding_file(majbind, mb_hashtab, make_mbind);
/*
* Since read_binding_file is common code, it doesn't enforce that all
- * of the binding file entries have major numbers <= MAXMAJ32. Thus,
+ * of the binding file entries have major numbers <= MAXMAJ32. Thus,
* ensure that we don't allocate some massive amount of space due to a
* bad entry. We can't have major numbers bigger than MAXMAJ32
* until file system support for larger major numbers exists.
@@ -1232,7 +1232,7 @@ modctl_devid2paths(ddi_devid_t udevid, char *uminor_name, uint_t flag,
char *minor_name = NULL;
dev_info_t *dip = NULL;
int circ;
- struct ddi_minor_data *dmdp;
+ struct ddi_minor_data *dmdp;
char *path = NULL;
int ulens;
int lens;
@@ -1998,7 +1998,7 @@ modctl_remdrv_cleanup(const char *u_drvname)
* instance of a device bound to the driver being
* removed, remove any underlying devfs attribute nodes.
*
- * This is a two-step process. First we go through
+ * This is a two-step process. First we go through
* the instance data itself, constructing a list of
* the nodes discovered. The second step is then
* to find and remove any devfs attribute nodes
@@ -2261,6 +2261,67 @@ err:
return (ret);
}
+static int
+modctl_hp(int subcmd, const char *path, char *cn_name, uintptr_t arg,
+ uintptr_t rval)
+{
+ int error = 0;
+ size_t pathsz, namesz;
+ char *devpath, *cn_name_str;
+
+ if (path == NULL)
+ return (EINVAL);
+
+ devpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+ error = copyinstr(path, devpath, MAXPATHLEN, &pathsz);
+ if (error != 0) {
+ kmem_free(devpath, MAXPATHLEN);
+ return (EFAULT);
+ }
+
+ cn_name_str = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
+ error = copyinstr(cn_name, cn_name_str, MAXNAMELEN, &namesz);
+ if (error != 0) {
+ kmem_free(devpath, MAXPATHLEN);
+ kmem_free(cn_name_str, MAXNAMELEN);
+
+ return (EFAULT);
+ }
+
+ switch (subcmd) {
+ case MODHPOPS_CHANGE_STATE:
+ error = ddihp_modctl(DDI_HPOP_CN_CHANGE_STATE, devpath,
+ cn_name_str, arg, NULL);
+ break;
+ case MODHPOPS_CREATE_PORT:
+ /* Create an empty PORT */
+ error = ddihp_modctl(DDI_HPOP_CN_CREATE_PORT, devpath,
+ cn_name_str, NULL, NULL);
+ break;
+ case MODHPOPS_REMOVE_PORT:
+ /* Remove an empty PORT */
+ error = ddihp_modctl(DDI_HPOP_CN_REMOVE_PORT, devpath,
+ cn_name_str, NULL, NULL);
+ break;
+ case MODHPOPS_BUS_GET:
+ error = ddihp_modctl(DDI_HPOP_CN_GET_PROPERTY, devpath,
+ cn_name_str, arg, rval);
+ break;
+ case MODHPOPS_BUS_SET:
+ error = ddihp_modctl(DDI_HPOP_CN_SET_PROPERTY, devpath,
+ cn_name_str, arg, rval);
+ break;
+ default:
+ error = ENOTSUP;
+ break;
+ }
+
+ kmem_free(devpath, MAXPATHLEN);
+ kmem_free(cn_name_str, MAXNAMELEN);
+
+ return (error);
+}
+
int
modctl_moddevname(int subcmd, uintptr_t a1, uintptr_t a2)
{
@@ -2421,7 +2482,7 @@ modctl(int cmd, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4,
#endif
break;
- case MODGETDEVFSPATH: /* get path name of (dev_t,spec) type */
+ case MODGETDEVFSPATH: /* get path name of (dev_t,spec) type */
if (get_udatamodel() == DATAMODEL_NATIVE) {
error = modctl_devfspath((dev_t)a1, (int)a2,
(uint_t)a3, (char *)a4);
@@ -2439,7 +2500,7 @@ modctl(int cmd, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4,
(uint_t *)a3);
break;
- case MODGETDEVFSPATH_MI: /* get path name of (major,instance) */
+ case MODGETDEVFSPATH_MI: /* get path name of (major,instance) */
error = modctl_devfspath_mi((major_t)a1, (int)a2,
(uint_t)a3, (char *)a4);
break;
@@ -2536,6 +2597,11 @@ modctl(int cmd, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4,
error = modctl_unretire((char *)a1);
break;
+ case MODHPOPS: /* hotplug operations */
+ /* device named by physpath a2 and Connection name a3 */
+ error = modctl_hp((int)a1, (char *)a2, (char *)a3, a4, a5);
+ break;
+
default:
error = EINVAL;
break;
@@ -3235,7 +3301,7 @@ modgetsymname(uintptr_t value, ulong_t *offset)
/*
* Lookup a symbol in a specified module. These are wrapper routines that
- * call kobj_lookup(). kobj_lookup() may go away but these wrappers will
+ * call kobj_lookup(). kobj_lookup() may go away but these wrappers will
* prevent callers from noticing.
*/
uintptr_t
@@ -4171,7 +4237,7 @@ mod_make_requisite(struct modctl *dependent, struct modctl *on_mod)
* which are dependent on it from being uninstalled and
* unloaded. "on_mod"'s mod_ref count decremented in
* mod_release_requisites when the "dependent" module
- * unload is complete. "on_mod" must be loaded, but may not
+ * unload is complete. "on_mod" must be loaded, but may not
* yet be installed.
*/
on_mod->mod_ref++;
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index 83a852b35b..c8be2f37c4 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -148,6 +148,8 @@ CHKHDRS= \
ddi.h \
ddifm.h \
ddifm_impl.h \
+ ddi_hp.h \
+ ddi_hp_impl.h \
ddi_intr.h \
ddi_intr_impl.h \
ddi_impldefs.h \
diff --git a/usr/src/uts/common/sys/autoconf.h b/usr/src/uts/common/sys/autoconf.h
index 0f83c24f0b..acf41f6155 100644
--- a/usr/src/uts/common/sys/autoconf.h
+++ b/usr/src/uts/common/sys/autoconf.h
@@ -26,7 +26,6 @@
#ifndef _SYS_AUTOCONF_H
#define _SYS_AUTOCONF_H
-
/* Derived from autoconf.h, SunOS 4.1.1 1.15 */
#ifdef __cplusplus
@@ -113,6 +112,9 @@ struct devnames {
#define LDI_EV_DEBUG 0x8000 /* LDI events debug messages */
#define LDI_EV_TRACE 0x10000 /* LDI events trace messages */
#define DDI_INTR_IRM 0x20000 /* interrupt resource management */
+#define DDI_HP_API 0x40000 /* Hotplug interface messages */
+#define DDI_HP_IMPL 0x80000 /* Hotplug implementation msgs */
+#define DDI_HP_NEXUS 0x100000 /* Hotplug messages from nexuses */
extern int ddidebug;
@@ -133,6 +135,9 @@ extern int ddidebug;
#define LDI_EVDBG(args) if (ddidebug & LDI_EV_DEBUG) cmn_err args
#define LDI_EVTRC(args) if (ddidebug & LDI_EV_TRACE) cmn_err args
#define DDI_INTR_IRMDBG(args) if (ddidebug & DDI_INTR_IRM) cmn_err args
+#define DDI_HP_APIDBG(args) if (ddidebug & DDI_HP_API) cmn_err args
+#define DDI_HP_IMPLDBG(args) if (ddidebug & DDI_HP_IMPL) cmn_err args
+#define DDI_HP_NEXDBG(args) if (ddidebug & DDI_HP_NEXUS) cmn_err args
#else
#define NDI_CONFIG_DEBUG(args)
#define BMDPRINTF(args)
@@ -150,6 +155,9 @@ extern int ddidebug;
#define LDI_EVDBG(args) if (ddidebug & LDI_EV_DEBUG) cmn_err args
#define LDI_EVTRC(args) if (ddidebug & LDI_EV_TRACE) cmn_err args
#define DDI_INTR_IRMDBG(args)
+#define DDI_HP_APIDBG(args)
+#define DDI_HP_IMPLDBG(args)
+#define DDI_HP_NEXDBG(args)
#endif
diff --git a/usr/src/uts/common/sys/ddi_hp.h b/usr/src/uts/common/sys/ddi_hp.h
new file mode 100644
index 0000000000..eadb88ed49
--- /dev/null
+++ b/usr/src/uts/common/sys/ddi_hp.h
@@ -0,0 +1,126 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_DDI_HP_H
+#define _SYS_DDI_HP_H
+
+/*
+ * Sun DDI hotplug support definitions
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * ddi_hp_cn_state_t
+ *
+ * Typedef of generic hotplug state machine for Hotplug Connection (CN)
+ */
+typedef enum {
+ DDI_HP_CN_STATE_EMPTY = 0x1000, /* Empty */
+ DDI_HP_CN_STATE_PRESENT = 0x2000, /* A Device Present */
+ DDI_HP_CN_STATE_POWERED = 0x3000, /* Powered */
+ DDI_HP_CN_STATE_ENABLED = 0x4000, /* Enabled */
+ DDI_HP_CN_STATE_PORT_EMPTY = 0x5000, /* PORT Empty */
+ DDI_HP_CN_STATE_PORT_PRESENT = 0x6000, /* A Device Node Present */
+ DDI_HP_CN_STATE_OFFLINE = 0x7000, /* Driver not attached */
+ DDI_HP_CN_STATE_ATTACHED = 0x8000, /* Device driver attached */
+ DDI_HP_CN_STATE_MAINTENANCE = 0x9000, /* Device in maintenance */
+ DDI_HP_CN_STATE_ONLINE = 0xa000 /* Device is ready */
+} ddi_hp_cn_state_t;
+
+/*
+ * ddi_hp_cn_type_t
+ *
+ * Typedef for Hotplug Connection (CN) types.
+ */
+typedef enum {
+ DDI_HP_CN_TYPE_VIRTUAL_PORT = 0x1, /* Virtual Hotplug Port */
+ DDI_HP_CN_TYPE_PCI = 0x2, /* PCI bus slot */
+ DDI_HP_CN_TYPE_PCIE = 0x3 /* PCI Express slot */
+} ddi_hp_cn_type_t;
+
+#define DDI_HP_CN_TYPE_STR_PORT "Virtual-Port"
+/*
+ * The value set to ddi_hp_cn_info_t->cn_num_dpd_on in the case of the
+ * connection does not depend on any other connections.
+ */
+#define DDI_HP_CN_NUM_NONE -1
+
+/*
+ * ddi_hp_cn_info_t
+ *
+ * Hotplug Connection (CN) information structure
+ */
+typedef struct ddi_hp_cn_info {
+ char *cn_name; /* Name of the Connection */
+ /*
+ * Connection number.
+ */
+ int cn_num;
+ /*
+ * Depend-on connection number;
+ * The connection number on which this connection is depending on.
+ * If this connection does not depend on any other connections
+ * under the same parent node, then it's cn_num_dpd_on is set to
+ * DDI_HP_CN_NUM_NONE.
+ */
+ int cn_num_dpd_on;
+
+ ddi_hp_cn_type_t cn_type; /* Type: Port, PCI, PCIE, ... */
+
+ /*
+ * Description string for types of Connection. Set by bus software
+ * and read by users only.
+ */
+ char *cn_type_str;
+ /*
+ * The child device of this Port.
+ * It is NULL if this is a Connector.
+ */
+ dev_info_t *cn_child;
+
+ ddi_hp_cn_state_t cn_state; /* Hotplug Connection state */
+ time32_t cn_last_change; /* Last time state changed. */
+} ddi_hp_cn_info_t;
+
+typedef struct ddi_hp_property {
+ char *nvlist_buf;
+ size_t buf_size;
+} ddi_hp_property_t;
+
+#if defined(_SYSCALL32)
+typedef struct ddi_hp_property32 {
+ caddr32_t nvlist_buf;
+ uint32_t buf_size;
+} ddi_hp_property32_t;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_DDI_HP_H */
diff --git a/usr/src/uts/common/sys/ddi_hp_impl.h b/usr/src/uts/common/sys/ddi_hp_impl.h
new file mode 100644
index 0000000000..9b5a193313
--- /dev/null
+++ b/usr/src/uts/common/sys/ddi_hp_impl.h
@@ -0,0 +1,160 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_DDI_HP_IMPL_H
+#define _SYS_DDI_HP_IMPL_H
+
+/*
+ * Sun DDI hotplug implementation specific definitions
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _KERNEL
+
+/* Flags for sync request and async hotplug request */
+#define DDI_HP_REQ_SYNC 0x0001
+#define DDI_HP_REQ_ASYNC 0x0002
+
+/* Check if a handle represents a port or a connector */
+#define DDI_HP_IS_VIRTUAL_PORT(hdlp) \
+ (hdlp->cn_info.cn_type == DDI_HP_CN_TYPE_VIRTUAL_PORT)
+
+/*
+ * ddi_hp_cn_handle_t
+ *
+ * DDI handle for a registered Hotplug Connection (CN)
+ */
+typedef struct ddi_hp_cn_handle {
+ dev_info_t *cn_dip; /* The dip that the handle is linked */
+ ddi_hp_cn_info_t cn_info; /* Connection info */
+ struct ddi_hp_cn_handle *next; /* Next Connector/Port. */
+} ddi_hp_cn_handle_t;
+
+typedef struct ddi_hp_cn_async_event_entry {
+ dev_info_t *dip;
+ char *cn_name;
+ ddi_hp_cn_state_t target_state;
+} ddi_hp_cn_async_event_entry_t;
+
+/*
+ * ddi_hp_op_t
+ *
+ * Typedef for Hotplug OPS commands used with bus_hp_op()
+ */
+typedef enum {
+ DDI_HPOP_CN_GET_STATE = 1, /* Get Connection state */
+ DDI_HPOP_CN_CHANGE_STATE, /* Change Connection state */
+ DDI_HPOP_CN_PROBE, /* Probe Connection */
+ DDI_HPOP_CN_UNPROBE, /* Unprobe Connection */
+ DDI_HPOP_CN_GET_PROPERTY, /* Get bus specific property */
+ DDI_HPOP_CN_SET_PROPERTY, /* Set bus specific property */
+ DDI_HPOP_CN_CREATE_PORT, /* Create a port for virtual hotplug */
+ DDI_HPOP_CN_REMOVE_PORT /* Remove an empty port */
+} ddi_hp_op_t;
+
+#define DDIHP_CN_OPS(hdlp, op, arg, result, ret) \
+ if (DDI_HP_IS_VIRTUAL_PORT(hdlp)) \
+ ret = ddihp_port_ops(hdlp, op, arg, result); \
+ else \
+ ret = ddihp_connector_ops(hdlp, op, arg, result);
+
+#define NEXUS_HAS_HP_OP(dip) \
+ ((DEVI(dip)->devi_ops->devo_bus_ops) && \
+ (DEVI(dip)->devi_ops->devo_bus_ops->busops_rev >= BUSO_REV_10) && \
+ (DEVI(dip)->devi_ops->devo_bus_ops->bus_hp_op))
+
+/*
+ * ddi_hp_cn_sysevent_t
+ *
+ * The following correspond to sysevent defined subclasses
+ */
+typedef enum {
+ DDI_HP_CN_STATE_CHANGE,
+ DDI_HP_CN_REQ
+} ddi_hp_cn_sysevent_t;
+
+/*
+ * Control structure for tree walk during configure/unconfigure operation.
+ */
+typedef struct ddi_hp_cn_cfg {
+ boolean_t online; /* TRUE for online children; */
+ /* FALSE for offline children. */
+ int rv; /* Return error code */
+} ddi_hp_cn_cfg_t;
+
+/*
+ * Misc
+ */
+
+/* Append a node to list */
+#define DDIHP_LIST_APPEND(type, head, node) \
+if (node) { \
+ type *curr, *prev = NULL; \
+ (node)->next = NULL; \
+ for (curr = (head); curr; prev = curr, curr = curr->next); \
+ if (prev == NULL) \
+ (head) = (node); \
+ else \
+ prev->next = (node); \
+}
+
+/* Remove a node from a list */
+#define DDIHP_LIST_REMOVE(type, head, node) \
+if (node) { \
+ type *curr, *prev = NULL; \
+ for (curr = (head); curr; prev = curr, curr = curr->next) { \
+ if (curr == (node)) \
+ break; \
+ } \
+ if (curr) { \
+ if (prev == NULL) \
+ (head) = (head)->next; \
+ else \
+ prev->next = curr->next; \
+ } \
+}
+
+int ddihp_modctl(int hp_op, char *path, char *cn_name, uintptr_t arg,
+ uintptr_t rval);
+ddi_hp_cn_handle_t *ddihp_cn_name_to_handle(dev_info_t *dip, char *cn_name);
+int ddihp_cn_getstate(ddi_hp_cn_handle_t *hdlp);
+int ddihp_port_ops(ddi_hp_cn_handle_t *hdlp, ddi_hp_op_t op,
+ void *arg, void *result);
+int ddihp_connector_ops(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_op_t op, void *arg, void *result);
+void ddihp_cn_gen_sysevent(ddi_hp_cn_handle_t *hdlp,
+ ddi_hp_cn_sysevent_t event_sub_class, int hint, int kmflag);
+int ddihp_cn_unregister(ddi_hp_cn_handle_t *hdlp);
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_DDI_HP_IMPL_H */
diff --git a/usr/src/uts/common/sys/ddi_impldefs.h b/usr/src/uts/common/sys/ddi_impldefs.h
index 5b5d103e08..388ecde590 100644
--- a/usr/src/uts/common/sys/ddi_impldefs.h
+++ b/usr/src/uts/common/sys/ddi_impldefs.h
@@ -39,6 +39,8 @@
#include <sys/epm.h>
#include <sys/ddidmareq.h>
#include <sys/ddi_intr.h>
+#include <sys/ddi_hp.h>
+#include <sys/ddi_hp_impl.h>
#include <sys/ddi_isa.h>
#include <sys/id_space.h>
#include <sys/modhash.h>
@@ -69,12 +71,12 @@ typedef enum {
* Definitions for generic callback mechanism.
*/
typedef enum {
- DDI_CB_INTR_ADD,
- DDI_CB_INTR_REMOVE
+ DDI_CB_INTR_ADD, /* More available interrupts */
+ DDI_CB_INTR_REMOVE /* Fewer available interrupts */
} ddi_cb_action_t;
typedef enum {
- DDI_CB_FLAG_INTR = 0x1
+ DDI_CB_FLAG_INTR = 0x1 /* Driver is IRM aware */
} ddi_cb_flags_t;
#define DDI_CB_FLAG_VALID(f) ((f) & DDI_CB_FLAG_INTR)
@@ -119,6 +121,7 @@ typedef struct devi_bus_priv {
struct iommulib_unit;
typedef struct iommulib_unit *iommulib_handle_t;
typedef uint8_t ndi_flavor_t;
+struct ddi_hp_cn_handle;
struct dev_info {
@@ -231,8 +234,8 @@ struct dev_info {
uint_t devi_cpr_flags;
- /* For interrupt support */
- devinfo_intr_t *devi_intr_p;
+ /* Owned by DDI interrupt framework */
+ devinfo_intr_t *devi_intr_p;
void *devi_nex_pm; /* nexus PM private */
@@ -267,6 +270,9 @@ struct dev_info {
ndi_flavor_t devi_flavor; /* flavor assigned by parent */
ndi_flavor_t devi_flavorv_n; /* number of child-flavors */
void **devi_flavorv; /* child-flavor specific data */
+
+ /* Owned by hotplug framework */
+ struct ddi_hp_cn_handle *devi_hp_hdlp; /* hotplug handle list */
};
#define DEVI(dev_info_type) ((struct dev_info *)(dev_info_type))
diff --git a/usr/src/uts/common/sys/devinfo_impl.h b/usr/src/uts/common/sys/devinfo_impl.h
index 4f874ea1e3..411ec3f5e2 100644
--- a/usr/src/uts/common/sys/devinfo_impl.h
+++ b/usr/src/uts/common/sys/devinfo_impl.h
@@ -64,6 +64,9 @@ extern "C" {
/* new public flag for the layered drivers framework */
#define DINFOLYR (DIIOC | 0x40) /* get device layering information */
+/* new public flag for the hotplug framework */
+#define DINFOHP (DIIOC | 0x400000) /* include hotplug information */
+
/*
* Straight ioctl commands, not bitwise operation
*/
@@ -119,6 +122,7 @@ extern "C" {
#define DI_LINK(addr) ((struct di_link *)((void *)(addr)))
#define DI_LNODE(addr) ((struct di_lnode *)((void *)(addr)))
#define DI_PRIV_FORMAT(addr) ((struct di_priv_format *)((void *)(addr)))
+#define DI_HP(addr) ((struct di_hp *)((void *)(addr)))
/*
* multipath component definitions: Follows the registered component of
@@ -269,12 +273,15 @@ struct di_node { /* useful info to export for each tree node */
di_off_t top_phci;
di_off_t next_phci;
uint32_t multipath_component; /* stores MDI_COMPONENT_* value. */
-
/*
* devi_flags field
*/
uint32_t flags;
uint32_t di_pad2; /* 4 byte padding for 32bit x86 app. */
+ /*
+ * offset to hotplug nodes.
+ */
+ di_off_t hp_data;
};
/*
@@ -321,6 +328,22 @@ struct di_path {
};
/*
+ * chain of hotplug information structures
+ */
+struct di_hp {
+ di_off_t self; /* make it self addressable */
+ di_off_t next; /* next one in chain */
+ di_off_t hp_name; /* name of hotplug connection */
+ int hp_connection; /* connection number */
+ int hp_depends_on; /* connection number depended upon */
+ int hp_state; /* current hotplug state */
+ int hp_type; /* connection type: PCI, ... */
+ di_off_t hp_type_str; /* description of connection type */
+ uint32_t hp_last_change; /* timestamp of last change */
+ di_off_t hp_child; /* child device node */
+};
+
+/*
* Flags for snap_state
*/
#define DI_PATH_SNAP_NOCLIENT 0x01 /* client endpt not in snapshot */
diff --git a/usr/src/uts/common/sys/devops.h b/usr/src/uts/common/sys/devops.h
index 705a8dba02..6efcdcc257 100644
--- a/usr/src/uts/common/sys/devops.h
+++ b/usr/src/uts/common/sys/devops.h
@@ -19,14 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_DEVOPS_H
#define _SYS_DEVOPS_H
-
#include <sys/types.h>
#include <sys/cred.h>
#include <sys/uio.h>
@@ -42,6 +41,8 @@
#include <sys/ddifm.h>
#include <sys/nexusdefs.h>
#include <sys/ddi_intr.h>
+#include <sys/ddi_hp.h>
+#include <sys/ddi_hp_impl.h>
#include <sys/aio_req.h>
#include <vm/page.h>
@@ -145,7 +146,8 @@ struct cb_ops {
#define BUSO_REV_7 7
#define BUSO_REV_8 8
#define BUSO_REV_9 9
-#define BUSO_REV BUSO_REV_9
+#define BUSO_REV_10 10
+#define BUSO_REV BUSO_REV_10
struct bus_ops {
@@ -268,6 +270,13 @@ struct bus_ops {
int (*bus_intr_op)(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_op_t op, ddi_intr_handle_impl_t *hdlp,
void *result);
+
+ /*
+ * NOTE: the following busop entrypoint is available with version
+ * 10 or greater.
+ */
+ int (*bus_hp_op)(dev_info_t *dip, char *cn_name,
+ ddi_hp_op_t op, void *arg, void *result);
};
/*
diff --git a/usr/src/uts/common/sys/hotplug/pci/pcicfg.h b/usr/src/uts/common/sys/hotplug/pci/pcicfg.h
index c800f90e5a..943ece4875 100644
--- a/usr/src/uts/common/sys/hotplug/pci/pcicfg.h
+++ b/usr/src/uts/common/sys/hotplug/pci/pcicfg.h
@@ -20,28 +20,35 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_HOTPLUG_PCI_PCICFG_H
#define _SYS_HOTPLUG_PCI_PCICFG_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
+typedef enum pcicfg_flags {
+ /* No probing; used in case of virtual hotplug */
+ PCICFG_FLAG_READ_ONLY = 0x1,
+ /* Enable ARI; used in case of boot case */
+ PCICFG_FLAG_ENABLE_ARI = 0x2
+} pcicfg_flags_t;
+
/*
* Interfaces exported by PCI configurator module, kernel/misc/pcicfg.
*/
-int pcicfg_configure(dev_info_t *, uint_t);
-int pcicfg_unconfigure(dev_info_t *, uint_t);
+int pcicfg_configure(dev_info_t *, uint_t, uint_t, pcicfg_flags_t);
+int pcicfg_unconfigure(dev_info_t *, uint_t, uint_t, pcicfg_flags_t);
#define PCICFG_SUCCESS DDI_SUCCESS
#define PCICFG_FAILURE DDI_FAILURE
+#define PCICFG_ALL_FUNC 0xffffffff
+
/*
* The following subclass definition for Non Transparent bridge should
* be moved to pci.h.
diff --git a/usr/src/uts/common/sys/hotplug/pci/pcie_hp.h b/usr/src/uts/common/sys/hotplug/pci/pcie_hp.h
new file mode 100644
index 0000000000..8810c5203c
--- /dev/null
+++ b/usr/src/uts/common/sys/hotplug/pci/pcie_hp.h
@@ -0,0 +1,332 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_PCIE_HP_H
+#define _SYS_PCIE_HP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _KERNEL
+#include <sys/ddi_hp.h>
+#include <sys/pcie_impl.h>
+#endif /* _KERNEL */
+#include "../../../../../common/pci/pci_strings.h"
+#include <sys/hotplug/pci/pcihp.h>
+
+#define PCIEHPC_PROP_HELP "help"
+#define PCIEHPC_PROP_ALL "all"
+#define PCIEHPC_PROP_LED_FAULT "fault_led"
+#define PCIEHPC_PROP_LED_POWER "power_led"
+#define PCIEHPC_PROP_LED_ATTN "attn_led"
+#define PCIEHPC_PROP_LED_ACTIVE "active_led"
+#define PCIEHPC_PROP_CARD_TYPE "card_type"
+#define PCIEHPC_PROP_BOARD_TYPE "board_type"
+#define PCIEHPC_PROP_SLOT_CONDITION "slot_condition"
+
+#define PCIEHPC_PROP_VALUE_UNKNOWN "unknown"
+#define PCIEHPC_PROP_VALUE_ON "on"
+#define PCIEHPC_PROP_VALUE_OFF "off"
+#define PCIEHPC_PROP_VALUE_BLINK "blink"
+#define PCIEHPC_PROP_VALUE_PCIHOTPLUG "pci hotplug"
+#define PCIEHPC_PROP_VALUE_OK "ok"
+#define PCIEHPC_PROP_VALUE_FAILING "failing"
+#define PCIEHPC_PROP_VALUE_FAILED "failed"
+#define PCIEHPC_PROP_VALUE_UNUSABLE "unusable"
+#define PCIEHPC_PROP_VALUE_LED "<on|off|blink>"
+#define PCIEHPC_PROP_VALUE_TYPE "<type description>"
+#define PCIEHPC_PROP_VALUE_CONDITION "<unknown|ok|failing|failed|unusable>"
+
+/* condition */
+#define PCIEHPC_PROP_COND_OK "ok"
+#define PCIEHPC_PROP_COND_FAILING "failing"
+#define PCIEHPC_PROP_COND_FAILED "failed"
+#define PCIEHPC_PROP_COND_UNUSABLE "unusable"
+#define PCIEHPC_PROP_COND_UNKNOWN "unknown"
+
+#ifdef _KERNEL
+
+#define PCIE_HP_MAX_SLOTS 31 /* Max # of slots */
+#define PCIE_HP_CMD_WAIT_TIME 10000 /* Delay in microseconds */
+#define PCIE_HP_CMD_WAIT_RETRY 100 /* Max retry count */
+#define PCIE_HP_DLL_STATE_CHANGE_TIMEOUT 1 /* Timeout in seconds */
+#define PCIE_HP_POWER_GOOD_WAIT_TIME 220000 /* Wait time after issuing a */
+ /* cmd to change slot state */
+
+/* definations for PCIEHPC/PCISHPC */
+#define PCIE_NATIVE_HP_TYPE "PCIe-Native" /* PCIe Native type */
+#define PCIE_ACPI_HP_TYPE "PCIe-ACPI" /* PCIe ACPI type */
+#define PCIE_PROP_HP_TYPE "PCIe-Proprietary" /* PCIe Prop type */
+#define PCIE_PCI_HP_TYPE "PCI-SHPC" /* PCI (SHPC) type */
+
+#define PCIE_GET_HP_CTRL(dip) \
+ (pcie_hp_ctrl_t *)PCIE_DIP2BUS(dip)->bus_hp_ctrl
+
+#define PCIE_SET_HP_CTRL(dip, ctrl_p) \
+ (PCIE_DIP2BUS(dip)->bus_hp_ctrl) = (pcie_hp_ctrl_t *)ctrl_p
+
+#define PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p) \
+ ((bus_p->bus_hp_sup_modes & PCIE_ACPI_HP_MODE) || \
+ (bus_p->bus_hp_sup_modes & PCIE_NATIVE_HP_MODE))
+
+#define PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p) \
+ (bus_p->bus_hp_sup_modes & PCIE_PCI_HP_MODE)
+
+#define PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p) \
+ ((bus_p->bus_hp_curr_mode == PCIE_ACPI_HP_MODE) || \
+ (bus_p->bus_hp_curr_mode == PCIE_NATIVE_HP_MODE))
+
+#define PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p) \
+ (bus_p->bus_hp_curr_mode & PCIE_PCI_HP_MODE)
+
+typedef struct pcie_hp_ctrl pcie_hp_ctrl_t;
+typedef struct pcie_hp_slot pcie_hp_slot_t;
+
+/*
+ * Maximum length of the string converted from the digital number of pci device
+ * number and function number, including the string's end mark. For example,
+ * device number 0 and function number 255 (ARI case), then the length is
+ * (1 + 3 + 1).
+ */
+#define PCIE_HP_DEV_FUNC_NUM_STRING_LEN 5
+
+/*
+ * Length of the characters in a PCI port name.
+ * The format of the PCI port name is: pci.d,f where d is device number, f is
+ * function number. The constant string and characters are "pci." and ",".
+ */
+#define PCIE_HP_PORT_NAME_STRING_LEN 5
+
+/* Platform specific ops (Native HP, ACPI, etc.) */
+typedef struct pcie_hp_ops {
+ /* initialize/setup hot plug controller hw */
+ int (*init_hpc_hw)(pcie_hp_ctrl_t *ctrl_p);
+
+ /* uninitialize hot plug controller hw */
+ int (*uninit_hpc_hw)(pcie_hp_ctrl_t *ctrl_p);
+
+ /* initialize slot information structure */
+ int (*init_hpc_slotinfo)(pcie_hp_ctrl_t *ctrl_p);
+
+ /* uninitialize slot information structure */
+ int (*uninit_hpc_slotinfo)(pcie_hp_ctrl_t *ctrl_p);
+
+ /* slot poweron */
+ int (*poweron_hpc_slot)(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t *result);
+
+ /* slot poweroff */
+ /* uninitialize hot plug controller hw */
+ int (*poweroff_hpc_slot)(pcie_hp_slot_t *slot_p,
+ ddi_hp_cn_state_t *result);
+
+ /* enable hot plug interrupts/events */
+ int (*enable_hpc_intr)(pcie_hp_ctrl_t *ctrl_p);
+
+ /* disable hot plug interrupts/events */
+ int (*disable_hpc_intr)(pcie_hp_ctrl_t *ctrl_p);
+} pcie_hp_ops_t;
+
+/* Slot occupant information structure */
+#define PCIE_HP_MAX_OCCUPANTS 128
+typedef struct pcie_hp_occupant_info {
+ int i;
+ char *id[PCIE_HP_MAX_OCCUPANTS];
+} pcie_hp_occupant_info_t;
+
+/*
+ * pcie_hp_led_t
+ *
+ * Type definitions for LED type
+ */
+typedef enum {
+ PCIE_HP_FAULT_LED,
+ PCIE_HP_POWER_LED,
+ PCIE_HP_ATTN_LED,
+ PCIE_HP_ACTIVE_LED
+} pcie_hp_led_t;
+
+/*
+ * pcie_hp_led_state_t
+ *
+ * Type definitions for LED state
+ */
+typedef enum {
+ PCIE_HP_LED_OFF,
+ PCIE_HP_LED_ON,
+ PCIE_HP_LED_BLINK
+} pcie_hp_led_state_t;
+
+/*
+ * PCI and PCI Express Hotplug slot structure
+ */
+struct pcie_hp_slot {
+ uint32_t hs_num; /* Logical slot number */
+ uint32_t hs_phy_slot_num; /* Physical slot number */
+ uint32_t hs_device_num; /* PCI device num for slot */
+ uint16_t hs_minor; /* Minor num for this slot */
+ ddi_hp_cn_info_t hs_info; /* Slot information */
+ ddi_hp_cn_state_t hs_state; /* Slot state */
+
+ pcie_hp_led_state_t hs_power_led_state; /* Power LED state */
+ pcie_hp_led_state_t hs_attn_led_state; /* Attn LED state */
+ pcie_hp_led_state_t hs_active_led_state; /* Active LED state */
+ pcie_hp_led_state_t hs_fault_led_state; /* Fault LED state */
+
+ ap_condition_t hs_condition; /* Condition of the slot. */
+ /* For cfgadm condition. */
+
+ /* Synchronization variable(s) for hot plug events */
+ kcondvar_t hs_attn_btn_cv; /* ATTN button pressed intr */
+ boolean_t hs_attn_btn_pending;
+ kthread_t *hs_attn_btn_threadp; /* ATTN button event thread */
+ boolean_t hs_attn_btn_thread_exit;
+ kcondvar_t hs_dll_active_cv; /* DLL State Changed intr */
+
+ pcie_hp_ctrl_t *hs_ctrl; /* Hotplug ctrl for this slot */
+};
+
+/*
+ * Register ops for read/write of non-standard HPC (e.g: OPL platform).
+ */
+typedef struct pcie_hp_regops {
+ uint_t (*get)(void *cookie, off_t offset);
+ uint_t (*put)(void *cookie, off_t offset, uint_t val);
+ void *cookie;
+} pcie_hp_regops_t;
+
+/*
+ * PCI and PCI Express Hotplug controller structure
+ */
+struct pcie_hp_ctrl {
+ dev_info_t *hc_dip; /* DIP for HP controller */
+ kmutex_t hc_mutex; /* Mutex for this ctrl */
+ uint_t hc_flags; /* Misc flags */
+
+ /* Slot information */
+ pcie_hp_slot_t *hc_slots[PCIE_HP_MAX_SLOTS]; /* Slot pointers */
+ boolean_t hc_has_attn; /* Do we have attn btn? */
+ boolean_t hc_has_mrl; /* Do we have MRL? */
+ kcondvar_t hc_cmd_comp_cv; /* Command Completion intr */
+ boolean_t hc_cmd_pending; /* Command completion pending */
+
+ /* PCI Express Hotplug specific fields */
+ boolean_t hc_has_emi_lock; /* Do we have EMI Lock? */
+ boolean_t hc_dll_active_rep; /* Report DLL DL_Active state */
+ pcie_hp_ops_t hc_ops; /* Platform specific ops */
+ /* (Native, ACPI) */
+
+ /* PCI Hotplug (SHPC) specific fields */
+ uint32_t hc_num_slots_impl; /* # of HP Slots Implemented */
+ uint32_t hc_num_slots_connected; /* # of HP Slots Connected */
+ int hc_curr_bus_speed; /* Current Bus Speed */
+ uint32_t hc_device_start; /* 1st PCI Device # */
+ uint32_t hc_phys_start; /* 1st Phys Device # */
+ uint32_t hc_device_increases; /* Device # Increases */
+ boolean_t hc_arbiter_timeout; /* Got a Arb timeout IRQ */
+
+ /* Register read/write ops for non-standard HPC (e.g: OPL) */
+ pcie_hp_regops_t hc_regops;
+
+ /* Platform implementation specific data if any: ACPI, CK804,... */
+ void *hc_misc_data;
+};
+
+/*
+ * Control structure for tree walk during configure/unconfigure operation.
+ */
+typedef struct pcie_hp_cn_cfg_t {
+ void *slotp;
+ boolean_t flag; /* Flag to ignore errors */
+ int rv; /* Return error code */
+ dev_info_t *dip; /* dip at which the (first) */
+ /* error occurred */
+ void *cn_private; /* Connection specific data */
+} pcie_hp_cn_cfg_t;
+
+/*
+ * arg for unregistering port of a pci bridge
+ */
+typedef struct pcie_hp_unreg_port {
+ /* pci bridge dip to which the port is associated */
+ dev_info_t *nexus_dip;
+ /*
+ * Connector number of the physical slot whose dependent ports will be
+ * unregistered. If NULL, then all the ports of the pci bridge dip will
+ * be unregistered.
+ */
+ int connector_num;
+ int rv;
+} pcie_hp_unreg_port_t;
+
+/*
+ * arg for getting a port's state
+ */
+typedef struct pcie_hp_port_state {
+ char *cn_name;
+ ddi_hp_cn_state_t cn_state;
+ int rv;
+} pcie_hp_port_state_t;
+
+/* hc_flags */
+#define PCIE_HP_INITIALIZED_FLAG (1 << 0) /* HPC initialized */
+
+/* PCIe hotplug friendly functions */
+extern int pcie_hp_init(dev_info_t *dip, caddr_t arg);
+extern int pcie_hp_uninit(dev_info_t *dip);
+extern int pcie_hp_intr(dev_info_t *dip);
+extern int pcie_hp_probe(pcie_hp_slot_t *slot_p);
+extern int pcie_hp_unprobe(pcie_hp_slot_t *slot_p);
+extern int pcie_hp_common_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
+ void *arg, void *result);
+extern dev_info_t *pcie_hp_devi_find(dev_info_t *dip, uint_t device,
+ uint_t function);
+extern void pcie_hp_create_occupant_props(dev_info_t *self, dev_t dev,
+ int pci_dev);
+extern void pcie_hp_create_occupant_props(dev_info_t *self, dev_t dev,
+ int pci_dev);
+extern void pcie_hp_delete_occupant_props(dev_info_t *dip, dev_t dev);
+extern int pcie_copyin_nvlist(char *packed_buf, size_t packed_sz,
+ nvlist_t **nvlp);
+extern int pcie_copyout_nvlist(nvlist_t *nvl, char *packed_buf,
+ size_t *packed_sz);
+extern char *pcie_led_state_text(pcie_hp_led_state_t state);
+extern char *pcie_slot_condition_text(ap_condition_t condition);
+extern int pcie_create_minor_node(pcie_hp_ctrl_t *, int);
+extern void pcie_remove_minor_node(pcie_hp_ctrl_t *, int);
+extern void pcie_hp_gen_sysevent_req(char *slot_name, int hint,
+ dev_info_t *self, int kmflag);
+
+extern const struct pci_class_strings_s class_pci[];
+extern int class_pci_items;
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_PCIE_HP_H */
diff --git a/usr/src/uts/common/sys/hotplug/pci/pciehpc.h b/usr/src/uts/common/sys/hotplug/pci/pciehpc.h
index dd1ad7c416..1d57720559 100644
--- a/usr/src/uts/common/sys/hotplug/pci/pciehpc.h
+++ b/usr/src/uts/common/sys/hotplug/pci/pciehpc.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,17 +18,14 @@
*
* CDDL HEADER END
*/
-
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_HOTPLUG_PCI_PCIEHPC_H
#define _SYS_HOTPLUG_PCI_PCIEHPC_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -37,17 +33,22 @@ extern "C" {
/*
* Interfaces exported by PCI-E nexus Hot Plug Controller extension module
*/
-
-/* register ops for read/write of non-standard HPC (e.g: OPL platform) */
-typedef struct pciehpc_regops {
- uint_t (*get)(void *cookie, off_t offset);
- uint_t (*put)(void *cookie, off_t offset, uint_t val);
- void *cookie;
-} pciehpc_regops_t;
-
-int pciehpc_init(dev_info_t *, pciehpc_regops_t *);
-int pciehpc_uninit(dev_info_t *);
-int pciehpc_intr(dev_info_t *);
+int pciehpc_init(dev_info_t *dip, caddr_t arg);
+int pciehpc_uninit(dev_info_t *dip);
+int pciehpc_intr(dev_info_t *dip);
+int pciehpc_hp_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op, void *arg,
+ void *result);
+void pciehpc_get_slot_state(pcie_hp_slot_t *slot_p);
+void pciehpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p);
+uint8_t pciehpc_reg_get8(pcie_hp_ctrl_t *ctrl_p, uint_t off);
+uint16_t pciehpc_reg_get16(pcie_hp_ctrl_t *ctrl_p, uint_t off);
+uint32_t pciehpc_reg_get32(pcie_hp_ctrl_t *ctrl_p, uint_t off);
+void pciehpc_reg_put8(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint8_t val);
+void pciehpc_reg_put16(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint16_t val);
+void pciehpc_reg_put32(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint32_t val);
+#if defined(__i386) || defined(__amd64)
+extern void pciehpc_update_ops(pcie_hp_ctrl_t *ctrl_p);
+#endif /* defined(__i386) || defined(__amd64) */
#ifdef __cplusplus
}
diff --git a/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h b/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h
deleted file mode 100644
index b2915d15e9..0000000000
--- a/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef _SYS_HOTPLUG_PCI_PCIEHPC_IMPL_H
-#define _SYS_HOTPLUG_PCI_PCIEHPC_IMPL_H
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <sys/disp.h>
-#include <sys/stat.h>
-#include <sys/condvar.h>
-#include <sys/pcie.h>
-#include <sys/hotplug/hpcsvc.h>
-#include <sys/hotplug/pci/pciehpc.h>
-
-/*
- * PCI Express Hot Plug slot softstate structure
- *
- */
-typedef struct pciehpc_slot
-{
- hpc_slot_info_t slot_info; /* HPS framework slot info */
- hpc_slot_t slot_handle; /* HPS framework handle */
- hpc_slot_ops_t slot_ops; /* HPS framework callbacks */
- uint32_t fault_led_state; /* Fault LED state */
- uint32_t power_led_state; /* Power LED state */
- uint32_t attn_led_state; /* Attn LED state */
- uint32_t active_led_state; /* Active LED state */
- hpc_slot_state_t slot_state; /* Slot State */
- uint32_t slotNum; /* slot number */
- /* synchronization variable(s) for hot plug events */
- kcondvar_t cmd_comp_cv; /* Command Completion intr. */
- boolean_t command_pending;
- kcondvar_t attn_btn_cv; /* ATTN button pressed intr */
- boolean_t attn_btn_pending;
- kthread_t *attn_btn_threadp; /* ATTN button event thread */
- boolean_t attn_btn_thread_exit;
- kcondvar_t dll_active_cv; /* DLL State Changed intr */
-} pciehpc_slot_t;
-
-typedef enum {
- PCIEHPC_NATIVE_HP_MODE, PCIEHPC_ACPI_HP_MODE
-} pciehpc_hp_mode_t;
-
-typedef uint32_t pciehpc_soft_state_t;
-
-/* init_flags */
-#define PCIEHPC_SOFT_STATE_UNINITIALIZED 0x01
-#define PCIEHPC_SOFT_STATE_INITIALIZED 0x02
-#define PCIEHPC_SOFT_STATE_INIT_HTABLE 0x04
-#define PCIEHPC_SOFT_STATE_INIT_ALLOC 0x08
-#define PCIEHPC_SOFT_STATE_INIT_HANDLER 0x10
-#define PCIEHPC_SOFT_STATE_INIT_ENABLE 0x20
-#define PCIEHPC_SOFT_STATE_INIT_BLOCK 0x40
-#define PCIEHPC_SOFT_STATE_INIT_FM 0x80
-#define PCIEHPC_SOFT_STATE_PCIE_DEV 0x10000
-
-/*
- * PCI Express Hotplug controller soft state structure
- */
-typedef struct pciehpc
-{
- dev_info_t *dip; /* DIP for the Nexus */
- uint8_t bus; /* primary bus number */
- uint8_t dev; /* device number */
- uint8_t func; /* function number */
- kmutex_t pciehpc_mutex; /* Mutex for this ctrl */
- pciehpc_soft_state_t soft_state; /* soft state flags */
- pciehpc_hp_mode_t hp_mode; /* HP mode (Native, ACPI) */
- struct pciehpc *nextp; /* Linked list pointer */
-
- /* PCIE Hot Plug Controller register access */
- ddi_acc_handle_t cfghdl; /* PCI cfg access handle */
- caddr_t regs_base; /* config regs base */
- uint_t pcie_caps_reg_offset; /* offset to PCIE Cap regs */
-
- /* slot information */
- pciehpc_slot_t slot; /* Slot info */
- boolean_t has_attn; /* Do we have attn btn? */
- boolean_t has_mrl; /* Do we have MRL? */
- boolean_t has_emi_lock; /* Do we have EMI Lock? */
-
- /* link capablities */
- boolean_t dll_active_rep; /* Do we report DLL DL_Active state? */
-
- /* register read/write ops for non-standard HPC (e.g: OPL) */
- pciehpc_regops_t regops;
-
- /* platform specific ops (Native HP, ACPI, etc.) */
- struct pciehpc_ops {
- /* initialize/setup hot plug controller hw */
- int (*init_hpc_hw)(struct pciehpc *ctrl_p);
- /* initialize slot information structure */
- int (*init_hpc_slotinfo)(struct pciehpc *ctrl_p);
- /* disable hot plug interrupts/events */
- int (*disable_hpc_intr)(struct pciehpc *ctrl_p);
- /* enable hot plug interrupts/events */
- int (*enable_hpc_intr)(struct pciehpc *ctrl_p);
- /* uninitialize hot plug controller hw */
- int (*uninit_hpc_hw)(struct pciehpc *ctrl_p);
- /* uninitialize slot information structure */
- int (*uninit_hpc_slotinfo)(struct pciehpc *ctrl_p);
- /* probe for HPC */
- int (*probe_hpc)(struct pciehpc *ctrl_p);
- } ops;
-
- /* platform implementation specific data if any: ACPI, CK804,... */
- void *misc_data;
-} pciehpc_t;
-
-typedef struct pciehpc_ops pciehpc_ops_t;
-
-/*
- * PCI-E HPC Command Completion delay in microseconds and the max retry
- * count.
- */
-#define PCIEHPC_CMD_WAIT_TIME 10000
-#define PCIEHPC_CMD_WAIT_RETRY 100
-
-/*
- * PCI-E HPC Dll State Change time out in seconds
- */
-#define PCIEHPC_DLL_STATE_CHANGE_TIMEOUT 1
-
-#define SLOTCTL_SUPPORTED_INTRS_MASK \
- (PCIE_SLOTCTL_ATTN_BTN_EN \
- | PCIE_SLOTCTL_PWR_FAULT_EN \
- | PCIE_SLOTCTL_MRL_SENSOR_EN \
- | PCIE_SLOTCTL_PRESENCE_CHANGE_EN \
- | PCIE_SLOTCTL_CMD_INTR_EN \
- | PCIE_SLOTCTL_HP_INTR_EN \
- | PCIE_SLOTCTL_DLL_STATE_EN)
-
-#define SLOT_STATUS_EVENTS \
- (PCIE_SLOTSTS_ATTN_BTN_PRESSED \
- | PCIE_SLOTSTS_PWR_FAULT_DETECTED \
- | PCIE_SLOTSTS_MRL_SENSOR_CHANGED \
- | PCIE_SLOTSTS_COMMAND_COMPLETED \
- | PCIE_SLOTSTS_PRESENCE_CHANGED \
- | PCIE_SLOTSTS_DLL_STATE_CHANGED)
-
-/*
- * function prototype defintions for common native mode functions in
- * PCIEHPC module.
- */
-int pciehpc_hpc_init(pciehpc_t *ctrl_p);
-int pciehpc_hpc_uninit(pciehpc_t *ctrl_p);
-int pciehpc_slotinfo_init(pciehpc_t *ctrl_p);
-int pciehpc_enable_intr(pciehpc_t *ctrl_p);
-int pciehpc_disable_intr(pciehpc_t *ctrl_p);
-int pciehpc_slotinfo_uninit(pciehpc_t *ctrl_p);
-int pciehpc_probe_hpc(pciehpc_t *ctrl_p);
-hpc_led_state_t pciehpc_led_state_to_hpc(uint16_t state);
-uint16_t pciehpc_led_state_to_pciehpc(hpc_led_state_t state);
-hpc_led_state_t pciehpc_get_led_state(pciehpc_t *ctrl_p, hpc_led_t led);
-void pciehpc_set_led_state(pciehpc_t *ctrl_p, hpc_led_t led,
- hpc_led_state_t state);
-int pciehpc_slot_connect(caddr_t ops_arg, hpc_slot_t slot_hdl,
- void *data, uint_t flags);
-int pciehpc_slot_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl,
- void *data, uint_t flags);
-int pciehpc_slot_control(caddr_t ops_arg, hpc_slot_t slot_hdl,
- int request, caddr_t arg);
-void pciehpc_get_slot_state(pciehpc_t *ctrl_p);
-void pciehpc_issue_hpc_command(pciehpc_t *ctrl_p, uint16_t control);
-int pciehpc_regs_setup(dev_info_t *dip, uint_t rnum, offset_t off,
- caddr_t *addrp, ddi_acc_handle_t *handle);
-void pciehpc_regs_teardown(ddi_acc_handle_t *handle);
-int pciehpc_register_slot(pciehpc_t *ctrl_p);
-int pciehpc_unregister_slot(pciehpc_t *ctrl_p);
-uint8_t pciehpc_reg_get8(pciehpc_t *ctrl_p, uint_t off);
-uint16_t pciehpc_reg_get16(pciehpc_t *ctrl_p, uint_t off);
-uint32_t pciehpc_reg_get32(pciehpc_t *ctrl_p, uint_t off);
-void pciehpc_reg_put8(pciehpc_t *ctrl_p, uint_t off, uint8_t val);
-void pciehpc_reg_put16(pciehpc_t *ctrl_p, uint_t off, uint16_t val);
-void pciehpc_reg_put32(pciehpc_t *ctrl_p, uint_t off, uint32_t val);
-void pciehpc_set_slot_name(pciehpc_t *ctrl_p);
-
-#if defined(__i386) || defined(__amd64)
-void pciehpc_update_ops(pciehpc_t *ctrl_p);
-#endif /* defined(__i386) || defined(__amd64) */
-
-#ifdef DEBUG
-extern int pciehpc_debug;
-#define PCIEHPC_DEBUG(args) if (pciehpc_debug >= 1) cmn_err args
-#define PCIEHPC_DEBUG2(args) if (pciehpc_debug >= 2) cmn_err args
-#define PCIEHPC_DEBUG3(args) if (pciehpc_debug >= 3) cmn_err args
-#else
-#define PCIEHPC_DEBUG(args)
-#define PCIEHPC_DEBUG2(args)
-#define PCIEHPC_DEBUG3(args)
-#endif
-
-/* default interrupt priority for Hot Plug interrupts */
-#define PCIEHPC_INTR_PRI 1
-
-#define PCIE_ENABLE_ERRORS(arg1) \
- pcie_enable_errors(arg1); \
- (void) pcie_enable_ce(arg1)
-#define PCIE_DISABLE_ERRORS(arg1) pcie_disable_errors(arg1)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _SYS_HOTPLUG_PCI_PCIEHPC_IMPL_H */
diff --git a/usr/src/uts/common/sys/hotplug/pci/pcihp.h b/usr/src/uts/common/sys/hotplug/pci/pcihp.h
index 75a4ee0c2d..eb96e924bd 100644
--- a/usr/src/uts/common/sys/hotplug/pci/pcihp.h
+++ b/usr/src/uts/common/sys/hotplug/pci/pcihp.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_HOTPLUG_PCI_PCIHP_H
#define _SYS_HOTPLUG_PCI_PCIHP_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/usr/src/uts/common/sys/hotplug/pci/pcishpc.h b/usr/src/uts/common/sys/hotplug/pci/pcishpc.h
index 81443bbaa4..12b9afd30a 100644
--- a/usr/src/uts/common/sys/hotplug/pci/pcishpc.h
+++ b/usr/src/uts/common/sys/hotplug/pci/pcishpc.h
@@ -18,32 +18,26 @@
*
* CDDL HEADER END
*/
-
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#ifndef _SYS_PCISHPC_H
-#define _SYS_PCISHPC_H
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+#ifndef _SYS_HOTPLUG_PCI_PCISHPC_H
+#define _SYS_HOTPLUG_PCI_PCISHPC_H
#ifdef __cplusplus
extern "C" {
#endif
-/*
- * Interfaces exported by SHPC Nexus extension module
- */
-
-int pcishpc_init(dev_info_t *);
-int pcishpc_uninit(dev_info_t *);
-int pcishpc_intr(dev_info_t *);
+int pcishpc_init(dev_info_t *dip);
+int pcishpc_uninit(dev_info_t *dip);
+int pcishpc_intr(dev_info_t *dip);
+int pcishpc_hp_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op, void *arg,
+ void *result);
-#define PCISHPC_INTR_PRI (LOCK_LEVEL - 1)
#ifdef __cplusplus
}
#endif
-#endif /* _SYS_PCISHPC_H */
+#endif /* _SYS_HOTPLUG_PCI_PCISHPC_H */
diff --git a/usr/src/uts/common/sys/hotplug/pci/pcishpc_regs.h b/usr/src/uts/common/sys/hotplug/pci/pcishpc_regs.h
deleted file mode 100755
index d487067f59..0000000000
--- a/usr/src/uts/common/sys/hotplug/pci/pcishpc_regs.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef _SYS_HOTPLUG_PCI_PCISHPC_REGS_H
-#define _SYS_HOTPLUG_PCI_PCISHPC_REGS_H
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * SHPC controller registers accessed via the SHPC DWORD select and DATA
- * registers in PCI configuration space relative to the SHPC capibility
- * pointer.
- */
-#define SHPC_DWORD_SELECT_OFF 0x2
-#define SHPC_DWORD_DATA_OFF 0x4
-
-#define SHPC_BASE_OFFSET_REG 0x00
-#define SHPC_SLOTS_AVAIL_I_REG 0x01
-#define SHPC_SLOTS_AVAIL_II_REG 0x02
-#define SHPC_SLOT_CONFIGURATION_REG 0x03
-#define SHPC_PROF_IF_SBCR_REG 0x04
-#define SHPC_COMMAND_STATUS_REG 0x05
-#define SHPC_IRQ_LOCATOR_REG 0x06
-#define SHPC_SERR_LOCATOR_REG 0x07
-#define SHPC_CTRL_SERR_INT_REG 0x08
-#define SHPC_LOGICAL_SLOT_REGS 0x09
-#define SHPC_VENDOR_SPECIFIC 0x28
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _SYS_HOTPLUG_PCI_PCISHPC_REGS_H */
diff --git a/usr/src/uts/common/sys/modctl.h b/usr/src/uts/common/sys/modctl.h
index 7ccd1d6dd4..3ac19b948e 100644
--- a/usr/src/uts/common/sys/modctl.h
+++ b/usr/src/uts/common/sys/modctl.h
@@ -269,6 +269,7 @@ struct modlinkage {
#define MODISRETIRED 42
#define MODDEVEMPTYDIR 43
#define MODREMDRVALIAS 44
+#define MODHPOPS 45
/*
* sub cmds for MODEVENTS
@@ -289,6 +290,15 @@ struct modlinkage {
#define MODDEVNAME_RECONFIG 4
#define MODDEVNAME_SYSAVAIL 5
+/*
+ * subcmds for MODHPOPS
+ */
+#define MODHPOPS_CHANGE_STATE 0
+#define MODHPOPS_CREATE_PORT 1
+#define MODHPOPS_REMOVE_PORT 2
+#define MODHPOPS_BUS_GET 3
+#define MODHPOPS_BUS_SET 4
+
/*
* Data structure passed to modconfig command in kernel to build devfs tree
diff --git a/usr/src/uts/common/sys/pci.h b/usr/src/uts/common/sys/pci.h
index efb19097e9..9d38b5bb37 100644
--- a/usr/src/uts/common/sys/pci.h
+++ b/usr/src/uts/common/sys/pci.h
@@ -603,7 +603,7 @@ extern "C" {
#define PCI_CAP_ID_VS 0x9 /* Vendor Specific */
#define PCI_CAP_ID_DEBUG_PORT 0xA /* Debug Port supported */
#define PCI_CAP_ID_cPCI_CRC 0xB /* CompactPCI central resource ctrl */
-#define PCI_CAP_ID_PCI_HOTPLUG 0xC /* PCI Hot Plug supported */
+#define PCI_CAP_ID_PCI_HOTPLUG 0xC /* PCI Hot Plug (SHPC) supported */
#define PCI_CAP_ID_P2P_SUBSYS 0xD /* PCI bridge Sub-system ID */
#define PCI_CAP_ID_AGP_8X 0xE /* AGP 8X supported */
#define PCI_CAP_ID_SECURE_DEV 0xF /* Secure Device supported */
@@ -796,6 +796,104 @@ typedef struct pcix_attr {
#define PCI_PCIX_BSS_SPL_DLY 0x20 /* Secondary split comp delayed */
/*
+ * PCI Hotplug capability entry offsets
+ *
+ * SHPC based PCI hotplug controller registers accessed via the DWORD
+ * select and DATA registers in PCI configuration space relative to the
+ * PCI HP capibility pointer.
+ */
+#define PCI_HP_DWORD_SELECT_OFF 0x2
+#define PCI_HP_DWORD_DATA_OFF 0x4
+
+#define PCI_HP_BASE_OFFSET_REG 0x00
+#define PCI_HP_SLOTS_AVAIL_I_REG 0x01
+#define PCI_HP_SLOTS_AVAIL_II_REG 0x02
+#define PCI_HP_SLOT_CONFIGURATION_REG 0x03
+#define PCI_HP_PROF_IF_SBCR_REG 0x04
+#define PCI_HP_COMMAND_STATUS_REG 0x05
+#define PCI_HP_IRQ_LOCATOR_REG 0x06
+#define PCI_HP_SERR_LOCATOR_REG 0x07
+#define PCI_HP_CTRL_SERR_INT_REG 0x08
+#define PCI_HP_LOGICAL_SLOT_REGS 0x09
+#define PCI_HP_VENDOR_SPECIFIC 0x28
+
+/* Definitions used with the PCI_HP_SLOTS_AVAIL_I_REG register */
+#define PCI_HP_AVAIL_33MHZ_CONV_SPEED_SHIFT 0
+#define PCI_HP_AVAIL_66MHZ_PCIX_SPEED_SHIFT 8
+#define PCI_HP_AVAIL_100MHZ_PCIX_SPEED_SHIFT 16
+#define PCI_HP_AVAIL_133MHZ_PCIX_SPEED_SHIFT 24
+#define PCI_HP_AVAIL_SPEED_MASK 0x1F
+
+/* Definitions used with the PCI_HP_SLOTS_AVAIL_II_REG register */
+#define PCI_HP_AVAIL_66MHZ_CONV_SPEED_SHIFT 0
+
+/* Register bits used with the PCI_HP_PROF_IF_SBCR_REG register */
+#define PCI_HP_SBCR_33MHZ_CONV_SPEED 0x0
+#define PCI_HP_SBCR_66MHZ_CONV_SPEED 0x1
+#define PCI_HP_SBCR_66MHZ_PCIX_SPEED 0x2
+#define PCI_HP_SBCR_100MHZ_PCIX_SPEED 0x3
+#define PCI_HP_SBCR_133MHZ_PCIX_SPEED 0x4
+#define PCI_HP_SBCR_SPEED_MASK 0x7
+
+/* Register bits used with the PCI_HP_COMMAND_STATUS_REG register */
+#define PCI_HP_COMM_STS_ERR_INVALID_SPEED 0x80000
+#define PCI_HP_COMM_STS_ERR_INVALID_COMMAND 0x40000
+#define PCI_HP_COMM_STS_ERR_MRL_OPEN 0x20000
+#define PCI_HP_COMM_STS_ERR_MASK 0xe0000
+#define PCI_HP_COMM_STS_CTRL_BUSY 0x10000
+#define PCI_HP_COMM_STS_SET_SPEED 0x40
+
+/* Register bits used with the PCI_HP_CTRL_SERR_INT_REG register */
+#define PCI_HP_SERR_INT_GLOBAL_IRQ_MASK 0x1
+#define PCI_HP_SERR_INT_GLOBAL_SERR_MASK 0x2
+#define PCI_HP_SERR_INT_CMD_COMPLETE_MASK 0x4
+#define PCI_HP_SERR_INT_ARBITER_SERR_MASK 0x8
+#define PCI_HP_SERR_INT_CMD_COMPLETE_IRQ 0x10000
+#define PCI_HP_SERR_INT_ARBITER_IRQ 0x20000
+#define PCI_HP_SERR_INT_MASK_ALL 0xf
+
+/* Register bits used with the PCI_HP_LOGICAL_SLOT_REGS register */
+#define PCI_HP_SLOT_POWER_ONLY 0x1
+#define PCI_HP_SLOT_ENABLED 0x2
+#define PCI_HP_SLOT_DISABLED 0x3
+#define PCI_HP_SLOT_STATE_MASK 0x3
+#define PCI_HP_SLOT_MRL_STATE_MASK 0x100
+#define PCI_HP_SLOT_66MHZ_CONV_CAPABLE 0x200
+#define PCI_HP_SLOT_CARD_EMPTY_MASK 0xc00
+#define PCI_HP_SLOT_66MHZ_PCIX_CAPABLE 0x1000
+#define PCI_HP_SLOT_100MHZ_PCIX_CAPABLE 0x2000
+#define PCI_HP_SLOT_133MHZ_PCIX_CAPABLE 0x3000
+#define PCI_HP_SLOT_PCIX_CAPABLE_MASK 0x3000
+#define PCI_HP_SLOT_PCIX_CAPABLE_SHIFT 12
+#define PCI_HP_SLOT_PRESENCE_DETECTED 0x10000
+#define PCI_HP_SLOT_ISO_PWR_DETECTED 0x20000
+#define PCI_HP_SLOT_ATTN_DETECTED 0x40000
+#define PCI_HP_SLOT_MRL_DETECTED 0x80000
+#define PCI_HP_SLOT_POWER_DETECTED 0x100000
+#define PCI_HP_SLOT_PRESENCE_MASK 0x1000000
+#define PCI_HP_SLOT_ISO_PWR_MASK 0x2000000
+#define PCI_HP_SLOT_ATTN_MASK 0x4000000
+#define PCI_HP_SLOT_MRL_MASK 0x8000000
+#define PCI_HP_SLOT_POWER_MASK 0x10000000
+#define PCI_HP_SLOT_MRL_SERR_MASK 0x20000000
+#define PCI_HP_SLOT_POWER_SERR_MASK 0x40000000
+#define PCI_HP_SLOT_MASK_ALL 0x5f000000
+
+/* Register bits used with the PCI_HP_IRQ_LOCATOR_REG register */
+#define PCI_HP_IRQ_CMD_COMPLETE 0x1
+#define PCI_HP_IRQ_SLOT_N_PENDING 0x2
+
+/* Register bits used with the PCI_HP_SERR_LOCATOR_REG register */
+#define PCI_HP_IRQ_SERR_ARBITER_PENDING 0x1
+#define PCI_HP_IRQ_SERR_SLOT_N_PENDING 0x2
+
+/* Register bits used with the PCI_HP_SLOT_CONFIGURATION_REG register */
+#define PCI_HP_SLOT_CONFIG_MRL_SENSOR 0x40000000
+#define PCI_HP_SLOT_CONFIG_ATTN_BUTTON 0x80000000
+#define PCI_HP_SLOT_CONFIG_PHY_SLOT_NUM_SHIFT 16
+#define PCI_HP_SLOT_CONFIG_PHY_SLOT_NUM_MASK 0x3FF
+
+/*
* PCI Message Signalled Interrupts (MSI) capability entry offsets for 32-bit
*/
#define PCI_MSI_CTRL 0x02 /* MSI control register, 2 bytes */
diff --git a/usr/src/uts/common/sys/pci_impl.h b/usr/src/uts/common/sys/pci_impl.h
index 0dc0679bd4..d485800b96 100644
--- a/usr/src/uts/common/sys/pci_impl.h
+++ b/usr/src/uts/common/sys/pci_impl.h
@@ -26,7 +26,6 @@
#ifndef _SYS_PCI_IMPL_H
#define _SYS_PCI_IMPL_H
-
#include <sys/dditypes.h>
#include <sys/memlist.h>
@@ -133,6 +132,24 @@ extern int memlist_count(struct memlist *);
#endif /* __i386 || __amd64 */
+/* Definitions for minor numbers */
+#define PCI_MINOR_NUM(x, y) (((uint_t)(x) << 8) | ((y) & 0xFF))
+#define PCI_MINOR_NUM_TO_PCI_DEVNUM(x) ((x) & 0xFF)
+#define PCI_MINOR_NUM_TO_INSTANCE(x) ((x) >> 8)
+#define PCI_DEVCTL_MINOR 0xFF
+
+/*
+ * Minor numbers for dedicated pcitool nodes.
+ * Note that FF and FE minor numbers are used for other minor nodes.
+ */
+#define PCI_TOOL_REG_MINOR_NUM 0xFD
+#define PCI_TOOL_INTR_MINOR_NUM 0xFC
+
+/* pci devctl soft state flag */
+#define PCI_SOFT_STATE_CLOSED 0x0
+#define PCI_SOFT_STATE_OPEN 0x1
+#define PCI_SOFT_STATE_OPEN_EXCL 0x2
+
/*
* PCI capability related definitions.
*/
diff --git a/usr/src/uts/common/sys/pcie.h b/usr/src/uts/common/sys/pcie.h
index 6ffa4ac033..05b70a56fa 100644
--- a/usr/src/uts/common/sys/pcie.h
+++ b/usr/src/uts/common/sys/pcie.h
@@ -50,6 +50,15 @@ extern "C" {
#define PCIE_SLOTSTS 0x1A /* Slot Status */
#define PCIE_ROOTCTL 0x1C /* Root Control */
#define PCIE_ROOTSTS 0x20 /* Root Status */
+#define PCIE_DEVCAP2 0x24 /* Device Capability 2 */
+#define PCIE_DEVCTL2 0x28 /* Device Control 2 */
+#define PCIE_DEVSTS2 0x2A /* Device Status 2 */
+#define PCIE_LINKCAP2 0x2C /* Link Capability 2 */
+#define PCIE_LINKCTL2 0x30 /* Link Control 2 */
+#define PCIE_LINKSTS2 0x32 /* Link Status 2 */
+#define PCIE_SLOTCAP2 0x34 /* Slot Capability 2 */
+#define PCIE_SLOTCTL2 0x38 /* Slot Control 2 */
+#define PCIE_SLOTSTS2 0x3A /* Slot Status 2 */
/*
* PCI-Express Config Space size
@@ -60,6 +69,7 @@ extern "C" {
* PCI-Express Capabilities Register (2 bytes)
*/
#define PCIE_PCIECAP_VER_1_0 0x1 /* PCI-E spec 1.0 */
+#define PCIE_PCIECAP_VER_2_0 0x2 /* PCI-E spec 2.0 */
#define PCIE_PCIECAP_VER_MASK 0xF /* Version Mask */
#define PCIE_PCIECAP_DEV_TYPE_PCIE_DEV 0x00 /* PCI-E Endpont Device */
#define PCIE_PCIECAP_DEV_TYPE_PCI_DEV 0x10 /* "Leg PCI" Endpont Device */
@@ -303,6 +313,7 @@ extern "C" {
#define PCIE_SLOTCTL_DLL_STATE_EN 0x1000 /* DLL State Changed En */
#define PCIE_SLOTCTL_ATTN_INDICATOR_MASK 0x00C0 /* Attn Indicator mask */
#define PCIE_SLOTCTL_PWR_INDICATOR_MASK 0x0300 /* Power Indicator mask */
+#define PCIE_SLOTCTL_INTR_MASK 0x103f /* Supported intr mask */
/* State values for the Power and Attention Indicators */
#define PCIE_SLOTCTL_INDICATOR_STATE_ON 0x1 /* indicator ON */
@@ -334,6 +345,7 @@ extern "C" {
#define PCIE_SLOTSTS_PRESENCE_DETECTED 0x40 /* Card Present in slot */
#define PCIE_SLOTSTS_EMI_LOCK_SET 0x0080 /* EMI Lock set */
#define PCIE_SLOTSTS_DLL_STATE_CHANGED 0x0100 /* DLL State Changed */
+#define PCIE_SLOTSTS_STATUS_EVENTS 0x11f /* Supported events */
/*
* Root Control Register (2 bytes)
@@ -352,6 +364,49 @@ extern "C" {
#define PCIE_ROOTSTS_PME_STATUS 0x10000 /* PME Status */
#define PCIE_ROOTSTS_PME_PENDING 0x20000 /* PME Pending */
+/*
+ * Device Capabilities 2 Register (4 bytes)
+ */
+#define PCIE_DEVCAP2_COM_TO_RANGE_MASK 0xF
+#define PCIE_DEVCAP2_COM_TO_DISABLE 0x10
+#define PCIE_DEVCAP2_ARI_FORWARD 0x20
+#define PCIE_DEVCAP2_ATOMICOP_ROUTING 0x40
+#define PCIE_DEVCAP2_32_ATOMICOP_COMPL 0x80
+#define PCIE_DEVCAP2_64_ATOMICOP_COMPL 0x100
+#define PCIE_DEVCAP2_128_CAS_COMPL 0x200
+#define PCIE_DEVCAP2_NO_RO_PR_PR_PASS 0x400
+#define PCIE_DEVCAP2_LTR_MECH 0x800
+#define PCIE_DEVCAP2_TPH_COMP_SHIFT 12
+#define PCIE_DEVCAP2_TPH_COMP_MASK 0x3
+#define PCIE_DEVCAP2_EXT_FMT_FIELD 0x100000
+#define PCIE_DEVCAP2_END_END_TLP_PREFIX 0x200000
+#define PCIE_DEVCAP2_MAX_END_END_SHIFT 22
+#define PCIE_DEVCAP2_MAX_END_END_MASK 0x3
+
+/*
+ * Device Control 2 Register (2 bytes)
+ */
+#define PCIE_DEVCTL2_COM_TO_RANGE_0 0x0
+#define PCIE_DEVCTL2_COM_TO_RANGE_1 0x1
+#define PCIE_DEVCTL2_COM_TO_RANGE_2 0x2
+#define PCIE_DEVCTL2_COM_TO_RANGE_3 0x5
+#define PCIE_DEVCTL2_COM_TO_RANGE_4 0x6
+#define PCIE_DEVCTL2_COM_TO_RANGE_5 0x9
+#define PCIE_DEVCTL2_COM_TO_RANGE_6 0xa
+#define PCIE_DEVCTL2_COM_TO_RANGE_7 0xd
+#define PCIE_DEVCTL2_COM_TO_RANGE_8 0xe
+#define PCIE_DEVCTL2_COM_TO_DISABLE 0x10
+#define PCIE_DEVCTL2_ARI_FORWARD_EN 0x20
+#define PCIE_DEVCTL2_ATOMICOP_REQ_EN 0x40
+#define PCIE_DEVCTL2_ATOMICOP_EGRS_BLK 0x80
+#define PCIE_DEVCTL2_IDO_REQ_EN 0x100
+#define PCIE_DEVCTL2_IDO_COMPL_EN 0x200
+#define PCIE_DEVCTL2_LTR_MECH_EN 0x400
+#define PCIE_DEVCTL2_END_END_TLP_PREFIX 0x8000
+
+
+
+
/*
* PCI-Express Enhanced Capabilities Link Entry Bit Offsets
@@ -549,6 +604,25 @@ extern "C" {
#define PCIE_SER_SID_UPPER_DW 0x8 /* Upper 32-bit Serial Number */
/*
+ * ARI Capability Offsets
+ */
+#define PCIE_ARI_HDR 0x0 /* Enhanced Capability Header */
+#define PCIE_ARI_CAP 0x4 /* ARI Capability Register */
+#define PCIE_ARI_CTL 0x6 /* ARI Control Register */
+
+#define PCIE_ARI_CAP_MFVC_FUNC_GRP 0x01
+#define PCIE_ARI_CAP_ASC_FUNC_GRP 0x02
+
+#define PCIE_ARI_CAP_NEXT_FUNC_SHIFT 8
+#define PCIE_ARI_CAP_NEXT_FUNC_MASK 0xffff
+
+#define PCIE_ARI_CTRL_MFVC_FUNC_GRP 0x01
+#define PCIE_ARI_CTRL_ASC_FUNC_GRP 0x02
+
+#define PCIE_ARI_CTRL_FUNC_GRP_SHIFT 4
+#define PCIE_ARI_CTRL_FUNC_GRP_MASK 0x7
+
+/*
* PCI-E Common TLP Header Fields
*/
#define PCIE_TLP_FMT_3DW 0x00
@@ -595,6 +669,7 @@ typedef uint16_t pcie_req_id_t;
#define PCIE_REQ_ID_DEV_MASK 0x00F8
#define PCIE_REQ_ID_FUNC_SHIFT 0
#define PCIE_REQ_ID_FUNC_MASK 0x0007
+#define PCIE_REQ_ID_ARI_FUNC_MASK 0x00FF
#define PCIE_CPL_STS_SUCCESS 0
#define PCIE_CPL_STS_UR 1
diff --git a/usr/src/uts/common/sys/pcie_impl.h b/usr/src/uts/common/sys/pcie_impl.h
index 1e88407b1c..0943da2c92 100644
--- a/usr/src/uts/common/sys/pcie_impl.h
+++ b/usr/src/uts/common/sys/pcie_impl.h
@@ -65,6 +65,14 @@ extern "C" {
#define PCIE_HAS_AER(bus_p) (bus_p->bus_aer_off)
/* IS_ROOT = is RC or RP */
#define PCIE_IS_ROOT(bus_p) (PCIE_IS_RC(bus_p) || PCIE_IS_RP(bus_p))
+
+#define PCIE_IS_HOTPLUG_CAPABLE(dip) \
+ (PCIE_DIP2BUS(dip)->bus_hp_sup_modes)
+
+#define PCIE_IS_HOTPLUG_ENABLED(dip) \
+ ((PCIE_DIP2BUS(dip)->bus_hp_curr_mode == PCIE_PCI_HP_MODE) || \
+ (PCIE_DIP2BUS(dip)->bus_hp_curr_mode == PCIE_NATIVE_HP_MODE))
+
/*
* This is a pseudo pcie "device type", but it's needed to explain describe
* nodes such as PX and NPE, which aren't really PCI devices but do control or
@@ -154,6 +162,14 @@ extern "C" {
#define PFD_IS_RC(pfd_p) PCIE_IS_RC(PCIE_PFD2BUS(pfd_p))
#define PFD_IS_RP(pfd_p) PCIE_IS_RP(PCIE_PFD2BUS(pfd_p))
+/* bus_hp_mode field */
+typedef enum {
+ PCIE_NONE_HP_MODE = 0x0,
+ PCIE_ACPI_HP_MODE = 0x1,
+ PCIE_PCI_HP_MODE = 0x2,
+ PCIE_NATIVE_HP_MODE = 0x4
+} pcie_hp_mode_t;
+
typedef struct pf_pci_bdg_err_regs {
uint16_t pci_bdg_sec_stat; /* PCI secondary status reg */
uint16_t pci_bdg_ctrl; /* PCI bridge control reg */
@@ -244,8 +260,9 @@ typedef struct pcie_bus {
/* Needed for PCI/PCIe fabric error handling */
dev_info_t *bus_dip;
dev_info_t *bus_rp_dip;
- ddi_acc_handle_t bus_cfg_hdl; /* error handling acc handle */
+ ddi_acc_handle_t bus_cfg_hdl; /* error handling acc hdle */
uint_t bus_fm_flags;
+ uint_t bus_soft_state;
/* Static PCI/PCIe information */
pcie_req_id_t bus_bdf;
@@ -258,6 +275,7 @@ typedef struct pcie_bus {
uint16_t bus_pcie_off; /* PCIe Capability Offset */
uint16_t bus_aer_off; /* PCIe Advanced Error Offset */
uint16_t bus_pcix_off; /* PCIx Capability Offset */
+ uint16_t bus_pci_hp_off; /* PCI HP (SHPC) Cap Offset */
uint16_t bus_ecc_ver; /* PCIX ecc version */
pci_bus_range_t bus_bus_range; /* pci bus-range property */
ppb_ranges_t *bus_addr_ranges; /* pci range property */
@@ -271,6 +289,11 @@ typedef struct pcie_bus {
int bus_mps; /* Maximum Payload Size */
void *bus_plat_private; /* Platform specific */
+ /* Hotplug specific fields */
+ pcie_hp_mode_t bus_hp_sup_modes; /* HP modes supported */
+ pcie_hp_mode_t bus_hp_curr_mode; /* HP mode used */
+ void *bus_hp_ctrl; /* HP bus ctrl data */
+ int bus_ari; /* ARI device */
} pcie_bus_t;
struct pf_data {
@@ -355,8 +378,28 @@ typedef struct {
int highest_common_mps;
} pcie_max_supported_t;
+/*
+ * Default interrupt priority for all PCI and PCIe nexus drivers including
+ * hotplug interrupts.
+ */
+#define PCIE_INTR_PRI (LOCK_LEVEL - 1)
+
+/*
+ * XXX - PCIE_IS_PCIE check is required in order not to invoke these macros
+ * for non-standard PCI or PCI Express Hotplug Controllers.
+ */
+#define PCIE_ENABLE_ERRORS(dip) \
+ if (PCIE_IS_PCIE(PCIE_DIP2BUS(dip))) { \
+ pcie_enable_errors(dip); \
+ (void) pcie_enable_ce(dip); \
+ }
+
+#define PCIE_DISABLE_ERRORS(dip) \
+ if (PCIE_IS_PCIE(PCIE_DIP2BUS(dip))) { \
+ pcie_disable_errors(dip); \
+ }
+
#ifdef DEBUG
-extern uint_t pcie_debug_flags;
#define PCIE_DBG pcie_dbg
/* Common Debugging shortcuts */
#define PCIE_DBG_CFG(dip, bus_p, name, sz, off, org) \
@@ -372,18 +415,29 @@ extern uint_t pcie_debug_flags;
ddi_get_instance(dip), bus_p->bus_bdf, name, off, org, \
PCIE_AER_GET(sz, bus_p, off))
-extern void pcie_dbg(char *fmt, ...);
-
#else /* DEBUG */
#define PCIE_DBG_CFG 0 &&
#define PCIE_DBG 0 &&
+#define PCIE_ARI_DBG 0 &&
#define PCIE_DBG_CAP 0 &&
#define PCIE_DBG_AER 0 &&
#endif /* DEBUG */
/* PCIe Friendly Functions */
+extern int pcie_init(dev_info_t *dip, caddr_t arg);
+extern int pcie_uninit(dev_info_t *dip);
+extern int pcie_intr(dev_info_t *dip);
+extern int pcie_open(dev_info_t *dip, dev_t *devp, int flags, int otyp,
+ cred_t *credp);
+extern int pcie_close(dev_info_t *dip, dev_t dev, int flags, int otyp,
+ cred_t *credp);
+extern int pcie_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg,
+ int mode, cred_t *credp, int *rvalp);
+extern int pcie_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
+ int flags, char *name, caddr_t valuep, int *lengthp);
+
extern void pcie_init_root_port_mps(dev_info_t *dip);
extern int pcie_initchild(dev_info_t *dip);
extern void pcie_uninitchild(dev_info_t *dip);
@@ -422,6 +476,26 @@ extern void pcie_set_aer_suce_mask(uint32_t mask);
extern void pcie_set_serr_mask(uint32_t mask);
extern void pcie_init_plat(dev_info_t *dip);
extern void pcie_fini_plat(dev_info_t *dip);
+extern int pcie_read_only_probe(dev_info_t *, char *, dev_info_t **);
+extern dev_info_t *pcie_func_to_dip(dev_info_t *dip, pcie_req_id_t function);
+extern int pcie_ari_disable(dev_info_t *dip);
+extern int pcie_ari_enable(dev_info_t *dip);
+
+#define PCIE_ARI_FORW_NOT_SUPPORTED 0
+#define PCIE_ARI_FORW_SUPPORTED 1
+
+extern int pcie_ari_supported(dev_info_t *dip);
+
+#define PCIE_ARI_FORW_DISABLED 0
+#define PCIE_ARI_FORW_ENABLED 1
+
+extern int pcie_ari_is_enabled(dev_info_t *dip);
+
+#define PCIE_NOT_ARI_DEVICE 0
+#define PCIE_ARI_DEVICE 1
+
+extern int pcie_ari_device(dev_info_t *dip);
+extern int pcie_ari_get_next_function(dev_info_t *dip, int *func);
/* PCIe error handling functions */
extern int pf_scan_fabric(dev_info_t *rpdip, ddi_fm_error_t *derr,
@@ -432,6 +506,11 @@ extern int pf_hdl_lookup(dev_info_t *, uint64_t, uint32_t, uint64_t,
pcie_req_id_t);
extern int pf_tlp_decode(pcie_bus_t *, pf_pcie_adv_err_regs_t *);
+#ifdef DEBUG
+extern uint_t pcie_debug_flags;
+extern void pcie_dbg(char *fmt, ...);
+#endif /* DEBUG */
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/sys/sunndi.h b/usr/src/uts/common/sys/sunndi.h
index 22e61db408..f17ab18a7c 100644
--- a/usr/src/uts/common/sys/sunndi.h
+++ b/usr/src/uts/common/sys/sunndi.h
@@ -42,11 +42,15 @@ extern "C" {
#define NDI_SUCCESS DDI_SUCCESS /* successful return */
#define NDI_FAILURE DDI_FAILURE /* unsuccessful return */
-#define NDI_NOMEM -2 /* failed to allocate resources */
-#define NDI_BADHANDLE -3 /* bad handle passed to in function */
-#define NDI_FAULT -4 /* fault during copyin/copyout */
-#define NDI_BUSY -5 /* device busy - could not offline */
-#define NDI_UNBOUND -6 /* device not bound to a driver */
+#define NDI_NOMEM -2 /* failed to allocate resources */
+#define NDI_BADHANDLE -3 /* bad handle passed to in function */
+#define NDI_FAULT -4 /* fault during copyin/copyout */
+#define NDI_BUSY -5 /* device busy - could not offline */
+#define NDI_UNBOUND -6 /* device not bound to a driver */
+#define NDI_EINVAL -7 /* invalid request or arguments */
+#define NDI_ENOTSUP -8 /* operation or event not supported */
+#define NDI_CLAIMED NDI_SUCCESS /* event is claimed */
+#define NDI_UNCLAIMED -9 /* event is not claimed */
/*
* Property functions: See also, ddipropdefs.h.
@@ -636,6 +640,23 @@ int
ndi_busop_bus_unconfig(dev_info_t *dip, uint_t flags, ddi_bus_config_op_t op,
void *arg);
+/*
+ * Called by the Nexus/HPC drivers to register, unregister and interact
+ * with the hotplug framework for the specified hotplug connection.
+ */
+int
+ndi_hp_register(dev_info_t *dip, ddi_hp_cn_info_t *info_p);
+
+int
+ndi_hp_unregister(dev_info_t *dip, char *cn_name);
+
+int
+ndi_hp_state_change_req(dev_info_t *dip, char *cn_name,
+ ddi_hp_cn_state_t state, uint_t flag);
+
+void
+ndi_hp_walk_cn(dev_info_t *dip, int (*f)(ddi_hp_cn_info_t *, void *),
+ void *arg);
/*
* Bus Resource allocation structures and function prototypes exported
@@ -705,12 +726,9 @@ typedef struct ndi_ra_request {
#define NDI_RA_TYPE_PCI_PREFETCH_MEM "pci_prefetchable_memory"
#define NDI_RA_TYPE_INTR "interrupt"
-
-
/* flag bit definition */
#define NDI_RA_PASS 0x0001 /* pass request up the dev tree */
-
/*
* Prototype definitions for functions exported
*/
@@ -729,7 +747,6 @@ int
ndi_ra_free(dev_info_t *dip, uint64_t base, uint64_t len, char *type,
uint_t flag);
-
/*
* ndi_dev_is_prom_node: Return non-zero if the node is a prom node
*/